├── LICENSE
├── README.md
├── bbcrd-logo.png
├── qtff-parameter-editor.sh
└── src
├── Makefile
├── movdump.cpp
├── rdd36dump.c
└── rdd36mod.c
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # QuickTime File Format and ProRes Video Parameter Editing
4 |
5 | Part of the [HDR-TV](http://www.bbc.co.uk/rd/projects/high-dynamic-range) series. Last updated April 2017.
6 |
7 | # Introduction
8 |
9 | Several post-production tools and utilities are now aware of the colour and transfer function parameters specified in [ITU-R BT.2100-0](https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-0-201607-I!!PDF-E.pdf). However, some tools are unable to correctly signal the correct parameters, and may result in a file with the incorrect video parameters. Subsequent tools or displays may then look at these video parameters and render the image incorrectly, for instance, video that this signalled as [ITU-R BT.709](https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf) colour primaries and in fact is [ITU-R BT.2020](https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-2-201510-I!!PDF-E.pdf) colour primary and then displayed on a monitor will look desaturated when the display is interpreting the signalling contained within the file. Incorrect signalling may also result in unnecessary and incorrect transcoding between colour spaces and transfer functions.
10 |
11 | This document introduces a series of tools to allow editing of the colour primaries, colour matrix and transfer function characteristics in a [QuickTime File Format](https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html) (MOV) using a [ProRes](https://support.apple.com/en-gb/HT202410) video codec.
12 |
13 | # QuickTime File Format (qtff)
14 |
15 | The [QuickTime File Format](https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html) (qtff) is a container file supporting a wide range of video, audio and other data formats. The format itself is object-orientated, consisting of a collection of objects that can be parsed and expanded.
16 |
17 | The basic data unit is known as an Atom. The Atom that defines the relevant information required to define the colour primaries, colour matrix and transfer function are found in the "colr" data Atom, which is located inside the [Video Media](https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-74522) Atom. The structure of the "colr" Atom is as follows:
18 |
19 | | Colour Atom | Bytes |
20 | | -------------------------------| ------ |
21 | | Atom Size | 4 |
22 | | Type = "colr" | 4 |
23 | | Colour Parameter type = "nclc" | 4 |
24 | | Primary index = 1 | 2 |
25 | | Transfer Function index = 1 | 2 |
26 | | Colour Matrix index = 1 | 2 |
27 |
28 | # SMPTE RDD 36:2015 - Apple ProRes Bitstream Syntax and Decoding Process
29 |
30 | [SMPTE RDD 36](http://ieeexplore.ieee.org/document/7438722/) describes the syntax and decoding process for the [Apple ProRes](https://support.apple.com/en-gb/HT202410) video compression scheme. It is an intra-frame codec, specifically designed for high-quality workflows and supports a variety of video formats, and is common usage.
31 |
32 | In addition to the colour information carried within the Color Atom, information regarding the transfer function, colour matrix and primaries are also stored within the frame header information of the ProRes elementary stream, alongside other parameters, such as frame rate, spatial resolution and chroma format. This header is repeated throughout the bitstream. Full details of the header layout can be found in the [SMPTE specification](http://ieeexplore.ieee.org/document/7438722/).
33 |
34 | To avoid any ambiguity in any workflows, it is imperative that the the information in the header for the ProRes stream match that of the qtff colr Atom.
35 |
36 | # Video Characteristics
37 |
38 | The colour primaries can be selected from the list:
39 |
40 | | No. | Colour Primaries |
41 | | -----| -------------- |
42 | |0 | Reserved |
43 | |1 | ITU-R BT.709 |
44 | |2 | Unspecified |
45 | |3 | Reserved |
46 | |4 | ITU-R BT.470M |
47 | |5 | ITU-R BT.470BG |
48 | |6 | SMPTE 170M |
49 | |7 | SMPTE 240M |
50 | |8 | FILM |
51 | |9 | ITU-R BT.2020 |
52 | |10 | SMPTE ST 428-1 |
53 | |11 | DCI P3 |
54 | |12 | P3 D65 |
55 |
56 | The transfer function can be selected from the list:
57 |
58 | | No. | Transfer Function |
59 | | -----| --------------------------------- |
60 | |0 | Reserved |
61 | |1 | ITU-R BT.709 |
62 | |2 | Unspecified |
63 | |3 | Reserved |
64 | |4 | Gamma 2.2 curve |
65 | |5 | Gamma 2.8 curve |
66 | |6 | SMPTE 170M |
67 | |7 | SMPTE 240M |
68 | |8 | Linear |
69 | |9 | Log |
70 | |10 | Log Sqrt |
71 | |11 | IEC 61966-2-4 |
72 | |12 | ITU-R BT.1361 Extended Colour Gamut |
73 | |13 | IEC 61966-2-1 |
74 | |14 | ITU-R BT.2020 10 bit |
75 | |15 | ITU-R BT.2020 12 bit |
76 | |16 | SMPTE ST 2084 (PQ) |
77 | |17 | SMPTE ST 428-1 |
78 | |18 | ARIB STD-B67 (HLG) |
79 |
80 | The colour matrix can be selected from the list:
81 |
82 | | No. | Colour Matrix |
83 | | -----| --------------------------- |
84 | |0 |GBR |
85 | |1 |BT709 |
86 | |2 |Unspecified |
87 | |3 |Reserved |
88 | |4 |FCC |
89 | |5 |BT470BG |
90 | |6 |SMPTE 170M |
91 | |7 |SMPTE 240M |
92 | |8 |YCOCG |
93 | |9 |BT2020 Non-constant Luminance |
94 | |10 |BT2020 Constant Luminance |
95 |
96 | # Tools
97 |
98 | This repository contains a number of tools that will aid in analysing a video file to obtain the video characteristics, and subsequently allow modification of the qttv container and ProRes bitstream to alter the characteristics
99 |
100 | ## Getting Started
101 |
102 | ### Prerequisites
103 |
104 | [ffprobe](https://ffmpeg.org/ffprobe.html) is required to find and extract the location of the frame headers from the ProRes bitstream. Downloads can be found [here](https://ffmpeg.org/download.html) for your OS. Static builds for [Linux](https://ffmpeg.org/download.html#build-linux) are available if building from source is not an option. Static builds for Windows and OSX are available via third party websites. Once obtained, ffprobe must be put into the `PATH`.
105 |
106 | [MediaInfo](https://mediaarea.net/en/MediaInfo) is a convenient unified display of the most relevant technical and tag data for video and audio files. It is useful to check the accuracy of the tools provided within this repository.
107 |
108 | Standard developer tools (gcc/g++, make, bash) will be required to build and run the code.
109 |
110 | ### Building the code
111 |
112 | A makefile has been provided to build the code.
113 |
114 | ```
115 | cd src
116 | make
117 | ```
118 | The repository contains 3 main programs:
119 |
120 | * `movdump` - This tool creates a text dump of the header data at the qtff (mov) level.
121 | * `rdd36dump` - This tool creates a text dump of the header data at the ProRes level.
122 | * `rdd36mod` - This tool modifies the ProRes data to adjust the transfer function, colour primaries and matrix.
123 |
124 | ### Running the code
125 |
126 | The programs above can be run individually as follows:
127 |
128 | `movdump ipFile > movdump.txt`
129 |
130 | Where the output movdump.txt will look something like this:
131 |
132 | ```
133 | mdat: s= 5884636816 (0x000000015ec06e90), o= 0 (0x00000000)
134 | ...skipped 5884636800 bytes
135 | free: s= 24332 (0x00005f0c), o= 5884636816 (0x000000015ec06e90)
136 | ...skipped 24324 bytes
137 | wide: s= 8 (0x00000008), o= 5884661148 (0x000000015ec0cd9c)
138 | mdat: s= 12 (0x0000000c), o= 5884661156 (0x000000015ec0cda4)
139 | ...skipped 4 bytes
140 | moov: s= 24938 (0x0000616a), o= 5884661168 (0x000000015ec0cdb0)
141 | mvhd: s= 108 (0x0000006c), o= 5884661176 (0x000000015ec0cdb8)
142 | version: 0
143 | flags: 0x000000
144 | ...
145 | ...
146 | ```
147 |
148 | To run `rdd36dump` and `rdd36mod`, the location of the headers must first be located with `ffprobe`.
149 |
150 | `ffprobe -loglevel panic -show_packets -select_streams v:0 ipFile.mov | grep pos > header_offsets.txt`
151 |
152 | The header_offsets.txt can then be provided to `rdddump`
153 |
154 | `rdd36dump --offsets header_offsets.txt ipFile.mov > rdd36dump.txt`
155 |
156 | Where the output rdd36dump.txt will look something like this:
157 |
158 | ```
159 | frame: num=0, pos=96256
160 | frame_size: 221808
161 | frame_identifier: 0x69637066 (icpf)
162 | frame_header:
163 | frame_header_size: 148
164 | reserved: 0x00
165 | bitstream_version: 0
166 | encoder_identifier: 0x61626d30 (abm0)
167 | horizontal_size: 3840
168 | vertical_size: 2160
169 | chroma_format: 2 (4:2:2)
170 | ...
171 | ...
172 | ```
173 |
174 | ### Modifying the video characteristics
175 |
176 | Using the tools above the transfer function, colour primaries and matrix can be edited using the binary offset information in the dump files. Alternatively, a script has been prepared that does it all for you.
177 | The help from the bash script describes its usage:
178 |
179 | ```
180 |
181 | qtff-parameter-editor.sh
182 |
183 | This script can be used to edit the primaries, transfer functions and matrix
184 | fields in a .mov file, it can be extended for further editting if required."
185 |
186 | Usage:
187 | qtff-parameter-editor.sh [--help] [-h] [-p priValue]
188 | [-t tfValue]
189 | [-m matValue]
190 | InputFile
191 | OutputFile
192 | or
193 | qtff-parameter-editor.sh InputFile
194 |
195 | Where:
196 | -h, --help : Displays this help page
197 | -p --primaries priValue: Where priValue is the required primaries value number
198 | -t --tf tfValue: Where tfValue is the required transfer function value number
199 | -m --matrix matValue: Where matValue is the required matrix function value number
200 | InputFile: Source mov file
201 | OutputFile: Output mov file (Can be the same as the Input file)
202 |
203 | If only an Input file is provided (as in the second example, the script will return
204 | the information for the input file
205 |
206 | Example 1: ./qtff-parameter-editor.sh input.mov
207 | Return video parameter information for input.mov
208 |
209 | Example 2: ./qtff-parameter-editor.sh -p 1 -t 1 -m 1 input.mov output.mov
210 | Will create a copy with input.mov and modify the video parameters to bt709
211 | primaries, transfer function and matrix.
212 |
213 | Example 3: ./qtff-parameter-editor.sh --primaries 9 --tf 18 --matrix 9 input.mov input.mov
214 | Will modify the video parameters in situ to bt2020 primaries, HLG transfer
215 | function and bt2020 non constant luminance matrix
216 |
217 |
218 | ```
219 |
220 | If a colr Atom is not present in the video file, the process will not attempt to insert one, and will quit the processing.
221 |
222 | # Resources
223 |
224 | Please visit our [project page](http://www.bbc.co.uk/rd/projects/high-dynamic-range) for more information about High Dynamic Range (HDR) and Hybrid Log-Gamma (HLG).
225 |
226 | # Authors
227 |
228 | This software was written by Philip de Nier (philip.denier at bbc.co.uk) and Manish Pindoria.
229 |
230 | # Contact and Legal Information
231 |
232 | Copyright 2009-2017 British Broadcasting Corporation
233 |
234 | The qtff-parameter-editor is free software; you can redistribute it and/or modify it under the terms of license agreement.
235 |
236 | The qtff-parameter-editor 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.
237 |
--------------------------------------------------------------------------------
/bbcrd-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bbc/qtff-parameter-editor/cf82d7f4a40d49179b27d02a9abb4e80f003956c/bbcrd-logo.png
--------------------------------------------------------------------------------
/qtff-parameter-editor.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ##
3 | ## Copyright (C) 2017, British Broadcasting Corporation
4 | ## All Rights Reserved.
5 | ##
6 | ## Author: Manish Pindoria (manish.pindoria@bbc.co.uk)
7 | ## Philip de Nier (philipn@rd.bbc.co.uk)
8 | ##
9 | ## Redistribution and use in source and binary forms, with or without
10 | ## modification, are permitted provided that the following conditions are met:
11 | ##
12 | ## * Redistributions of source code must retain the above copyright notice,
13 | ## this list of conditions and the following disclaimer.
14 | ## * Redistributions in binary form must reproduce the above copyright
15 | ## notice, this list of conditions and the following disclaimer in the
16 | ## documentation and/or other materials provided with the distribution.
17 | ## * Neither the name of the British Broadcasting Corporation nor the names
18 | ## of its contributors may be used to endorse or promote products derived
19 | ## from this software without specific prior written permission.
20 | ##
21 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | ## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 | ## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 | ## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 | ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 | ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 | ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 | ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 | ## POSSIBILITY OF SUCH DAMAGE.
32 | ##
33 |
34 | ffprobe_exe=""
35 |
36 | #define the tabels as globals
37 | primaries=("Reserved"
38 | "ITU-R BT.709"
39 | "Unspecified"
40 | "Reserved"
41 | "ITU-R BT.470M"
42 | "ITU-R BT.470BG"
43 | "SMPTE 170M"
44 | "SMPTE 240M"
45 | "FILM"
46 | "ITU-R BT.2020"
47 | "SMPTE ST 428-1"
48 | "DCI P3"
49 | "P3 D65")
50 |
51 |
52 |
53 | ###########################################################################################
54 |
55 | tf=("Reserved"
56 | "ITU-R BT.709"
57 | "Unspecified"
58 | "Reserved"
59 | "Gamma 2.2 curve"
60 | "Gamma 2.8 curve"
61 | "SMPTE 170M"
62 | "SMPTE 240M"
63 | "Linear"
64 | "Log"
65 | "Log Sqrt"
66 | "IEC 61966-2-4"
67 | "ITU-R BT.1361 Extended Colour Gamut"
68 | "IEC 61966-2-1"
69 | "ITU-R BT.2020 10 bit"
70 | "ITU-R BT.2020 12 bit"
71 | "SMPTE ST 2084 (PQ)"
72 | "SMPTE ST 428-1"
73 | "ARIB STD-B67 (HLG)")
74 |
75 |
76 | ###########################################################################################
77 |
78 |
79 | matrix=("GBR"
80 | "BT709"
81 | "Unspecified"
82 | "Reserved"
83 | "FCC"
84 | "BT470BG"
85 | "SMPTE 170M"
86 | "SMPTE 240M"
87 | "YCOCG"
88 | "BT2020 Non-constant Luminance"
89 | "BT2020 Constant Luminance)")
90 |
91 |
92 | ###########################################################################################
93 |
94 |
95 | printPrimariesTable()
96 | {
97 | echo "The colour primaries of the video. For clarity, the value and meanings"
98 | echo "for Primaries are adopted from Table 2 of ISO/IEC 23001-8:2013/DCOR1."
99 |
100 | for i in $(seq 0 10)
101 | do
102 | echo ${i}: ${primaries[${i}]}
103 | done
104 |
105 | echo
106 | }
107 |
108 |
109 | ###########################################################################################
110 |
111 |
112 | printTFTable()
113 | {
114 | echo "TransferCharacteristics, 5bits, The transfer characteristics of the video."
115 | echo "For clarity, the value and meanings for TransferCharacteristics"
116 | echo "1-15 are adopted from Table 3 of ISO/IEC 23001-8:2013/DCOR1."
117 | echo "TransferCharacteristics 16-18 are proposed values."
118 |
119 | for i in $(seq 0 18)
120 | do
121 | echo ${i}: ${tf[${i}]}
122 | done
123 |
124 | echo
125 | }
126 |
127 |
128 | ###########################################################################################
129 |
130 |
131 | printMatrixTable()
132 | {
133 | echo "The Matrix Coefficients of the video used to derive luma and chroma values"
134 | echo "from reg, green, and blue color primaries."
135 | echo "For clarity, the value and meanings for MatrixCoefficients are adopted from"
136 | echo "Table 4 of ISO/IEC 23001-8:2013/DCOR1."
137 |
138 | for i in $(seq 0 10)
139 | do
140 | echo ${i}: ${matrix[${i}]}
141 | done
142 | }
143 |
144 |
145 | ###########################################################################################
146 |
147 |
148 | outputHelp()
149 | {
150 | echo
151 | echo "qtff-parameter-editor.sh"
152 | echo
153 | echo "This script can be used to edit the primaries, transfer functions and matrix"
154 | echo "fields in a .mov file, it can be extended for further editting if required."
155 | echo
156 | echo " Usage:"
157 | echo " qtff-parameter-editor.sh [--help] [-h] [-p priValue] "
158 | echo " [-t tfValue] "
159 | echo " [-m matValue] "
160 | echo " InputFile"
161 | echo " OutputFile"
162 | echo " or "
163 | echo " qtff-parameter-editor.sh InputFile"
164 | echo
165 | echo " Where:"
166 | echo " -h, --help : Displays this help page"
167 | echo " -p --primaries priValue: Where priValue is the required primaries value number"
168 | echo " -t --tf tfValue: Where tfValue is the required transfer function value number"
169 | echo " -m --matrix matValue: Where matValue is the required matrix function value number"
170 | echo " InputFile: Source mov file"
171 | echo " OutputFile: Output mov file (Can be the same as the Input file)"
172 | echo
173 | echo "If only an Input file is provided (as in the second example, the script will return "
174 | echo "the information for the input file"
175 | echo
176 | echo "Example 1: ./qtff-parameter-editor.sh input.mov"
177 | echo " Return video parameter information for input.mov"
178 | echo
179 | echo "Example 2: ./qtff-parameter-editor.sh -p 1 -t 1 -m 1 input.mov output.mov "
180 | echo " Will create a copy with input.mov and modify the video parameters to bt709 "
181 | echo " primaries, transfer function and matrix."
182 | echo
183 | echo "Example 3: ./qtff-parameter-editor.sh --primaries 9 --tf 18 --matrix 9 input.mov input.mov "
184 | echo " Will modify the video parameters in situ to bt2020 primaries, HLG transfer "
185 | echo " function and bt2020 non constant luminance matrix"
186 |
187 | echo -e "\n\nThe values for the options are given by:\n\n"
188 | printPrimariesTable
189 | printTFTable
190 | printMatrixTable
191 | exit
192 | }
193 |
194 |
195 | ###########################################################################################
196 |
197 |
198 |
199 | outputInfoMovWrapper()
200 | {
201 | ipFile=$1
202 | ${dir}/src/movdump $ipFile > ${ipFile%.mov}.dump
203 | colrline=$(grep -hnr "colr:" ${ipFile%.mov}.dump)
204 | colrline=${colrline%%:*}
205 |
206 | if [ "$colrline" == "" ]
207 | then
208 | echo "colr atom not located, unable to display any further information or make any edits"
209 | exit
210 | else
211 | head -n $(($colrline+4)) ${ipFile%.mov}.dump | tail -n 5 | sed -e 's/^[[:space:]]*//' > ${ipFile%.mov}_useful.dump
212 | thisPrimary=$(grep "primaries" ${ipFile%.mov}_useful.dump)
213 | thistf=$(grep "transfer" ${ipFile%.mov}_useful.dump)
214 | thismatrix=$(grep "matrix" ${ipFile%.mov}_useful.dump)
215 |
216 | thisPrimary=${thisPrimary#*:}
217 | thistf=${thistf#*:}
218 | thismatrix=${thismatrix#*:}
219 | thisPrimary=$(echo ${thisPrimary} | tr -d '[:space:]')
220 | thistf=$(echo ${thistf} | tr -d '[:space:]')
221 | thismatrix=$(echo ${thismatrix} | tr -d '[:space:]')
222 |
223 | echo
224 | echo "File = $ipFile"
225 | echo
226 | echo "MOV Wrapper Information (COLR atom)"
227 | echo "Primary = $thisPrimary (${primaries[${thisPrimary}]})"
228 | echo "Transfer Function = $thistf (${tf[${thistf}]})"
229 | echo "Matrix = $thismatrix (${matrix[${thismatrix}]})"
230 | echo
231 |
232 |
233 | fi
234 | }
235 |
236 |
237 | ###########################################################################################
238 |
239 |
240 | outputInfoProRes()
241 | {
242 | ipFile=$1
243 | leaf=${ipFile%.mov}
244 | ${ffprobe_exe} -loglevel panic -show_packets -select_streams v:0 ${ipFile} | grep pos > ${leaf}_offsets.txt
245 | ${dir}/src/rdd36mod -s -o ${leaf}_offsets.txt ${ipFile} > ${leaf}_rdd36mod.txt 2>&1
246 |
247 | #cat ${leaf}_rdd36mod.txt
248 | errorline=$(grep "failed" ${leaf}_rdd36mod.txt)
249 | if [ "$errorline" == "" ]
250 | then
251 |
252 | thisPrimary=$(grep "primaries" ${leaf}_rdd36mod.txt)
253 | thistf=$(grep "transfer" ${leaf}_rdd36mod.txt)
254 | thismatrix=$(grep "matrix" ${leaf}_rdd36mod.txt)
255 |
256 | thisPrimary=${thisPrimary#*:}
257 | thistf=${thistf#*:}
258 | thismatrix=${thismatrix#*:}
259 | thisPrimary=$(echo ${thisPrimary} | tr -d '[:space:]')
260 | thistf=$(echo ${thistf} | tr -d '[:space:]')
261 | thismatrix=$(echo ${thismatrix} | tr -d '[:space:]')
262 |
263 | echo
264 | echo "SMPTE RDD 36 (Apple ProRes) Information"
265 | echo "Primary = $thisPrimary (${primaries[${thisPrimary}]})"
266 | echo "Transfer Function = $thistf (${tf[${thistf}]})"
267 | echo "Matrix = $thismatrix (${matrix[${thismatrix}]})"
268 | echo
269 |
270 | else
271 | echo "ProRes header not found, unable to edit this file"
272 | exit
273 | fi
274 | }
275 |
276 |
277 | ###########################################################################################
278 |
279 |
280 |
281 | cleanup()
282 | {
283 | ipFile=$1
284 | leaf=${ipFile%.mov}
285 | rm -f ${leaf}_useful.dump
286 | rm -f ${leaf}.dump
287 | rm -f ${leaf}_rdd36mod.txt
288 | rm -f ${leaf}_offsets.txt
289 | }
290 |
291 |
292 | ###########################################################################################
293 |
294 |
295 |
296 | cloneMovAndModify()
297 | {
298 | ipFile=$1
299 | opFile=$2
300 | newPrim=$3
301 | newTF=$4
302 | newMatrix=$5
303 |
304 | leaf=${ipFile%.mov}
305 |
306 | outputInfoMovWrapper $ipFile
307 | outputInfoProRes $ipFile
308 |
309 | if [ "$ipFile" = "$opFile" ]
310 | then
311 | echo "Output file is the same as Input, are you sure you want to continue,"
312 | echo "type yes or no, followed by [ENTER] "
313 | read response
314 | if [ "$response" = "yes" ]
315 | then
316 | echo "Adjusting file in situ ..."
317 | else
318 | echo "Not updating file ..."
319 | echo "Exiting"
320 | cleanup $ipFile
321 | exit
322 | fi
323 |
324 | echo "Processing with adjustment ..."
325 | else
326 | echo "Cloning ..."
327 | pvavail=$(command -v pv)
328 | if [[ -n "${pvavail/[ ]*\n/}" ]]
329 | then
330 | pv $ipFile > $opFile
331 | else
332 | cp -v $ipFile $opFile
333 | fi
334 | fi
335 |
336 | #get the offset
337 | offset=$(grep -hr "colr" ${ipFile%.mov}_useful.dump)
338 | offset=${offset#*o=}
339 | offset=${offset%(*}
340 | offset=$(echo ${offset} | tr -d '[:space:]')
341 |
342 | if [ "$newPrim" != "-1" ]
343 | then
344 | echo "Modifying the primary ..."
345 | primhex=$(printf "%02x" $newPrim)
346 | printf "\x${primhex}" | dd conv=notrunc of=$opFile bs=1 seek=$(($offset + 13)) &> /dev/null
347 | ${dir}/src/rdd36mod -o ${leaf}_offsets.txt -p $newPrim ${opFile}
348 | fi
349 |
350 | if [ "$newTF" != "-1" ]
351 | then
352 | echo "Modifying the transfer function ..."
353 | tfhex=$(printf "%02x" $newTF)
354 | printf "\x${tfhex}" | dd conv=notrunc of=$opFile bs=1 seek=$(($offset + 15)) &> /dev/null
355 | ${dir}/src/rdd36mod -o ${leaf}_offsets.txt -t $newTF ${opFile}
356 | fi
357 |
358 | if [ "$newMatrix" != "-1" ]
359 | then
360 | echo "Modifying the matrix ..."
361 | matrixhex=$(printf "%02x" $newMatrix)
362 | printf "\x${matrixhex}" | dd conv=notrunc of=$opFile bs=1 seek=$(($offset + 17)) &> /dev/null
363 | ${dir}/src/rdd36mod -o ${leaf}_offsets.txt -m $newMatrix ${opFile}
364 | fi
365 |
366 | outputInfoMovWrapper $opFile
367 | outputInfoProRes $opFile
368 | }
369 |
370 |
371 | ###########################################################################################
372 |
373 |
374 | checkffprobe()
375 | {
376 | if type ffprobe 2>/dev/null
377 | then
378 | echo "Found ffprobe in the PATH"
379 | ffprobe_exe=$(which ffprobe)
380 | else
381 | echo "ffprobe not found in the PATH,"
382 | echo "Please provide the location of ffprobe, followed by [ENTER] "
383 | read ffprobe_exe
384 | if type ${ffprobe_exe} >/dev/null 2>&1
385 | then
386 | echo "Found ffprobe in the location ${ffprobe_exe}"
387 | else
388 | echo "ffprobe not found, it is required"
389 | echo "Exiting"
390 | exit
391 | fi
392 | fi
393 | }
394 |
395 |
396 | ###########################################################################################
397 |
398 | dir=$(dirname $0)
399 |
400 | if [ "$#" = "0" ]
401 | then
402 | outputHelp
403 | elif [ "$#" = "1" ]
404 | then
405 | if [ $1 == "-h" ] || [ $1 == "--help" ]
406 | then
407 | outputHelp
408 | else
409 | checkffprobe
410 | outputInfoMovWrapper $1
411 | outputInfoProRes $1
412 | cleanup $1
413 | fi
414 | else
415 | newPrim=-1
416 | newTF=-1
417 | newMatrix=-1
418 | while [ "$#" != "2" ]
419 | do
420 | if [ "$1" == "--primaries" ] || [ "$1" == "-p" ]
421 | then
422 | newPrim=$2
423 | shift 2
424 | elif [ "$1" == "--tf" ] || [ "$1" == "-t" ]
425 | then
426 | newTF=$2
427 | shift 2
428 | elif [ "$1" == "--matrix" ] || [ "$1" == "-m" ]
429 | then
430 | newMatrix=$2
431 | shift 2
432 | else
433 | echo "Unknown option $1 - exiting"
434 | exit
435 | fi
436 | done
437 |
438 | checkffprobe
439 | cloneMovAndModify $1 $2 $newPrim $newTF $newMatrix
440 | cleanup $1
441 | cleanup $2
442 | fi
443 |
444 |
445 | ###########################################################################################
446 |
447 |
448 |
449 |
450 |
451 |
452 |
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
2 |
3 | .PHONY: all
4 | all: rdd36dump rdd36mod movdump
5 |
6 | rdd36dump: rdd36dump.o
7 | gcc $< -o $@
8 |
9 | rdd36dump.o: rdd36dump.c
10 | gcc -c ${CFLAGS} $< -o $@
11 |
12 | rdd36mod: rdd36mod.o
13 | gcc $< -o $@
14 |
15 | rdd36mod.o: rdd36mod.c
16 | gcc -c ${CFLAGS} $< -o $@
17 |
18 | movdump: movdump.o
19 | g++ $< -o $@
20 |
21 | movdump.o: movdump.cpp
22 | g++ -c ${CFLAGS} $< -o $@
23 |
24 | .PHONY: clean
25 | clean:
26 | @rm -f rdd36dump.o rdd36mod.o movdump.o rdd36dump rdd36mod movdump
27 |
--------------------------------------------------------------------------------
/src/movdump.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017, British Broadcasting Corporation
3 | * All Rights Reserved.
4 | *
5 | * Author: Philip de Nier
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice,
11 | * this list of conditions and the following disclaimer.
12 | * * Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | * * Neither the name of the British Broadcasting Corporation nor the names
16 | * of its contributors may be used to endorse or promote products derived
17 | * from this software without specific prior written permission.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | * POSSIBILITY OF SUCH DAMAGE.
30 | */
31 |
32 | #ifdef HAVE_CONFIG_H
33 | #include "config.h"
34 | #endif
35 |
36 | #define __STDC_FORMAT_MACROS
37 | #define __STDC_LIMIT_MACROS
38 |
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #ifdef HAVE_UNISTD_H
50 | #include
51 | #endif
52 | #include
53 |
54 | #include
55 | #include
56 | #include
57 |
58 | using namespace std;
59 |
60 |
61 |
62 | #define MOV_CHECK(cmd) \
63 | if (!(cmd)) { \
64 | throw MOVException("%s failed at line %d", #cmd, __LINE__); \
65 | }
66 |
67 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
68 |
69 | #define MKTAG(cs) ((((uint32_t)cs[0])<<24)|(((uint32_t)cs[1])<<16)|(((uint32_t)cs[2])<<8)|(((uint32_t)cs[3])))
70 |
71 | #define ATOM_INDENT " "
72 | #define ATOM_VALUE_INDENT " "
73 |
74 | #define CURRENT_ATOM g_atoms.back()
75 | #define HAVE_PREV_ATOM (g_atoms.size() >= 2)
76 | #define PREV_ATOM g_atoms[g_atoms.size() - 2]
77 |
78 | #define DUMP_FUNC_MAP_SIZE (sizeof(dump_func_map) / sizeof(DumpFuncMap))
79 |
80 |
81 |
82 | typedef struct
83 | {
84 | uint64_t size;
85 | char type[4];
86 | uint64_t rem_size;
87 | uint64_t offset;
88 | } MOVAtom;
89 |
90 | typedef struct
91 | {
92 | char type[4];
93 | void (*dump)();
94 | } DumpFuncMap;
95 |
96 |
97 | static const uint32_t MHLR_COMPONENT_TYPE = MKTAG("mhlr");
98 | static const uint32_t VIDE_COMPONENT_SUB_TYPE = MKTAG("vide");
99 | static const uint32_t SOUN_COMPONENT_SUB_TYPE = MKTAG("soun");
100 | static const uint32_t TMCD_COMPONENT_SUB_TYPE = MKTAG("tmcd");
101 |
102 |
103 | static FILE *g_mov_file = 0;
104 | static vector g_atoms;
105 | static uint64_t g_file_offset;
106 | static vector g_meta_keys;
107 | static uint32_t g_movie_timescale;
108 | static uint32_t g_component_type = 0;
109 | static uint32_t g_component_sub_type = 0;
110 | static bool g_qt_brand = true;
111 | static const char *g_avcc_filename = 0;
112 | static FILE *g_avcc_file = 0;
113 | static int mp4_object_desc_level = 0;
114 |
115 |
116 | class MOVException : public std::exception
117 | {
118 | public:
119 | MOVException()
120 | : exception()
121 | {
122 | }
123 |
124 | MOVException(const char *format, ...)
125 | : exception()
126 | {
127 | char message[1024];
128 |
129 | va_list varg;
130 | va_start(varg, format);
131 | #if defined(_MSC_VER)
132 | int res = _vsnprintf(message, sizeof(message), format, varg);
133 | if (res == -1 && errno == EINVAL)
134 | message[0] = 0;
135 | else
136 | message[sizeof(message) - 1] = 0;
137 | #else
138 | if (vsnprintf(message, sizeof(message), format, varg) < 0)
139 | message[0] = 0;
140 | #endif
141 | va_end(varg);
142 |
143 | mMessage = message;
144 | }
145 |
146 | MOVException(const string &message)
147 | : exception()
148 | {
149 | mMessage = message;
150 | }
151 |
152 | ~MOVException() throw()
153 | {
154 | }
155 |
156 | const char* what() const throw()
157 | {
158 | return mMessage.c_str();
159 | }
160 |
161 | protected:
162 | string mMessage;
163 | };
164 |
165 |
166 | static uint32_t dump_mp4_object_descriptor(uint32_t length);
167 |
168 |
169 | static bool equals_type(const char *left, const char *right)
170 | {
171 | return memcmp(left, right, 4) == 0;
172 | }
173 |
174 | static void update_atom_read(uint64_t num_read)
175 | {
176 | MOV_CHECK(!g_atoms.empty());
177 | MOV_CHECK(num_read <= CURRENT_ATOM.rem_size);
178 | CURRENT_ATOM.rem_size -= num_read;
179 |
180 | g_file_offset += num_read;
181 | }
182 |
183 | static void skip_bytes(uint64_t num_bytes)
184 | {
185 | #if defined(_WIN32)
186 | MOV_CHECK(_fseeki64(g_mov_file, num_bytes, SEEK_CUR) == 0);
187 | #else
188 | MOV_CHECK(fseeko(g_mov_file, num_bytes, SEEK_CUR) == 0);
189 | #endif
190 | update_atom_read(num_bytes);
191 | }
192 |
193 | static void push_atom()
194 | {
195 | MOVAtom atom;
196 | memset(&atom, 0, sizeof(atom));
197 | g_atoms.push_back(atom);
198 | }
199 |
200 | static void pop_atom()
201 | {
202 | MOV_CHECK(!g_atoms.empty());
203 | MOV_CHECK(CURRENT_ATOM.rem_size == 0);
204 |
205 | if (g_atoms.size() > 1)
206 | PREV_ATOM.rem_size -= CURRENT_ATOM.size;
207 |
208 | g_atoms.pop_back();
209 | }
210 |
211 | static bool read_bytes(unsigned char *bytes, uint32_t size)
212 | {
213 | if (fread(bytes, 1, size, g_mov_file) != size) {
214 | if (ferror(g_mov_file))
215 | throw MOVException("Failed to read bytes: %s", strerror(errno));
216 | return false;
217 | }
218 |
219 | update_atom_read(size);
220 | return true;
221 | }
222 |
223 | static bool read_uint64(uint64_t *value)
224 | {
225 | unsigned char bytes[8];
226 | if (!read_bytes(bytes, 8))
227 | return false;
228 |
229 | *value = (((uint64_t)bytes[0]) << 56) |
230 | (((uint64_t)bytes[1]) << 48) |
231 | (((uint64_t)bytes[2]) << 40) |
232 | (((uint64_t)bytes[3]) << 32) |
233 | (((uint64_t)bytes[4]) << 24) |
234 | (((uint64_t)bytes[5]) << 16) |
235 | (((uint64_t)bytes[6]) << 8) |
236 | (uint64_t)bytes[7];
237 |
238 | return true;
239 | }
240 |
241 | static bool read_int64(int64_t *value)
242 | {
243 | uint64_t uvalue;
244 | if (!read_uint64(&uvalue))
245 | return false;
246 |
247 | *value = (int64_t)uvalue;
248 | return true;
249 | }
250 |
251 | static bool read_uint32(uint32_t *value)
252 | {
253 | unsigned char bytes[4];
254 | if (!read_bytes(bytes, 4))
255 | return false;
256 |
257 | *value = (((uint32_t)bytes[0]) << 24) |
258 | (((uint32_t)bytes[1]) << 16) |
259 | (((uint32_t)bytes[2]) << 8) |
260 | (uint32_t)bytes[3];
261 |
262 | return true;
263 | }
264 |
265 | static bool read_int32(int32_t *value)
266 | {
267 | uint32_t uvalue;
268 | if (!read_uint32(&uvalue))
269 | return false;
270 |
271 | *value = (int32_t)uvalue;
272 | return true;
273 | }
274 |
275 | static bool read_uint24(uint32_t *value)
276 | {
277 | unsigned char bytes[3];
278 | if (!read_bytes(bytes, 3))
279 | return false;
280 |
281 | *value = (((uint32_t)bytes[0]) << 16) |
282 | (((uint32_t)bytes[1]) << 8) |
283 | (uint32_t)bytes[2];
284 |
285 | return true;
286 | }
287 |
288 | static bool read_int24(int32_t *value)
289 | {
290 | uint32_t uvalue;
291 | if (!read_uint24(&uvalue))
292 | return false;
293 |
294 | *value = (int32_t)uvalue;
295 | return true;
296 | }
297 |
298 | static bool read_uint16(uint16_t *value)
299 | {
300 | unsigned char bytes[2];
301 | if (!read_bytes(bytes, 2))
302 | return false;
303 |
304 | *value = (((uint16_t)bytes[0]) << 8) |
305 | (uint16_t)bytes[1];
306 |
307 | return true;
308 | }
309 |
310 | static bool read_int16(int16_t *value)
311 | {
312 | uint16_t uvalue;
313 | if (!read_uint16(&uvalue))
314 | return false;
315 |
316 | *value = (int16_t)uvalue;
317 | return true;
318 | }
319 |
320 | static bool read_uint8(uint8_t *value)
321 | {
322 | unsigned char bytes[1];
323 | if (!read_bytes(bytes, 1))
324 | return false;
325 |
326 | *value = bytes[0];
327 |
328 | return true;
329 | }
330 |
331 | static bool read_int8(int8_t *value)
332 | {
333 | uint8_t uvalue;
334 | if (!read_uint8(&uvalue))
335 | return false;
336 |
337 | *value = (int8_t)uvalue;
338 | return true;
339 | }
340 |
341 | static void write_avcc_ps(unsigned char **buffer, size_t *buffer_size, uint8_t length_size, uint16_t ps_size)
342 | {
343 | if (!g_avcc_file) {
344 | g_avcc_file = fopen(g_avcc_filename, "wb");
345 | if (!g_avcc_file)
346 | throw MOVException("Failed to open avcc file '%s': %s", g_avcc_filename, strerror(errno));
347 | }
348 |
349 | unsigned char length_bytes[4];
350 | if (length_size == 1) {
351 | length_bytes[0] = (unsigned char)(ps_size & 0xff);
352 | } else if (length_size == 2) {
353 | length_bytes[0] = (unsigned char)((ps_size >> 8) & 0xff);
354 | length_bytes[1] = (unsigned char)( ps_size & 0xff);
355 | } else if (length_size == 3) {
356 | length_bytes[0] = 0; // ps_size is uint16_t
357 | length_bytes[1] = (unsigned char)((ps_size >> 8) & 0xff);
358 | length_bytes[2] = (unsigned char)( ps_size & 0xff);
359 | } else { // length_size == 4
360 | length_bytes[0] = 0; // ps_size is uint16_t
361 | length_bytes[1] = 0; // ps_size is uint16_t
362 | length_bytes[2] = (unsigned char)((ps_size >> 8) & 0xff);
363 | length_bytes[3] = (unsigned char)( ps_size & 0xff);
364 | }
365 | MOV_CHECK(fwrite(length_bytes, 1, length_size, g_avcc_file) == length_size);
366 |
367 | if (ps_size > 0) {
368 | if ((*buffer_size) < ps_size) {
369 | size_t new_buffer_size = (ps_size + 255) & ~255;
370 | void *new_buffer = realloc((*buffer), new_buffer_size);
371 | if (!new_buffer)
372 | throw MOVException("Failed to allocate buffer");
373 | *buffer = (unsigned char*)new_buffer;
374 | *buffer_size = new_buffer_size;
375 | }
376 | MOV_CHECK(read_bytes(*buffer, ps_size));
377 | MOV_CHECK(fwrite(*buffer, 1, ps_size, g_avcc_file) == ps_size);
378 | }
379 | }
380 |
381 | static bool read_type(char *type)
382 | {
383 | uint32_t value;
384 | if (!read_uint32(&value))
385 | return false;
386 |
387 | type[0] = (char)((value >> 24) & 0xff);
388 | type[1] = (char)((value >> 16) & 0xff);
389 | type[2] = (char)((value >> 8) & 0xff);
390 | type[3] = (char)(value & 0xff);
391 |
392 | return true;
393 | }
394 |
395 | static bool read_atom_start()
396 | {
397 | MOVAtom &atom = CURRENT_ATOM;
398 | atom.size = 8;
399 | atom.rem_size = 8;
400 | atom.offset = g_file_offset;
401 |
402 | uint32_t uint32_size;
403 | uint64_t uint64_size;
404 |
405 | if (!read_uint32(&uint32_size))
406 | return false; // end-of-file
407 |
408 | MOV_CHECK(read_type(atom.type));
409 | MOV_CHECK(uint32_size == 1 || uint32_size >= 8);
410 | if (uint32_size == 1) {
411 | // extended size
412 | atom.size += 8;
413 | atom.rem_size += 8;
414 | MOV_CHECK(read_uint64(&uint64_size));
415 | } else {
416 | uint64_size = uint32_size;
417 | }
418 |
419 | atom.rem_size = uint64_size - atom.size;
420 | atom.size = uint64_size;
421 |
422 | return true;
423 | }
424 |
425 | static bool read_matrix(uint32_t *value)
426 | {
427 | int i;
428 | for (i = 0; i < 9; i++)
429 | MOV_CHECK(read_uint32(&value[i]));
430 |
431 | return true;
432 | }
433 |
434 | static const char* get_profile_string(uint8_t profile_idc, uint8_t constraint_flags_byte)
435 | {
436 | typedef struct
437 | {
438 | uint8_t profile_idc;
439 | uint8_t flags_mask;
440 | const char *profile_str;
441 | } ProfileName;
442 |
443 | static const ProfileName PROFILE_NAMES[] =
444 | {
445 | { 66, 0x40, "Constrained Baseline"},
446 | { 66, 0x00, "Baseline"},
447 | { 77, 0x00, "Main"},
448 | { 88, 0x00, "Extended"},
449 | {100, 0x00, "High"},
450 | {110, 0x10, "High 10 Intra"},
451 | {110, 0x00, "High 10"},
452 | {122, 0x10, "High 4:2:2 Intra"},
453 | {122, 0x00, "High 4:2:2"},
454 | {244, 0x10, "High 4:4:4 Intra"},
455 | {244, 0x00, "High 4:4:4"},
456 | { 44, 0x00, "CAVLC 4:4:4 Intra"},
457 | };
458 |
459 | const char *profile_str = "unknown";
460 | size_t i;
461 | for (i = 0; i < ARRAY_SIZE(PROFILE_NAMES); i++) {
462 | if (profile_idc == PROFILE_NAMES[i].profile_idc &&
463 | (PROFILE_NAMES[i].flags_mask == 0 ||
464 | (constraint_flags_byte & PROFILE_NAMES[i].flags_mask)))
465 | {
466 | profile_str = PROFILE_NAMES[i].profile_str;
467 | break;
468 | }
469 | }
470 |
471 | return profile_str;
472 | }
473 |
474 | static const char* get_chroma_format_string(uint8_t chroma_format_idc)
475 | {
476 | static const char *CHROMA_FORMAT_STRINGS[] =
477 | {
478 | "Monochrome",
479 | "4:2:0",
480 | "4:2:2",
481 | "4:4:4",
482 | };
483 |
484 | return CHROMA_FORMAT_STRINGS[chroma_format_idc & 0x03];
485 | }
486 |
487 | static double get_duration_sec(int64_t duration, uint32_t timescale)
488 | {
489 | if (timescale)
490 | return duration / (double)timescale;
491 | else
492 | return 0.0;
493 | }
494 |
495 | static void indent_atom_header()
496 | {
497 | size_t i;
498 | for (i = 1; i < g_atoms.size(); i++)
499 | printf(ATOM_INDENT);
500 | }
501 |
502 | static void indent(int extra_amount = 0)
503 | {
504 | int i;
505 | for (i = 1; i < (int)g_atoms.size(); i++)
506 | printf(ATOM_INDENT);
507 | printf(ATOM_VALUE_INDENT);
508 | for (i = 0; i < extra_amount; i++)
509 | printf(" ");
510 | }
511 |
512 | static void dump_uint64_index(uint64_t count, uint64_t index)
513 | {
514 | if (count < 0xffff)
515 | printf("%04" PRIx64, index);
516 | else if (count < 0xffffff)
517 | printf("%06" PRIx64, index);
518 | else if (count < 0xffffffff)
519 | printf("%08" PRIx64, index);
520 | else
521 | printf("%016" PRIx64, index);
522 | }
523 |
524 | static void dump_uint32_index(uint32_t count, uint32_t index)
525 | {
526 | if (count < 0xffff)
527 | printf("%04x", index);
528 | else if (count < 0xffffff)
529 | printf("%06x", index);
530 | else
531 | printf("%08x", index);
532 | }
533 |
534 | static void dump_uint16_index(uint16_t count, uint16_t index)
535 | {
536 | if (count < 0xff)
537 | printf("%02x", index);
538 | else
539 | printf("%04x", index);
540 | }
541 |
542 | static void dump_inline_bytes(unsigned char *bytes, uint32_t size)
543 | {
544 | printf("(size %u) ", size);
545 |
546 | uint32_t i;
547 | for (i = 0; i < size; i++)
548 | printf(" %02x", bytes[i]);
549 |
550 | printf(" |");
551 | for (i = 0; i < size; i++) {
552 | if (isprint(bytes[i]))
553 | printf("%c", bytes[i]);
554 | else
555 | printf(".");
556 | }
557 | printf("|");
558 | }
559 |
560 | static void dump_bytes_line(uint64_t size, uint64_t offset, unsigned char *line, uint32_t line_size)
561 | {
562 | dump_uint64_index(size, offset);
563 | printf(" ");
564 |
565 | uint32_t i;
566 | for (i = 0; i < line_size; i++) {
567 | if (i == 8)
568 | printf(" ");
569 | printf(" %02x", line[i]);
570 | }
571 | for (; i < 16; i++) {
572 | if (i == 8)
573 | printf(" ");
574 | printf(" ");
575 | }
576 |
577 | printf(" |");
578 | for (i = 0; i < line_size; i++) {
579 | if (isprint(line[i]))
580 | printf("%c", line[i]);
581 | else
582 | printf(".");
583 | }
584 | printf("|");
585 | }
586 |
587 | static void dump_bytes(uint64_t size, int extra_indent_amount = 0)
588 | {
589 | if (size == 0)
590 | return;
591 |
592 |
593 | indent(extra_indent_amount);
594 |
595 | unsigned char buffer[16];
596 | uint64_t total_read = 0;
597 | uint32_t num_read;
598 | while (total_read < size) {
599 | num_read = 16;
600 | if (total_read + num_read > size)
601 | num_read = (uint32_t)(size - total_read);
602 | MOV_CHECK(read_bytes(buffer, num_read));
603 |
604 | if (total_read > 0) {
605 | printf("\n");
606 | indent(extra_indent_amount);
607 | }
608 |
609 | dump_bytes_line(size, total_read, buffer, num_read);
610 |
611 | total_read += num_read;
612 | }
613 |
614 | printf("\n");
615 | }
616 |
617 | static void dump_bytes(unsigned char *bytes, uint64_t size, int extra_indent_amount = 0)
618 | {
619 | indent(extra_indent_amount);
620 |
621 | uint64_t num_lines = size / 16;
622 | uint64_t i;
623 | for (i = 0; i < num_lines; i++) {
624 | if (i > 0) {
625 | printf("\n");
626 | indent(extra_indent_amount);
627 | }
628 |
629 | dump_bytes_line(size, i * 16, &bytes[i * 16], 16);
630 | }
631 |
632 | if (num_lines > 0)
633 | printf("\n");
634 |
635 | if ((size % 16) > 0) {
636 | if (num_lines > 0)
637 | indent(extra_indent_amount);
638 | dump_bytes_line(size, num_lines * 16, &bytes[num_lines * 16], (uint32_t)(size % 16));
639 | printf("\n");
640 | }
641 | }
642 |
643 | static void dump_string(uint64_t size, int extra_indent_amount = 0)
644 | {
645 | if (size == 0) {
646 | printf("\n");
647 | return;
648 | }
649 |
650 | if (size > 256) {
651 | printf("\n");
652 | indent(extra_indent_amount);
653 | dump_bytes(size, extra_indent_amount);
654 | return;
655 | }
656 |
657 | unsigned char buffer[256];
658 | MOV_CHECK(read_bytes(buffer, (uint32_t)size));
659 |
660 | uint64_t i;
661 | for (i = 0; i < size; i++) {
662 | if (!isprint(buffer[i]))
663 | break;
664 | }
665 | if (i < size) {
666 | for (; i < size; i++) {
667 | if (buffer[i] != '\0')
668 | break;
669 | }
670 | if (i < size) {
671 | printf("\n");
672 | dump_bytes(buffer, size, extra_indent_amount);
673 | return;
674 | }
675 | }
676 |
677 | printf("'");
678 | for (i = 0; i < size; i++) {
679 | if (buffer[i] == '\0')
680 | break;
681 |
682 | printf("%c", buffer[i]);
683 | }
684 | printf("'");
685 | if (i < size) {
686 | printf(" +");
687 | for (; i < size; i++)
688 | printf(" 0x00");
689 | }
690 | printf("\n");
691 | }
692 |
693 | static void dump_type(const char *type)
694 | {
695 | size_t i;
696 | for (i = 0; i < 4; i++)
697 | printf("%c", type[i]);
698 | }
699 |
700 | static void dump_uint32_tag(uint32_t value)
701 | {
702 | int i;
703 | for (i = 3; i >= 0; i--)
704 | printf("%c", (value >> (8 * i)) & 0xff);
705 | }
706 |
707 | static void dump_file_size(uint64_t value)
708 | {
709 | if (value > UINT32_MAX)
710 | printf("%20" PRIu64 " (0x%016" PRIx64 ")", value, value);
711 | else
712 | printf("%10u (0x%08x)", (uint32_t)value, (uint32_t)value);
713 | }
714 |
715 | static void dump_uint64_size(uint64_t value)
716 | {
717 | printf("%20" PRIu64 " (0x%016" PRIx64 ")", value, value);
718 | }
719 |
720 | static void dump_uint32_size(uint32_t value)
721 | {
722 | printf("%10u (0x%08x)", value, value);
723 | }
724 |
725 | static void dump_uint64(uint64_t value, bool hex)
726 | {
727 | if (hex)
728 | printf("0x%016" PRIx64, value);
729 | else
730 | printf("%20" PRIu64, value);
731 | }
732 |
733 | static void dump_int64(int64_t value)
734 | {
735 | printf("%20" PRId64, value);
736 | }
737 |
738 | static void dump_uint32(uint32_t value, bool hex)
739 | {
740 | if (hex)
741 | printf("0x%08x", value);
742 | else
743 | printf("%10u", value);
744 | }
745 |
746 | static void dump_int32(uint32_t value)
747 | {
748 | printf("%10d", value);
749 | }
750 |
751 | static void dump_uint16(uint16_t value, bool hex)
752 | {
753 | if (hex)
754 | printf("0x%04x", value);
755 | else
756 | printf("%5u", value);
757 | }
758 |
759 | static void dump_uint8(uint8_t value, bool hex)
760 | {
761 | if (hex)
762 | printf("0x%02x", value);
763 | else
764 | printf("%3u", value);
765 | }
766 |
767 | static void dump_uint32_chars(uint32_t value)
768 | {
769 | int i;
770 | for (i = 3; i >= 0; i--) {
771 | unsigned char c = (value >> (8 * i)) & 0xff;
772 | if (isprint(c))
773 | printf("%c", c);
774 | else
775 | printf(".");
776 | }
777 | printf(" (");
778 | for (i = 3; i >= 0; i--) {
779 | unsigned char c = (value >> (8 * i)) & 0xff;
780 | if (i != 3)
781 | printf(" ");
782 | printf("%02x", c);
783 | }
784 | printf(")");
785 | }
786 |
787 | static void dump_language(uint16_t value)
788 | {
789 | unsigned char letter_1 = (unsigned char)((value >> 10) & 0x1f);
790 | unsigned char letter_2 = (unsigned char)((value >> 5) & 0x1f);
791 | unsigned char letter_3 = (unsigned char)( value & 0x1f);
792 |
793 | if (letter_1 >= 1 && letter_1 <= 26 &&
794 | letter_2 >= 1 && letter_2 <= 26 &&
795 | letter_3 >= 1 && letter_3 <= 26)
796 | {
797 | printf("0x%04x (%c%c%c)", value, letter_1 + 0x60, letter_2 + 0x60, letter_3 + 0x60);
798 | }
799 | else
800 | {
801 | printf("0x%04x", value);
802 | }
803 | }
804 |
805 | static void dump_uint32_fp(uint32_t value, uint8_t bits_left)
806 | {
807 | printf("%f", value / (double)(1 << (32 - bits_left)));
808 | }
809 |
810 | static void dump_uint16_fp(uint16_t value, uint8_t bits_left)
811 | {
812 | printf("%f", value / (double)(1 << (16 - bits_left)));
813 | }
814 |
815 | static void dump_int16_fp(int16_t value, uint8_t bits_left)
816 | {
817 | printf("%f", value / (double)(1 << (16 - bits_left)));
818 | }
819 |
820 | static void dump_timestamp(uint64_t value)
821 | {
822 | // 2082844800 = difference between Unix epoch (1970-01-01) and Apple epoch (1904-01-01)
823 | time_t unix_secs = (time_t)(value - 2082844800);
824 | struct tm *utc;
825 | utc = gmtime(&unix_secs);
826 | if (utc == 0) {
827 | printf("%" PRIu64 " seconds since 1904-01-01", value);
828 | } else {
829 | printf("%04d-%02d-%02dT%02d:%02d:%02dZ (%" PRIu64 " sec since 1904-01-01)",
830 | utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
831 | utc->tm_hour, utc->tm_min, utc->tm_sec,
832 | value);
833 | }
834 | }
835 |
836 | static void dump_matrix(uint32_t *matrix, int extra_indent_amount = 0)
837 | {
838 | // matrix:
839 | // a b u
840 | // c d v
841 | // tx ty w
842 | //
843 | // order is: a, b, u, c, d, v, tx, ty, w
844 | // all are fixed point 16.16, except u, v and w which are 2.30, hence w = 0x40000000 (1.0)
845 |
846 | int i, j;
847 | for (i = 0; i < 3; i++) {
848 | indent(extra_indent_amount);
849 | for (j = 0; j < 3; j++) {
850 | if (j != 0)
851 | printf(" ");
852 | if (j == 2)
853 | dump_uint32_fp(matrix[i * 3 + j], 2);
854 | else
855 | dump_uint32_fp(matrix[i * 3 + j], 16);
856 | }
857 | printf("\n");
858 | }
859 | }
860 |
861 | static void dump_color(uint16_t red, uint16_t green, uint16_t blue)
862 | {
863 | printf("RGB(0x%04x,0x%04x,0x%04x)", red, green, blue);
864 | }
865 |
866 | static void dump_fragment_sample_flags(uint32_t flags)
867 | {
868 | printf("res=0x%x, ", (flags >> 28) & 0x0f);
869 | printf("lead=0x%x, ", (flags >> 26) & 0x03);
870 | printf("deps_on=0x%x, ", (flags >> 24) & 0x03);
871 | printf("depd_on=0x%x, ", (flags >> 22) & 0x03);
872 | printf("red=0x%x, ", (flags >> 20) & 0x03);
873 | printf("pad=0x%x, ", (flags >> 17) & 0x07);
874 | printf("nsync=0x%x, ", (flags >> 16) & 0x01);
875 | printf("priority=0x%04x", flags & 0xffff);
876 | }
877 |
878 | static void dump_atom_header()
879 | {
880 | indent_atom_header();
881 | dump_type(CURRENT_ATOM.type);
882 | printf(": s=");
883 | dump_file_size(CURRENT_ATOM.size);
884 | printf(", o=");
885 | dump_file_size(CURRENT_ATOM.offset);
886 | printf("\n");
887 | }
888 |
889 | static void dump_atom()
890 | {
891 | dump_atom_header();
892 |
893 | if (CURRENT_ATOM.rem_size > 0)
894 | dump_bytes(CURRENT_ATOM.rem_size);
895 | }
896 |
897 | static void dump_child_atom(const DumpFuncMap *dump_func_map, size_t dump_func_map_size)
898 | {
899 | size_t i;
900 | for (i = 0; i < dump_func_map_size; i++) {
901 | if (dump_func_map[i].type[0] == '\0' || // any type
902 | (dump_func_map[i].type[0] == (char)0xa9 && dump_func_map[i].type[1] == '\0' && // any international text type
903 | CURRENT_ATOM.type[0] == (char)0xa9) ||
904 | memcmp(dump_func_map[i].type, CURRENT_ATOM.type, 4) == 0)
905 | {
906 | dump_func_map[i].dump();
907 | if (CURRENT_ATOM.rem_size > 0) {
908 | indent();
909 | printf("remainder...: %" PRIu64 " unparsed bytes\n", CURRENT_ATOM.rem_size);
910 | dump_bytes(CURRENT_ATOM.rem_size, 2);
911 | }
912 | break;
913 | }
914 | }
915 | if (i >= dump_func_map_size)
916 | dump_atom();
917 | }
918 |
919 | static void dump_container_atom(const DumpFuncMap *dump_func_map, size_t dump_func_map_size)
920 | {
921 | dump_atom_header();
922 |
923 | while (CURRENT_ATOM.rem_size > 0) {
924 | push_atom();
925 |
926 | if (!read_atom_start())
927 | break;
928 |
929 | dump_child_atom(dump_func_map, dump_func_map_size);
930 |
931 | pop_atom();
932 | }
933 | }
934 |
935 | static void dump_full_atom_header(uint8_t *version, uint32_t *flags, bool newline_flags = true)
936 | {
937 | dump_atom_header();
938 |
939 | MOV_CHECK(read_uint8(version));
940 | indent();
941 | printf("version: %u\n", (*version));
942 |
943 | MOV_CHECK(read_uint24(flags));
944 | indent();
945 | printf("flags: 0x%06x", (*flags));
946 | if (newline_flags)
947 | printf("\n");
948 | }
949 |
950 | static void dump_unknown_version(uint8_t version)
951 | {
952 | indent();
953 | printf("remainder...: unknown version %u, %" PRIu64 " unparsed bytes\n", version, CURRENT_ATOM.rem_size);
954 | dump_bytes(CURRENT_ATOM.rem_size, 2);
955 | }
956 |
957 |
958 | static void dump_ftyp_styp_atom()
959 | {
960 | dump_atom_header();
961 |
962 | uint32_t major_brand;
963 | MOV_CHECK(read_uint32(&major_brand));
964 | g_qt_brand = (major_brand == MKTAG("qt "));
965 | indent();
966 | printf("major_brand: ");
967 | dump_uint32_chars(major_brand);
968 | printf("\n");
969 |
970 | uint32_t minor_version;
971 | MOV_CHECK(read_uint32(&minor_version));
972 | indent();
973 | printf("minor_version: ");
974 | dump_uint32(minor_version, true);
975 | printf("\n");
976 |
977 | bool first = true;
978 | uint32_t compatible_brand;
979 | indent();
980 | printf("compatible_brands: ");
981 | while (CURRENT_ATOM.rem_size >= 4) {
982 | MOV_CHECK(read_uint32(&compatible_brand));
983 | if (!first)
984 | printf(", ");
985 | else
986 | first = false;
987 | dump_uint32_chars(compatible_brand);
988 | }
989 | printf("\n");
990 | }
991 |
992 | static void dump_mdat_atom()
993 | {
994 | dump_atom_header();
995 |
996 | if (CURRENT_ATOM.rem_size > 0) {
997 | indent();
998 | printf("...skipped %" PRIu64 " bytes\n", CURRENT_ATOM.rem_size);
999 | skip_bytes(CURRENT_ATOM.rem_size);
1000 | }
1001 | }
1002 |
1003 | static void dump_free_atom()
1004 | {
1005 | dump_atom_header();
1006 |
1007 | if (CURRENT_ATOM.rem_size > 0) {
1008 | indent();
1009 | printf("...skipped %" PRIu64 " bytes\n", CURRENT_ATOM.rem_size);
1010 | skip_bytes(CURRENT_ATOM.rem_size);
1011 | }
1012 | }
1013 |
1014 | static void dump_skip_atom()
1015 | {
1016 | dump_atom_header();
1017 |
1018 | if (CURRENT_ATOM.rem_size > 0) {
1019 | indent();
1020 | printf("...skipped %" PRIu64 " bytes\n", CURRENT_ATOM.rem_size);
1021 | skip_bytes(CURRENT_ATOM.rem_size);
1022 | }
1023 | }
1024 |
1025 | static void dump_dref_child_atom()
1026 | {
1027 | uint8_t version;
1028 | uint32_t flags;
1029 | dump_full_atom_header(&version, &flags, false);
1030 | if ((flags & 0x000001))
1031 | printf(" (self reference)\n");
1032 | else
1033 | printf("\n");
1034 |
1035 | if (version != 0x00) {
1036 | dump_unknown_version(version);
1037 | return;
1038 | }
1039 |
1040 |
1041 | if (equals_type(CURRENT_ATOM.type, "url ")) {
1042 | indent();
1043 | printf("data: (%" PRIu64 " bytes) url: ", CURRENT_ATOM.rem_size);
1044 | dump_string(CURRENT_ATOM.rem_size);
1045 | return;
1046 | } else {
1047 | indent();
1048 | printf("data: (%" PRIu64 " bytes)\n", CURRENT_ATOM.rem_size);
1049 | dump_bytes(CURRENT_ATOM.rem_size, 2);
1050 | return;
1051 | }
1052 | }
1053 |
1054 | static void dump_dref_atom()
1055 | {
1056 | uint8_t version;
1057 | uint32_t flags;
1058 | dump_full_atom_header(&version, &flags);
1059 |
1060 | if (version != 0x00) {
1061 | dump_unknown_version(version);
1062 | return;
1063 | }
1064 |
1065 |
1066 | uint32_t num_entries;
1067 | MOV_CHECK(read_uint32(&num_entries));
1068 | indent();
1069 | printf("entries (");
1070 | dump_uint32(num_entries, false);
1071 | printf("):\n");
1072 |
1073 | uint32_t i;
1074 | for (i = 0; i < num_entries; i++) {
1075 | push_atom();
1076 |
1077 | if (!read_atom_start())
1078 | break;
1079 |
1080 | dump_dref_child_atom();
1081 |
1082 | pop_atom();
1083 | }
1084 | }
1085 |
1086 | static void dump_stts_atom()
1087 | {
1088 | uint8_t version;
1089 | uint32_t flags;
1090 | dump_full_atom_header(&version, &flags);
1091 |
1092 | if (version != 0x00) {
1093 | dump_unknown_version(version);
1094 | return;
1095 | }
1096 |
1097 |
1098 | uint32_t num_entries;
1099 | MOV_CHECK(read_uint32(&num_entries));
1100 | indent();
1101 | printf("entries (");
1102 | dump_uint32(num_entries, false);
1103 | printf("):\n");
1104 |
1105 | if (num_entries > 0) {
1106 | indent(4);
1107 | if (num_entries < 0xffff)
1108 | printf(" i");
1109 | else if (num_entries < 0xffffff)
1110 | printf(" i");
1111 | else
1112 | printf(" i");
1113 | printf(" count duration\n");
1114 |
1115 | uint32_t i;
1116 | for (i = 0; i < num_entries; i++) {
1117 | uint32_t sample_count;
1118 | MOV_CHECK(read_uint32(&sample_count));
1119 | uint32_t sample_duration;
1120 | MOV_CHECK(read_uint32(&sample_duration));
1121 |
1122 | indent(4);
1123 | dump_uint32_index(num_entries, i);
1124 | printf(" ");
1125 |
1126 | dump_uint32(sample_count, true);
1127 | printf(" ");
1128 | dump_uint32(sample_duration, true);
1129 | printf("\n");
1130 | }
1131 | }
1132 | }
1133 |
1134 | static void dump_ctts_atom()
1135 | {
1136 | uint8_t version;
1137 | uint32_t flags;
1138 | dump_full_atom_header(&version, &flags);
1139 |
1140 | if (version != 0x00) {
1141 | dump_unknown_version(version);
1142 | return;
1143 | }
1144 |
1145 |
1146 | uint32_t num_entries;
1147 | MOV_CHECK(read_uint32(&num_entries));
1148 | indent();
1149 | printf("entries (");
1150 | dump_uint32(num_entries, false);
1151 | printf("):\n");
1152 |
1153 | if (num_entries > 0) {
1154 | indent(4);
1155 | if (num_entries < 0xffff)
1156 | printf(" i");
1157 | else if (num_entries < 0xffffff)
1158 | printf(" i");
1159 | else
1160 | printf(" i");
1161 | printf(" count offset\n");
1162 |
1163 | uint32_t i;
1164 | for (i = 0; i < num_entries; i++) {
1165 | uint32_t sample_count;
1166 | MOV_CHECK(read_uint32(&sample_count));
1167 | int32_t sample_offset;
1168 | MOV_CHECK(read_int32(&sample_offset));
1169 |
1170 | indent(4);
1171 | dump_uint32_index(num_entries, i);
1172 | printf(" ");
1173 |
1174 | dump_uint32(sample_count, true);
1175 | printf(" ");
1176 | dump_int32(sample_offset);
1177 | printf("\n");
1178 | }
1179 | }
1180 | }
1181 |
1182 | static void dump_cslg_atom()
1183 | {
1184 | uint8_t version;
1185 | uint32_t flags;
1186 | dump_full_atom_header(&version, &flags);
1187 |
1188 | if (version != 0x00) {
1189 | dump_unknown_version(version);
1190 | return;
1191 | }
1192 |
1193 |
1194 | int32_t dts_shift;
1195 | MOV_CHECK(read_int32(&dts_shift));
1196 | indent();
1197 | printf("dts_shift: %d\n", dts_shift);
1198 |
1199 | int32_t min_cts;
1200 | MOV_CHECK(read_int32(&min_cts));
1201 | indent();
1202 | printf("min_cts: %d\n", min_cts);
1203 |
1204 | int32_t max_cts;
1205 | MOV_CHECK(read_int32(&max_cts));
1206 | indent();
1207 | printf("max_cts: %d\n", max_cts);
1208 |
1209 | int32_t pts_start;
1210 | MOV_CHECK(read_int32(&pts_start));
1211 | indent();
1212 | printf("pts_start: %d\n", pts_start);
1213 |
1214 | int32_t pts_end;
1215 | MOV_CHECK(read_int32(&pts_end));
1216 | indent();
1217 | printf("pts_end: %d\n", pts_end);
1218 | }
1219 |
1220 | static void dump_stss_stps_atom()
1221 | {
1222 | uint8_t version;
1223 | uint32_t flags;
1224 | dump_full_atom_header(&version, &flags);
1225 |
1226 | if (version != 0x00) {
1227 | dump_unknown_version(version);
1228 | return;
1229 | }
1230 |
1231 |
1232 | uint32_t num_entries;
1233 | MOV_CHECK(read_uint32(&num_entries));
1234 | indent();
1235 | printf("entries (");
1236 | dump_uint32(num_entries, false);
1237 | printf("):\n");
1238 |
1239 | if (num_entries > 0) {
1240 | indent(4);
1241 | if (num_entries < 0xffff)
1242 | printf(" i");
1243 | else if (num_entries < 0xffffff)
1244 | printf(" i");
1245 | else
1246 | printf(" i");
1247 | printf(" sample\n");
1248 |
1249 | uint32_t i;
1250 | for (i = 0; i < num_entries; i++) {
1251 | uint32_t sample;
1252 | MOV_CHECK(read_uint32(&sample));
1253 |
1254 | indent(4);
1255 | dump_uint32_index(num_entries, i);
1256 | printf(" ");
1257 |
1258 | dump_uint32(sample, true);
1259 | printf("\n");
1260 | }
1261 | }
1262 | }
1263 |
1264 | static void dump_sdtp_atom()
1265 | {
1266 | uint8_t version;
1267 | uint32_t flags;
1268 | dump_full_atom_header(&version, &flags);
1269 |
1270 | if (version != 0x00) {
1271 | dump_unknown_version(version);
1272 | return;
1273 | }
1274 |
1275 |
1276 | uint32_t num_entries = (uint32_t)CURRENT_ATOM.rem_size;
1277 | indent();
1278 | printf("entries (");
1279 | dump_uint32(num_entries, false);
1280 | printf("):\n");
1281 |
1282 | if (num_entries > 0) {
1283 | indent(4);
1284 | if (num_entries < 0xffff)
1285 | printf(" i");
1286 | else if (num_entries < 0xffffff)
1287 | printf(" i");
1288 | else
1289 | printf(" i");
1290 | printf(" is_leading depends dependent redundancy\n");
1291 |
1292 | uint32_t i;
1293 | for (i = 0; i < num_entries; i++) {
1294 | uint8_t sample;
1295 | MOV_CHECK(read_uint8(&sample));
1296 |
1297 | // NOTE: not sure whether this is correct for qt
1298 | // sample files had reserved == 1 for I and P-frames, and depends_on only 2 when I-frame
1299 | uint8_t is_leading = (sample & 0xc0) >> 6;
1300 | uint8_t depends_on = (sample & 0x30) >> 4;
1301 | uint8_t dependent_on = (sample & 0x0c) >> 2;
1302 | uint8_t has_redundancy = (sample & 0x03);
1303 |
1304 | indent(4);
1305 | dump_uint32_index(num_entries, i);
1306 | printf(" ");
1307 |
1308 | printf(" %d %d %d %d\n",
1309 | is_leading, depends_on, dependent_on, has_redundancy);
1310 | }
1311 | }
1312 | }
1313 |
1314 | static void dump_stsc_atom()
1315 | {
1316 | uint8_t version;
1317 | uint32_t flags;
1318 | dump_full_atom_header(&version, &flags);
1319 |
1320 | if (version != 0x00) {
1321 | dump_unknown_version(version);
1322 | return;
1323 | }
1324 |
1325 |
1326 | uint32_t num_entries;
1327 | MOV_CHECK(read_uint32(&num_entries));
1328 | indent();
1329 | printf("entries (");
1330 | dump_uint32(num_entries, false);
1331 | printf("):\n");
1332 |
1333 | if (num_entries > 0) {
1334 | indent(4);
1335 | if (num_entries < 0xffff)
1336 | printf(" i");
1337 | else if (num_entries < 0xffffff)
1338 | printf(" i");
1339 | else
1340 | printf(" i");
1341 | printf(" first chunk samples-per-chunk descr. id\n");
1342 |
1343 | uint32_t i;
1344 | for (i = 0; i < num_entries; i++) {
1345 | uint32_t first_chunk;
1346 | MOV_CHECK(read_uint32(&first_chunk));
1347 | uint32_t samples_per_chunk;
1348 | MOV_CHECK(read_uint32(&samples_per_chunk));
1349 | uint32_t sample_description_id;
1350 | MOV_CHECK(read_uint32(&sample_description_id));
1351 |
1352 | indent(4);
1353 | dump_uint32_index(num_entries, i);
1354 | printf(" ");
1355 |
1356 | printf(" ");
1357 | dump_uint32(first_chunk, true);
1358 | printf(" ");
1359 | dump_uint32(samples_per_chunk, true);
1360 | printf(" ");
1361 | dump_uint32(sample_description_id, false);
1362 | printf("\n");
1363 | }
1364 | }
1365 | }
1366 |
1367 | static void dump_stsz_atom()
1368 | {
1369 | uint8_t version;
1370 | uint32_t flags;
1371 | dump_full_atom_header(&version, &flags);
1372 |
1373 | if (version != 0x00) {
1374 | dump_unknown_version(version);
1375 | return;
1376 | }
1377 |
1378 |
1379 | uint32_t sample_size;
1380 | MOV_CHECK(read_uint32(&sample_size));
1381 | indent();
1382 | printf("sample_size: %d\n", sample_size);
1383 |
1384 | uint32_t num_entries;
1385 | MOV_CHECK(read_uint32(&num_entries));
1386 | indent();
1387 | printf("entries (");
1388 | dump_uint32(num_entries, false);
1389 | printf("):\n");
1390 |
1391 | if (CURRENT_ATOM.rem_size == 0) {
1392 | if (num_entries > 0) {
1393 | indent(4);
1394 | printf("...none\n");
1395 | }
1396 | MOV_CHECK(sample_size > 0 || num_entries == 0);
1397 | return;
1398 | }
1399 |
1400 | if (num_entries > 0) {
1401 | indent(4);
1402 | if (num_entries < 0xffff)
1403 | printf(" i");
1404 | else if (num_entries < 0xffffff)
1405 | printf(" i");
1406 | else
1407 | printf(" i");
1408 | printf(" size\n");
1409 |
1410 | uint32_t i;
1411 | for (i = 0; i < num_entries; i++) {
1412 | int32_t size;
1413 | MOV_CHECK(read_int32(&size));
1414 |
1415 | indent(4);
1416 | dump_uint32_index(num_entries, i);
1417 | printf(" ");
1418 |
1419 | printf(" ");
1420 | dump_uint32(size, true);
1421 | printf("\n");
1422 | }
1423 | }
1424 | }
1425 |
1426 | static void dump_stco_atom()
1427 | {
1428 | uint8_t version;
1429 | uint32_t flags;
1430 | dump_full_atom_header(&version, &flags);
1431 |
1432 | if (version != 0x00) {
1433 | dump_unknown_version(version);
1434 | return;
1435 | }
1436 |
1437 |
1438 | uint32_t num_entries;
1439 | MOV_CHECK(read_uint32(&num_entries));
1440 | indent();
1441 | printf("entries (");
1442 | dump_uint32(num_entries, false);
1443 | printf("):\n");
1444 |
1445 | if (num_entries > 0) {
1446 | indent(4);
1447 | if (num_entries < 0xffff)
1448 | printf(" i");
1449 | else if (num_entries < 0xffffff)
1450 | printf(" i");
1451 | else
1452 | printf(" i");
1453 | printf(" offset (hex offset)\n");
1454 |
1455 | uint32_t i;
1456 | for (i = 0; i < num_entries; i++) {
1457 | uint32_t offset;
1458 | MOV_CHECK(read_uint32(&offset));
1459 |
1460 | indent(4);
1461 | dump_uint32_index(num_entries, i);
1462 | printf(" ");
1463 |
1464 | dump_uint32_size(offset);
1465 | printf("\n");
1466 | }
1467 | }
1468 | }
1469 |
1470 | static void dump_co64_atom()
1471 | {
1472 | uint8_t version;
1473 | uint32_t flags;
1474 | dump_full_atom_header(&version, &flags);
1475 |
1476 | if (version != 0x00) {
1477 | dump_unknown_version(version);
1478 | return;
1479 | }
1480 |
1481 |
1482 | uint32_t num_entries;
1483 | MOV_CHECK(read_uint32(&num_entries));
1484 | indent();
1485 | printf("entries (");
1486 | dump_uint32(num_entries, false);
1487 | printf("):\n");
1488 |
1489 | if (num_entries > 0) {
1490 | indent(4);
1491 | if (num_entries < 0xffff)
1492 | printf(" i");
1493 | else if (num_entries < 0xffffff)
1494 | printf(" i");
1495 | else
1496 | printf(" i");
1497 | printf(" offset (hex offset)\n");
1498 |
1499 | uint32_t i;
1500 | for (i = 0; i < num_entries; i++) {
1501 | uint64_t offset;
1502 | MOV_CHECK(read_uint64(&offset));
1503 |
1504 | indent(4);
1505 | dump_uint32_index(num_entries, i);
1506 | printf(" ");
1507 |
1508 | dump_uint64_size(offset);
1509 | printf("\n");
1510 | }
1511 | }
1512 | }
1513 |
1514 | static void dump_hdlr_atom()
1515 | {
1516 | uint8_t version;
1517 | uint32_t flags;
1518 | dump_full_atom_header(&version, &flags);
1519 |
1520 | if (version != 0x00) {
1521 | dump_unknown_version(version);
1522 | return;
1523 | }
1524 |
1525 |
1526 | uint32_t component_type;
1527 | MOV_CHECK(read_uint32(&component_type));
1528 | indent();
1529 | printf("component_type: ");
1530 | dump_uint32_chars(component_type);
1531 | printf("\n");
1532 |
1533 | uint32_t component_sub_type;
1534 | MOV_CHECK(read_uint32(&component_sub_type));
1535 | indent();
1536 | printf("component_sub_type: ");
1537 | dump_uint32_tag(component_sub_type);
1538 | printf("\n");
1539 |
1540 | if (HAVE_PREV_ATOM && strncmp(PREV_ATOM.type, "mdia", 4) == 0) {
1541 | g_component_type = component_type;
1542 | g_component_sub_type = component_sub_type;
1543 | }
1544 |
1545 | uint32_t component_manufacturer;
1546 | MOV_CHECK(read_uint32(&component_manufacturer));
1547 | indent();
1548 | printf("component_manufacturer: %u\n", component_manufacturer);
1549 |
1550 | uint32_t component_flags;
1551 | MOV_CHECK(read_uint32(&component_flags));
1552 | indent();
1553 | printf("component_flags: 0x%08x\n", component_flags);
1554 |
1555 | uint32_t component_flags_mask;
1556 | MOV_CHECK(read_uint32(&component_flags_mask));
1557 | indent();
1558 | printf("component_flags_mask: 0x%08x\n", component_flags_mask);
1559 |
1560 | if (CURRENT_ATOM.rem_size > 0) {
1561 | uint64_t component_name_len;
1562 | if (g_qt_brand) {
1563 | uint8_t qt_component_name_len;
1564 | MOV_CHECK(read_uint8(&qt_component_name_len));
1565 | component_name_len = qt_component_name_len;
1566 | } else {
1567 | component_name_len = CURRENT_ATOM.rem_size;
1568 | }
1569 | indent();
1570 | printf("component_name: ");
1571 | if (component_name_len == 0) {
1572 | printf("\n");
1573 | } else {
1574 | dump_string(component_name_len, 2);
1575 | }
1576 | }
1577 | }
1578 |
1579 | static void dump_international_string_atom()
1580 | {
1581 | dump_atom_header();
1582 |
1583 | uint16_t len;
1584 | MOV_CHECK(read_uint16(&len));
1585 | uint16_t language_code;
1586 | MOV_CHECK(read_uint16(&language_code));
1587 |
1588 | indent();
1589 | printf("value: (len=%d,lang=0x%04x) ", len, language_code);
1590 | dump_string(len, 2);
1591 | }
1592 |
1593 | static void dump_colr_atom()
1594 | {
1595 | dump_atom_header();
1596 |
1597 | uint32_t color_param_type;
1598 | MOV_CHECK(read_uint32(&color_param_type));
1599 | indent();
1600 | printf("color_param_type: ");
1601 | dump_uint32_tag(color_param_type);
1602 | printf("\n");
1603 |
1604 | if (color_param_type == MKTAG("nclc")) {
1605 | uint16_t primaries;
1606 | MOV_CHECK(read_uint16(&primaries));
1607 | indent();
1608 | printf("primaries: %u\n", primaries);
1609 |
1610 | uint16_t transfer_func;
1611 | MOV_CHECK(read_uint16(&transfer_func));
1612 | indent();
1613 | printf("transfer_func: %u\n", transfer_func);
1614 |
1615 | uint16_t matrix;
1616 | MOV_CHECK(read_uint16(&matrix));
1617 | indent();
1618 | printf("matrix: %u\n", matrix);
1619 | }
1620 | }
1621 |
1622 | static void dump_fiel_atom()
1623 | {
1624 | dump_atom_header();
1625 |
1626 | uint8_t fields;
1627 | MOV_CHECK(read_uint8(&fields));
1628 | indent();
1629 | printf("fields: %u", fields);
1630 | if (fields == 1)
1631 | printf(" (progressive)\n");
1632 | else if (fields == 2)
1633 | printf(" (interlaced)\n");
1634 | else
1635 | printf("\n");
1636 |
1637 | uint8_t detail;
1638 | MOV_CHECK(read_uint8(&detail));
1639 | indent();
1640 | printf("detail: %u\n", detail);
1641 | }
1642 |
1643 | static void dump_pasp_atom()
1644 | {
1645 | dump_atom_header();
1646 |
1647 | int32_t h_spacing;
1648 | MOV_CHECK(read_int32(&h_spacing));
1649 | indent();
1650 | printf("h_spacing: %d\n", h_spacing);
1651 |
1652 | int32_t v_spacing;
1653 | MOV_CHECK(read_int32(&v_spacing));
1654 | indent();
1655 | printf("v_spacing: %d\n", v_spacing);
1656 | }
1657 |
1658 | static void dump_clap_atom()
1659 | {
1660 | dump_atom_header();
1661 |
1662 | int32_t clean_ap_width_num, clean_ap_width_den;
1663 | MOV_CHECK(read_int32(&clean_ap_width_num));
1664 | MOV_CHECK(read_int32(&clean_ap_width_den));
1665 | indent();
1666 | printf("clean_aperture_width: %d/%d\n", clean_ap_width_num, clean_ap_width_den);
1667 |
1668 | int32_t clean_ap_height_num, clean_ap_height_den;
1669 | MOV_CHECK(read_int32(&clean_ap_height_num));
1670 | MOV_CHECK(read_int32(&clean_ap_height_den));
1671 | indent();
1672 | printf("clean_aperture_height: %d/%d\n", clean_ap_height_num, clean_ap_height_den);
1673 |
1674 | int32_t horiz_offset_num, horiz_offset_den;
1675 | MOV_CHECK(read_int32(&horiz_offset_num));
1676 | MOV_CHECK(read_int32(&horiz_offset_den));
1677 | indent();
1678 | printf("horiz_offset: %d/%d\n", horiz_offset_num, horiz_offset_den);
1679 |
1680 | int32_t vert_offset_num, vert_offset_den;
1681 | MOV_CHECK(read_int32(&vert_offset_num));
1682 | MOV_CHECK(read_int32(&vert_offset_den));
1683 | indent();
1684 | printf("vert_offset: %d/%d\n", vert_offset_num, vert_offset_den);
1685 | }
1686 |
1687 | static void dump_avcc_atom()
1688 | {
1689 | dump_atom_header();
1690 |
1691 | uint8_t configuration_version;
1692 | MOV_CHECK(read_uint8(&configuration_version));
1693 | indent();
1694 | printf("configuration_version: %u\n", configuration_version);
1695 |
1696 | uint8_t profile_idc;
1697 | MOV_CHECK(read_uint8(&profile_idc));
1698 | uint8_t constraint_flags_byte;
1699 | MOV_CHECK(read_uint8(&constraint_flags_byte));
1700 | indent();
1701 | printf("profile_idc: %u ('%s')\n", profile_idc, get_profile_string(profile_idc, constraint_flags_byte));
1702 | indent();
1703 | printf("constraint_flags_byte: ");
1704 | dump_uint8(constraint_flags_byte, true);
1705 | printf("\n");
1706 |
1707 | uint8_t level_idc;
1708 | MOV_CHECK(read_uint8(&level_idc));
1709 | indent();
1710 | if (level_idc == 11 && (constraint_flags_byte & 0x10))
1711 | printf("level_idc: %u (1b)\n", level_idc);
1712 | else
1713 | printf("level_idc: %u (%.1f)\n", level_idc, level_idc / 10.0);
1714 |
1715 | uint8_t length_size_minus1_byte, length_size;
1716 | MOV_CHECK(read_uint8(&length_size_minus1_byte));
1717 | length_size = (length_size_minus1_byte & 0x03) + 1;
1718 | indent();
1719 | printf("length_size_minus1_byte: 0x%02x (length_size=%u)\n", length_size_minus1_byte, length_size);
1720 |
1721 | uint8_t num_sps_byte, num_sps;
1722 | MOV_CHECK(read_uint8(&num_sps_byte));
1723 | num_sps = num_sps_byte & 0x1f;
1724 | indent();
1725 | printf("num_sps_byte: 0x%02x (num_sps=%u)\n", num_sps_byte, num_sps);
1726 |
1727 | unsigned char *buffer = 0;
1728 | size_t buffer_size = 0;
1729 | uint8_t i;
1730 | for (i = 0; i < num_sps; i++) {
1731 | uint16_t sps_size;
1732 | MOV_CHECK(read_uint16(&sps_size));
1733 |
1734 | indent(4);
1735 | printf("sps %u:\n", i);
1736 |
1737 | if (g_avcc_filename) {
1738 | write_avcc_ps(&buffer, &buffer_size, length_size, sps_size);
1739 | dump_bytes(buffer, sps_size, 6);
1740 | } else {
1741 | dump_bytes(sps_size, 6);
1742 | }
1743 | }
1744 |
1745 | uint8_t num_pps;
1746 | MOV_CHECK(read_uint8(&num_pps));
1747 | indent();
1748 | printf("num_pps: %u\n", num_pps);
1749 |
1750 | for (i = 0; i < num_pps; i++) {
1751 | uint16_t pps_size;
1752 | MOV_CHECK(read_uint16(&pps_size));
1753 |
1754 | indent(4);
1755 | printf("pps %u:\n", i);
1756 | if (g_avcc_filename) {
1757 | write_avcc_ps(&buffer, &buffer_size, length_size, pps_size);
1758 | dump_bytes(buffer, pps_size, 6);
1759 | } else {
1760 | dump_bytes(pps_size, 6);
1761 | }
1762 | }
1763 |
1764 | if (CURRENT_ATOM.rem_size >= 4) {
1765 | uint8_t chroma_format_byte, chroma_format;
1766 | MOV_CHECK(read_uint8(&chroma_format_byte));
1767 | chroma_format = chroma_format_byte & 0x03;
1768 | indent();
1769 | printf("chroma_format_byte: 0x%02x (chroma_format=%u '%s')\n", chroma_format_byte, chroma_format,
1770 | get_chroma_format_string(chroma_format));
1771 |
1772 | uint8_t bit_depth_luma_minus8_byte, bit_depth_luma;
1773 | MOV_CHECK(read_uint8(&bit_depth_luma_minus8_byte));
1774 | bit_depth_luma = (bit_depth_luma_minus8_byte & 0x07) + 8;
1775 | indent();
1776 | printf("bit_depth_luma_minus8_byte: 0x%02x (bit_depth_luma=%u)\n",
1777 | bit_depth_luma_minus8_byte, bit_depth_luma);
1778 |
1779 | uint8_t bit_depth_chroma_minus8_byte, bit_depth_chroma;
1780 | MOV_CHECK(read_uint8(&bit_depth_chroma_minus8_byte));
1781 | bit_depth_chroma = (bit_depth_chroma_minus8_byte & 0x07) + 8;
1782 | indent();
1783 | printf("bit_depth_chroma_minus8_byte: 0x%02x (bit_depth_chroma=%u)\n",
1784 | bit_depth_chroma_minus8_byte, bit_depth_chroma);
1785 |
1786 | uint8_t num_sps_ext;
1787 | MOV_CHECK(read_uint8(&num_sps_ext));
1788 | indent();
1789 | printf("num_sps_ext: %u\n", num_sps_ext);
1790 |
1791 | for (i = 0; i < num_sps_ext; i++) {
1792 | uint16_t sps_ext_size;
1793 | MOV_CHECK(read_uint16(&sps_ext_size));
1794 |
1795 | indent(4);
1796 | printf("sps ext %u:\n", i);
1797 | if (g_avcc_filename) {
1798 | write_avcc_ps(&buffer, &buffer_size, length_size, sps_ext_size);
1799 | dump_bytes(buffer, sps_ext_size, 6);
1800 | } else {
1801 | dump_bytes(sps_ext_size, 6);
1802 | }
1803 | }
1804 | }
1805 |
1806 | free(buffer);
1807 | }
1808 |
1809 | static void dump_btrt_atom()
1810 | {
1811 | dump_atom_header();
1812 |
1813 | uint32_t buffer_size_db;
1814 | MOV_CHECK(read_uint32(&buffer_size_db));
1815 | indent();
1816 | printf("buffer_size_db: 0x%04x\n", buffer_size_db);
1817 |
1818 | uint32_t max_bitrate;
1819 | MOV_CHECK(read_uint32(&max_bitrate));
1820 | indent();
1821 | printf("max_bitrate: %u\n", max_bitrate);
1822 |
1823 | uint32_t avg_bitrate;
1824 | MOV_CHECK(read_uint32(&avg_bitrate));
1825 | indent();
1826 | printf("avg_bitrate: %u\n", avg_bitrate);
1827 | }
1828 |
1829 | static uint32_t dump_stbl_vide(uint32_t size)
1830 | {
1831 | static const DumpFuncMap dump_func_map[] =
1832 | {
1833 | {{'c','o','l','r'}, dump_colr_atom},
1834 | {{'f','i','e','l'}, dump_fiel_atom},
1835 | {{'p','a','s','p'}, dump_pasp_atom},
1836 | {{'c','l','a','p'}, dump_clap_atom},
1837 | {{'a','v','c','C'}, dump_avcc_atom},
1838 | {{'b','t','r','t'}, dump_btrt_atom},
1839 | };
1840 |
1841 | MOV_CHECK(size <= CURRENT_ATOM.rem_size);
1842 | uint64_t end_atom_rem_size = CURRENT_ATOM.rem_size - size;
1843 |
1844 | uint16_t version;
1845 | MOV_CHECK(read_uint16(&version));
1846 | indent(2);
1847 | printf("version: %u\n", version);
1848 |
1849 | uint16_t revision;
1850 | MOV_CHECK(read_uint16(&revision));
1851 | indent(2);
1852 | printf("revision: 0x%04x\n", revision);
1853 |
1854 | uint32_t vendor;
1855 | MOV_CHECK(read_uint32(&vendor));
1856 | indent(2);
1857 | printf("vendor: ");
1858 | dump_uint32_chars(vendor);
1859 | printf("\n");
1860 |
1861 | uint32_t temporal_quality;
1862 | MOV_CHECK(read_uint32(&temporal_quality));
1863 | indent(2);
1864 | printf("temporal_quality: 0x%08x\n", temporal_quality);
1865 |
1866 | uint32_t spatial_quality;
1867 | MOV_CHECK(read_uint32(&spatial_quality));
1868 | indent(2);
1869 | printf("spatial_quality: 0x%08x\n", spatial_quality);
1870 |
1871 | uint16_t width;
1872 | MOV_CHECK(read_uint16(&width));
1873 | indent(2);
1874 | printf("width: %u\n", width);
1875 |
1876 | uint16_t height;
1877 | MOV_CHECK(read_uint16(&height));
1878 | indent(2);
1879 | printf("height: %u\n", height);
1880 |
1881 | uint32_t horizontal_resolution;
1882 | MOV_CHECK(read_uint32(&horizontal_resolution));
1883 | indent(2);
1884 | printf("horizontal_resolution: ");
1885 | dump_uint32_fp(horizontal_resolution, 16);
1886 | printf("\n");
1887 |
1888 | uint32_t vertical_resolution;
1889 | MOV_CHECK(read_uint32(&vertical_resolution));
1890 | indent(2);
1891 | printf("vertical_resolution: ");
1892 | dump_uint32_fp(vertical_resolution, 16);
1893 | printf("\n");
1894 |
1895 | uint32_t data_size;
1896 | MOV_CHECK(read_uint32(&data_size));
1897 | indent(2);
1898 | printf("data_size: %u\n", data_size);
1899 |
1900 | uint16_t frame_count;
1901 | MOV_CHECK(read_uint16(&frame_count));
1902 | indent(2);
1903 | printf("frame_count: %u\n", frame_count);
1904 |
1905 | uint8_t compressor_name_len;
1906 | MOV_CHECK(read_uint8(&compressor_name_len));
1907 | MOV_CHECK(compressor_name_len - 1 <= 32);
1908 | indent(2);
1909 | printf("compressor_name: ");
1910 | dump_string(31, 4);
1911 |
1912 | uint16_t depth;
1913 | MOV_CHECK(read_uint16(&depth));
1914 | indent(2);
1915 | printf("depth: %u\n", depth);
1916 |
1917 | uint16_t color_table_id;
1918 | MOV_CHECK(read_uint16(&color_table_id));
1919 | indent(2);
1920 | printf("color_table_id: 0x%04x\n", color_table_id);
1921 |
1922 | // extensions
1923 | while (CURRENT_ATOM.rem_size > end_atom_rem_size + 8) {
1924 | push_atom();
1925 |
1926 | if (!read_atom_start())
1927 | break;
1928 |
1929 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
1930 |
1931 | pop_atom();
1932 | }
1933 |
1934 | MOV_CHECK(CURRENT_ATOM.rem_size >= end_atom_rem_size);
1935 | return (uint32_t)(CURRENT_ATOM.rem_size - end_atom_rem_size);
1936 | }
1937 |
1938 | static uint32_t dump_mp4_es_descriptor(uint32_t length)
1939 | {
1940 | MOV_CHECK(length >= 3);
1941 |
1942 | indent(4 * mp4_object_desc_level + 2);
1943 | printf("es_descriptor:\n");
1944 |
1945 | uint16_t es_id;
1946 | MOV_CHECK(read_uint16(&es_id));
1947 | indent(4 * mp4_object_desc_level + 4);
1948 | printf("es_id: 0x%04x\n", es_id);
1949 |
1950 | uint8_t flag_bits;
1951 | MOV_CHECK(read_uint8(&flag_bits));
1952 | indent(4 * mp4_object_desc_level + 4);
1953 | printf("stream_dep_flag: %u\n", !!(flag_bits & 0x80));
1954 | indent(4 * mp4_object_desc_level + 4);
1955 | printf("url_flag: %u\n", !!(flag_bits & 0x40));
1956 | indent(4 * mp4_object_desc_level + 4);
1957 | printf("reserved: %u\n", !!(flag_bits & 0x20));
1958 | indent(4 * mp4_object_desc_level + 4);
1959 | printf("stream_priority: 0x%02x\n", flag_bits & 0x1f);
1960 |
1961 | uint32_t rem_length = length - 3;
1962 |
1963 | if ((flag_bits & 0x80)) {
1964 | MOV_CHECK(rem_length >= 2);
1965 | uint16_t dependson_es_id;
1966 | MOV_CHECK(read_uint16(&dependson_es_id));
1967 | indent(4 * mp4_object_desc_level + 4);
1968 | printf("dependson_es_id: 0x%04x\n", dependson_es_id);
1969 | rem_length -= 2;
1970 | }
1971 |
1972 | if ((flag_bits & 0x40)) {
1973 | MOV_CHECK(rem_length >= 1);
1974 | uint8_t url_len;
1975 | MOV_CHECK(read_uint8(&url_len));
1976 | rem_length--;
1977 | MOV_CHECK(rem_length >= url_len);
1978 | indent(4 * mp4_object_desc_level + 4);
1979 | printf("url: ");
1980 | dump_string(url_len, 2);
1981 | rem_length -= url_len;
1982 | }
1983 |
1984 | while (rem_length > 0) {
1985 | mp4_object_desc_level++;
1986 | rem_length -= dump_mp4_object_descriptor(rem_length);
1987 | mp4_object_desc_level--;
1988 | }
1989 |
1990 | return length;
1991 | }
1992 |
1993 | static uint32_t dump_mp4_dc_descriptor(uint32_t length)
1994 | {
1995 | MOV_CHECK(length >= 13);
1996 |
1997 | indent(4 * mp4_object_desc_level + 2);
1998 | printf("decoder_config:\n");
1999 |
2000 | uint8_t obj_profile_indication;
2001 | MOV_CHECK(read_uint8(&obj_profile_indication));
2002 | indent(4 * mp4_object_desc_level + 4);
2003 | printf("obj_profile_indication: 0x%02x\n", obj_profile_indication);
2004 |
2005 | uint8_t stream_bits;
2006 | MOV_CHECK(read_uint8(&stream_bits));
2007 | indent(4 * mp4_object_desc_level + 4);
2008 | printf("stream_type: 0x%02x\n", stream_bits >> 2);
2009 | indent(4 * mp4_object_desc_level + 4);
2010 | printf("up_stream: %u\n", !!(stream_bits & 0x02));
2011 | indent(4 * mp4_object_desc_level + 4);
2012 | printf("reserved: %u\n", !!(stream_bits & 0x01));
2013 |
2014 | uint32_t buffer_size_db;
2015 | MOV_CHECK(read_uint24(&buffer_size_db));
2016 | indent(4 * mp4_object_desc_level + 4);
2017 | printf("buffer_size_db: %u\n", buffer_size_db);
2018 |
2019 | uint32_t max_bitrate;
2020 | MOV_CHECK(read_uint32(&max_bitrate));
2021 | indent(4 * mp4_object_desc_level + 4);
2022 | printf("max_bitrate: %u\n", max_bitrate);
2023 |
2024 | uint32_t avg_bitrate;
2025 | MOV_CHECK(read_uint32(&avg_bitrate));
2026 | indent(4 * mp4_object_desc_level + 4);
2027 | printf("avg_bitrate: %u\n", avg_bitrate);
2028 |
2029 | uint32_t rem_length = length - 13;
2030 | while (rem_length > 0) {
2031 | mp4_object_desc_level++;
2032 | rem_length -= dump_mp4_object_descriptor(rem_length);
2033 | mp4_object_desc_level--;
2034 | }
2035 |
2036 | return length;
2037 | }
2038 |
2039 | static uint32_t dump_mp4_ds_info(uint32_t length)
2040 | {
2041 | indent(4 * mp4_object_desc_level + 2);
2042 | printf("decoder_specific_info:\n");
2043 |
2044 | dump_bytes(length, 4 * mp4_object_desc_level + 4);
2045 |
2046 | return length;
2047 | }
2048 |
2049 | static uint32_t dump_mp4_slc_descriptor(uint32_t length)
2050 | {
2051 | MOV_CHECK(length >= 1);
2052 |
2053 | indent(4 * mp4_object_desc_level + 2);
2054 | printf("sl_config:\n");
2055 |
2056 | uint8_t predefined;
2057 | MOV_CHECK(read_uint8(&predefined));
2058 | indent(4 * mp4_object_desc_level + 4);
2059 | printf("predefined: 0x%02x\n", predefined);
2060 |
2061 | if (length > 1)
2062 | dump_bytes(length - 1, 4 * mp4_object_desc_level + 6);
2063 |
2064 | return length;
2065 | }
2066 |
2067 | static uint32_t dump_mp4_object_descriptor(uint32_t parent_length)
2068 | {
2069 | MOV_CHECK(parent_length >= 2);
2070 |
2071 | indent(4 * mp4_object_desc_level);
2072 | printf("descriptor:\n");
2073 |
2074 | uint32_t head_length = 0;
2075 |
2076 | uint8_t tag;
2077 | MOV_CHECK(read_uint8(&tag));
2078 | indent(4 * mp4_object_desc_level + 2);
2079 | printf("tag: 0x%02x\n", tag);
2080 | head_length++;
2081 |
2082 | uint32_t length = 0;
2083 | uint8_t byte;
2084 | do {
2085 | MOV_CHECK(read_uint8(&byte));
2086 | head_length++;
2087 | length <<= 7;
2088 | length |= byte & 0x7f;
2089 | } while (byte & 0x80);
2090 | indent(4 * mp4_object_desc_level + 2);
2091 | printf("length: %u\n", length);
2092 |
2093 | MOV_CHECK(parent_length >= head_length + length);
2094 |
2095 | uint32_t used_length;
2096 | switch (tag)
2097 | {
2098 | case 0x03:
2099 | used_length = dump_mp4_es_descriptor(length);
2100 | break;
2101 | case 0x04:
2102 | used_length = dump_mp4_dc_descriptor(length);
2103 | break;
2104 | case 0x05:
2105 | used_length = dump_mp4_ds_info(length);
2106 | break;
2107 | case 0x06:
2108 | used_length = dump_mp4_slc_descriptor(length);
2109 | break;
2110 | default:
2111 | dump_bytes(length, 4 * mp4_object_desc_level + 4);
2112 | used_length = length;
2113 | break;
2114 | }
2115 |
2116 | return head_length + used_length;
2117 | }
2118 |
2119 | static void dump_esds_atom()
2120 | {
2121 | uint8_t version;
2122 | uint32_t flags;
2123 | dump_full_atom_header(&version, &flags);
2124 |
2125 | if (version != 0) {
2126 | dump_unknown_version(version);
2127 | return;
2128 | }
2129 |
2130 | while (CURRENT_ATOM.rem_size > 2)
2131 | dump_mp4_object_descriptor((uint32_t)CURRENT_ATOM.rem_size);
2132 | }
2133 |
2134 | static uint32_t dump_stbl_soun(uint32_t size)
2135 | {
2136 | static const DumpFuncMap dump_func_map[] =
2137 | {
2138 | {{'e','s','d','s'}, dump_esds_atom},
2139 | {{'b','t','r','t'}, dump_btrt_atom},
2140 | };
2141 |
2142 | MOV_CHECK(size <= CURRENT_ATOM.rem_size);
2143 | uint64_t end_atom_rem_size = CURRENT_ATOM.rem_size - size;
2144 |
2145 | uint16_t version;
2146 | MOV_CHECK(read_uint16(&version));
2147 | indent(2);
2148 | printf("version: %u\n", version);
2149 |
2150 | uint16_t revision;
2151 | MOV_CHECK(read_uint16(&revision));
2152 | indent(2);
2153 | printf("revision: 0x%04x\n", revision);
2154 |
2155 | uint32_t vendor;
2156 | MOV_CHECK(read_uint32(&vendor));
2157 | indent(2);
2158 | printf("vendor: ");
2159 | dump_uint32_chars(vendor);
2160 | printf("\n");
2161 |
2162 | uint16_t channel_count;
2163 | MOV_CHECK(read_uint16(&channel_count));
2164 | indent(2);
2165 | printf("channel_count: %u\n", channel_count);
2166 |
2167 | uint16_t sample_size;
2168 | MOV_CHECK(read_uint16(&sample_size));
2169 | indent(2);
2170 | printf("sample_size: %u\n", sample_size);
2171 |
2172 | int16_t compression_id;
2173 | MOV_CHECK(read_int16(&compression_id));
2174 | indent(2);
2175 | printf("compression_id: %d\n", compression_id);
2176 |
2177 | uint16_t packet_size;
2178 | MOV_CHECK(read_uint16(&packet_size));
2179 | indent(2);
2180 | printf("packet_size: %u\n", packet_size);
2181 |
2182 | uint32_t sample_rate;
2183 | MOV_CHECK(read_uint32(&sample_rate));
2184 | indent(2);
2185 | printf("sample_rate: ");
2186 | dump_uint32_fp(sample_rate, 16);
2187 | printf("\n");
2188 |
2189 | if (version == 1) {
2190 | uint32_t samples_per_packet;
2191 | MOV_CHECK(read_uint32(&samples_per_packet));
2192 | indent(2);
2193 | printf("samples_per_packet: %u\n", samples_per_packet);
2194 |
2195 | uint32_t bytes_per_packet;
2196 | MOV_CHECK(read_uint32(&bytes_per_packet));
2197 | indent(2);
2198 | printf("bytes_per_packet: %u\n", bytes_per_packet);
2199 |
2200 | uint32_t bytes_per_frame;
2201 | MOV_CHECK(read_uint32(&bytes_per_frame));
2202 | indent(2);
2203 | printf("bytes_per_frame: %u\n", bytes_per_frame);
2204 |
2205 | uint32_t bytes_per_sample;
2206 | MOV_CHECK(read_uint32(&bytes_per_sample));
2207 | indent(2);
2208 | printf("bytes_per_sample: %u\n", bytes_per_sample);
2209 | }
2210 |
2211 | // extensions
2212 | if (version == 0 || version == 1) { // size is known for these versions and can be sure what follows will be atoms
2213 | while (CURRENT_ATOM.rem_size > end_atom_rem_size + 8) {
2214 | push_atom();
2215 |
2216 | if (!read_atom_start())
2217 | break;
2218 |
2219 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2220 |
2221 | pop_atom();
2222 | }
2223 | }
2224 |
2225 | MOV_CHECK(CURRENT_ATOM.rem_size >= end_atom_rem_size);
2226 | return (uint32_t)(CURRENT_ATOM.rem_size - end_atom_rem_size);
2227 | }
2228 |
2229 | static uint32_t dump_stbl_tmcd(uint32_t size)
2230 | {
2231 | static const DumpFuncMap dump_func_map[] =
2232 | {
2233 | {{'n','a','m','e'}, dump_international_string_atom},
2234 | };
2235 |
2236 | MOV_CHECK(size <= CURRENT_ATOM.rem_size);
2237 | uint64_t end_atom_rem_size = CURRENT_ATOM.rem_size - size;
2238 |
2239 | uint32_t reserved1;
2240 | MOV_CHECK(read_uint32(&reserved1));
2241 | indent(2);
2242 | printf("reserved: 0x%08x\n", reserved1);
2243 |
2244 | uint32_t flags;
2245 | MOV_CHECK(read_uint32(&flags));
2246 | indent(2);
2247 | printf("flags: 0x%08x\n", flags);
2248 |
2249 | uint32_t timescale;
2250 | MOV_CHECK(read_uint32(×cale));
2251 | indent(2);
2252 | printf("timescale: %u\n", timescale);
2253 |
2254 | int32_t frame_duration;
2255 | MOV_CHECK(read_int32(&frame_duration));
2256 | indent(2);
2257 | printf("frame_duration: %d (%f sec)\n", frame_duration, get_duration_sec(frame_duration, timescale));
2258 |
2259 | uint8_t number_of_frames;
2260 | MOV_CHECK(read_uint8(&number_of_frames));
2261 | indent(2);
2262 | printf("number_of_frames: %u\n", number_of_frames);
2263 |
2264 | uint8_t reserved2;
2265 | MOV_CHECK(read_uint8(&reserved2));
2266 | indent(2);
2267 | printf("reserved: 0x%02x\n", reserved2);
2268 |
2269 | // extensions
2270 | while (CURRENT_ATOM.rem_size > end_atom_rem_size + 8) {
2271 | push_atom();
2272 |
2273 | if (!read_atom_start())
2274 | break;
2275 |
2276 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2277 |
2278 | pop_atom();
2279 | }
2280 |
2281 | MOV_CHECK(CURRENT_ATOM.rem_size >= end_atom_rem_size);
2282 | return (uint32_t)(CURRENT_ATOM.rem_size - end_atom_rem_size);
2283 | }
2284 |
2285 | static void dump_stsd_atom()
2286 | {
2287 | uint8_t version;
2288 | uint32_t flags;
2289 | dump_full_atom_header(&version, &flags);
2290 |
2291 | if (version != 0x00) {
2292 | dump_unknown_version(version);
2293 | return;
2294 | }
2295 |
2296 |
2297 | uint32_t num_entries;
2298 | MOV_CHECK(read_uint32(&num_entries));
2299 | indent();
2300 | printf("sample_descriptions (");
2301 | dump_uint32(num_entries, true);
2302 | printf("):\n");
2303 |
2304 | uint32_t i;
2305 | for (i = 0; i < num_entries; i++) {
2306 | uint32_t size;
2307 | MOV_CHECK(read_uint32(&size));
2308 | indent(2);
2309 | printf("size: %08x\n", size);
2310 | MOV_CHECK(size >= 16);
2311 |
2312 | uint32_t data_format;
2313 | MOV_CHECK(read_uint32(&data_format));
2314 | indent(2);
2315 | printf("data_format: ");
2316 | dump_uint32_chars(data_format);
2317 | printf("\n");
2318 |
2319 | unsigned char reserved[6];
2320 | MOV_CHECK(read_bytes(reserved, 6));
2321 | indent(2);
2322 | printf("reserved: ");
2323 | dump_inline_bytes(reserved, 6);
2324 | printf("\n");
2325 |
2326 | uint16_t data_ref_index;
2327 | MOV_CHECK(read_uint16(&data_ref_index));
2328 | indent(2);
2329 | printf("data_ref_index: 0x%04x\n", data_ref_index);
2330 |
2331 | uint32_t rem_size = size - 16;
2332 | if (g_component_type == MHLR_COMPONENT_TYPE || (!g_component_type && !g_qt_brand)) {
2333 | if (g_component_sub_type == VIDE_COMPONENT_SUB_TYPE)
2334 | rem_size = dump_stbl_vide(rem_size);
2335 | else if (g_component_sub_type == SOUN_COMPONENT_SUB_TYPE)
2336 | rem_size = dump_stbl_soun(rem_size);
2337 | else if (g_component_sub_type == TMCD_COMPONENT_SUB_TYPE)
2338 | rem_size = dump_stbl_tmcd(rem_size);
2339 | }
2340 | if (rem_size > 0) {
2341 | indent(2);
2342 | printf("remainder...: %u unparsed bytes\n", rem_size);
2343 | dump_bytes(rem_size, 4);
2344 | }
2345 | }
2346 | }
2347 |
2348 | static void dump_stbl_atom()
2349 | {
2350 | static const DumpFuncMap dump_func_map[] =
2351 | {
2352 | {{'s','t','s','d'}, dump_stsd_atom},
2353 | {{'s','t','t','s'}, dump_stts_atom},
2354 | {{'c','t','t','s'}, dump_ctts_atom},
2355 | {{'c','s','l','g'}, dump_cslg_atom},
2356 | {{'s','t','s','s'}, dump_stss_stps_atom},
2357 | {{'s','t','p','s'}, dump_stss_stps_atom},
2358 | {{'s','d','t','p'}, dump_sdtp_atom},
2359 | {{'s','t','s','c'}, dump_stsc_atom},
2360 | {{'s','t','s','z'}, dump_stsz_atom},
2361 | {{'s','t','c','o'}, dump_stco_atom},
2362 | {{'c','o','6','4'}, dump_co64_atom},
2363 | };
2364 |
2365 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2366 | }
2367 |
2368 | static void dump_dinf_atom()
2369 | {
2370 | static const DumpFuncMap dump_func_map[] =
2371 | {
2372 | {{'d','r','e','f'}, dump_dref_atom},
2373 | };
2374 |
2375 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2376 | }
2377 |
2378 | static void dump_vmhd_atom()
2379 | {
2380 | uint8_t version;
2381 | uint32_t flags;
2382 | dump_full_atom_header(&version, &flags, false);
2383 | if ((flags & 0x0001))
2384 | printf(" (no lean ahead)");
2385 | printf("\n");
2386 |
2387 | if (version != 0x00) {
2388 | dump_unknown_version(version);
2389 | return;
2390 | }
2391 |
2392 |
2393 | uint16_t graphics_mode;
2394 | MOV_CHECK(read_uint16(&graphics_mode));
2395 | indent();
2396 | printf("graphics_mode: %02x\n", graphics_mode);
2397 |
2398 | uint16_t opcolor_r, opcolor_g, opcolor_b;
2399 | MOV_CHECK(read_uint16(&opcolor_r));
2400 | MOV_CHECK(read_uint16(&opcolor_g));
2401 | MOV_CHECK(read_uint16(&opcolor_b));
2402 | indent();
2403 | printf("opcolor: ");
2404 | dump_color(opcolor_r, opcolor_g, opcolor_b);
2405 | printf("\n");
2406 | }
2407 |
2408 | static void dump_smhd_atom()
2409 | {
2410 | uint8_t version;
2411 | uint32_t flags;
2412 | dump_full_atom_header(&version, &flags);
2413 |
2414 | if (version != 0x00) {
2415 | dump_unknown_version(version);
2416 | return;
2417 | }
2418 |
2419 |
2420 | int16_t balance;
2421 | MOV_CHECK(read_int16(&balance));
2422 | indent();
2423 | printf("balance: ");
2424 | dump_int16_fp(balance, 8);
2425 | printf("\n");
2426 |
2427 | uint16_t reserved;
2428 | MOV_CHECK(read_uint16(&reserved));
2429 | indent();
2430 | printf("reserved: ");
2431 | dump_uint16(reserved, true);
2432 | printf("\n");
2433 | }
2434 |
2435 | static void dump_gmin_atom()
2436 | {
2437 | uint8_t version;
2438 | uint32_t flags;
2439 | dump_full_atom_header(&version, &flags, false);
2440 | if ((flags & 0x0001))
2441 | printf(" (no lean ahead)");
2442 | printf("\n");
2443 |
2444 | if (version != 0x00) {
2445 | dump_unknown_version(version);
2446 | return;
2447 | }
2448 |
2449 |
2450 | uint16_t graphics_mode;
2451 | MOV_CHECK(read_uint16(&graphics_mode));
2452 | indent();
2453 | printf("graphics_mode: %02x\n", graphics_mode);
2454 |
2455 | uint16_t opcolor_r, opcolor_g, opcolor_b;
2456 | MOV_CHECK(read_uint16(&opcolor_r));
2457 | MOV_CHECK(read_uint16(&opcolor_g));
2458 | MOV_CHECK(read_uint16(&opcolor_b));
2459 | indent();
2460 | printf("opcolor: ");
2461 | dump_color(opcolor_r, opcolor_g, opcolor_b);
2462 | printf("\n");
2463 |
2464 | int16_t balance;
2465 | MOV_CHECK(read_int16(&balance));
2466 | indent();
2467 | printf("balance: ");
2468 | dump_int16_fp(balance, 8);
2469 | printf("\n");
2470 |
2471 | uint16_t reserved;
2472 | MOV_CHECK(read_uint16(&reserved));
2473 | indent();
2474 | printf("reserved: ");
2475 | dump_uint16(reserved, true);
2476 | printf("\n");
2477 | }
2478 |
2479 | static void dump_tcmi_atom()
2480 | {
2481 | uint8_t version;
2482 | uint32_t flags;
2483 | dump_full_atom_header(&version, &flags, false);
2484 | if ((flags & 0x0001))
2485 | printf(" (no lean ahead)");
2486 | printf("\n");
2487 |
2488 | if (version != 0x00) {
2489 | dump_unknown_version(version);
2490 | return;
2491 | }
2492 |
2493 |
2494 | uint16_t text_font;
2495 | MOV_CHECK(read_uint16(&text_font));
2496 | indent();
2497 | printf("text_font: %02x\n", text_font);
2498 |
2499 | uint16_t text_face;
2500 | MOV_CHECK(read_uint16(&text_face));
2501 | indent();
2502 | printf("text_face: %02x\n", text_face);
2503 |
2504 | uint32_t text_size;
2505 | MOV_CHECK(read_uint32(&text_size));
2506 | indent();
2507 | printf("text_size: ");
2508 | dump_uint32_fp(text_size, 16);
2509 | printf("\n");
2510 |
2511 | uint16_t text_color_r, text_color_g, text_color_b;
2512 | MOV_CHECK(read_uint16(&text_color_r));
2513 | MOV_CHECK(read_uint16(&text_color_g));
2514 | MOV_CHECK(read_uint16(&text_color_b));
2515 | indent();
2516 | printf("text_color: ");
2517 | dump_color(text_color_r, text_color_g, text_color_b);
2518 | printf("\n");
2519 |
2520 | uint16_t bg_color_r, bg_color_g, bg_color_b;
2521 | MOV_CHECK(read_uint16(&bg_color_r));
2522 | MOV_CHECK(read_uint16(&bg_color_g));
2523 | MOV_CHECK(read_uint16(&bg_color_b));
2524 | indent();
2525 | printf("background_color: ");
2526 | dump_color(bg_color_r, bg_color_g, bg_color_b);
2527 | printf("\n");
2528 |
2529 | uint8_t font_name_size;
2530 | MOV_CHECK(read_uint8(&font_name_size));
2531 | indent();
2532 | printf("font_name: ");
2533 | dump_string(font_name_size, 2);
2534 | }
2535 |
2536 | static void dump_tmcd_atom()
2537 | {
2538 | static const DumpFuncMap dump_func_map[] =
2539 | {
2540 | {{'t','c','m','i'}, dump_tcmi_atom},
2541 | };
2542 |
2543 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2544 | }
2545 |
2546 | static void dump_gmhd_atom()
2547 | {
2548 | static const DumpFuncMap dump_func_map[] =
2549 | {
2550 | {{'g','m','i','n'}, dump_gmin_atom},
2551 | {{'t','m','c','d'}, dump_tmcd_atom},
2552 | };
2553 |
2554 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2555 | }
2556 |
2557 | static void dump_minf_atom()
2558 | {
2559 | static const DumpFuncMap dump_func_map[] =
2560 | {
2561 | {{'v','m','h','d'}, dump_vmhd_atom},
2562 | {{'s','m','h','d'}, dump_smhd_atom},
2563 | {{'g','m','h','d'}, dump_gmhd_atom},
2564 | {{'h','d','l','r'}, dump_hdlr_atom},
2565 | {{'d','i','n','f'}, dump_dinf_atom},
2566 | {{'s','t','b','l'}, dump_stbl_atom},
2567 | };
2568 |
2569 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2570 | }
2571 |
2572 | static void dump_mdhd_atom()
2573 | {
2574 | uint8_t version;
2575 | uint32_t flags;
2576 | dump_full_atom_header(&version, &flags);
2577 |
2578 | if (version != 0x00 && version != 0x01) {
2579 | dump_unknown_version(version);
2580 | return;
2581 | }
2582 |
2583 |
2584 | if (version == 0x00) {
2585 | uint32_t creation_time;
2586 | MOV_CHECK(read_uint32(&creation_time));
2587 | indent();
2588 | printf("creation_time: ");
2589 | dump_timestamp(creation_time);
2590 | printf("\n");
2591 |
2592 | uint32_t modification_time;
2593 | MOV_CHECK(read_uint32(&modification_time));
2594 | indent();
2595 | printf("modification_time: ");
2596 | dump_timestamp(modification_time);
2597 | printf("\n");
2598 | } else {
2599 | uint64_t creation_time;
2600 | MOV_CHECK(read_uint64(&creation_time));
2601 | indent();
2602 | printf("creation_time: ");
2603 | dump_timestamp(creation_time);
2604 | printf("\n");
2605 |
2606 | uint64_t modification_time;
2607 | MOV_CHECK(read_uint64(&modification_time));
2608 | indent();
2609 | printf("modification_time: ");
2610 | dump_timestamp(modification_time);
2611 | printf("\n");
2612 | }
2613 |
2614 | uint32_t timescale;
2615 | MOV_CHECK(read_uint32(×cale));
2616 | indent();
2617 | printf("timescale: %u\n", timescale);
2618 |
2619 | if (version == 0x00) {
2620 | int32_t duration;
2621 | MOV_CHECK(read_int32(&duration));
2622 | indent();
2623 | printf("duration: %d (%f sec)\n", duration, get_duration_sec(duration, timescale));
2624 | } else {
2625 | int64_t duration;
2626 | MOV_CHECK(read_int64(&duration));
2627 | indent();
2628 | printf("duration: %" PRId64 " (%f sec)\n", duration, get_duration_sec(duration, timescale));
2629 | }
2630 |
2631 | uint16_t language;
2632 | MOV_CHECK(read_uint16(&language));
2633 | indent();
2634 | printf("language: ");
2635 | dump_language(language);
2636 | printf("\n");
2637 |
2638 | uint16_t quality;
2639 | MOV_CHECK(read_uint16(&quality));
2640 | indent();
2641 | printf("quality: %u\n", quality);
2642 | }
2643 |
2644 | static void dump_mdia_atom()
2645 | {
2646 | static const DumpFuncMap dump_func_map[] =
2647 | {
2648 | {{'m','d','h','d'}, dump_mdhd_atom},
2649 | {{'h','d','l','r'}, dump_hdlr_atom},
2650 | {{'m','i','n','f'}, dump_minf_atom},
2651 | };
2652 |
2653 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2654 | }
2655 |
2656 | static void dump_keys_atom()
2657 | {
2658 | static uint32_t mdta_key_namespace = MKTAG("mdta");
2659 |
2660 | g_meta_keys.clear();
2661 |
2662 | uint8_t version;
2663 | uint32_t flags;
2664 | dump_full_atom_header(&version, &flags);
2665 |
2666 | if (version != 0x00) {
2667 | dump_unknown_version(version);
2668 | return;
2669 | }
2670 |
2671 |
2672 | uint32_t count;
2673 | MOV_CHECK(read_uint32(&count));
2674 |
2675 | indent();
2676 | printf("key_values (");
2677 | dump_uint32(count, false);
2678 | printf("):\n");
2679 |
2680 | unsigned char key_value_buffer[129];
2681 | uint32_t total_count = 0;
2682 | for (total_count = 0; total_count < count; total_count++) {
2683 | indent(2);
2684 |
2685 | uint32_t key_size;
2686 | uint32_t key_value_size;
2687 | MOV_CHECK(read_uint32(&key_size));
2688 | MOV_CHECK(key_size >= 8);
2689 | uint32_t key_namespace;
2690 | MOV_CHECK(read_uint32(&key_namespace));
2691 |
2692 | printf("%4u ", total_count + 1);
2693 | dump_uint32(key_size, true);
2694 |
2695 | key_value_size = key_size - 8;
2696 |
2697 | if (key_namespace == mdta_key_namespace && key_value_size < sizeof(key_value_buffer)) {
2698 | MOV_CHECK(read_bytes(key_value_buffer, key_value_size));
2699 |
2700 | size_t i;
2701 | for (i = 0; i < key_value_size; i++) {
2702 | if (!isprint(key_value_buffer[i]))
2703 | break;
2704 | }
2705 | if (i >= key_value_size) {
2706 | key_value_buffer[key_value_size] = '\0';
2707 | printf(" mdta '%s'\n", (char*)key_value_buffer);
2708 | g_meta_keys.push_back((char*)key_value_buffer);
2709 | } else {
2710 | printf(" ");
2711 | dump_uint32_chars(key_namespace);
2712 | printf("\n");
2713 | dump_bytes(key_value_buffer, key_value_size, 4);
2714 | g_meta_keys.push_back("");
2715 | }
2716 | } else {
2717 | printf(" ");
2718 | dump_uint32_chars(key_namespace);
2719 | printf("\n");
2720 | dump_bytes(key_value_size, 4);
2721 | g_meta_keys.push_back("");
2722 | }
2723 | }
2724 | }
2725 |
2726 | static void dump_ilst_data_atom()
2727 | {
2728 | dump_atom_header();
2729 |
2730 | uint8_t type_field_1;
2731 | uint32_t type_field_2;
2732 | MOV_CHECK(read_uint8(&type_field_1));
2733 | MOV_CHECK(read_uint24(&type_field_2));
2734 |
2735 | indent();
2736 | printf("type 1: %u\n", type_field_1);
2737 | indent();
2738 | printf("type 2: %u\n", type_field_2);
2739 |
2740 | uint16_t locale;
2741 | uint16_t country;
2742 | MOV_CHECK(read_uint16(&locale));
2743 | MOV_CHECK(read_uint16(&country));
2744 |
2745 | indent();
2746 | printf("locale: %u\n", locale);
2747 | indent();
2748 | printf("country: %u\n", country);
2749 |
2750 | indent();
2751 | if (type_field_1 == 0 &&
2752 | (type_field_2 == 1 || // utf-8
2753 | type_field_2 == 21 || // be signed integer
2754 | type_field_2 == 22)) // be unsigned integer
2755 | {
2756 | if (type_field_2 == 21) {
2757 | if (CURRENT_ATOM.rem_size == 8) {
2758 | int64_t value;
2759 | MOV_CHECK(read_int64(&value));
2760 | printf("value (int64): %" PRId64 "\n", value);
2761 | } else if (CURRENT_ATOM.rem_size == 4) {
2762 | int32_t value;
2763 | MOV_CHECK(read_int32(&value));
2764 | printf("value (int32): %d\n", value);
2765 | } else if (CURRENT_ATOM.rem_size == 3) {
2766 | int32_t value;
2767 | MOV_CHECK(read_int24(&value));
2768 | printf("value (int24): %d\n", value);
2769 | } else if (CURRENT_ATOM.rem_size == 2) {
2770 | int16_t value;
2771 | MOV_CHECK(read_int16(&value));
2772 | printf("value (int16): %d\n", value);
2773 | } else if (CURRENT_ATOM.rem_size == 1) {
2774 | int8_t value;
2775 | MOV_CHECK(read_int8(&value));
2776 | printf("value (int8): %d\n", value);
2777 | } else {
2778 | printf("value:\n");
2779 | dump_bytes(CURRENT_ATOM.rem_size, 4);
2780 | }
2781 | } else if (type_field_2 == 22) {
2782 | if (CURRENT_ATOM.rem_size == 8) {
2783 | uint64_t value;
2784 | MOV_CHECK(read_uint64(&value));
2785 | printf("value (uint64): %" PRIu64 "\n", value);
2786 | } else if (CURRENT_ATOM.rem_size == 4) {
2787 | uint32_t value;
2788 | MOV_CHECK(read_uint32(&value));
2789 | printf("value (uint32): %u\n", value);
2790 | } else if (CURRENT_ATOM.rem_size == 3) {
2791 | uint32_t value;
2792 | MOV_CHECK(read_uint24(&value));
2793 | printf("value (uint24): %u\n", value);
2794 | } else if (CURRENT_ATOM.rem_size == 2) {
2795 | uint16_t value;
2796 | MOV_CHECK(read_uint16(&value));
2797 | printf("value (uint16): %u\n", value);
2798 | } else if (CURRENT_ATOM.rem_size == 1) {
2799 | uint8_t value;
2800 | MOV_CHECK(read_uint8(&value));
2801 | printf("value (uint8): %u\n", value);
2802 | } else {
2803 | printf("value:\n");
2804 | dump_bytes(CURRENT_ATOM.rem_size, 4);
2805 | }
2806 | } else { // type_field_2 == 1
2807 | uint64_t utf8_value_size = CURRENT_ATOM.rem_size;
2808 | unsigned char utf8_value_buffer[129];
2809 | if (utf8_value_size == 0) {
2810 | printf("value: ''\n");
2811 | } else if (utf8_value_size < sizeof(utf8_value_buffer)) {
2812 | MOV_CHECK(read_bytes(utf8_value_buffer, (uint32_t)utf8_value_size));
2813 |
2814 | size_t i;
2815 | for (i = 0; i < utf8_value_size; i++) {
2816 | if (!isprint(utf8_value_buffer[i]))
2817 | break;
2818 | }
2819 | if (i >= utf8_value_size) {
2820 | utf8_value_buffer[utf8_value_size] = '\0';
2821 | printf("value: '%s'\n", (char*)utf8_value_buffer);
2822 | } else {
2823 | printf("value:\n");
2824 | dump_bytes(utf8_value_buffer, utf8_value_size, 4);
2825 | }
2826 | } else {
2827 | printf("value:\n");
2828 | dump_bytes(utf8_value_size, 4);
2829 | }
2830 | }
2831 | }
2832 | else
2833 | {
2834 | printf("value:\n");
2835 | dump_bytes(CURRENT_ATOM.rem_size, 4);
2836 | }
2837 | }
2838 |
2839 | static void dump_ilst_atom()
2840 | {
2841 | static const DumpFuncMap dump_func_map[] =
2842 | {
2843 | {{'d','a','t','a'}, dump_ilst_data_atom},
2844 | };
2845 |
2846 | dump_atom_header();
2847 |
2848 | while (CURRENT_ATOM.rem_size > 0) {
2849 | uint32_t element_size;
2850 | MOV_CHECK(read_uint32(&element_size));
2851 | uint32_t key_index;
2852 | MOV_CHECK(read_uint32(&key_index));
2853 | MOV_CHECK(key_index >= 1 && (g_meta_keys.empty() || key_index <= g_meta_keys.size()));
2854 |
2855 | indent();
2856 | printf("size: ");
2857 | dump_uint32_size(element_size);
2858 | printf("\n");
2859 | indent();
2860 | printf("key_index: %u", key_index);
2861 | if (!g_meta_keys.empty()) {
2862 | if (!g_meta_keys[key_index - 1].empty())
2863 | printf(" ('%s')", g_meta_keys[key_index - 1].c_str());
2864 | }
2865 | printf("\n");
2866 |
2867 |
2868 | push_atom();
2869 |
2870 | if (!read_atom_start())
2871 | break;
2872 |
2873 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2874 |
2875 | pop_atom();
2876 | }
2877 | }
2878 |
2879 | static void dump_clefprofenof_atom()
2880 | {
2881 | uint8_t version;
2882 | uint32_t flags;
2883 | dump_full_atom_header(&version, &flags);
2884 |
2885 | if (version != 0x00) {
2886 | dump_unknown_version(version);
2887 | return;
2888 | }
2889 |
2890 |
2891 | uint32_t fp_width;
2892 | MOV_CHECK(read_uint32(&fp_width));
2893 | indent();
2894 | printf("width: ");
2895 | dump_uint32_fp(fp_width, 16);
2896 | printf("\n");
2897 |
2898 | uint32_t fp_height;
2899 | MOV_CHECK(read_uint32(&fp_height));
2900 | indent();
2901 | printf("height: ");
2902 | dump_uint32_fp(fp_height, 16);
2903 | printf("\n");
2904 | }
2905 |
2906 | static void dump_tapt_atom()
2907 | {
2908 | static const DumpFuncMap dump_func_map[] =
2909 | {
2910 | {{'c','l','e','f'}, dump_clefprofenof_atom},
2911 | {{'p','r','o','f'}, dump_clefprofenof_atom},
2912 | {{'e','n','o','f'}, dump_clefprofenof_atom},
2913 | };
2914 |
2915 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2916 | }
2917 |
2918 | static void dump_tref_child_atom()
2919 | {
2920 | dump_atom_header();
2921 |
2922 | uint32_t count = (uint32_t)(CURRENT_ATOM.rem_size / 4);
2923 |
2924 | indent();
2925 | printf("track_ids (");
2926 | dump_uint32(count, false);
2927 | printf("):\n");
2928 |
2929 | indent(4);
2930 | if (count < 0xffff)
2931 | printf(" i");
2932 | else if (count < 0xffffff)
2933 | printf(" i");
2934 | else
2935 | printf(" i");
2936 | printf(" id\n");
2937 |
2938 |
2939 | uint32_t i;
2940 | for (i = 0; i < count; i++) {
2941 | uint32_t track_id;
2942 | MOV_CHECK(read_uint32(&track_id));
2943 |
2944 | indent(4);
2945 | dump_uint32_index(count, i);
2946 |
2947 | printf(" ");
2948 | dump_uint32(track_id, true);
2949 | }
2950 | printf("\n");
2951 | }
2952 |
2953 | static void dump_tref_atom()
2954 | {
2955 | static const DumpFuncMap dump_func_map[] =
2956 | {
2957 | {{'\0','\0','\0','\0'}, dump_tref_child_atom},
2958 | };
2959 |
2960 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
2961 | }
2962 |
2963 | static void dump_elst_atom()
2964 | {
2965 | uint8_t version;
2966 | uint32_t flags;
2967 | dump_full_atom_header(&version, &flags);
2968 |
2969 | if (version != 0x00 && version != 0x01) {
2970 | dump_unknown_version(version);
2971 | return;
2972 | }
2973 |
2974 |
2975 | uint32_t count;
2976 | MOV_CHECK(read_uint32(&count));
2977 |
2978 | indent();
2979 | printf("edit_list_table (");
2980 | dump_uint32(count, false);
2981 | printf("):\n");
2982 |
2983 | indent(4);
2984 | if (count < 0xffff)
2985 | printf(" i");
2986 | else if (count < 0xffffff)
2987 | printf(" i");
2988 | else
2989 | printf(" i");
2990 | if (version == 0)
2991 | printf(" duration time rate\n");
2992 | else
2993 | printf(" duration time rate\n");
2994 |
2995 |
2996 | uint32_t i = 0;
2997 | for (i = 0; i < count; i++) {
2998 | indent(4);
2999 | dump_uint32_index(count, i);
3000 | if (version == 0) {
3001 | uint32_t track_duration;
3002 | MOV_CHECK(read_uint32(&track_duration));
3003 | int32_t media_time;
3004 | MOV_CHECK(read_int32(&media_time));
3005 |
3006 | printf(" ");
3007 | dump_uint32(track_duration, false);
3008 |
3009 | printf(" ");
3010 | dump_int32(media_time);
3011 | } else {
3012 | uint64_t track_duration;
3013 | MOV_CHECK(read_uint64(&track_duration));
3014 | int64_t media_time;
3015 | MOV_CHECK(read_int64(&media_time));
3016 |
3017 | printf(" ");
3018 | dump_uint64(track_duration, false);
3019 |
3020 | printf(" ");
3021 | dump_int64(media_time);
3022 | }
3023 |
3024 | uint32_t media_rate;
3025 | MOV_CHECK(read_uint32(&media_rate));
3026 |
3027 | printf(" ");
3028 | dump_uint32_fp(media_rate, 16);
3029 | printf("\n");
3030 | }
3031 | }
3032 |
3033 | static void dump_edts_atom()
3034 | {
3035 | static const DumpFuncMap dump_func_map[] =
3036 | {
3037 | {{'e','l','s','t'}, dump_elst_atom},
3038 | };
3039 |
3040 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3041 | }
3042 |
3043 | static void dump_meta_atom()
3044 | {
3045 | static const DumpFuncMap dump_func_map[] =
3046 | {
3047 | {{'h','d','l','r'}, dump_hdlr_atom},
3048 | {{'k','e','y','s'}, dump_keys_atom},
3049 | {{'i','l','s','t'}, dump_ilst_atom},
3050 | };
3051 |
3052 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3053 | }
3054 |
3055 | static void dump_tkhd_atom()
3056 | {
3057 | uint8_t version;
3058 | uint32_t flags;
3059 | dump_full_atom_header(&version, &flags);
3060 |
3061 | if (version != 0x00 && version != 0x01) {
3062 | dump_unknown_version(version);
3063 | return;
3064 | }
3065 |
3066 |
3067 | if (version == 0x00) {
3068 | uint32_t creation_time;
3069 | MOV_CHECK(read_uint32(&creation_time));
3070 | indent();
3071 | printf("creation_time: ");
3072 | dump_timestamp(creation_time);
3073 | printf("\n");
3074 |
3075 | uint32_t modification_time;
3076 | MOV_CHECK(read_uint32(&modification_time));
3077 | indent();
3078 | printf("modification_time: ");
3079 | dump_timestamp(modification_time);
3080 | printf("\n");
3081 | } else {
3082 | uint64_t creation_time;
3083 | MOV_CHECK(read_uint64(&creation_time));
3084 | indent();
3085 | printf("creation_time: ");
3086 | dump_timestamp(creation_time);
3087 | printf("\n");
3088 |
3089 | uint64_t modification_time;
3090 | MOV_CHECK(read_uint64(&modification_time));
3091 | indent();
3092 | printf("modification_time: ");
3093 | dump_timestamp(modification_time);
3094 | printf("\n");
3095 | }
3096 |
3097 | uint32_t track_id;
3098 | MOV_CHECK(read_uint32(&track_id));
3099 | indent();
3100 | printf("track_id: %u\n", track_id);
3101 |
3102 | uint32_t reserved_uint32;
3103 | MOV_CHECK(read_uint32(&reserved_uint32));
3104 | indent();
3105 | printf("reserved: ");
3106 | dump_uint32(reserved_uint32, true);
3107 | printf("\n");
3108 |
3109 | if (version == 0x00) {
3110 | int32_t duration;
3111 | MOV_CHECK(read_int32(&duration));
3112 | indent();
3113 | printf("duration: %d (%f sec)\n", duration, get_duration_sec(duration, g_movie_timescale));
3114 | } else {
3115 | int64_t duration;
3116 | MOV_CHECK(read_int64(&duration));
3117 | indent();
3118 | printf("duration: %" PRId64 " (%f sec)\n", duration, get_duration_sec(duration, g_movie_timescale));
3119 | }
3120 |
3121 | unsigned char reserved_bytes[8];
3122 | MOV_CHECK(read_bytes(reserved_bytes, 8));
3123 | indent();
3124 | printf("reserved: ");
3125 | dump_inline_bytes(reserved_bytes, 8);
3126 | printf("\n");
3127 |
3128 | uint16_t layer;
3129 | MOV_CHECK(read_uint16(&layer));
3130 | indent();
3131 | printf("layer: %u\n", layer);
3132 |
3133 | uint16_t alternate_group;
3134 | MOV_CHECK(read_uint16(&alternate_group));
3135 | indent();
3136 | printf("alternate_group: %u\n", alternate_group);
3137 |
3138 | uint16_t volume;
3139 | MOV_CHECK(read_uint16(&volume));
3140 | indent();
3141 | printf("volume: ");
3142 | dump_uint16_fp(volume, 8);
3143 | printf("\n");
3144 |
3145 | uint16_t reserved_uint16;
3146 | MOV_CHECK(read_uint16(&reserved_uint16));
3147 | indent();
3148 | printf("reserved: ");
3149 | dump_uint16(reserved_uint16, true);
3150 | printf("\n");
3151 |
3152 | uint32_t matrix[9];
3153 | MOV_CHECK(read_matrix(matrix));
3154 | indent();
3155 | printf("matrix: \n");
3156 | dump_matrix(matrix, 2);
3157 |
3158 | uint32_t track_width;
3159 | MOV_CHECK(read_uint32(&track_width));
3160 | indent();
3161 | printf("track_width: ");
3162 | dump_uint32_fp(track_width, 16);
3163 | printf("\n");
3164 |
3165 | uint32_t track_height;
3166 | MOV_CHECK(read_uint32(&track_height));
3167 | indent();
3168 | printf("track_height: ");
3169 | dump_uint32_fp(track_height, 16);
3170 | printf("\n");
3171 | }
3172 |
3173 | static void dump_udta_name_atom()
3174 | {
3175 | dump_atom_header();
3176 |
3177 | indent();
3178 | printf("value: (len=%" PRId64 ") ", CURRENT_ATOM.rem_size);
3179 | dump_string(CURRENT_ATOM.rem_size, 2);
3180 | }
3181 |
3182 | static void dump_udta_atom()
3183 | {
3184 | static const DumpFuncMap dump_func_map[] =
3185 | {
3186 | {{'n','a','m','e'}, dump_udta_name_atom},
3187 | {{(char)0xa9,'\0','\0','\0'}, dump_international_string_atom},
3188 | };
3189 |
3190 | dump_atom_header();
3191 |
3192 | while (CURRENT_ATOM.rem_size > 8) {
3193 | push_atom();
3194 |
3195 | if (!read_atom_start())
3196 | break;
3197 |
3198 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3199 |
3200 | pop_atom();
3201 | }
3202 | }
3203 |
3204 | static void dump_trak_atom()
3205 | {
3206 | static const DumpFuncMap dump_func_map[] =
3207 | {
3208 | {{'t','k','h','d'}, dump_tkhd_atom},
3209 | {{'t','a','p','t'}, dump_tapt_atom},
3210 | {{'e','d','t','s'}, dump_edts_atom},
3211 | {{'t','r','e','f'}, dump_tref_atom},
3212 | {{'m','d','i','a'}, dump_mdia_atom},
3213 | {{'m','e','t','a'}, dump_meta_atom},
3214 | {{'u','d','t','a'}, dump_udta_atom},
3215 | };
3216 |
3217 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3218 | }
3219 |
3220 | static void dump_mvhd_atom()
3221 | {
3222 | uint8_t version;
3223 | uint32_t flags;
3224 | dump_full_atom_header(&version, &flags);
3225 |
3226 | if (version != 0x00 && version != 0x01) {
3227 | dump_unknown_version(version);
3228 | return;
3229 | }
3230 |
3231 |
3232 | if (version == 0x00) {
3233 | uint32_t creation_time;
3234 | MOV_CHECK(read_uint32(&creation_time));
3235 | indent();
3236 | printf("creation_time: ");
3237 | dump_timestamp(creation_time);
3238 | printf("\n");
3239 |
3240 | uint32_t modification_time;
3241 | MOV_CHECK(read_uint32(&modification_time));
3242 | indent();
3243 | printf("modification_time: ");
3244 | dump_timestamp(modification_time);
3245 | printf("\n");
3246 | } else {
3247 | uint64_t creation_time;
3248 | MOV_CHECK(read_uint64(&creation_time));
3249 | indent();
3250 | printf("creation_time: ");
3251 | dump_timestamp(creation_time);
3252 | printf("\n");
3253 |
3254 | uint64_t modification_time;
3255 | MOV_CHECK(read_uint64(&modification_time));
3256 | indent();
3257 | printf("modification_time: ");
3258 | dump_timestamp(modification_time);
3259 | printf("\n");
3260 | }
3261 |
3262 | MOV_CHECK(read_uint32(&g_movie_timescale));
3263 | indent();
3264 | printf("timescale: %u\n", g_movie_timescale);
3265 |
3266 | if (version == 0x00) {
3267 | int32_t duration;
3268 | MOV_CHECK(read_int32(&duration));
3269 | indent();
3270 | printf("duration: %d (%f sec)\n", duration, get_duration_sec(duration, g_movie_timescale));
3271 | } else {
3272 | int64_t duration;
3273 | MOV_CHECK(read_int64(&duration));
3274 | indent();
3275 | printf("duration: %" PRId64 " (%f sec)\n", duration, get_duration_sec(duration, g_movie_timescale));
3276 | }
3277 |
3278 | uint32_t preferred_rate;
3279 | MOV_CHECK(read_uint32(&preferred_rate));
3280 | indent();
3281 | printf("preferred_rate: ");
3282 | dump_uint32_fp(preferred_rate, 16);
3283 | printf("\n");
3284 |
3285 | uint16_t preferred_volume;
3286 | MOV_CHECK(read_uint16(&preferred_volume));
3287 | indent();
3288 | printf("preferred_volume: ");
3289 | dump_uint16_fp(preferred_volume, 8);
3290 | printf("\n");
3291 |
3292 | unsigned char bytes[10];
3293 | MOV_CHECK(read_bytes(bytes, 10));
3294 | indent();
3295 | printf("reserved: ");
3296 | dump_inline_bytes(bytes, 10);
3297 | printf("\n");
3298 |
3299 | uint32_t matrix[9];
3300 | MOV_CHECK(read_matrix(matrix));
3301 | indent();
3302 | printf("matrix: \n");
3303 | dump_matrix(matrix, 2);
3304 |
3305 |
3306 | // NOTE/TODO: spec. does not clearly state the xxx_time values are in timescale units
3307 |
3308 | uint32_t preview_time;
3309 | MOV_CHECK(read_uint32(&preview_time));
3310 | indent();
3311 | printf("preview_time: %u\n", preview_time);
3312 |
3313 | uint32_t preview_duration;
3314 | MOV_CHECK(read_uint32(&preview_duration));
3315 | indent();
3316 | printf("preview_duration: %u (%f sec)\n", preview_duration, get_duration_sec(preview_duration, g_movie_timescale));
3317 |
3318 | uint32_t poster_time;
3319 | MOV_CHECK(read_uint32(&poster_time));
3320 | indent();
3321 | printf("poster_time: %u\n", poster_time);
3322 |
3323 | uint32_t selection_time;
3324 | MOV_CHECK(read_uint32(&selection_time));
3325 | indent();
3326 | printf("selection_time: %u\n", selection_time);
3327 |
3328 | uint32_t selection_duration;
3329 | MOV_CHECK(read_uint32(&selection_duration));
3330 | indent();
3331 | printf("selection_duration: %u (%f sec)\n", selection_duration, get_duration_sec(selection_duration, g_movie_timescale));
3332 |
3333 | uint32_t current_time;
3334 | MOV_CHECK(read_uint32(¤t_time));
3335 | indent();
3336 | printf("current_time: %u\n", current_time);
3337 |
3338 | uint32_t next_track_id;
3339 | MOV_CHECK(read_uint32(&next_track_id));
3340 | indent();
3341 | printf("next_track_id: %u\n", next_track_id);
3342 | }
3343 |
3344 | static void dump_mehd_atom()
3345 | {
3346 | uint8_t version;
3347 | uint32_t flags;
3348 | dump_full_atom_header(&version, &flags);
3349 |
3350 |
3351 | if (version == 0) {
3352 | uint32_t fragment_duration;
3353 | MOV_CHECK(read_uint32(&fragment_duration));
3354 | indent();
3355 | printf("fragment_duration: ");
3356 | dump_uint32(fragment_duration, true);
3357 | printf("\n");
3358 | } else {
3359 | uint64_t fragment_duration;
3360 | MOV_CHECK(read_uint64(&fragment_duration));
3361 | indent();
3362 | printf("fragment_duration: ");
3363 | dump_uint64(fragment_duration, true);
3364 | printf("\n");
3365 | }
3366 | }
3367 |
3368 | static void dump_trex_atom()
3369 | {
3370 | uint8_t version;
3371 | uint32_t flags;
3372 | dump_full_atom_header(&version, &flags);
3373 |
3374 |
3375 | uint32_t track_id;
3376 | MOV_CHECK(read_uint32(&track_id));
3377 | indent();
3378 | printf("track_id: %u\n", track_id);
3379 |
3380 | uint32_t default_sample_description_index;
3381 | MOV_CHECK(read_uint32(&default_sample_description_index));
3382 | indent();
3383 | printf("default_sample_description_index: %u\n", default_sample_description_index);
3384 |
3385 | uint32_t default_sample_duration;
3386 | MOV_CHECK(read_uint32(&default_sample_duration));
3387 | indent();
3388 | printf("default_sample_duration: ");
3389 | dump_uint32(default_sample_duration, true);
3390 | printf("\n");
3391 |
3392 | uint32_t default_sample_size;
3393 | MOV_CHECK(read_uint32(&default_sample_size));
3394 | indent();
3395 | printf("default_sample_size: ");
3396 | dump_uint32(default_sample_size, true);
3397 | printf("\n");
3398 |
3399 | uint32_t default_sample_flags;
3400 | MOV_CHECK(read_uint32(&default_sample_flags));
3401 | indent();
3402 | printf("default_sample_flags: 0x%08x (", default_sample_flags);
3403 | dump_fragment_sample_flags(default_sample_flags);
3404 | printf(")\n");
3405 | }
3406 |
3407 | static void dump_mvex_atom()
3408 | {
3409 | static const DumpFuncMap dump_func_map[] =
3410 | {
3411 | {{'m','e','h','d'}, dump_mehd_atom},
3412 | {{'t','r','e','x'}, dump_trex_atom},
3413 | };
3414 |
3415 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3416 | }
3417 |
3418 | static void dump_moov_atom()
3419 | {
3420 | static const DumpFuncMap dump_func_map[] =
3421 | {
3422 | {{'m','v','h','d'}, dump_mvhd_atom},
3423 | {{'t','r','a','k'}, dump_trak_atom},
3424 | {{'m','e','t','a'}, dump_meta_atom},
3425 | {{'u','d','t','a'}, dump_udta_atom},
3426 | {{'m','v','e','x'}, dump_mvex_atom},
3427 | };
3428 |
3429 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3430 | }
3431 |
3432 | static void dump_sidx_atom()
3433 | {
3434 | uint8_t version;
3435 | uint32_t flags;
3436 | dump_full_atom_header(&version, &flags);
3437 |
3438 |
3439 | uint32_t reference_id;
3440 | MOV_CHECK(read_uint32(&reference_id));
3441 | indent();
3442 | printf("reference_id: %u\n", reference_id);
3443 |
3444 | uint32_t timescale;
3445 | MOV_CHECK(read_uint32(×cale));
3446 | indent();
3447 | printf("timescale: %u\n", timescale);
3448 |
3449 | if (version == 0x00) {
3450 | uint32_t earliest_pres_time;
3451 | MOV_CHECK(read_uint32(&earliest_pres_time));
3452 | indent();
3453 | printf("earliest_presentation_time: %u\n", earliest_pres_time);
3454 |
3455 | uint32_t first_offset;
3456 | MOV_CHECK(read_uint32(&first_offset));
3457 | indent();
3458 | printf("first_offset: %u\n", first_offset);
3459 | } else {
3460 | uint64_t earliest_pres_time;
3461 | MOV_CHECK(read_uint64(&earliest_pres_time));
3462 | indent();
3463 | printf("earliest_presentation_time: %" PRIu64 "\n", earliest_pres_time);
3464 |
3465 | uint64_t first_offset;
3466 | MOV_CHECK(read_uint64(&first_offset));
3467 | indent();
3468 | printf("first_offset: %" PRIu64 "\n", first_offset);
3469 | }
3470 |
3471 | uint16_t reserved_uint16;
3472 | MOV_CHECK(read_uint16(&reserved_uint16));
3473 | indent();
3474 | printf("reserved: ");
3475 | dump_uint16(reserved_uint16, true);
3476 | printf("\n");
3477 |
3478 | uint16_t num_entries;
3479 | MOV_CHECK(read_uint16(&num_entries));
3480 | indent();
3481 | printf("references (");
3482 | dump_uint16(num_entries, false);
3483 | printf("):\n");
3484 |
3485 | indent(4);
3486 | if (num_entries < 0xff)
3487 | printf("%2s", "i");
3488 | else
3489 | printf("%4s", "i");
3490 | printf("%10s%12s%14s%16s%10s%16s\n",
3491 | "ref_type", "ref_size", "subseg_dur", "start_with_sap", "sap_type", "sap_delta_time");
3492 |
3493 | uint16_t i;
3494 | for (i = 0; i < num_entries; i++) {
3495 | uint32_t reference_word;
3496 | MOV_CHECK(read_uint32(&reference_word));
3497 | uint32_t subsegment_duration;
3498 | MOV_CHECK(read_uint32(&subsegment_duration));
3499 | uint32_t sap_word;
3500 | MOV_CHECK(read_uint32(&sap_word));
3501 |
3502 | indent(4);
3503 | dump_uint16_index(num_entries, i);
3504 |
3505 | if (reference_word & 0x80000000)
3506 | printf("%10s", "sidx");
3507 | else
3508 | printf("%10s", "media");
3509 |
3510 | printf("%*c", 2, ' ');
3511 | dump_uint32(reference_word & 0x7fffffff, true);
3512 |
3513 | printf("%*c", 4, ' ');
3514 | dump_uint32(subsegment_duration, true);
3515 |
3516 | if (sap_word & 0x80000000)
3517 | printf("%16s", "true");
3518 | else
3519 | printf("%16s", "false");
3520 |
3521 | printf("%*c", 7, ' ');
3522 | dump_uint8((sap_word >> 28) & ((1 << 3) - 1), false);
3523 |
3524 | printf("%*c", 6, ' ');
3525 | dump_uint32(sap_word & ((1 << 28) - 1), false);
3526 | printf("\n");
3527 | }
3528 | }
3529 |
3530 | static void dump_mfhd_atom()
3531 | {
3532 | uint8_t version;
3533 | uint32_t flags;
3534 | dump_full_atom_header(&version, &flags);
3535 |
3536 |
3537 | uint32_t sequence_number;
3538 | MOV_CHECK(read_uint32(&sequence_number));
3539 | indent();
3540 | printf("sequence_number: %u\n", sequence_number);
3541 | }
3542 |
3543 | static void dump_tfhd_atom()
3544 | {
3545 | uint8_t version;
3546 | uint32_t flags;
3547 | dump_full_atom_header(&version, &flags);
3548 |
3549 |
3550 | uint32_t track_id;
3551 | MOV_CHECK(read_uint32(&track_id));
3552 | indent();
3553 | printf("track_id: %u\n", track_id);
3554 |
3555 | if (flags & 0x000001) {
3556 | uint64_t base_data_offset;
3557 | MOV_CHECK(read_uint64(&base_data_offset));
3558 | indent();
3559 | printf("base_data_offset: %" PRIu64 "\n", base_data_offset);
3560 | }
3561 | if (flags & 0x000002) {
3562 | uint32_t sample_description_index;
3563 | MOV_CHECK(read_uint32(&sample_description_index));
3564 | indent();
3565 | printf("sample_description_index: %u\n", sample_description_index);
3566 | }
3567 | if (flags & 0x000008) {
3568 | uint32_t default_sample_duration;
3569 | MOV_CHECK(read_uint32(&default_sample_duration));
3570 | indent();
3571 | printf("default_sample_duration: %u\n", default_sample_duration);
3572 | }
3573 | if (flags & 0x000010) {
3574 | uint32_t default_sample_size;
3575 | MOV_CHECK(read_uint32(&default_sample_size));
3576 | indent();
3577 | printf("default_sample_size: %u\n", default_sample_size);
3578 | }
3579 | if (flags & 0x000020) {
3580 | uint32_t default_sample_flags;
3581 | MOV_CHECK(read_uint32(&default_sample_flags));
3582 | indent();
3583 | printf("default_sample_flags: 0x%08x (", default_sample_flags);
3584 | dump_fragment_sample_flags(default_sample_flags);
3585 | printf(")\n");
3586 | }
3587 | }
3588 |
3589 | static void dump_trun_atom()
3590 | {
3591 | uint8_t version;
3592 | uint32_t flags;
3593 | dump_full_atom_header(&version, &flags);
3594 |
3595 |
3596 | uint32_t num_entries;
3597 | MOV_CHECK(read_uint32(&num_entries));
3598 |
3599 | if (flags & 0x000001) {
3600 | int32_t data_offset;
3601 | MOV_CHECK(read_int32(&data_offset));
3602 | indent();
3603 | printf("data_offset: %d\n", data_offset);
3604 | }
3605 | if (flags & 0x000004) {
3606 | uint32_t first_sample_flags;
3607 | MOV_CHECK(read_uint32(&first_sample_flags));
3608 | indent();
3609 | printf("first_sample_flags: 0x%08x (", first_sample_flags);
3610 | dump_fragment_sample_flags(first_sample_flags);
3611 | printf(")\n");
3612 | }
3613 |
3614 | if (num_entries > 0) {
3615 | indent();
3616 | printf("samples (");
3617 | dump_uint32(num_entries, false);
3618 | printf("):\n");
3619 |
3620 | indent(4);
3621 | if (num_entries < 0xffff)
3622 | printf("%4s", "i");
3623 | else if (num_entries < 0xffffff)
3624 | printf("%6s", "i");
3625 | else
3626 | printf("%8s", "i");
3627 | printf("%12s%12s%12s%12s\n",
3628 | "duration", "size", "flags", "ct_offset");
3629 |
3630 | uint32_t i;
3631 | for (i = 0; i < num_entries; i++) {
3632 | indent(4);
3633 | dump_uint32_index(num_entries, i);
3634 |
3635 | if (flags & 0x000100) {
3636 | uint32_t sample_duration;
3637 | MOV_CHECK(read_uint32(&sample_duration));
3638 | printf("%*c", 2, ' ');
3639 | dump_uint32(sample_duration, true);
3640 | } else {
3641 | printf("%12s", "x");
3642 | }
3643 | if (flags & 0x000200) {
3644 | uint32_t sample_size;
3645 | MOV_CHECK(read_uint32(&sample_size));
3646 | printf("%*c", 2, ' ');
3647 | dump_uint32(sample_size, true);
3648 | } else {
3649 | printf("%12s", "x");
3650 | }
3651 | if (flags & 0x000400) {
3652 | uint32_t sample_flags;
3653 | MOV_CHECK(read_uint32(&sample_flags));
3654 | printf("%*c", 2, ' ');
3655 | dump_uint32(sample_flags, true);
3656 | } else {
3657 | printf("%12s", "x");
3658 | }
3659 | if (flags & 0x000800) {
3660 | if (version == 0) {
3661 | uint32_t composition_time_offset;
3662 | MOV_CHECK(read_uint32(&composition_time_offset));
3663 | printf("%*c", 2, ' ');
3664 | dump_uint32(composition_time_offset, false);
3665 | } else {
3666 | int32_t composition_time_offset;
3667 | MOV_CHECK(read_int32(&composition_time_offset));
3668 | printf("%*c", 2, ' ');
3669 | dump_int32(composition_time_offset);
3670 | }
3671 | } else {
3672 | printf("%12s", "x");
3673 | }
3674 | printf("\n");
3675 | }
3676 | }
3677 | }
3678 |
3679 | static void dump_tfdt_atom()
3680 | {
3681 | uint8_t version;
3682 | uint32_t flags;
3683 | dump_full_atom_header(&version, &flags);
3684 |
3685 |
3686 | if (version == 0) {
3687 | uint32_t base_media_decode_time;
3688 | MOV_CHECK(read_uint32(&base_media_decode_time));
3689 | indent();
3690 | printf("base_media_decode_time: ");
3691 | dump_uint32(base_media_decode_time, true);
3692 | printf("\n");
3693 | } else {
3694 | uint64_t base_media_decode_time;
3695 | MOV_CHECK(read_uint64(&base_media_decode_time));
3696 | indent();
3697 | printf("base_media_decode_time: ");
3698 | dump_uint64(base_media_decode_time, true);
3699 | printf("\n");
3700 | }
3701 | }
3702 |
3703 | static void dump_traf_atom()
3704 | {
3705 | static const DumpFuncMap dump_func_map[] =
3706 | {
3707 | {{'t','f','h','d'}, dump_tfhd_atom},
3708 | {{'t','r','u','n'}, dump_trun_atom},
3709 | {{'t','f','d','t'}, dump_tfdt_atom},
3710 | };
3711 |
3712 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3713 | }
3714 |
3715 | static void dump_moof_atom()
3716 | {
3717 | static const DumpFuncMap dump_func_map[] =
3718 | {
3719 | {{'m','f','h','d'}, dump_mfhd_atom},
3720 | {{'t','r','a','f'}, dump_traf_atom},
3721 | };
3722 |
3723 | dump_container_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3724 | }
3725 |
3726 | static void dump_ssix_atom()
3727 | {
3728 | uint8_t version;
3729 | uint32_t flags;
3730 | dump_full_atom_header(&version, &flags);
3731 |
3732 |
3733 | uint32_t sub_seg_count;
3734 | MOV_CHECK(read_uint32(&sub_seg_count));
3735 | indent();
3736 | printf("sub_segments (");
3737 | dump_uint32(sub_seg_count, false);
3738 | printf("):\n");
3739 |
3740 | uint32_t i;
3741 | for (i = 0; i < sub_seg_count; i++) {
3742 | indent(4);
3743 | dump_uint32_index(sub_seg_count, i);
3744 |
3745 | uint32_t ranges_count;
3746 | MOV_CHECK(read_uint32(&ranges_count));
3747 | printf(": ranges (");
3748 | dump_uint32(ranges_count, false);
3749 | printf("):\n");
3750 |
3751 | indent(4);
3752 | if (ranges_count < 0xffff)
3753 | printf("%4s", "i");
3754 | else if (ranges_count < 0xffffff)
3755 | printf("%6s", "i");
3756 | else
3757 | printf("%8s", "i");
3758 | printf("%8s%12s\n",
3759 | "level", "range_size");
3760 |
3761 | uint32_t j;
3762 | for (j = 0; j < ranges_count; j++) {
3763 | indent(4);
3764 | dump_uint32_index(ranges_count, j);
3765 |
3766 | uint8_t level;
3767 | MOV_CHECK(read_uint8(&level));
3768 | printf("%*c", 4, ' ');
3769 | dump_uint8(level, true);
3770 |
3771 | uint32_t range_size;
3772 | MOV_CHECK(read_uint24(&range_size));
3773 | printf("%*c", 2, ' ');
3774 | dump_uint32(range_size, true);
3775 | printf("\n");
3776 | }
3777 | }
3778 | }
3779 |
3780 | static void dump_top_atom()
3781 | {
3782 | static const DumpFuncMap dump_func_map[] =
3783 | {
3784 | {{'f','t','y','p'}, dump_ftyp_styp_atom},
3785 | {{'s','t','y','p'}, dump_ftyp_styp_atom},
3786 | {{'m','d','a','t'}, dump_mdat_atom},
3787 | {{'f','r','e','e'}, dump_free_atom},
3788 | {{'s','k','i','p'}, dump_skip_atom},
3789 | {{'m','o','o','v'}, dump_moov_atom},
3790 | {{'s','i','d','x'}, dump_sidx_atom},
3791 | {{'m','o','o','f'}, dump_moof_atom},
3792 | {{'s','s','i','x'}, dump_ssix_atom},
3793 | };
3794 |
3795 | dump_child_atom(dump_func_map, DUMP_FUNC_MAP_SIZE);
3796 | }
3797 |
3798 |
3799 | static void dump_file()
3800 | {
3801 | while (true) {
3802 | push_atom();
3803 |
3804 | if (!read_atom_start())
3805 | break;
3806 |
3807 | dump_top_atom();
3808 |
3809 | pop_atom();
3810 | }
3811 | }
3812 |
3813 | static void usage(const char *cmd)
3814 | {
3815 | fprintf(stderr, "Usage: %s [options] \n", cmd);
3816 | fprintf(stderr, "Options:\n");
3817 | fprintf(stderr, " -h | --help Print this usage message and exit\n");
3818 | fprintf(stderr, " --avcc Write SPS and PPS NAL units in the 'avcC' box to file\n");
3819 | fprintf(stderr, " The NAL units are prefixed by a length word with size defined in the 'avcC' box\n");
3820 | }
3821 |
3822 | int main(int argc, const char **argv)
3823 | {
3824 | const char *filename;
3825 | int cmdln_index;
3826 |
3827 | // parse commandline arguments
3828 |
3829 | for (cmdln_index = 1; cmdln_index < argc; cmdln_index++) {
3830 | if (strcmp(argv[cmdln_index], "-h") == 0 ||
3831 | strcmp(argv[cmdln_index], "--help") == 0)
3832 | {
3833 | usage(argv[0]);
3834 | return 0;
3835 | }
3836 | else if (strcmp(argv[cmdln_index], "--avcc") == 0)
3837 | {
3838 | if (cmdln_index + 1 >= argc)
3839 | {
3840 | usage(argv[0]);
3841 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
3842 | return 1;
3843 | }
3844 | g_avcc_filename = argv[cmdln_index + 1];
3845 | cmdln_index++;
3846 | }
3847 | else
3848 | {
3849 | break;
3850 | }
3851 | }
3852 |
3853 | if (cmdln_index + 1 < argc) {
3854 | usage(argv[0]);
3855 | fprintf(stderr, "Unknown argument '%s'\n", argv[cmdln_index]);
3856 | return 1;
3857 | }
3858 | if (cmdln_index + 1 > argc) {
3859 | if (argc == 1) {
3860 | usage(argv[0]);
3861 | return 0;
3862 | }
3863 | usage(argv[0]);
3864 | fprintf(stderr, "Missing quicktime filename\n");
3865 | return 1;
3866 | }
3867 |
3868 | filename = argv[cmdln_index];
3869 |
3870 |
3871 | // open file
3872 |
3873 | g_mov_file = fopen(filename, "rb");
3874 | if (!g_mov_file) {
3875 | fprintf(stderr, "Failed to open quicktime file '%s': %s\n", filename, strerror(errno));
3876 | return 1;
3877 | }
3878 |
3879 |
3880 | // dump file
3881 |
3882 | try
3883 | {
3884 | dump_file();
3885 | }
3886 | catch (exception &ex)
3887 | {
3888 | fprintf(stderr, "Exception: %s\n", ex.what());
3889 | return 1;
3890 | }
3891 | catch (...)
3892 | {
3893 | fprintf(stderr, "Unexpected exception\n");
3894 | return 1;
3895 | }
3896 |
3897 | if (g_mov_file)
3898 | fclose(g_mov_file);
3899 | if (g_avcc_file)
3900 | fclose(g_avcc_file);
3901 |
3902 | return 0;
3903 | }
3904 |
3905 |
--------------------------------------------------------------------------------
/src/rdd36dump.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017, British Broadcasting Corporation
3 | * All Rights Reserved.
4 | *
5 | * Author: Philip de Nier
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice,
11 | * this list of conditions and the following disclaimer.
12 | * * Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | * * Neither the name of the British Broadcasting Corporation nor the names
16 | * of its contributors may be used to endorse or promote products derived
17 | * from this software without specific prior written permission.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | * POSSIBILITY OF SUCH DAMAGE.
30 | */
31 |
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 |
42 |
43 | #define PRINT_UINT(name) printf("%*c " name ": %" PRIu64 "\n", context->indent * 4, ' ', context->value)
44 | #define PRINT_UINT8_HEX(name) printf("%*c " name ": 0x%02" PRIx64 "\n", context->indent * 4, ' ', context->value)
45 |
46 | #define PRINT_ENUM(name, strings, default_string) \
47 | print_enum(context, name, strings, ARRAY_SIZE(strings), default_string)
48 |
49 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
50 |
51 | #define CHK(cmd) \
52 | do { \
53 | if (!(cmd)) { \
54 | fprintf(stderr, "'%s' check failed at line %d\n", #cmd, __LINE__); \
55 | return 0; \
56 | } \
57 | } while (0)
58 |
59 |
60 | typedef struct
61 | {
62 | FILE *file;
63 | int64_t next_read_pos;
64 | int eof;
65 |
66 | uint8_t current_byte;
67 | int next_bit;
68 | uint64_t value;
69 |
70 | int indent;
71 | int64_t frame_count;
72 |
73 | uint8_t interlace_mode;
74 | uint8_t picture_header_size;
75 | uint32_t picture_size;
76 | } ParseContext;
77 |
78 |
79 | static const char *CHROMA_FORMAT_STRINGS[] =
80 | {
81 | "Reserved",
82 | "Reserved",
83 | "4:2:2",
84 | "4:4:4",
85 | };
86 |
87 | static const char *INTERLACE_MODE_STRINGS[] =
88 | {
89 | "Progressive frame",
90 | "Interlaced frame (TFF)",
91 | "Interlaced frame (BFF)",
92 | "Reserved",
93 | };
94 |
95 | static const char *ASPECT_RATIO_STRINGS[] =
96 | {
97 | "Unknown/unspecified",
98 | "Square pixels",
99 | "4:3",
100 | "16:9",
101 | };
102 |
103 | static const char *FRAME_RATE_STRINGS[] =
104 | {
105 | "Unknown/unspecified",
106 | "24/1.001",
107 | "24",
108 | "25",
109 | "30/1.001",
110 | "30",
111 | "50",
112 | "60/1.001",
113 | "60",
114 | "100",
115 | "120/1.001",
116 | "120",
117 | };
118 |
119 | static const char *COLOR_PRIMARY_STRINGS[] =
120 | {
121 | "Unknown/unspecified",
122 | "ITU-R BT.709",
123 | "Unknown/unspecified",
124 | "Reserved",
125 | "Reserved",
126 | "ITU-R BT.601 625",
127 | "ITU-R BT.601 525",
128 | "Reserved",
129 | "Reserved",
130 | "ITU-R BT.2020",
131 | "Reserved",
132 | "DCI P3",
133 | "P3 D65",
134 | };
135 |
136 | static const char *TRANSFER_CHAR_STRINGS[] =
137 | {
138 | "Unknown/unspecified",
139 | "ITU-R BT.601/BT.709/BT.2020",
140 | "Unknown/unspecified",
141 | "Reserved",
142 | "Reserved",
143 | "Reserved",
144 | "Reserved",
145 | "Reserved",
146 | "Reserved",
147 | "Reserved",
148 | "Reserved",
149 | "Reserved",
150 | "Reserved",
151 | "Reserved",
152 | "Reserved",
153 | "Reserved",
154 | "SMPTE ST 2084",
155 | "Reserved",
156 | "HLG OETF",
157 | };
158 |
159 | static const char *MATRIX_COEFF_STRINGS[] =
160 | {
161 | "Unknown/unspecified",
162 | "ITU-R BT.709",
163 | "Unknown/unspecified",
164 | "Reserved",
165 | "Reserved",
166 | "Reserved",
167 | "ITU-R BT.601",
168 | "Reserved",
169 | "Reserved",
170 | "ITU-R BT.2020",
171 | };
172 |
173 | static const char *ALPHA_CHANNEL_TYPE_STRINGS[] =
174 | {
175 | "Not present",
176 | "8 bits/sample integral",
177 | "16 bits/sample integral",
178 | };
179 |
180 |
181 |
182 | static int read_next_byte(ParseContext *context)
183 | {
184 | int c;
185 |
186 | c = fgetc(context->file);
187 | if (c == EOF) {
188 | if (feof(context->file)) {
189 | context->eof = 1;
190 | } else {
191 | fprintf(stderr, "File I/O error: %s\n", strerror(errno));
192 | }
193 | return 0;
194 | }
195 |
196 | context->next_read_pos++;
197 | context->next_bit = 7;
198 | context->current_byte = (uint8_t)c;
199 |
200 | return 1;
201 | }
202 |
203 | static int64_t get_file_pos(ParseContext *context)
204 | {
205 | return context->next_read_pos - (context->next_bit >= 0 ? 1 : 0);
206 | }
207 |
208 | static int seek_to_offset(ParseContext *context, int64_t offset)
209 | {
210 | if (fseeko(context->file, offset, SEEK_SET) < 0)
211 | return 0;
212 |
213 | context->next_bit = -1;
214 | context->next_read_pos = offset;
215 |
216 | return 1;
217 | }
218 |
219 | static int have_byte(ParseContext *context)
220 | {
221 | return context->next_bit >= 0 || read_next_byte(context);
222 | }
223 |
224 | static int skip_bytes_align(ParseContext *context, int64_t count)
225 | {
226 | int64_t offset = count;
227 |
228 | if (context->next_bit >= 0)
229 | offset--;
230 |
231 | if (offset <= 0) {
232 | offset = 0;
233 | } else if (fseeko(context->file, offset, SEEK_CUR) < 0) {
234 | fprintf(stderr, "Seek error: %s\n", strerror(errno));
235 | return 0;
236 | }
237 |
238 | context->next_bit = -1;
239 | context->next_read_pos += offset;
240 |
241 | return 1;
242 | }
243 |
244 | static int read_bits(ParseContext *context, int n)
245 | {
246 | int i;
247 |
248 | assert(n <= 64);
249 |
250 | context->value = 0;
251 | for (i = 0; i < n; i++) {
252 | if (context->next_bit < 0 && !read_next_byte(context))
253 | return 0;
254 | context->value <<= 1;
255 | context->value |= (context->current_byte >> context->next_bit) & 0x1;
256 | context->next_bit--;
257 | }
258 |
259 | return 1;
260 | }
261 |
262 | #define f(a) CHK(_f(context, a))
263 | static int _f(ParseContext *context, int num_bits)
264 | {
265 | return read_bits(context, num_bits);
266 | }
267 |
268 | #define u(a) CHK(_u(context, a))
269 | static int _u(ParseContext *context, int num_bits)
270 | {
271 | return read_bits(context, num_bits);
272 | }
273 |
274 | static void print_structure_start(ParseContext *context, const char *name)
275 | {
276 | int64_t file_pos = get_file_pos(context);
277 |
278 | printf("%*c %s: pos=%" PRId64 "\n", context->indent * 4, ' ', name, file_pos);
279 | }
280 |
281 | static void print_fourcc(ParseContext *context, const char *name)
282 | {
283 | int i;
284 | uint32_t value = (uint32_t)context->value;
285 |
286 | printf("%*c %s: 0x%08x (", context->indent * 4, ' ', name, value);
287 |
288 | for (i = 0; i < 4; i++) {
289 | char c = (char)(value >> (8 * (3 - i)));
290 | if (isprint(c))
291 | printf("%c", c);
292 | else
293 | printf(".");
294 | }
295 | printf(")\n");
296 | }
297 |
298 | static void print_enum(ParseContext *context, const char *name, const char **strings, size_t strings_size,
299 | const char *default_string)
300 | {
301 | uint8_t value = (uint8_t)context->value;
302 |
303 | printf("%*c %s: %" PRIu64 , context->indent * 4, ' ', name, context->value);
304 |
305 | if (value < strings_size)
306 | printf(" (%s)\n", strings[value]);
307 | else
308 | printf(" (%s)\n", default_string);
309 | }
310 |
311 | static int dump_quantization_matrix(ParseContext *context, const char *name)
312 | {
313 | int u, v;
314 |
315 | printf("%*c %s:\n", context->indent * 4, ' ', name);
316 |
317 | context->indent++;
318 |
319 | for (v = 0; v < 8; v++) {
320 | printf("%*c ", context->indent * 4, ' ');
321 | for (u = 0; u < 8; u++) {
322 | u(8); printf(" %02x", (uint8_t)context->value);
323 | }
324 | printf("\n");
325 | }
326 |
327 | context->indent--;
328 |
329 | return 1;
330 | }
331 |
332 | static int stuffing(ParseContext *context, int64_t size)
333 | {
334 | print_structure_start(context, "stuffing");
335 |
336 | context->indent++;
337 |
338 | // TODO: report remainder bits?
339 | printf("%*c size: %" PRIi64 "\n", context->indent * 4, ' ', size);
340 | CHK(skip_bytes_align(context, size));
341 |
342 | context->indent--;
343 |
344 | return 1;
345 | }
346 |
347 | static int picture_header(ParseContext *context)
348 | {
349 | int64_t file_pos = get_file_pos(context);
350 |
351 | u(5); PRINT_UINT("picture_header_size");
352 | context->picture_header_size = (uint8_t)context->value;
353 | u(3); PRINT_UINT8_HEX("reserved");
354 | u(32); PRINT_UINT("picture_size");
355 | context->picture_size = (uint32_t)context->value;
356 | u(16); PRINT_UINT("deprecated_number_of_slices");
357 | u(2); PRINT_UINT8_HEX("reserved");
358 | u(2); PRINT_UINT("log2_desired_slice_size_in_mb");
359 | u(4); PRINT_UINT8_HEX("reserved");
360 |
361 | CHK(context->picture_size >= context->picture_header_size);
362 | int64_t rem_picture_header = context->picture_header_size - (get_file_pos(context) - file_pos);
363 | CHK(rem_picture_header >= 0);
364 | // TODO: dump bytes
365 | CHK(skip_bytes_align(context, rem_picture_header));
366 |
367 | return 1;
368 | }
369 |
370 | static int picture(ParseContext *context, int temporal_order)
371 | {
372 | print_structure_start(context, "picture");
373 |
374 | context->indent++;
375 |
376 | picture_header(context);
377 |
378 | CHK(skip_bytes_align(context, context->picture_size - context->picture_header_size));
379 |
380 | context->indent--;
381 |
382 | return 1;
383 | }
384 |
385 | static int frame_header(ParseContext *context)
386 | {
387 | uint16_t frame_header_size;
388 | int load_luma_quantization_matrix;
389 | int load_chroma_quantization_matrix;
390 | int64_t file_pos = get_file_pos(context);
391 |
392 | printf("%*c frame_header:\n", context->indent * 4, ' ');
393 |
394 | context->indent++;
395 |
396 | u(16); PRINT_UINT("frame_header_size");
397 | frame_header_size = (uint16_t)context->value;
398 | u(8); PRINT_UINT8_HEX("reserved");
399 | u(8); PRINT_UINT("bitstream_version");
400 | f(32); print_fourcc(context, "encoder_identifier");
401 | u(16); PRINT_UINT("horizontal_size");
402 | u(16); PRINT_UINT("vertical_size");
403 | u(2); PRINT_ENUM("chroma_format", CHROMA_FORMAT_STRINGS, "");
404 | u(2); PRINT_UINT8_HEX("reserved");
405 | u(2); PRINT_ENUM("interlace_mode", INTERLACE_MODE_STRINGS, "");
406 | u(2); PRINT_UINT8_HEX("reserved");
407 | u(4); PRINT_ENUM("aspect_ratio_information", ASPECT_RATIO_STRINGS, "Reserved");
408 | u(4); PRINT_ENUM("frame_rate_code", FRAME_RATE_STRINGS, "Reserved");
409 | u(8); PRINT_ENUM("color_primaries", COLOR_PRIMARY_STRINGS, "Reserved");
410 | u(8); PRINT_ENUM("transfer_characteristic", TRANSFER_CHAR_STRINGS, "Reserved");
411 | u(8); PRINT_ENUM("matrix_coefficients", MATRIX_COEFF_STRINGS, "Reserved");
412 | u(4); PRINT_UINT8_HEX("reserved");
413 | u(4); PRINT_ENUM("alpha_channel_type", ALPHA_CHANNEL_TYPE_STRINGS, "Reserved");
414 | u(14); PRINT_UINT8_HEX("reserved");
415 | u(1); PRINT_UINT("load_luma_quantization_matrix");
416 | load_luma_quantization_matrix = !!context->value;
417 | u(1); PRINT_UINT("load_chroma_quantization_matrix");
418 | load_chroma_quantization_matrix = !!context->value;
419 | if (load_luma_quantization_matrix)
420 | CHK(dump_quantization_matrix(context, "luma_quantization_matrix"));
421 | if (load_chroma_quantization_matrix)
422 | CHK(dump_quantization_matrix(context, "chroma_quantization_matrix"));
423 |
424 | int64_t rem_frame_header = frame_header_size - (get_file_pos(context) - file_pos);
425 | CHK(rem_frame_header >= 0);
426 | // TODO: dump bytes
427 | CHK(skip_bytes_align(context, rem_frame_header));
428 |
429 | context->indent--;
430 |
431 | return 1;
432 | }
433 |
434 | static int frame(ParseContext *context)
435 | {
436 | static const uint32_t RDD36_FRAME_ID = 0x69637066; // 'icpf'
437 | uint32_t frame_size;
438 | int64_t file_pos = get_file_pos(context);
439 | int64_t stuffing_size;
440 |
441 | printf("frame: num=%" PRId64 ", pos=%" PRId64 "\n", context->frame_count, file_pos);
442 |
443 | context->indent++;
444 |
445 | u(32); PRINT_UINT("frame_size");
446 | frame_size = (uint32_t)context->value;
447 | f(32); print_fourcc(context, "frame_identifier");
448 | CHK(context->value == RDD36_FRAME_ID);
449 | CHK(frame_header(context));
450 | CHK(picture(context, 1));
451 | if (context->interlace_mode == 1 || context->interlace_mode == 2)
452 | CHK(picture(context, 2));
453 | stuffing_size = frame_size - (get_file_pos(context) - file_pos);
454 | if (stuffing_size > 0)
455 | CHK(stuffing(context, stuffing_size));
456 |
457 | context->indent--;
458 |
459 | return 1;
460 | }
461 |
462 | static int read_next_frame_offset(FILE *offsets_file, int64_t *offset_out)
463 | {
464 | char line[1024];
465 | size_t i;
466 |
467 | while (1) {
468 | if (!fgets(line, sizeof(line), offsets_file))
469 | return 0;
470 | for (i = 0; i < sizeof(line); i++) {
471 | if ((line[i] >= '0' && line[i] <= '9') || !line[i])
472 | break;
473 | }
474 | if (i < sizeof(line) && line[i]) {
475 | int64_t offset;
476 | if (sscanf(&line[i], "%" PRId64, &offset) == 1 && offset >= 0) {
477 | *offset_out = offset;
478 | return 1;
479 | }
480 | }
481 | }
482 |
483 | return 0;
484 | }
485 |
486 | static void print_usage(const char *cmd)
487 | {
488 | fprintf(stderr, "Usage: %s [options] \n", cmd);
489 | fprintf(stderr, "Options:\n");
490 | fprintf(stderr, " -h | --help Show help and exit\n");
491 | fprintf(stderr, " --offsets Text file containing decimal file offsets for each frame separated by newlines\n");
492 | fprintf(stderr, " E.g. using ffprobe to extract offsets from a Quicktime file:\n");
493 | fprintf(stderr, " 'ffprobe -show_packets -select_streams v:0 example.mov | grep pos >offsets.txt'\n");
494 | }
495 |
496 | int main(int argc, const char **argv)
497 | {
498 | const char *offsets_filename = NULL;
499 | const char *filename;
500 | int cmdln_index;
501 | ParseContext context;
502 | FILE *offsets_file = NULL;
503 | int result = 0;
504 |
505 | // TODO: options to limit the dump start and count
506 |
507 | if (argc <= 1) {
508 | print_usage(argv[0]);
509 | return 0;
510 | }
511 |
512 | for (cmdln_index = 1; cmdln_index < argc; cmdln_index++) {
513 | if (strcmp(argv[cmdln_index], "-h") == 0 ||
514 | strcmp(argv[cmdln_index], "--help") == 0)
515 | {
516 | print_usage(argv[0]);
517 | return 0;
518 | }
519 | else if (strcmp(argv[cmdln_index], "--offsets") == 0)
520 | {
521 | if (cmdln_index + 1 >= argc)
522 | {
523 | print_usage(argv[0]);
524 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
525 | return 1;
526 | }
527 | offsets_filename = argv[cmdln_index + 1];
528 | cmdln_index++;
529 | }
530 | else
531 | {
532 | break;
533 | }
534 | }
535 |
536 | if (cmdln_index + 1 < argc) {
537 | print_usage(argv[0]);
538 | fprintf(stderr, "Unknown option '%s'\n", argv[cmdln_index]);
539 | return 1;
540 | }
541 | if (cmdln_index >= argc) {
542 | print_usage(argv[0]);
543 | fprintf(stderr, "Missing \n");
544 | return 1;
545 | }
546 |
547 | filename = argv[cmdln_index];
548 |
549 |
550 | memset(&context, 0, sizeof(context));
551 | context.next_bit = -1;
552 | context.file = fopen(filename, "rb");
553 | if (!context.file) {
554 | fprintf(stderr, "Failed to open input file '%s': %s\n", filename, strerror(errno));
555 | return 1;
556 | }
557 |
558 | if (offsets_filename) {
559 | offsets_file = fopen(offsets_filename, "rb");
560 | if (!offsets_file) {
561 | fprintf(stderr, "Failed to open offsets file '%s': %s\n", offsets_filename, strerror(errno));
562 | return 1;
563 | }
564 | }
565 |
566 | while (1) {
567 | if (offsets_file) {
568 | int64_t offset;
569 | if (!read_next_frame_offset(offsets_file, &offset) || !seek_to_offset(&context, offset))
570 | break;
571 | } else if (!have_byte(&context)) {
572 | break;
573 | }
574 | if (!frame(&context)) {
575 | result = 1;
576 | break;
577 | }
578 | context.frame_count++;
579 | }
580 |
581 | if (context.file)
582 | fclose(context.file);
583 | if (offsets_file)
584 | fclose(offsets_file);
585 |
586 |
587 | return result;
588 | }
--------------------------------------------------------------------------------
/src/rdd36mod.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017, British Broadcasting Corporation
3 | * All Rights Reserved.
4 | *
5 | * Author: Philip de Nier
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright notice,
11 | * this list of conditions and the following disclaimer.
12 | * * Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | * * Neither the name of the British Broadcasting Corporation nor the names
16 | * of its contributors may be used to endorse or promote products derived
17 | * from this software without specific prior written permission.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | * POSSIBILITY OF SUCH DAMAGE.
30 | */
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 |
41 | #define CHK(cmd) \
42 | do { \
43 | if (!(cmd)) { \
44 | fprintf(stderr, "'%s' check failed at line %d\n", #cmd, __LINE__); \
45 | return 0; \
46 | } \
47 | } while (0)
48 |
49 |
50 | typedef struct
51 | {
52 | int skip_frame_data;
53 | int show_props;
54 | int color_prim_update;
55 | int transfer_ch_update;
56 | int matrix_coeff_update;
57 |
58 | FILE *file;
59 | int eof;
60 |
61 | uint8_t current_byte;
62 | int next_bit;
63 | uint64_t value;
64 | } ParseContext;
65 |
66 |
67 |
68 | static int read_next_byte(ParseContext *context)
69 | {
70 | int c;
71 |
72 | c = fgetc(context->file);
73 | if (c == EOF) {
74 | if (feof(context->file))
75 | context->eof = 1;
76 | else
77 | fprintf(stderr, "File read error: %s\n", strerror(errno));
78 | return 0;
79 | }
80 |
81 | context->next_bit = 7;
82 | context->current_byte = (uint8_t)c;
83 |
84 | return 1;
85 | }
86 |
87 | static int64_t get_file_pos(ParseContext *context)
88 | {
89 | return ftello(context->file) - (context->next_bit >= 0 ? 1 : 0);
90 | }
91 |
92 | static int seek_to_offset(ParseContext *context, int64_t offset)
93 | {
94 | if (fseeko(context->file, offset, SEEK_SET) < 0)
95 | return 0;
96 |
97 | context->next_bit = -1;
98 |
99 | return 1;
100 | }
101 |
102 | static int update_file(ParseContext *context, const uint8_t *data, size_t size)
103 | {
104 | if (fwrite(data, size, 1, context->file) != 1) {
105 | fprintf(stderr, "Failed to update file: %s\n", strerror(errno));
106 | return 0;
107 | }
108 |
109 | context->next_bit = -1;
110 |
111 | return 1;
112 | }
113 |
114 |
115 | static int have_byte(ParseContext *context)
116 | {
117 | return context->next_bit >= 0 || read_next_byte(context);
118 | }
119 |
120 | static int skip_bytes_align(ParseContext *context, int64_t count)
121 | {
122 | int64_t offset = count;
123 |
124 | if (context->next_bit >= 0)
125 | offset--;
126 |
127 | if (offset <= 0) {
128 | offset = 0;
129 | } else if (fseeko(context->file, offset, SEEK_CUR) < 0) {
130 | fprintf(stderr, "Seek error: %s\n", strerror(errno));
131 | return 0;
132 | }
133 |
134 | context->next_bit = -1;
135 |
136 | return 1;
137 | }
138 |
139 | static int read_bits(ParseContext *context, int n)
140 | {
141 | int i;
142 |
143 | assert(n <= 64);
144 |
145 | context->value = 0;
146 | for (i = 0; i < n; i++) {
147 | if (context->next_bit < 0 && !read_next_byte(context))
148 | return 0;
149 | context->value <<= 1;
150 | context->value |= (context->current_byte >> context->next_bit) & 0x1;
151 | context->next_bit--;
152 | }
153 |
154 | return 1;
155 | }
156 |
157 | #define f(a) CHK(read_bits(context, a))
158 | #define u(a) CHK(read_bits(context, a))
159 |
160 | static int frame_header(ParseContext *context)
161 | {
162 | static const int64_t COLOR_PRIMARIES_OFFSET = 14;
163 | uint8_t color_primaries;
164 | uint8_t transfer_characteristic;
165 | uint8_t matrix_coefficients;
166 | int64_t file_pos = get_file_pos(context);
167 |
168 | CHK(seek_to_offset(context, file_pos + COLOR_PRIMARIES_OFFSET));
169 |
170 | u(8); color_primaries = (uint8_t)context->value;
171 | u(8); transfer_characteristic = (uint8_t)context->value;
172 | u(8); matrix_coefficients = (uint8_t)context->value;
173 |
174 | if (context->show_props) {
175 | printf("First frame properties:\n");
176 | printf(" color_primaries : %u\n", color_primaries);
177 | printf(" transfer_characteristic : %u\n", transfer_characteristic);
178 | printf(" matrix_coefficients : %u\n", matrix_coefficients);
179 | } else {
180 | uint8_t update[3];
181 | update[0] = (context->color_prim_update >= 0 ? context->color_prim_update : color_primaries);
182 | update[1] = (context->transfer_ch_update >= 0 ? context->transfer_ch_update : transfer_characteristic);
183 | update[2] = (context->matrix_coeff_update >= 0 ? context->matrix_coeff_update : matrix_coefficients);
184 | CHK(seek_to_offset(context, file_pos + COLOR_PRIMARIES_OFFSET));
185 | CHK(update_file(context, update, sizeof(update)));
186 | }
187 |
188 | return 1;
189 | }
190 |
191 | static int frame(ParseContext *context)
192 | {
193 | static const uint32_t RDD36_FRAME_ID = 0x69637066; // 'icpf'
194 | uint32_t frame_size;
195 | int64_t file_pos = get_file_pos(context);
196 | int64_t skip_size;
197 |
198 | u(32); frame_size = (uint32_t)context->value;
199 | f(32);
200 | CHK(context->value == RDD36_FRAME_ID);
201 | CHK(frame_header(context));
202 |
203 | if (context->skip_frame_data) {
204 | skip_size = frame_size - (get_file_pos(context) - file_pos);
205 | if (skip_size > 0)
206 | CHK(skip_bytes_align(context, skip_size));
207 | }
208 |
209 | return 1;
210 | }
211 |
212 | static int read_next_frame_offset(FILE *offsets_file, int64_t *offset_out)
213 | {
214 | char line[1024];
215 | size_t i;
216 |
217 | while (1) {
218 | if (!fgets(line, sizeof(line), offsets_file))
219 | return 0;
220 | for (i = 0; i < sizeof(line); i++) {
221 | if ((line[i] >= '0' && line[i] <= '9') || !line[i])
222 | break;
223 | }
224 | if (i < sizeof(line) && line[i]) {
225 | int64_t offset;
226 | if (sscanf(&line[i], "%" PRId64, &offset) == 1 && offset >= 0) {
227 | *offset_out = offset;
228 | return 1;
229 | }
230 | }
231 | }
232 |
233 | return 0;
234 | }
235 |
236 | static void print_usage(const char *cmd)
237 | {
238 | fprintf(stderr, "Usage: %s [options] \n", cmd);
239 | fprintf(stderr, "Options:\n");
240 | fprintf(stderr, " -h | --help Show help and exit\n");
241 | fprintf(stderr, " -s Show properties in the first frame and exit\n");
242 | fprintf(stderr, " -p Modify the 'color_primaries' property to \n");
243 | fprintf(stderr, " -t Modify the 'transfer_characteristic' property to \n");
244 | fprintf(stderr, " -m Modify the 'matrix_coefficients' property to \n");
245 | fprintf(stderr, " -o Text file containing decimal file offsets for each frame separated by a newline\n");
246 | fprintf(stderr, " E.g. using ffprobe to extract offsets from a Quicktime file:\n");
247 | fprintf(stderr, " 'ffprobe -show_packets -select_streams v:0 example.mov | grep pos >offsets.txt'\n");
248 | }
249 |
250 | int main(int argc, const char **argv)
251 | {
252 | const char *offsets_filename = NULL;
253 | const char *filename;
254 | int cmdln_index;
255 | ParseContext context;
256 | FILE *offsets_file = NULL;
257 | int result = 0;
258 |
259 | memset(&context, 0, sizeof(context));
260 | context.next_bit = -1;
261 | context.transfer_ch_update = -1;
262 | context.matrix_coeff_update = -1;
263 | context.color_prim_update = -1;
264 |
265 | if (argc <= 1) {
266 | print_usage(argv[0]);
267 | return 0;
268 | }
269 |
270 | for (cmdln_index = 1; cmdln_index < argc; cmdln_index++) {
271 | if (strcmp(argv[cmdln_index], "-h") == 0 ||
272 | strcmp(argv[cmdln_index], "--help") == 0)
273 | {
274 | print_usage(argv[0]);
275 | return 0;
276 | }
277 | else if (strcmp(argv[cmdln_index], "-s") == 0)
278 | {
279 | context.show_props = 1;
280 | }
281 | else if (strcmp(argv[cmdln_index], "-t") == 0)
282 | {
283 | if (cmdln_index + 1 >= argc)
284 | {
285 | print_usage(argv[0]);
286 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
287 | return 1;
288 | }
289 | if (sscanf(argv[cmdln_index + 1], "%d", &context.transfer_ch_update) != 1 ||
290 | context.transfer_ch_update < 0 || context.transfer_ch_update > 0xff)
291 | {
292 | print_usage(argv[0]);
293 | fprintf(stderr, "Invalid value '%s' for option '%s'\n", argv[cmdln_index + 1], argv[cmdln_index]);
294 | return 1;
295 | }
296 | cmdln_index++;
297 | }
298 | else if (strcmp(argv[cmdln_index], "-m") == 0)
299 | {
300 | if (cmdln_index + 1 >= argc)
301 | {
302 | print_usage(argv[0]);
303 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
304 | return 1;
305 | }
306 | if (sscanf(argv[cmdln_index + 1], "%d", &context.matrix_coeff_update) != 1 ||
307 | context.matrix_coeff_update < 0 || context.matrix_coeff_update > 0xff)
308 | {
309 | print_usage(argv[0]);
310 | fprintf(stderr, "Invalid value '%s' for option '%s'\n", argv[cmdln_index + 1], argv[cmdln_index]);
311 | return 1;
312 | }
313 | cmdln_index++;
314 | }
315 | else if (strcmp(argv[cmdln_index], "-p") == 0)
316 | {
317 | if (cmdln_index + 1 >= argc)
318 | {
319 | print_usage(argv[0]);
320 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
321 | return 1;
322 | }
323 | if (sscanf(argv[cmdln_index + 1], "%d", &context.color_prim_update) != 1 ||
324 | context.color_prim_update < 0 || context.color_prim_update > 0xff)
325 | {
326 | print_usage(argv[0]);
327 | fprintf(stderr, "Invalid value '%s' for option '%s'\n", argv[cmdln_index + 1], argv[cmdln_index]);
328 | return 1;
329 | }
330 | cmdln_index++;
331 | }
332 | else if (strcmp(argv[cmdln_index], "-o") == 0)
333 | {
334 | if (cmdln_index + 1 >= argc)
335 | {
336 | print_usage(argv[0]);
337 | fprintf(stderr, "Missing argument for option '%s'\n", argv[cmdln_index]);
338 | return 1;
339 | }
340 | offsets_filename = argv[cmdln_index + 1];
341 | cmdln_index++;
342 | }
343 | else
344 | {
345 | break;
346 | }
347 | }
348 |
349 | if (cmdln_index + 1 < argc) {
350 | print_usage(argv[0]);
351 | fprintf(stderr, "Unknown option '%s'\n", argv[cmdln_index]);
352 | return 1;
353 | }
354 | if (cmdln_index >= argc) {
355 | print_usage(argv[0]);
356 | fprintf(stderr, "Missing \n");
357 | return 1;
358 | }
359 |
360 | filename = argv[cmdln_index];
361 | if (context.transfer_ch_update < 0 && context.matrix_coeff_update < 0 && context.color_prim_update < 0)
362 | context.show_props = 1;
363 | if (!offsets_filename)
364 | context.skip_frame_data = 1;
365 |
366 |
367 | if (context.show_props)
368 | context.file = fopen(filename, "rb");
369 | else
370 | context.file = fopen(filename, "r+b");
371 | if (!context.file) {
372 | fprintf(stderr, "Failed to open input file '%s': %s\n", filename, strerror(errno));
373 | return 1;
374 | }
375 |
376 | if (offsets_filename) {
377 | offsets_file = fopen(offsets_filename, "rb");
378 | if (!offsets_file) {
379 | fprintf(stderr, "Failed to open offsets file '%s': %s\n", offsets_filename, strerror(errno));
380 | return 1;
381 | }
382 | }
383 |
384 | while (1) {
385 | if (offsets_file) {
386 | int64_t offset;
387 | if (!read_next_frame_offset(offsets_file, &offset) || !seek_to_offset(&context, offset))
388 | break;
389 | }
390 | if (!have_byte(&context))
391 | break;
392 | if (!frame(&context)) {
393 | result = 1;
394 | break;
395 | }
396 | if (context.show_props)
397 | break;
398 | }
399 |
400 | if (context.file)
401 | fclose(context.file);
402 | if (offsets_file)
403 | fclose(offsets_file);
404 |
405 |
406 | return result;
407 | }
408 |
--------------------------------------------------------------------------------