├── 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 | ![BBC RD Logo](./bbcrd-logo.png) 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 | --------------------------------------------------------------------------------