avif-parse-1.4.0/.cargo_vcs_info.json0000644000000001360000000000100130730ustar { "git": { "sha1": "de5ec3408eb00766ed45ba7fc51974a315c8035a" }, "path_in_vcs": "" }avif-parse-1.4.0/Cargo.lock0000644000000247320000000000100110560ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys", ] [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "avif-parse" version = "1.4.0" dependencies = [ "arrayvec", "bitreader", "byteorder", "env_logger", "fallible_collections", "leb128", "log", "walkdir", ] [[package]] name = "bitreader" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "886559b1e163d56c765bc3a985febb4eee8009f625244511d8ee3c432e08c066" dependencies = [ "cfg-if", ] [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "env_filter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", "jiff", "log", ] [[package]] name = "fallible_collections" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b3e85d14d419ba3e1db925519461c0d17a49bdd2d67ea6316fa965ca7acdf74" [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "jiff" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", "serde", ] [[package]] name = "jiff-static" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "leb128" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" avif-parse-1.4.0/Cargo.toml0000644000000036110000000000100110720ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.69" name = "avif-parse" version = "1.4.0" authors = [ "Kornel Lesiński ", "Ralph Giles ", "Matthew Gregan ", "Alfredo Yang ", "Jon Bauman ", ] build = false include = [ "/README.md", "/Cargo.toml", "/LICENSE", "/src/*.rs", "/avif_parse.h", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Parser for AVIF image files" documentation = "https://docs.rs/avif-parse/" readme = "README.md" keywords = [ "demuxer", "image", "parser", "heif", ] categories = ["multimedia::images"] license = "MPL-2.0" repository = "https://github.com/kornelski/avif-parse" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [badges.maintenance] status = "passively-maintained" [features] mp4parse_fallible = [] [lib] name = "avif_parse" crate-type = [ "rlib", "staticlib", ] path = "src/lib.rs" [dependencies.arrayvec] version = "0.7.6" [dependencies.bitreader] version = "0.3.8" [dependencies.byteorder] version = "1.5.0" [dependencies.fallible_collections] version = "0.5.1" features = ["std"] default-features = false [dependencies.leb128] version = "0.2.5" [dependencies.log] version = "0.4.20" [dev-dependencies.env_logger] version = "0.11" [dev-dependencies.walkdir] version = "2.4.0" avif-parse-1.4.0/Cargo.toml.orig000064400000000000000000000022111046102023000145460ustar 00000000000000[package] name = "avif-parse" version = "1.4.0" authors = [ "Kornel Lesiński ", "Ralph Giles ", "Matthew Gregan ", "Alfredo Yang ", "Jon Bauman ", ] edition = "2021" description = "Parser for AVIF image files" documentation = "https://docs.rs/avif-parse/" license = "MPL-2.0" categories = ["multimedia::images"] repository = "https://github.com/kornelski/avif-parse" readme = "README.md" include = ["/README.md", "/Cargo.toml", "/LICENSE", "/src/*.rs", "/avif_parse.h"] keywords = ["demuxer", "image", "parser", "heif"] rust-version = "1.69" [lib] crate-type = ["rlib", "staticlib"] [dependencies] byteorder = "1.5.0" bitreader = "0.3.8" leb128 = "0.2.5" log = "0.4.20" fallible_collections = { version = "0.5.1", default-features = false, features = ["std"] } arrayvec = "0.7.6" [dev-dependencies] env_logger = "0.11" walkdir = "2.4.0" [features] # Obsolete (fallible allocations are used anyway) mp4parse_fallible = [] [badges] maintenance = { status = "passively-maintained" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] avif-parse-1.4.0/LICENSE000064400000000000000000000405261046102023000126770ustar 00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. avif-parse-1.4.0/README.md000064400000000000000000000036401046102023000131450ustar 00000000000000# AVIF file structure parser (demuxer) Get AV1 payload and the alpha channel metadata out of AVIF image files. This is a minimal ISOBMFF/MIAF and AV1 OBU parser. It is a fork of Mozilla's MP4 parser used in Firefox, so it's designed to be robust and safely handle untrusted data. This crate is written entirely in safe Rust code. The parser is compatible with files supported by libavif, Chrome 85 and Firefox 81a. It parses all files in [the AOM test suite](https://github.com/AOMediaCodec/av1-avif). [API documentation](https://docs.rs/avif-parse/) This crate doesn't include an AV1 decoder. To display the pixels you will additionally need [dav1d](https://code.videolan.org/videolan/dav1d) or [libaom](https://lib.rs/libaom-sys) ([full decoder example](https://gitlab.com/kornelski/aom-decode)). ## Usage from Rust It takes `io::Read`, so you can use any readable input, such as a byte slice (`vec.as_slice()`), or a `File` wrapped in `BufReader`, etc. ```rust let data = read_avif(&mut slice)?; av1_decode(&data.primary_item)?; if let Some(alpha) = &data.alpha_item { av1_decode(alpha)?; } if data.premultiplied_alpha { // after decoding, remember to divide R,G,B values by A } ``` ## Usage from C Install Rust 1.68 or later, preferably via [rustup](https://rustup.rs), and run: ```bash cargo build --release ``` It will build `./target/release/libavif_parse.a` (or `avif_parse.lib` on Windows). Link it with your project. Cargo supports cross-compilation, so you can easily build it for other platforms (e.g. [iOS](https://lib.rs/crates/cargo-xcode)). ```c #include "avif_parse.h" avif_data_t data = avif_parse(file_data, file_length); if (data) { av1_decode(data.primary_data, data.primary_size); if (data.alpha_data) { av1_decode(data.alpha_data, data.alpha_size); } if (data.premultiplied_alpha) { // after decoding, remember to divide R,G,B values by A } } avif_data_free(data); ``` avif-parse-1.4.0/avif_parse.h000064400000000000000000000020341046102023000141520ustar 00000000000000#include #ifdef __cplusplus extern "C" { #endif // __cplusplus /** * Result of parsing an AVIF file. Contains AV1-compressed data. */ typedef struct avif_data_t { /** * AV1 data for color channels */ const unsigned char *primary_data; size_t primary_size; /** * AV1 data for alpha channel (may be NULL if the image is fully opaque) */ const unsigned char *alpha_data; size_t alpha_size; /** * 0 = normal, uncorrelated alpha channel * 1 = premultiplied alpha. You must divide RGB values by alpha. * * ```c * if (a != 0) {r = r * 255 / a} * ``` */ char premultiplied_alpha; void *reserved; } avif_data_t; /** * Parse AVIF image file and return results. Returns `NULL` if the file can't be parsed. * * Call `avif_data_free` on the result when done. */ const avif_data_t *avif_parse(const unsigned char *bytes, size_t bytes_len); /** * Free all data related to `avif_data_t` */ void avif_data_free(const avif_data_t *data); #ifdef __cplusplus } // extern "C" #endif // __cplusplus avif-parse-1.4.0/src/boxes.rs000064400000000000000000000226631046102023000141510ustar 00000000000000// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::fmt; // To ensure we don't use stdlib allocating types by accident #[allow(dead_code)] struct Vec; #[allow(dead_code)] struct Box; #[allow(dead_code)] struct HashMap; #[allow(dead_code)] struct String; macro_rules! box_database { ($($(#[$attr:meta])* $boxenum:ident $boxtype:expr),*,) => { #[derive(Clone, Copy, PartialEq)] pub enum BoxType { $($(#[$attr])* $boxenum),*, UnknownBox(u32), } #[deny(unreachable_patterns)] impl From for BoxType { fn from(t: u32) -> BoxType { use self::BoxType::*; match t { $($(#[$attr])* $boxtype => $boxenum),*, _ => UnknownBox(t), } } } #[deny(unreachable_patterns)] impl From for u32 { fn from(box_type: BoxType) -> u32 { use self::BoxType::*; match box_type { $($(#[$attr])* $boxenum => $boxtype),*, UnknownBox(t) => t, } } } impl fmt::Debug for BoxType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fourcc: FourCC = From::from(self.clone()); fourcc.fmt(f) } } } } #[derive(Default, PartialEq, Clone)] pub struct FourCC { pub value: [u8; 4], } impl From for FourCC { fn from(number: u32) -> Self { Self { value: number.to_be_bytes() } } } impl From for FourCC { fn from(t: BoxType) -> Self { let box_num: u32 = Into::into(t); From::from(box_num) } } impl From<[u8; 4]> for FourCC { fn from(v: [u8; 4]) -> Self { Self { value: v } } } impl fmt::Debug for FourCC { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match std::str::from_utf8(&self.value) { Ok(s) => f.write_str(s), Err(_) => self.value.fmt(f), } } } impl fmt::Display for FourCC { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(std::str::from_utf8(&self.value).unwrap_or("null")) } } impl PartialEq<&[u8; 4]> for FourCC { fn eq(&self, other: &&[u8; 4]) -> bool { self.value.eq(*other) } } box_database!( FileTypeBox 0x6674_7970, // "ftyp" MediaDataBox 0x6d64_6174, // "mdat" PrimaryItemBox 0x7069_746d, // "pitm" ItemInfoBox 0x6969_6e66, // "iinf" ItemInfoEntry 0x696e_6665, // "infe" ItemLocationBox 0x696c_6f63, // "iloc" MovieBox 0x6d6f_6f76, // "moov" MovieHeaderBox 0x6d76_6864, // "mvhd" TrackBox 0x7472_616b, // "trak" TrackHeaderBox 0x746b_6864, // "tkhd" EditBox 0x6564_7473, // "edts" MediaBox 0x6d64_6961, // "mdia" EditListBox 0x656c_7374, // "elst" MediaHeaderBox 0x6d64_6864, // "mdhd" HandlerBox 0x6864_6c72, // "hdlr" MediaInformationBox 0x6d69_6e66, // "minf" ImageReferenceBox 0x6972_6566, // "iref" ImagePropertiesBox 0x6970_7270, // "iprp" ItemPropertyContainerBox 0x6970_636f, // "ipco" ItemPropertyAssociationBox 0x6970_6d61, // "ipma" ColorInformationBox 0x636f_6c72, // "colr" PixelInformationBox 0x7069_7869, // "pixi" AuxiliaryTypeProperty 0x6175_7843, // "auxC" SampleTableBox 0x7374_626c, // "stbl" SampleDescriptionBox 0x7374_7364, // "stsd" TimeToSampleBox 0x7374_7473, // "stts" SampleToChunkBox 0x7374_7363, // "stsc" SampleSizeBox 0x7374_737a, // "stsz" ChunkOffsetBox 0x7374_636f, // "stco" ChunkLargeOffsetBox 0x636f_3634, // "co64" SyncSampleBox 0x7374_7373, // "stss" AVCSampleEntry 0x6176_6331, // "avc1" AVC3SampleEntry 0x6176_6333, // "avc3" - Need to check official name in spec. AVCConfigurationBox 0x6176_6343, // "avcC" MP4AudioSampleEntry 0x6d70_3461, // "mp4a" MP4VideoSampleEntry 0x6d70_3476, // "mp4v" ESDBox 0x6573_6473, // "esds" VP8SampleEntry 0x7670_3038, // "vp08" VP9SampleEntry 0x7670_3039, // "vp09" VPCodecConfigurationBox 0x7670_6343, // "vpcC" AV1SampleEntry 0x6176_3031, // "av01" AV1CodecConfigurationBox 0x6176_3143, // "av1C" FLACSampleEntry 0x664c_6143, // "fLaC" FLACSpecificBox 0x6466_4c61, // "dfLa" OpusSampleEntry 0x4f70_7573, // "Opus" OpusSpecificBox 0x644f_7073, // "dOps" ProtectedVisualSampleEntry 0x656e_6376, // "encv" - Need to check official name in spec. ProtectedAudioSampleEntry 0x656e_6361, // "enca" - Need to check official name in spec. MovieExtendsBox 0x6d76_6578, // "mvex" MovieExtendsHeaderBox 0x6d65_6864, // "mehd" QTWaveAtom 0x7761_7665, // "wave" - quicktime atom ProtectionSystemSpecificHeaderBox 0x7073_7368, // "pssh" SchemeInformationBox 0x7363_6869, // "schi" TrackEncryptionBox 0x7465_6e63, // "tenc" ProtectionSchemeInfoBox 0x7369_6e66, // "sinf" OriginalFormatBox 0x6672_6d61, // "frma" SchemeTypeBox 0x7363_686d, // "schm" MP3AudioSampleEntry 0x2e6d_7033, // ".mp3" - from F4V. CompositionOffsetBox 0x6374_7473, // "ctts" LPCMAudioSampleEntry 0x6c70_636d, // "lpcm" - quicktime atom ALACSpecificBox 0x616c_6163, // "alac" - Also used by ALACSampleEntry UuidBox 0x7575_6964, // "uuid" MetadataBox 0x6d65_7461, // "meta" MetadataHeaderBox 0x6d68_6472, // "mhdr" MetadataItemKeysBox 0x6b65_7973, // "keys" MetadataItemListEntry 0x696c_7374, // "ilst" MetadataItemDataEntry 0x6461_7461, // "data" MetadataItemNameBox 0x6e61_6d65, // "name" UserdataBox 0x7564_7461, // "udta" AlbumEntry 0xa961_6c62, // "©alb" ArtistEntry 0xa941_5254, // "©ART" ArtistLowercaseEntry 0xa961_7274, // "©art" AlbumArtistEntry 0x6141_5254, // "aART" CommentEntry 0xa963_6d74, // "©cmt" DateEntry 0xa964_6179, // "©day" TitleEntry 0xa96e_616d, // "©nam" CustomGenreEntry 0xa967_656e, // "©gen" StandardGenreEntry 0x676e_7265, // "gnre" TrackNumberEntry 0x7472_6b6e, // "trkn" DiskNumberEntry 0x6469_736b, // "disk" ComposerEntry 0xa977_7274, // "©wrt" EncoderEntry 0xa974_6f6f, // "©too" EncodedByEntry 0xa965_6e63, // "©enc" TempoEntry 0x746d_706f, // "tmpo" CopyrightEntry 0x6370_7274, // "cprt" CompilationEntry 0x6370_696c, // "cpil" CoverArtEntry 0x636f_7672, // "covr" AdvisoryEntry 0x7274_6e67, // "rtng" RatingEntry 0x7261_7465, // "rate" GroupingEntry 0xa967_7270, // "©grp" MediaTypeEntry 0x7374_696b, // "stik" PodcastEntry 0x7063_7374, // "pcst" CategoryEntry 0x6361_7467, // "catg" KeywordEntry 0x6b65_7977, // "keyw" PodcastUrlEntry 0x7075_726c, // "purl" PodcastGuidEntry 0x6567_6964, // "egid" DescriptionEntry 0x6465_7363, // "desc" LongDescriptionEntry 0x6c64_6573, // "ldes" LyricsEntry 0xa96c_7972, // "©lyr" TVNetworkNameEntry 0x7476_6e6e, // "tvnn" TVShowNameEntry 0x7476_7368, // "tvsh" TVEpisodeNameEntry 0x7476_656e, // "tven" TVSeasonNumberEntry 0x7476_736e, // "tvsn" TVEpisodeNumberEntry 0x7476_6573, // "tves" PurchaseDateEntry 0x7075_7264, // "purd" GaplessPlaybackEntry 0x7067_6170, // "pgap" OwnerEntry 0x6f77_6e72, // "ownr" HDVideoEntry 0x6864_7664, // "hdvd" SortNameEntry 0x736f_6e6d, // "sonm" SortAlbumEntry 0x736f_616c, // "soal" SortArtistEntry 0x736f_6172, // "soar" SortAlbumArtistEntry 0x736f_6161, // "soaa" SortComposerEntry 0x736f_636f, // "soco" ); avif-parse-1.4.0/src/c_api.rs000064400000000000000000000036061046102023000141000ustar 00000000000000use crate::AvifData as AvifDataRust; /// Result of parsing an AVIF file. Contains AV1-compressed data. #[allow(bad_style)] #[repr(C)] pub struct avif_data_t { /// AV1 data for color channels pub primary_data: *const u8, pub primary_size: usize, /// AV1 data for alpha channel (may be NULL if the image is fully opaque) pub alpha_data: *const u8, pub alpha_size: usize, /// 0 = normal, uncorrelated alpha channel /// 1 = premultiplied alpha. You must divide RGB values by alpha. /// /// ```c /// if (a != 0) {r = r * 255 / a} /// ``` pub premultiplied_alpha: u8, rusty_handle: *mut AvifDataRust, } /// Parse AVIF image file and return results. Returns `NULL` if the file can't be parsed. /// /// Call [`avif_data_free`] on the result when done. #[no_mangle] pub unsafe extern "C" fn avif_parse(bytes: *const u8, bytes_len: usize) -> *const avif_data_t { if bytes.is_null() || bytes_len == 0 { return std::ptr::null(); } let mut data = std::slice::from_raw_parts(bytes, bytes_len); match crate::read_avif(&mut data) { Ok(data) => Box::into_raw(Box::new(avif_data_t { primary_data: data.primary_item.as_ptr(), primary_size: data.primary_item.len(), alpha_data: data .alpha_item .as_ref() .map_or(std::ptr::null(), |a| a.as_ptr()), alpha_size: data.alpha_item.as_ref().map_or(0, |a| a.len()), premultiplied_alpha: u8::from(data.premultiplied_alpha), rusty_handle: Box::into_raw(Box::new(data)), })), Err(_) => std::ptr::null(), } } /// Free all data related to [`avif_data_t`] #[no_mangle] pub unsafe extern "C" fn avif_data_free(data: *const avif_data_t) { if data.is_null() { return; } let _ = Box::from_raw((*data).rusty_handle); let _ = Box::from_raw(data.cast_mut()); } avif-parse-1.4.0/src/lib.rs000064400000000000000000001242141046102023000135720ustar 00000000000000#![allow(clippy::missing_safety_doc)] //! Module for parsing ISO Base Media Format aka video/mp4 streams. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use arrayvec::ArrayVec; use log::{debug, warn}; use bitreader::BitReader; use byteorder::ReadBytesExt; use fallible_collections::{TryClone, TryReserveError}; use std::convert::{TryFrom, TryInto as _}; use std::io::{Read, Take}; use std::num::NonZeroU32; use std::ops::{Range, RangeFrom}; mod obu; mod boxes; use crate::boxes::{BoxType, FourCC}; /// This crate can be used from C. pub mod c_api; // Arbitrary buffer size limit used for raw read_bufs on a box. // const BUF_SIZE_LIMIT: u64 = 10 * 1024 * 1024; /// A trait to indicate a type can be infallibly converted to `u64`. /// This should only be implemented for infallible conversions, so only unsigned types are valid. trait ToU64 { fn to_u64(self) -> u64; } /// Statically verify that the platform `usize` can fit within a `u64`. /// If the size won't fit on the given platform, this will fail at compile time, but if a type /// which can fail `TryInto` is used, it may panic. impl ToU64 for usize { fn to_u64(self) -> u64 { const _: () = assert!(std::mem::size_of::() <= std::mem::size_of::()); self.try_into().ok().unwrap() } } /// A trait to indicate a type can be infallibly converted to `usize`. /// This should only be implemented for infallible conversions, so only unsigned types are valid. pub(crate) trait ToUsize { fn to_usize(self) -> usize; } /// Statically verify that the given type can fit within a `usize`. /// If the size won't fit on the given platform, this will fail at compile time, but if a type /// which can fail `TryInto` is used, it may panic. macro_rules! impl_to_usize_from { ( $from_type:ty ) => { impl ToUsize for $from_type { fn to_usize(self) -> usize { const _: () = assert!(std::mem::size_of::<$from_type>() <= std::mem::size_of::()); self.try_into().ok().unwrap() } } }; } impl_to_usize_from!(u8); impl_to_usize_from!(u16); impl_to_usize_from!(u32); /// Indicate the current offset (i.e., bytes already read) in a reader trait Offset { fn offset(&self) -> u64; } /// Wraps a reader to track the current offset struct OffsetReader<'a, T> { reader: &'a mut T, offset: u64, } impl<'a, T> OffsetReader<'a, T> { fn new(reader: &'a mut T) -> Self { Self { reader, offset: 0 } } } impl Offset for OffsetReader<'_, T> { fn offset(&self) -> u64 { self.offset } } impl Read for OffsetReader<'_, T> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let bytes_read = self.reader.read(buf)?; self.offset = self .offset .checked_add(bytes_read.to_u64()) .ok_or(Error::Unsupported("total bytes read too large for offset type"))?; Ok(bytes_read) } } #[doc(hidden)] pub type TryVec = fallible_collections::TryVec; #[doc(hidden)] pub type TryString = fallible_collections::TryVec; #[doc(hidden)] pub type TryHashMap = std::collections::HashMap; #[doc(hidden)] pub type TryBox = fallible_collections::TryBox; // To ensure we don't use stdlib allocating types by accident #[allow(dead_code)] struct Vec; #[allow(dead_code)] struct Box; #[allow(dead_code)] struct HashMap; #[allow(dead_code)] struct String; /// Describes parser failures. /// /// This enum wraps the standard `io::Error` type, unified with /// our own parser error states and those of crates we use. #[derive(Debug)] pub enum Error { /// Parse error caused by corrupt or malformed data. InvalidData(&'static str), /// Parse error caused by limited parser support rather than invalid data. Unsupported(&'static str), /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data. UnexpectedEOF, /// Propagate underlying errors from `std::io`. Io(std::io::Error), /// `read_mp4` terminated without detecting a moov box. NoMoov, /// Out of memory OutOfMemory, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let msg = match self { Self::InvalidData(s) | Self::Unsupported(s) => s, Self::UnexpectedEOF => "EOF", Self::Io(err) => return err.fmt(f), Self::NoMoov => "Missing Moov box", Self::OutOfMemory => "OOM", }; f.write_str(msg) } } impl std::error::Error for Error {} impl From for Error { #[cold] #[cfg_attr(debug_assertions, track_caller)] fn from(err: bitreader::BitReaderError) -> Self { log::warn!("bitreader: {err}"); debug_assert!(!matches!(err, bitreader::BitReaderError::TooManyBitsForType { .. })); // bug Self::InvalidData("truncated bits") } } impl From for Error { fn from(err: std::io::Error) -> Self { match err.kind() { std::io::ErrorKind::UnexpectedEof => Self::UnexpectedEOF, _ => Self::Io(err), } } } impl From for Error { fn from(_: std::string::FromUtf8Error) -> Self { Self::InvalidData("invalid utf8") } } impl From for Error { fn from(_: std::num::TryFromIntError) -> Self { Self::Unsupported("integer conversion failed") } } impl From for std::io::Error { fn from(err: Error) -> Self { let kind = match err { Error::InvalidData(_) => std::io::ErrorKind::InvalidData, Error::UnexpectedEOF => std::io::ErrorKind::UnexpectedEof, Error::Io(io_err) => return io_err, _ => std::io::ErrorKind::Other, }; Self::new(kind, err) } } impl From for Error { fn from(_: TryReserveError) -> Self { Self::OutOfMemory } } /// Result shorthand using our Error enum. pub type Result = std::result::Result; /// Basic ISO box structure. /// /// mp4 files are a sequence of possibly-nested 'box' structures. Each box /// begins with a header describing the length of the box's data and a /// four-byte box type which identifies the type of the box. Together these /// are enough to interpret the contents of that section of the file. /// /// See ISO 14496-12:2015 § 4.2 #[derive(Debug, Clone, Copy)] struct BoxHeader { /// Box type. name: BoxType, /// Size of the box in bytes. size: u64, /// Offset to the start of the contained data (or header size). offset: u64, /// Uuid for extended type. #[allow(unused)] uuid: Option<[u8; 16]>, } impl BoxHeader { /// 4-byte size + 4-byte type const MIN_SIZE: u64 = 8; /// 4-byte size + 4-byte type + 16-byte size const MIN_LARGE_SIZE: u64 = 16; } /// File type box 'ftyp'. #[derive(Debug)] #[allow(unused)] struct FileTypeBox { major_brand: FourCC, minor_version: u32, compatible_brands: TryVec, } // Handler reference box 'hdlr' #[derive(Debug)] #[allow(unused)] struct HandlerBox { handler_type: FourCC, } #[derive(Debug)] #[allow(unused)] pub(crate) struct AV1ConfigBox { pub(crate) profile: u8, pub(crate) level: u8, pub(crate) tier: u8, pub(crate) bit_depth: u8, pub(crate) monochrome: bool, pub(crate) chroma_subsampling_x: u8, pub(crate) chroma_subsampling_y: u8, pub(crate) chroma_sample_position: u8, pub(crate) initial_presentation_delay_present: bool, pub(crate) initial_presentation_delay_minus_one: u8, pub(crate) config_obus: TryVec, } #[derive(Debug, Default)] pub struct AvifData { /// AV1 data for the color channels. /// /// The collected data indicated by the `pitm` box, See ISO 14496-12:2015 § 8.11.4 pub primary_item: TryVec, /// AV1 data for alpha channel. /// /// Associated alpha channel for the primary item, if any pub alpha_item: Option>, /// If true, divide RGB values by the alpha value. /// /// See `prem` in MIAF § 7.3.5.2 pub premultiplied_alpha: bool, } impl AvifData { pub fn from_reader(reader: &mut R) -> Result { read_avif(reader) } /// Parses AV1 data to get basic properties of the opaque channel pub fn primary_item_metadata(&self) -> Result { AV1Metadata::parse_av1_bitstream(&self.primary_item) } /// Parses AV1 data to get basic properties about the alpha channel, if any pub fn alpha_item_metadata(&self) -> Result> { self.alpha_item.as_deref().map(AV1Metadata::parse_av1_bitstream).transpose() } } /// See [`AvifData::primary_item_metadata()`] #[non_exhaustive] #[derive(Debug, Clone)] pub struct AV1Metadata { /// Should be true for non-animated AVIF pub still_picture: bool, pub max_frame_width: NonZeroU32, pub max_frame_height: NonZeroU32, /// 8, 10, or 12 pub bit_depth: u8, /// 0, 1 or 2 for the level of complexity pub seq_profile: u8, /// Horizontal and vertical. `false` is full-res. pub chroma_subsampling: (bool, bool), pub monochrome: bool, } impl AV1Metadata { /// Parses raw AV1 bitstream (OBU sequence header) only. /// /// This is for the bare image payload from an encoder, not an AVIF/HEIF file. /// To parse AVIF files, see [`AvifData::from_reader()`]. #[inline(never)] pub fn parse_av1_bitstream(obu_bitstream: &[u8]) -> Result { let h = obu::parse_obu(obu_bitstream)?; Ok(Self { still_picture: h.still_picture, max_frame_width: h.max_frame_width, max_frame_height: h.max_frame_height, bit_depth: h.color.bit_depth, seq_profile: h.seq_profile, chroma_subsampling: h.color.chroma_subsampling, monochrome: h.color.monochrome, }) } } struct AvifInternalMeta { item_references: TryVec, properties: TryVec, primary_item_id: u32, iloc_items: TryVec, } /// A Media Data Box /// See ISO 14496-12:2015 § 8.1.1 struct MediaDataBox { /// Offset of `data` from the beginning of the file. See `ConstructionMethod::File` offset: u64, data: TryVec, } impl MediaDataBox { /// Check whether the beginning of `extent` is within the bounds of the `MediaDataBox`. /// We assume extents to not cross box boundaries. If so, this will cause an error /// in `read_extent`. fn contains_extent(&self, extent: &ExtentRange) -> bool { if self.offset <= extent.start() { let start_offset = extent.start() - self.offset; start_offset < self.data.len().to_u64() } else { false } } /// Check whether `extent` covers the `MediaDataBox` exactly. fn matches_extent(&self, extent: &ExtentRange) -> bool { if self.offset == extent.start() { match extent { ExtentRange::WithLength(range) => { if let Some(end) = self.offset.checked_add(self.data.len().to_u64()) { end == range.end } else { false } }, ExtentRange::ToEnd(_) => true, } } else { false } } /// Copy the range specified by `extent` to the end of `buf` or return an error if the range /// is not fully contained within `MediaDataBox`. fn read_extent(&self, extent: &ExtentRange, buf: &mut TryVec) -> Result<()> { let start_offset = extent .start() .checked_sub(self.offset) .ok_or(Error::InvalidData("mdat does not contain extent"))?; let slice = match extent { ExtentRange::WithLength(range) => { let range_len = range .end .checked_sub(range.start) .ok_or(Error::InvalidData("range start > end"))?; let end = start_offset .checked_add(range_len) .ok_or(Error::InvalidData("extent end overflow"))?; self.data.get(start_offset.try_into()?..end.try_into()?) }, ExtentRange::ToEnd(_) => self.data.get(start_offset.try_into()?..), }; let slice = slice.ok_or(Error::InvalidData("extent crosses box boundary"))?; buf.extend_from_slice(slice)?; Ok(()) } } /// Used for 'infe' boxes within 'iinf' boxes /// See ISO 14496-12:2015 § 8.11.6 /// Only versions {2, 3} are supported #[derive(Debug)] struct ItemInfoEntry { item_id: u32, item_type: FourCC, } /// See ISO 14496-12:2015 § 8.11.12 #[derive(Debug)] struct SingleItemTypeReferenceBox { item_type: FourCC, from_item_id: u32, to_item_id: u32, } /// Potential sizes (in bytes) of variable-sized fields of the 'iloc' box /// See ISO 14496-12:2015 § 8.11.3 #[derive(Debug)] enum IlocFieldSize { Zero, Four, Eight, } impl IlocFieldSize { const fn to_bits(&self) -> u8 { match self { Self::Zero => 0, Self::Four => 32, Self::Eight => 64, } } } impl TryFrom for IlocFieldSize { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::Zero), 4 => Ok(Self::Four), 8 => Ok(Self::Eight), _ => Err(Error::InvalidData("value must be in the set {0, 4, 8}")), } } } #[derive(PartialEq)] enum IlocVersion { Zero, One, Two, } impl TryFrom for IlocVersion { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::Zero), 1 => Ok(Self::One), 2 => Ok(Self::Two), _ => Err(Error::Unsupported("unsupported version in 'iloc' box")), } } } /// Used for 'iloc' boxes /// See ISO 14496-12:2015 § 8.11.3 /// `base_offset` is omitted since it is integrated into the ranges in `extents` /// `data_reference_index` is omitted, since only 0 (i.e., this file) is supported #[derive(Debug)] struct ItemLocationBoxItem { item_id: u32, construction_method: ConstructionMethod, /// Unused for `ConstructionMethod::Idat` extents: TryVec, } #[derive(Clone, Copy, Debug, PartialEq)] enum ConstructionMethod { File, Idat, #[allow(dead_code)] // TODO: see https://github.com/mozilla/mp4parse-rust/issues/196 Item, } /// `extent_index` is omitted since it's only used for `ConstructionMethod::Item` which /// is currently not implemented. #[derive(Clone, Debug)] struct ItemLocationBoxExtent { extent_range: ExtentRange, } #[derive(Clone, Debug)] enum ExtentRange { WithLength(Range), ToEnd(RangeFrom), } impl ExtentRange { const fn start(&self) -> u64 { match self { Self::WithLength(r) => r.start, Self::ToEnd(r) => r.start, } } } /// See ISO 14496-12:2015 § 4.2 struct BMFFBox<'a, T> { head: BoxHeader, content: Take<&'a mut T>, } impl BMFFBox<'_, T> { fn read_into_try_vec(&mut self) -> std::io::Result> { let mut vec = std::vec::Vec::new(); vec.try_reserve_exact(self.content.limit() as usize) .map_err(|_| std::io::ErrorKind::OutOfMemory)?; self.content.read_to_end(&mut vec)?; // The default impl Ok(vec.into()) } } #[test] fn box_read_to_end() { let tmp = &mut b"1234567890".as_slice(); let mut src = BMFFBox { head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None }, content: <_ as Read>::take(tmp, 5), }; let buf = src.read_into_try_vec().unwrap(); assert_eq!(buf.len(), 5); assert_eq!(buf, b"12345".as_ref()); } #[test] fn box_read_to_end_oom() { let tmp = &mut b"1234567890".as_slice(); let mut src = BMFFBox { head: BoxHeader { name: BoxType::FileTypeBox, size: 5, offset: 0, uuid: None }, content: <_ as Read>::take(tmp, usize::MAX.try_into().expect("usize < u64")), }; assert!(src.read_into_try_vec().is_err()); } struct BoxIter<'a, T> { src: &'a mut T, } impl BoxIter<'_, T> { fn new(src: &mut T) -> BoxIter<'_, T> { BoxIter { src } } fn next_box(&mut self) -> Result>> { let r = read_box_header(self.src); match r { Ok(h) => Ok(Some(BMFFBox { head: h, content: self.src.take(h.size - h.offset), })), Err(Error::UnexpectedEOF) => Ok(None), Err(e) => Err(e), } } } impl Read for BMFFBox<'_, T> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.content.read(buf) } } impl Offset for BMFFBox<'_, T> { fn offset(&self) -> u64 { self.content.get_ref().offset() } } impl BMFFBox<'_, T> { fn bytes_left(&self) -> u64 { self.content.limit() } const fn get_header(&self) -> &BoxHeader { &self.head } fn box_iter(&mut self) -> BoxIter<'_, Self> { BoxIter::new(self) } } impl Drop for BMFFBox<'_, T> { fn drop(&mut self) { if self.content.limit() > 0 { let name: FourCC = From::from(self.head.name); debug!("Dropping {} bytes in '{}'", self.content.limit(), name); } } } /// Read and parse a box header. /// /// Call this first to determine the type of a particular mp4 box /// and its length. Used internally for dispatching to specific /// parsers for the internal content, or to get the length to /// skip unknown or uninteresting boxes. /// /// See ISO 14496-12:2015 § 4.2 fn read_box_header(src: &mut T) -> Result { let size32 = be_u32(src)?; let name = BoxType::from(be_u32(src)?); let size = match size32 { // valid only for top-level box and indicates it's the last box in the file. usually mdat. 0 => return Err(Error::Unsupported("unknown sized box")), 1 => { let size64 = be_u64(src)?; if size64 < BoxHeader::MIN_LARGE_SIZE { return Err(Error::InvalidData("malformed wide size")); } size64 }, _ => { if u64::from(size32) < BoxHeader::MIN_SIZE { return Err(Error::InvalidData("malformed size")); } u64::from(size32) }, }; let mut offset = match size32 { 1 => BoxHeader::MIN_LARGE_SIZE, _ => BoxHeader::MIN_SIZE, }; let uuid = if name == BoxType::UuidBox { if size >= offset + 16 { let mut buffer = [0u8; 16]; let count = src.read(&mut buffer)?; offset += count.to_u64(); if count == 16 { Some(buffer) } else { debug!("malformed uuid (short read), skipping"); None } } else { debug!("malformed uuid, skipping"); None } } else { None }; assert!(offset <= size); Ok(BoxHeader { name, size, offset, uuid }) } /// Parse the extra header fields for a full box. fn read_fullbox_extra(src: &mut T) -> Result<(u8, u32)> { let version = src.read_u8()?; let flags_a = src.read_u8()?; let flags_b = src.read_u8()?; let flags_c = src.read_u8()?; Ok(( version, u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c), )) } // Parse the extra fields for a full box whose flag fields must be zero. fn read_fullbox_version_no_flags(src: &mut T) -> Result { let (version, flags) = read_fullbox_extra(src)?; if flags != 0 { return Err(Error::Unsupported("expected flags to be 0")); } Ok(version) } /// Skip over the entire contents of a box. fn skip_box_content(src: &mut BMFFBox<'_, T>) -> Result<()> { // Skip the contents of unknown chunks. let to_skip = { let header = src.get_header(); debug!("{header:?} (skipped)"); header .size .checked_sub(header.offset) .ok_or(Error::InvalidData("header offset > size"))? }; assert_eq!(to_skip, src.bytes_left()); skip(src, to_skip) } /// Skip over the remain data of a box. fn skip_box_remain(src: &mut BMFFBox<'_, T>) -> Result<()> { let remain = { let header = src.get_header(); let len = src.bytes_left(); debug!("remain {len} (skipped) in {header:?}"); len }; skip(src, remain) } /// Read the contents of an AVIF file /// /// Metadata is accumulated and returned in [`AvifData`] struct, pub fn read_avif(f: &mut T) -> Result { let mut f = OffsetReader::new(f); let mut iter = BoxIter::new(&mut f); // 'ftyp' box must occur first; see ISO 14496-12:2015 § 4.3.1 if let Some(mut b) = iter.next_box()? { if b.head.name == BoxType::FileTypeBox { let ftyp = read_ftyp(&mut b)?; if ftyp.major_brand != b"avif" { if ftyp.major_brand == b"avis" { return Err(Error::Unsupported("Animated AVIF is not supported. Please use real AV1 videos instead.")); } warn!("major_brand: {}", ftyp.major_brand); return Err(Error::InvalidData("ftyp must be 'avif'")); } } else { return Err(Error::InvalidData("'ftyp' box must occur first")); } } let mut meta = None; let mut mdats = TryVec::new(); while let Some(mut b) = iter.next_box()? { match b.head.name { BoxType::MetadataBox => { if meta.is_some() { return Err(Error::InvalidData("There should be zero or one meta boxes per ISO 14496-12:2015 § 8.11.1.1")); } meta = Some(read_avif_meta(&mut b)?); }, BoxType::MediaDataBox => { if b.bytes_left() > 0 { let offset = b.offset(); let data = b.read_into_try_vec()?; mdats.push(MediaDataBox { offset, data })?; } }, _ => skip_box_content(&mut b)?, } check_parser_state(&b.content)?; } let meta = meta.ok_or(Error::InvalidData("missing meta"))?; let alpha_item_id = meta .item_references .iter() // Auxiliary image for the primary image .filter(|iref| { iref.to_item_id == meta.primary_item_id && iref.from_item_id != meta.primary_item_id && iref.item_type == b"auxl" }) .map(|iref| iref.from_item_id) // which has the alpha property .find(|&item_id| { meta.properties.iter().any(|prop| { prop.item_id == item_id && match &prop.property { ItemProperty::AuxiliaryType(urn) => { urn.type_subtype().0 == b"urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" } _ => false, } }) }); let mut context = AvifData { premultiplied_alpha: alpha_item_id.map_or(false, |alpha_item_id| { meta.item_references.iter().any(|iref| { iref.from_item_id == meta.primary_item_id && iref.to_item_id == alpha_item_id && iref.item_type == b"prem" }) }), ..Default::default() }; // load data of relevant items for loc in meta.iloc_items.iter() { let item_data = if loc.item_id == meta.primary_item_id { &mut context.primary_item } else if Some(loc.item_id) == alpha_item_id { context.alpha_item.get_or_insert_with(TryVec::new) } else { continue; }; if loc.construction_method != ConstructionMethod::File { return Err(Error::Unsupported("unsupported construction_method")); } for extent in loc.extents.iter() { let mut found = false; // try to find an overlapping mdat for mdat in mdats.iter_mut() { if mdat.matches_extent(&extent.extent_range) { item_data.append(&mut mdat.data)?; found = true; break; } else if mdat.contains_extent(&extent.extent_range) { mdat.read_extent(&extent.extent_range, item_data)?; found = true; break; } } if !found { return Err(Error::InvalidData("iloc contains an extent that is not in mdat")); } } } Ok(context) } /// Parse a metadata box in the context of an AVIF /// Currently requires the primary item to be an av01 item type and generates /// an error otherwise. /// See ISO 14496-12:2015 § 8.11.1 fn read_avif_meta(src: &mut BMFFBox<'_, T>) -> Result { let version = read_fullbox_version_no_flags(src)?; if version != 0 { return Err(Error::Unsupported("unsupported meta version")); } let mut primary_item_id = None; let mut item_infos = None; let mut iloc_items = None; let mut item_references = TryVec::new(); let mut properties = TryVec::new(); let mut iter = src.box_iter(); while let Some(mut b) = iter.next_box()? { match b.head.name { BoxType::ItemInfoBox => { if item_infos.is_some() { return Err(Error::InvalidData("There should be zero or one iinf boxes per ISO 14496-12:2015 § 8.11.6.1")); } item_infos = Some(read_iinf(&mut b)?); }, BoxType::ItemLocationBox => { if iloc_items.is_some() { return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.3.1")); } iloc_items = Some(read_iloc(&mut b)?); }, BoxType::PrimaryItemBox => { if primary_item_id.is_some() { return Err(Error::InvalidData("There should be zero or one iloc boxes per ISO 14496-12:2015 § 8.11.4.1")); } primary_item_id = Some(read_pitm(&mut b)?); }, BoxType::ImageReferenceBox => { item_references.append(&mut read_iref(&mut b)?)?; }, BoxType::ImagePropertiesBox => { properties = read_iprp(&mut b)?; }, _ => skip_box_content(&mut b)?, } check_parser_state(&b.content)?; } let primary_item_id = primary_item_id.ok_or(Error::InvalidData("Required pitm box not present in meta box"))?; let item_infos = item_infos.ok_or(Error::InvalidData("iinf missing"))?; if let Some(item_info) = item_infos.iter().find(|x| x.item_id == primary_item_id) { if item_info.item_type != b"av01" { if item_info.item_type == b"grid" { return Err(Error::Unsupported("Grid-based AVIF collage is not supported")); } warn!("primary_item_id type: {}", item_info.item_type); return Err(Error::InvalidData("primary_item_id type is not av01")); } } else { return Err(Error::InvalidData("primary_item_id not present in iinf box")); } Ok(AvifInternalMeta { properties, item_references, primary_item_id, iloc_items: iloc_items.ok_or(Error::InvalidData("iloc missing"))?, }) } /// Parse a Primary Item Box /// See ISO 14496-12:2015 § 8.11.4 fn read_pitm(src: &mut BMFFBox<'_, T>) -> Result { let version = read_fullbox_version_no_flags(src)?; let item_id = match version { 0 => be_u16(src)?.into(), 1 => be_u32(src)?, _ => return Err(Error::Unsupported("unsupported pitm version")), }; Ok(item_id) } /// Parse an Item Information Box /// See ISO 14496-12:2015 § 8.11.6 fn read_iinf(src: &mut BMFFBox<'_, T>) -> Result> { let version = read_fullbox_version_no_flags(src)?; match version { 0 | 1 => (), _ => return Err(Error::Unsupported("unsupported iinf version")), } let entry_count = if version == 0 { be_u16(src)?.to_usize() } else { be_u32(src)?.to_usize() }; let mut item_infos = TryVec::with_capacity(entry_count)?; let mut iter = src.box_iter(); while let Some(mut b) = iter.next_box()? { if b.head.name != BoxType::ItemInfoEntry { return Err(Error::InvalidData("iinf box should contain only infe boxes")); } item_infos.push(read_infe(&mut b)?)?; check_parser_state(&b.content)?; } Ok(item_infos) } /// Parse an Item Info Entry /// See ISO 14496-12:2015 § 8.11.6.2 fn read_infe(src: &mut BMFFBox<'_, T>) -> Result { // According to the standard, it seems the flags field should be 0, but // at least one sample AVIF image has a nonzero value. let (version, _) = read_fullbox_extra(src)?; // mif1 brand (see ISO 23008-12:2017 § 10.2.1) only requires v2 and 3 let item_id = match version { 2 => be_u16(src)?.into(), 3 => be_u32(src)?, _ => return Err(Error::Unsupported("unsupported version in 'infe' box")), }; let item_protection_index = be_u16(src)?; if item_protection_index != 0 { return Err(Error::Unsupported("protected items (infe.item_protection_index != 0) are not supported")); } let item_type = FourCC::from(be_u32(src)?); debug!("infe item_id {item_id} item_type: {item_type}"); // There are some additional fields here, but they're not of interest to us skip_box_remain(src)?; Ok(ItemInfoEntry { item_id, item_type }) } fn read_iref(src: &mut BMFFBox<'_, T>) -> Result> { let mut item_references = TryVec::new(); let version = read_fullbox_version_no_flags(src)?; if version > 1 { return Err(Error::Unsupported("iref version")); } let mut iter = src.box_iter(); while let Some(mut b) = iter.next_box()? { let from_item_id = if version == 0 { be_u16(&mut b)?.into() } else { be_u32(&mut b)? }; let reference_count = be_u16(&mut b)?; for _ in 0..reference_count { let to_item_id = if version == 0 { be_u16(&mut b)?.into() } else { be_u32(&mut b)? }; if from_item_id == to_item_id { return Err(Error::InvalidData("from_item_id and to_item_id must be different")); } item_references.push(SingleItemTypeReferenceBox { item_type: b.head.name.into(), from_item_id, to_item_id, })?; } check_parser_state(&b.content)?; } Ok(item_references) } fn read_iprp(src: &mut BMFFBox<'_, T>) -> Result> { let mut iter = src.box_iter(); let mut properties = TryVec::new(); let mut associations = TryVec::new(); while let Some(mut b) = iter.next_box()? { match b.head.name { BoxType::ItemPropertyContainerBox => { properties = read_ipco(&mut b)?; }, BoxType::ItemPropertyAssociationBox => { associations = read_ipma(&mut b)?; }, _ => return Err(Error::InvalidData("unexpected ipco child")), } } let mut associated = TryVec::new(); for a in associations { let index = match a.property_index { 0 => continue, x => x as usize - 1, }; if let Some(prop) = properties.get(index) { if *prop != ItemProperty::Unsupported { associated.push(AssociatedProperty { item_id: a.item_id, property: prop.try_clone()?, })?; } } } Ok(associated) } #[derive(Debug, PartialEq)] pub(crate) enum ItemProperty { Channels(ArrayVec), AuxiliaryType(AuxiliaryTypeProperty), Unsupported, } impl TryClone for ItemProperty { fn try_clone(&self) -> Result { Ok(match self { Self::Channels(val) => Self::Channels(val.clone()), Self::AuxiliaryType(val) => Self::AuxiliaryType(val.try_clone()?), Self::Unsupported => Self::Unsupported, }) } } struct Association { item_id: u32, #[allow(unused)] essential: bool, property_index: u16, } pub(crate) struct AssociatedProperty { pub item_id: u32, pub property: ItemProperty, } fn read_ipma(src: &mut BMFFBox<'_, T>) -> Result> { let (version, flags) = read_fullbox_extra(src)?; let mut associations = TryVec::new(); let entry_count = be_u32(src)?; for _ in 0..entry_count { let item_id = if version == 0 { be_u16(src)?.into() } else { be_u32(src)? }; let association_count = src.read_u8()?; for _ in 0..association_count { let num_association_bytes = if flags & 1 == 1 { 2 } else { 1 }; let association = &mut [0; 2][..num_association_bytes]; src.read_exact(association)?; let mut association = BitReader::new(association); let essential = association.read_bool()?; let property_index = association.read_u16(association.remaining().try_into()?)?; associations.push(Association { item_id, essential, property_index, })?; } } Ok(associations) } fn read_ipco(src: &mut BMFFBox<'_, T>) -> Result> { let mut properties = TryVec::new(); let mut iter = src.box_iter(); while let Some(mut b) = iter.next_box()? { // Must push for every property to have correct index for them properties.push(match b.head.name { BoxType::PixelInformationBox => ItemProperty::Channels(read_pixi(&mut b)?), BoxType::AuxiliaryTypeProperty => ItemProperty::AuxiliaryType(read_auxc(&mut b)?), _ => { skip_box_remain(&mut b)?; ItemProperty::Unsupported }, })?; } Ok(properties) } fn read_pixi(src: &mut BMFFBox<'_, T>) -> Result> { let version = read_fullbox_version_no_flags(src)?; if version != 0 { return Err(Error::Unsupported("pixi version")); } let num_channels = usize::from(src.read_u8()?); let mut channels = ArrayVec::new(); channels.extend((0..num_channels.min(channels.capacity())).map(|_| 0)); debug_assert_eq!(num_channels, channels.len()); src.read_exact(&mut channels).map_err(|_| Error::InvalidData("invalid num_channels"))?; check_parser_state(&src.content)?; Ok(channels) } #[derive(Debug, PartialEq)] #[doc(hidden)] // this wasn't supposed to be public pub struct AuxiliaryTypeProperty { aux_data: TryString, } impl AuxiliaryTypeProperty { #[must_use] pub fn type_subtype(&self) -> (&[u8], &[u8]) { let split = self.aux_data.iter().position(|&b| b == b'\0') .map(|pos| self.aux_data.split_at(pos)); if let Some((aux_type, rest)) = split { (aux_type, &rest[1..]) } else { (&self.aux_data, &[]) } } } impl TryClone for AuxiliaryTypeProperty { fn try_clone(&self) -> Result { Ok(Self { aux_data: self.aux_data.try_clone()?, }) } } fn read_auxc(src: &mut BMFFBox<'_, T>) -> Result { let version = read_fullbox_version_no_flags(src)?; if version != 0 { return Err(Error::Unsupported("auxC version")); } let aux_data = src.read_into_try_vec()?; Ok(AuxiliaryTypeProperty { aux_data }) } /// Parse an item location box inside a meta box /// See ISO 14496-12:2015 § 8.11.3 fn read_iloc(src: &mut BMFFBox<'_, T>) -> Result> { let version: IlocVersion = read_fullbox_version_no_flags(src)?.try_into()?; let iloc = src.read_into_try_vec()?; let mut iloc = BitReader::new(&iloc); let offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?; let length_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?; let base_offset_size: IlocFieldSize = iloc.read_u8(4)?.try_into()?; let index_size: Option = match version { IlocVersion::One | IlocVersion::Two => Some(iloc.read_u8(4)?.try_into()?), IlocVersion::Zero => { let _reserved = iloc.read_u8(4)?; None }, }; let item_count = match version { IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?, IlocVersion::Two => iloc.read_u32(32)?, }; let mut items = TryVec::with_capacity(item_count.to_usize())?; for _ in 0..item_count { let item_id = match version { IlocVersion::Zero | IlocVersion::One => iloc.read_u32(16)?, IlocVersion::Two => iloc.read_u32(32)?, }; // The spec isn't entirely clear how an `iloc` should be interpreted for version 0, // which has no `construction_method` field. It does say: // "For maximum compatibility, version 0 of this box should be used in preference to // version 1 with `construction_method==0`, or version 2 when possible." // We take this to imply version 0 can be interpreted as using file offsets. let construction_method = match version { IlocVersion::Zero => ConstructionMethod::File, IlocVersion::One | IlocVersion::Two => { let _reserved = iloc.read_u16(12)?; match iloc.read_u16(4)? { 0 => ConstructionMethod::File, 1 => ConstructionMethod::Idat, 2 => return Err(Error::Unsupported("construction_method 'item_offset' is not supported")), _ => return Err(Error::InvalidData("construction_method is taken from the set 0, 1 or 2 per ISO 14496-12:2015 § 8.11.3.3")), } }, }; let data_reference_index = iloc.read_u16(16)?; if data_reference_index != 0 { return Err(Error::Unsupported("external file references (iloc.data_reference_index != 0) are not supported")); } let base_offset = iloc.read_u64(base_offset_size.to_bits())?; let extent_count = iloc.read_u16(16)?; if extent_count < 1 { return Err(Error::InvalidData("extent_count must have a value 1 or greater per ISO 14496-12:2015 § 8.11.3.3")); } let mut extents = TryVec::with_capacity(extent_count.to_usize())?; for _ in 0..extent_count { // Parsed but currently ignored, see `ItemLocationBoxExtent` let _extent_index = match &index_size { None | Some(IlocFieldSize::Zero) => None, Some(index_size) => { debug_assert!(version == IlocVersion::One || version == IlocVersion::Two); Some(iloc.read_u64(index_size.to_bits())?) }, }; // Per ISO 14496-12:2015 § 8.11.3.1: // "If the offset is not identified (the field has a length of zero), then the // beginning of the source (offset 0) is implied" // This behavior will follow from BitReader::read_u64(0) -> 0. let extent_offset = iloc.read_u64(offset_size.to_bits())?; let extent_length = iloc.read_u64(length_size.to_bits())?; // "If the length is not specified, or specified as zero, then the entire length of // the source is implied" (ibid) let start = base_offset .checked_add(extent_offset) .ok_or(Error::InvalidData("offset calculation overflow"))?; let extent_range = if extent_length == 0 { ExtentRange::ToEnd(RangeFrom { start }) } else { let end = start .checked_add(extent_length) .ok_or(Error::InvalidData("end calculation overflow"))?; ExtentRange::WithLength(Range { start, end }) }; extents.push(ItemLocationBoxExtent { extent_range })?; } items.push(ItemLocationBoxItem { item_id, construction_method, extents })?; } if iloc.remaining() == 0 { Ok(items) } else { Err(Error::InvalidData("invalid iloc size")) } } /// Parse an ftyp box. /// See ISO 14496-12:2015 § 4.3 fn read_ftyp(src: &mut BMFFBox<'_, T>) -> Result { let major = be_u32(src)?; let minor = be_u32(src)?; let bytes_left = src.bytes_left(); if bytes_left % 4 != 0 { return Err(Error::InvalidData("invalid ftyp size")); } // Is a brand_count of zero valid? let brand_count = bytes_left / 4; let mut brands = TryVec::with_capacity(brand_count.try_into()?)?; for _ in 0..brand_count { brands.push(be_u32(src)?.into())?; } Ok(FileTypeBox { major_brand: From::from(major), minor_version: minor, compatible_brands: brands, }) } #[cfg_attr(debug_assertions, track_caller)] fn check_parser_state(left: &Take) -> Result<(), Error> { let limit = left.limit(); if limit == 0 { Ok(()) } else { debug_assert_eq!(0, limit, "bad parser state bytes left"); Err(Error::InvalidData("unread box content or bad parser sync")) } } /// Skip a number of bytes that we don't care to parse. fn skip(src: &mut T, bytes: u64) -> Result<()> { std::io::copy(&mut src.take(bytes), &mut std::io::sink())?; Ok(()) } fn be_u16(src: &mut T) -> Result { src.read_u16::().map_err(From::from) } fn be_u32(src: &mut T) -> Result { src.read_u32::().map_err(From::from) } fn be_u64(src: &mut T) -> Result { src.read_u64::().map_err(From::from) } avif-parse-1.4.0/src/obu.rs000064400000000000000000000700151046102023000136100ustar 00000000000000#![allow(unused)] #![allow(bad_style)] use crate::{Error, Result}; use bitreader::BitReader; use std::num::{NonZeroU32, NonZeroU8}; #[derive(Debug, Clone)] struct Header { obu_size: usize, is_sequence_header: bool, } fn get_byte(data: &mut &[u8]) -> Result { let (&b, rest) = (*data).split_first().ok_or(Error::UnexpectedEOF)?; *data = rest; Ok(b) } const INTRA_FRAME: usize = 0; const LAST_FRAME: usize = 1; const LAST2_FRAME: usize = 2; const LAST3_FRAME: usize = 3; const GOLDEN_FRAME: usize = 4; const BWDREF_FRAME: usize = 5; const ALTREF2_FRAME: usize = 6; const ALTREF_FRAME: usize = 7; pub(crate) fn parse_obu(mut data: &[u8]) -> Result { while !data.is_empty() { let h = obu_header(&mut data)?; let mut remaining_data = data.get(..h.obu_size).ok_or(Error::UnexpectedEOF)?; data = &data[h.obu_size..]; if h.is_sequence_header { return SequenceHeaderObu::read(remaining_data); } } Err(Error::UnexpectedEOF) } impl SequenceHeaderObu { fn read(data: &[u8]) -> Result { let mut b = BitReader::new(data); let mut enable_superres = false; let mut enable_cdef = false; let mut enable_restoration = false; let seq_profile = b.read_u8(3)?; if seq_profile > 2 { return Err(Error::InvalidData("seq_profile")); } let still_picture = b.read_bool()?; let reduced_still_picture_header = b.read_bool()?; let decoder_model_info_present_flag = false; if reduced_still_picture_header { let timing_info_present_flag = 0; let initial_display_delay_present_flag = 0; let operating_points_cnt_minus_1 = 0; let operating_point_idc = 0; // [ 0 ] let seq_level_idx = b.read_u8(5)?; let seq_tier = 0; // [ 0 ] let decoder_model_present_for_this_op = 0; // [ 0 ] let initial_display_delay_present_for_this_op = 0; // [ 0 ] } else { let timing_info_present_flag = b.read_bool()?; if timing_info_present_flag { return Err(Error::Unsupported("timing_info_present_flag")); } let initial_display_delay_present_flag = b.read_bool()?; let operating_points_cnt = 1 + b.read_u8(5)?; for _ in 0..operating_points_cnt { let operating_point_idc = b.read_u16(12)?; let seq_level_idx = b.read_u8(5)?; let seq_tier = if seq_level_idx > 7 { b.read_bool()? } else { false }; let decoder_model_present_for_this_op = if decoder_model_info_present_flag { b.read_bool()?; return Err(Error::Unsupported("decoder_model_info_present_flag")); } else { false }; if initial_display_delay_present_flag { let initial_display_delay_present_for_this_op = b.read_bool()?; if initial_display_delay_present_for_this_op { let initial_display_delay = 1 + b.read_u8(4)?; } } } // let operating_point = choose_operating_point(); // let OperatingPointIdc = operating_point_idc[ operating_point ]; } let frame_width_bits = 1 + b.read_u8(4)?; let frame_height_bits = 1 + b.read_u8(4)?; let frame_width_bits = NonZeroU8::new(frame_width_bits).ok_or(Error::InvalidData("overflow"))?; let frame_height_bits = NonZeroU8::new(frame_height_bits).ok_or(Error::InvalidData("overflow"))?; let max_frame_width = 1 + b.read_u32(frame_width_bits.get())?; let max_frame_height = 1 + b.read_u32(frame_height_bits.get())?; let max_frame_width = NonZeroU32::new(max_frame_width).ok_or(Error::InvalidData("overflow"))?; let max_frame_height = NonZeroU32::new(max_frame_height).ok_or(Error::InvalidData("overflow"))?; let frame_id_numbers_present_flag = if reduced_still_picture_header { false } else { b.read_bool()? }; let delta_frame_id_length = if frame_id_numbers_present_flag { 2 + b.read_u8(4)? } else { 0 }; let additional_frame_id_length = if frame_id_numbers_present_flag { 1 + b.read_u8(3)? } else { 0 }; let use_128x128_superblock = b.read_bool()?; let enable_filter_intra = b.read_bool()?; let enable_intra_edge_filter = b.read_bool()?; let mut enable_interintra_compound = false; let mut enable_masked_compound = false; let mut enable_warped_motion = false; let mut enable_dual_filter = false; let mut enable_jnt_comp = false; let mut enable_ref_frame_mvs = false; let mut seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS; let mut seq_force_integer_mv = SELECT_INTEGER_MV; let mut order_hint_bits = 0; let mut enable_order_hint = false; if !reduced_still_picture_header { enable_interintra_compound = b.read_bool()?; enable_masked_compound = b.read_bool()?; enable_warped_motion = b.read_bool()?; enable_dual_filter = b.read_bool()?; enable_order_hint = b.read_bool()?; if enable_order_hint { enable_jnt_comp = b.read_bool()?; enable_ref_frame_mvs = b.read_bool()?; } let seq_choose_screen_content_tools = b.read_bool()?; if !seq_choose_screen_content_tools { seq_force_screen_content_tools = b.read_u8(1)?; } if seq_force_screen_content_tools > 0 { let seq_choose_integer_mv = b.read_bool()?; if !seq_choose_integer_mv { seq_force_integer_mv = b.read_u8(1)?; } } if enable_order_hint { order_hint_bits = 1 + b.read_u8(3)?; } } let enable_superres = b.read_bool()?; let enable_cdef = b.read_bool()?; let enable_restoration = b.read_bool()?; let color = color_config(&mut b, seq_profile)?; let film_grain_params_present = b.read_bool()?; Ok(Self { color, seq_profile, still_picture, reduced_still_picture_header, max_frame_width, max_frame_height, enable_superres, enable_cdef, enable_restoration, frame_id_numbers_present_flag, delta_frame_id_length, additional_frame_id_length, film_grain_params_present, decoder_model_info_present_flag, seq_force_screen_content_tools, seq_force_integer_mv, order_hint_bits, enable_order_hint, use_128x128_superblock, enable_interintra_compound, enable_masked_compound, enable_warped_motion, enable_dual_filter, enable_jnt_comp, enable_ref_frame_mvs, }) } } #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct SequenceHeaderObu { pub color: ColorConfig, pub seq_profile: u8, pub still_picture: bool, pub reduced_still_picture_header: bool, pub max_frame_width: NonZeroU32, pub max_frame_height: NonZeroU32, pub enable_superres: bool, pub enable_cdef: bool, pub enable_restoration: bool, pub frame_id_numbers_present_flag: bool, pub delta_frame_id_length: u8, pub additional_frame_id_length: u8, pub film_grain_params_present: bool, pub decoder_model_info_present_flag: bool, pub seq_force_screen_content_tools: u8, pub seq_force_integer_mv: u8, pub order_hint_bits: u8, pub enable_order_hint: bool, pub use_128x128_superblock: bool, pub enable_interintra_compound: bool, pub enable_masked_compound: bool, pub enable_warped_motion: bool, pub enable_dual_filter: bool, pub enable_jnt_comp: bool, pub enable_ref_frame_mvs: bool, } #[derive(Debug, Clone)] pub(crate) struct ColorConfig { pub chroma_subsampling: (bool, bool), pub chroma_sample_position: u8, pub separate_uv_delta_q: bool, pub color_range: u8, pub bit_depth: u8, pub monochrome: bool, pub color_primaries: u8, pub transfer_characteristics: u8, pub matrix_coefficients: u8, } fn color_config(b: &mut BitReader, seq_profile: u8) -> Result { let high_bitdepth = b.read_bool()?; let bit_depth = if seq_profile == 2 && high_bitdepth { let twelve_bit = b.read_bool()?; if twelve_bit { 12 } else { 10 } } else { // if seq_profile <= 2 if high_bitdepth { 10 } else { 8 } }; let monochrome = if seq_profile == 1 { false } else { b.read_bool()? }; let num_planes = if monochrome { 1 } else { 3 }; let color_description_present_flag = b.read_bool()?; let mut color_primaries = 2; let mut transfer_characteristics = 2; let matrix_coefficients = if color_description_present_flag { color_primaries = b.read_u8(8)?; transfer_characteristics = b.read_u8(8)?; b.read_u8(8)? } else { 2 }; let chroma_subsampling; let chroma_sample_position; let separate_uv_delta_q; let color_range; if monochrome { color_range = b.read_u8(1)?; chroma_subsampling = (false, false); chroma_sample_position = 0; separate_uv_delta_q = false; } else if color_primaries == 1 //Bt709 && transfer_characteristics == 13 // Srgb && matrix_coefficients == 0 { color_range = 1; chroma_subsampling = (false, false); chroma_sample_position = 0; separate_uv_delta_q = false; } else { color_range = b.read_u8(1)?; if seq_profile == 0 { chroma_subsampling = (true, true); } else if seq_profile == 1 { chroma_subsampling = (false, false); } else if bit_depth == 12 { let x = b.read_bool()?; chroma_subsampling = if x { (x, b.read_bool()?) } else { (false, false) } } else { chroma_subsampling = (true, false); } debug_assert!(!monochrome); chroma_sample_position = if chroma_subsampling.0 && chroma_subsampling.1 { b.read_u8(2)? } else { 0 }; separate_uv_delta_q = b.read_bool()?; } Ok(ColorConfig { chroma_subsampling, chroma_sample_position, separate_uv_delta_q, color_range, bit_depth, monochrome, color_primaries, transfer_characteristics, matrix_coefficients, }) } fn obu_header(data: &mut &[u8]) -> Result
{ let mut b = get_byte(data)?; if 0 != b & 0b1000_0000 { return Err(Error::InvalidData("not obu")); } let is_sequence_header = 1 == (b >> 3); let obu_extension_flag = 0 != (b & 0b100); let obu_has_size_field = 0 != (b & 0b010); if obu_extension_flag { // obu_extension_header let mut b = get_byte(data)?; } let obu_size = if obu_has_size_field { leb128::read::unsigned(data) .map_err(|_| Error::InvalidData("leb"))? .try_into() .map_err(|_| Error::UnexpectedEOF)? } else { data.len() }; Ok(Header { obu_size, is_sequence_header }) } const REFS_PER_FRAME: usize = 7; // Number of reference frames that can be used for inter prediction const TOTAL_REFS_PER_FRAME: usize = 8; // Number of reference frame types (including intra type) const BLOCK_SIZE_GROUPS: usize = 4; // Number of contexts when decoding y_mode const BLOCK_SIZES: usize = 22; // Number of different block sizes used const BLOCK_INVALID: usize = 22; // Sentinel value to mark partition choices that are not allowed const MAX_SB_SIZE: usize = 128; // Maximum size of a superblock in luma samples const MI_SIZE: usize = 4; // Smallest size of a mode info block in luma samples const MI_SIZE_LOG2: usize = 2; // Base 2 logarithm of smallest size of a mode info block const MAX_TILE_WIDTH: usize = 4096; // Maximum width of a tile in units of luma samples const MAX_TILE_AREA: usize = 4096; // * 2304 Maximum area of a tile in units of luma samples const MAX_TILE_ROWS: usize = 64; // Maximum number of tile rows const MAX_TILE_COLS: usize = 64; // Maximum number of tile columns const INTRABC_DELAY_PIXELS: usize = 256; // Number of horizontal luma samples before intra block copy can be used const INTRABC_DELAY_SB64: usize = 4; // Number of 64 by 64 blocks before intra block copy can be used const NUM_REF_FRAMES: usize = 8; // Number of frames that can be stored for future reference const REF_CONTEXTS: usize = 3; // Number of contexts for single_ref, comp_ref, comp_bwdref, uni_comp_ref, uni_comp_ref_p1 and uni_comp_ref_p2 const MAX_SEGMENTS: usize = 8; // Number of segments allowed in segmentation map const SEGMENT_ID_CONTEXTS: usize = 3; // Number of contexts for segment_id const SEG_LVL_ALT_Q: usize = 0; // Index for quantizer segment feature const SEG_LVL_ALT_LF_Y_V: usize = 1; // Index for vertical luma loop filter segment feature const SEG_LVL_REF_FRAME: usize = 5; // Index for reference frame segment feature const SEG_LVL_SKIP: usize = 6; // Index for skip segment feature const SEG_LVL_GLOBALMV: usize = 7; // Index for global mv feature const SEG_LVL_MAX: usize = 8; // Number of segment features const PLANE_TYPES: usize = 2; // Number of different plane types (luma or chroma) const TX_SIZE_CONTEXTS: usize = 3; // Number of contexts for transform size const INTERP_FILTERS: usize = 3; // Number of values for interp_filter const INTERP_FILTER_CONTEXTS: usize = 16; // Number of contexts for interp_filter const SKIP_MODE_CONTEXTS: usize = 3; // Number of contexts for decoding skip_mode const SKIP_CONTEXTS: usize = 3; // Number of contexts for decoding skip const PARTITION_CONTEXTS: usize = 4; // Number of contexts when decoding partition const TX_SIZES: usize = 5; // Number of square transform sizes const TX_SIZES_ALL: usize = 19; // Number of transform sizes (including non-square sizes) const TX_MODES: usize = 3; // Number of values for tx_mode const DCT_DCT: usize = 0; // Inverse transform rows with DCT and columns with DCT const ADST_DCT: usize = 1; // Inverse transform rows with DCT and columns with ADST const DCT_ADST: usize = 2; // Inverse transform rows with ADST and columns with DCT const ADST_ADST: usize = 3; // Inverse transform rows with ADST and columns with ADST const FLIPADST_DCT: usize = 4; // Inverse transform rows with DCT and columns with FLIPADST const DCT_FLIPADST: usize = 5; // Inverse transform rows with FLIPADST and columns with DCT const FLIPADST_FLIPADST: usize = 6; // Inverse transform rows with FLIPADST and columns with FLIPADST const ADST_FLIPADST: usize = 7; // Inverse transform rows with FLIPADST and columns with ADST const FLIPADST_ADST: usize = 8; // Inverse transform rows with ADST and columns with FLIPADST const IDTX: usize = 9; // Inverse transform rows with identity and columns with identity const V_DCT: usize = 10; // Inverse transform rows with identity and columns with DCT const H_DCT: usize = 11; // Inverse transform rows with DCT and columns with identity const V_ADST: usize = 12; // Inverse transform rows with identity and columns with ADST const H_ADST: usize = 13; // Inverse transform rows with ADST and columns with identity const V_FLIPADST: usize = 14; // Inverse transform rows with identity and columns with FLIPADST const H_FLIPADST: usize = 15; // Inverse transform rows with FLIPADST and columns with identity const TX_TYPES: usize = 16; // Number of inverse transform types const MB_MODE_COUNT: usize = 17; // Number of values for YMode const INTRA_MODES: usize = 13; // Number of values for y_mode const UV_INTRA_MODES_CFL_NOT_ALLOWED: usize = 13; // Number of values for uv_mode when chroma from luma is not allowed const UV_INTRA_MODES_CFL_ALLOWED: usize = 14; // Number of values for uv_mode when chroma from luma is allowed const COMPOUND_MODES: usize = 8; // Number of values for compound_mode const COMPOUND_MODE_CONTEXTS: usize = 8; // Number of contexts for compound_mode const COMP_NEWMV_CTXS: usize = 5; // Number of new mv values used when constructing context for compound_mode const NEW_MV_CONTEXTS: usize = 6; // Number of contexts for new_mv const ZERO_MV_CONTEXTS: usize = 2; // Number of contexts for zero_mv const REF_MV_CONTEXTS: usize = 6; // Number of contexts for ref_mv const DRL_MODE_CONTEXTS: usize = 3; // Number of contexts for drl_mode const MV_CONTEXTS: usize = 2; // Number of contexts for decoding motion vectors including one for intra block copy const MV_INTRABC_CONTEXT: usize = 1; // Motion vector context used for intra block copy const MV_JOINTS: usize = 4; // Number of values for mv_joint const MV_CLASSES: usize = 11; // Number of values for mv_class const CLASS0_SIZE: usize = 2; // Number of values for mv_class0_bit const MV_OFFSET_BITS: usize = 10; // Maximum number of bits for decoding motion vectors const MAX_LOOP_FILTER: usize = 63; // Maximum value used for loop filtering const REF_SCALE_SHIFT: usize = 14; // Number of bits of precision when scaling reference frames const SUBPEL_BITS: usize = 4; // Number of bits of precision when choosing an inter prediction filter kernel const SUBPEL_MASK: usize = 15; // ( 1 << SUBPEL_BITS ) - 1 const SCALE_SUBPEL_BITS: usize = 10; // Number of bits of precision when computing inter prediction locations const MV_BORDER: usize = 128; // Value used when clipping motion vectors const PALETTE_COLOR_CONTEXTS: usize = 5; // Number of values for color contexts const PALETTE_MAX_COLOR_CONTEXT_HASH: usize = 8; // Number of mappings between color context hash and color context const PALETTE_BLOCK_SIZE_CONTEXTS: usize = 7; // Number of values for palette block size const PALETTE_Y_MODE_CONTEXTS: usize = 3; // Number of values for palette Y plane mode contexts const PALETTE_UV_MODE_CONTEXTS: usize = 2; // Number of values for palette U and V plane mode contexts const PALETTE_SIZES: usize = 7; // Number of values for palette_size const PALETTE_COLORS: usize = 8; // Number of values for palette_color const PALETTE_NUM_NEIGHBORS: usize = 3; // Number of neighbors considered within palette computation const DELTA_Q_SMALL: usize = 3; // Value indicating alternative encoding of quantizer index delta values const DELTA_LF_SMALL: usize = 3; // Value indicating alternative encoding of loop filter delta values const QM_TOTAL_SIZE: usize = 3344; // Number of values in the quantizer matrix const MAX_ANGLE_DELTA: usize = 3; // Maximum magnitude of AngleDeltaY and AngleDeltaUV const DIRECTIONAL_MODES: usize = 8; // Number of directional intra modes const ANGLE_STEP: usize = 3; // Number of degrees of step per unit increase in AngleDeltaY or AngleDeltaUV. const TX_SET_TYPES_INTRA: usize = 3; // Number of intra transform set types const TX_SET_TYPES_INTER: usize = 4; // Number of inter transform set types const WARPEDMODEL_PREC_BITS: usize = 16; // Internal precision of warped motion models const IDENTITY: usize = 0; // Warp model is just an identity transform const TRANSLATION: usize = 1; // Warp model is a pure translation const ROTZOOM: usize = 2; // Warp model is a rotation + symmetric zoom + translation const AFFINE: usize = 3; // Warp model is a general affine transform const GM_ABS_TRANS_BITS: usize = 12; // Number of bits encoded for translational components of global motion models, if part of a ROTZOOM or AFFINE model const GM_ABS_TRANS_ONLY_BITS: usize = 9; // Number of bits encoded for translational components of global motion models, if part of a TRANSLATION model const GM_ABS_ALPHA_BITS: usize = 12; // Number of bits encoded for non-translational components of global motion models const DIV_LUT_PREC_BITS: usize = 14; // Number of fractional bits of entries in divisor lookup table const DIV_LUT_BITS: usize = 8; // Number of fractional bits for lookup in divisor lookup table const DIV_LUT_NUM: usize = 257; // Number of entries in divisor lookup table const MOTION_MODES: usize = 3; // Number of values for motion modes const SIMPLE: usize = 0; // Use translation or global motion compensation const OBMC: usize = 1; // Use overlapped block motion compensation const LOCALWARP: usize = 2; // Use local warp motion compensation const LEAST_SQUARES_SAMPLES_MAX: usize = 8; // Largest number of samples used when computing a local warp const LS_MV_MAX: usize = 256; // Largest motion vector difference to include in local warp computation const WARPEDMODEL_TRANS_CLAMP: usize = 1; //<<23 Clamping value used for translation components of warp const WARPEDMODEL_NONDIAGAFFINE_CLAMP: usize = 1; //<<13 Clamping value used for matrix components of warp const WARPEDPIXEL_PREC_SHIFTS: usize = 1; //<<6 Number of phases used in warped filtering const WARPEDDIFF_PREC_BITS: usize = 10; // Number of extra bits of precision in warped filtering const GM_ALPHA_PREC_BITS: usize = 15; // Number of fractional bits for sending non-translational warp model coefficients const GM_TRANS_PREC_BITS: usize = 6; // Number of fractional bits for sending translational warp model coefficients const GM_TRANS_ONLY_PREC_BITS: usize = 3; // Number of fractional bits used for pure translational warps const INTERINTRA_MODES: usize = 4; // Number of inter intra modes const MASK_MASTER_SIZE: usize = 64; // Size of MasterMask array const SEGMENT_ID_PREDICTED_CONTEXTS: usize = 3; // Number of contexts for segment_id_predicted const IS_INTER_CONTEXTS: usize = 4; // Number of contexts for is_inter const FWD_REFS: usize = 4; // Number of syntax elements for forward reference frames const BWD_REFS: usize = 3; // Number of syntax elements for backward reference frames const SINGLE_REFS: usize = 7; // Number of syntax elements for single reference frames const UNIDIR_COMP_REFS: usize = 4; // Number of syntax elements for unidirectional compound reference frames const COMPOUND_TYPES: usize = 2; // Number of values for compound_type const CFL_JOINT_SIGNS: usize = 8; // Number of values for cfl_alpha_signs const CFL_ALPHABET_SIZE: usize = 16; // Number of values for cfl_alpha_u and cfl_alpha_v const COMP_INTER_CONTEXTS: usize = 5; // Number of contexts for comp_mode const COMP_REF_TYPE_CONTEXTS: usize = 5; // Number of contexts for comp_ref_type const CFL_ALPHA_CONTEXTS: usize = 6; // Number of contexts for cfl_alpha_u and cfl_alpha_v const INTRA_MODE_CONTEXTS: usize = 5; // Number of each of left and above contexts for intra_frame_y_mode const COMP_GROUP_IDX_CONTEXTS: usize = 6; // Number of contexts for comp_group_idx const COMPOUND_IDX_CONTEXTS: usize = 6; // Number of contexts for compound_idx const INTRA_EDGE_KERNELS: usize = 3; // Number of filter kernels for the intra edge filter const INTRA_EDGE_TAPS: usize = 5; // Number of kernel taps for the intra edge filter const FRAME_LF_COUNT: usize = 4; // Number of loop filter strength values const MAX_VARTX_DEPTH: usize = 2; // Maximum depth for variable transform trees const TXFM_PARTITION_CONTEXTS: usize = 21; // Number of contexts for txfm_split const REF_CAT_LEVEL: usize = 640; // Bonus weight for close motion vectors const MAX_REF_MV_STACK_SIZE: usize = 8; // Maximum number of motion vectors in the stack const MFMV_STACK_SIZE: usize = 3; // Stack size for motion field motion vectors const MAX_TX_DEPTH: usize = 2; // Maximum times the transform can be split const WEDGE_TYPES: usize = 16; // Number of directions for the wedge mask process const FILTER_BITS: usize = 7; // Number of bits used in Wiener filter coefficients const WIENER_COEFFS: usize = 3; // Number of Wiener filter coefficients to read const SGRPROJ_PARAMS_BITS: usize = 4; // Number of bits needed to specify self guided filter set const SGRPROJ_PRJ_SUBEXP_K: usize = 4; // Controls how self guided deltas are read const SGRPROJ_PRJ_BITS: usize = 7; // Precision bits during self guided restoration const SGRPROJ_RST_BITS: usize = 4; // Restoration precision bits generated higher than source before projection const SGRPROJ_MTABLE_BITS: usize = 20; // Precision of mtable division table const SGRPROJ_RECIP_BITS: usize = 12; // Precision of division by n table const SGRPROJ_SGR_BITS: usize = 8; // Internal precision bits for core selfguided_restoration const EC_PROB_SHIFT: usize = 6; // Number of bits to reduce CDF precision during arithmetic coding const EC_MIN_PROB: usize = 4; // Minimum probability assigned to each symbol during arithmetic coding const SELECT_SCREEN_CONTENT_TOOLS: u8 = 2; // Value that indicates the allow_screen_content_tools syntax element is coded const SELECT_INTEGER_MV: u8 = 2; // Value that indicates the force_integer_mv syntax element is coded const RESTORATION_TILESIZE_MAX: usize = 256; // Maximum size of a loop restoration tile const MAX_FRAME_DISTANCE: usize = 31; // Maximum distance when computing weighted prediction const MAX_OFFSET_WIDTH: usize = 8; // Maximum horizontal offset of a projected motion vector const MAX_OFFSET_HEIGHT: usize = 0; // Maximum vertical offset of a projected motion vector const WARP_PARAM_REDUCE_BITS: usize = 6; // Rounding bitwidth for the parameters to the shear process const NUM_BASE_LEVELS: usize = 2; // Number of quantizer base levels const COEFF_BASE_RANGE: usize = 12; // The quantizer range above NUM_BASE_LEVELS above which the Exp-Golomb coding process is activated const BR_CDF_SIZE: usize = 4; // Number of values for coeff_br const SIG_COEF_CONTEXTS_EOB: usize = 4; // Number of contexts for coeff_base_eob const SIG_COEF_CONTEXTS_2D: usize = 26; // Context offset for coeff_base for horizontal-only or vertical-only transforms. const SIG_COEF_CONTEXTS: usize = 42; // Number of contexts for coeff_base const SIG_REF_DIFF_OFFSET_NUM: usize = 5; // Maximum number of context samples to be used in determining the context index for coeff_base and coeff_base_eob. const SUPERRES_NUM: usize = 8; // Numerator for upscaling ratio const SUPERRES_DENOM_MIN: usize = 9; // Smallest denominator for upscaling ratio const SUPERRES_DENOM_BITS: usize = 3; // Number of bits sent to specify denominator of upscaling ratio const SUPERRES_FILTER_BITS: usize = 6; // Number of bits of fractional precision for upscaling filter selection const SUPERRES_FILTER_SHIFTS: usize = 1; // << SUPERRES_FILTER_BITS Number of phases of upscaling filters const SUPERRES_FILTER_TAPS: usize = 8; // Number of taps of upscaling filters const SUPERRES_FILTER_OFFSET: usize = 3; // Sample offset for upscaling filters const SUPERRES_SCALE_BITS: usize = 14; // Number of fractional bits for computing position in upscaling const SUPERRES_SCALE_MASK: usize = (1 << 14) - 1; // Mask for computing position in upscaling const SUPERRES_EXTRA_BITS: usize = 8; // Difference in precision between SUPERRES_SCALE_BITS and SUPERRES_FILTER_BITS const TXB_SKIP_CONTEXTS: usize = 13; // Number of contexts for all_zero const EOB_COEF_CONTEXTS: usize = 9; // Number of contexts for eob_extra const DC_SIGN_CONTEXTS: usize = 3; // Number of contexts for dc_sign const LEVEL_CONTEXTS: usize = 21; // Number of contexts for coeff_br const TX_CLASS_2D: usize = 0; // Transform class for transform types performing non-identity transforms in both directions const TX_CLASS_HORIZ: usize = 1; // Transform class for transforms performing only a horizontal non-identity transform const TX_CLASS_VERT: usize = 2; // Transform class for transforms performing only a vertical non-identity transform const REFMVS_LIMIT: usize = (1 << 12) - 1; // Largest reference MV component that can be saved const INTRA_FILTER_SCALE_BITS: usize = 4; // Scaling shift for intra filtering process const INTRA_FILTER_MODES: usize = 5; // Number of types of intra filtering const COEFF_CDF_Q_CTXS: usize = 4; // Number of selectable context types for the coeff( ) syntax structure const PRIMARY_REF_NONE: usize = 7; // Value of primary_ref_frame indicating that there is no primary reference frame const BUFFER_POOL_MAX_SIZE: usize = 10; // Number of frames in buffer pool