├── .github └── workflows │ └── ci.yml ├── .gitignore ├── BENCHMARKS.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── Cargo.toml ├── GETTING_STARTED.md ├── LICENSE ├── README.md ├── benchtest └── benchtest.py ├── clippy.toml ├── rustfmt.toml ├── symphonia-bundle-flac ├── Cargo.toml ├── README.md └── src │ ├── decoder.rs │ ├── demuxer.rs │ ├── frame.rs │ ├── lib.rs │ ├── parser.rs │ └── validate.rs ├── symphonia-bundle-mp3 ├── Cargo.toml ├── README.md └── src │ ├── common.rs │ ├── decoder.rs │ ├── demuxer.rs │ ├── header.rs │ ├── layer1 │ └── mod.rs │ ├── layer12.rs │ ├── layer2 │ └── mod.rs │ ├── layer3 │ ├── bitstream.rs │ ├── codebooks.rs │ ├── common.rs │ ├── hybrid_synthesis.rs │ ├── mod.rs │ ├── requantize.rs │ └── stereo.rs │ ├── lib.rs │ └── synthesis.rs ├── symphonia-check ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── symphonia-codec-aac ├── Cargo.toml ├── README.md ├── src │ ├── aac │ │ ├── codebooks.rs │ │ ├── common.rs │ │ ├── cpe.rs │ │ ├── dsp.rs │ │ ├── ics │ │ │ ├── gain.rs │ │ │ ├── ltp.rs │ │ │ ├── mod.rs │ │ │ ├── pulse.rs │ │ │ └── tns.rs │ │ ├── mod.rs │ │ └── window.rs │ ├── adts.rs │ ├── common.rs │ └── lib.rs └── tests │ └── tests.rs ├── symphonia-codec-adpcm ├── Cargo.toml ├── README.md └── src │ ├── codec_ima.rs │ ├── codec_ms.rs │ ├── common.rs │ └── lib.rs ├── symphonia-codec-alac ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── symphonia-codec-opus ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── symphonia-codec-pcm ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── symphonia-codec-vorbis ├── Cargo.toml ├── README.md └── src │ ├── codebook.rs │ ├── common.rs │ ├── dsp.rs │ ├── floor.rs │ ├── lib.rs │ ├── residue.rs │ └── window.rs ├── symphonia-codec-wavpack ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── symphonia-core ├── Cargo.toml ├── README.md └── src │ ├── audio.rs │ ├── checksum │ ├── crc16.rs │ ├── crc32.rs │ ├── crc8.rs │ ├── md5.rs │ └── mod.rs │ ├── codecs.rs │ ├── conv.rs │ ├── dsp │ ├── complex.rs │ ├── fft.rs │ ├── mdct │ │ ├── mod.rs │ │ ├── no_simd.rs │ │ └── simd.rs │ └── mod.rs │ ├── errors.rs │ ├── formats.rs │ ├── io │ ├── bit.rs │ ├── buf_reader.rs │ ├── media_source_stream.rs │ ├── mod.rs │ ├── monitor_stream.rs │ └── scoped_stream.rs │ ├── lib.rs │ ├── meta.rs │ ├── probe.rs │ ├── sample.rs │ ├── units.rs │ └── util.rs ├── symphonia-format-caf ├── Cargo.toml ├── README.md └── src │ ├── chunks.rs │ ├── demuxer.rs │ └── lib.rs ├── symphonia-format-isomp4 ├── Cargo.toml ├── README.md └── src │ ├── atoms │ ├── alac.rs │ ├── co64.rs │ ├── ctts.rs │ ├── edts.rs │ ├── elst.rs │ ├── esds.rs │ ├── flac.rs │ ├── ftyp.rs │ ├── hdlr.rs │ ├── ilst.rs │ ├── mdhd.rs │ ├── mdia.rs │ ├── mehd.rs │ ├── meta.rs │ ├── mfhd.rs │ ├── minf.rs │ ├── mod.rs │ ├── moof.rs │ ├── moov.rs │ ├── mvex.rs │ ├── mvhd.rs │ ├── opus.rs │ ├── sidx.rs │ ├── smhd.rs │ ├── stbl.rs │ ├── stco.rs │ ├── stsc.rs │ ├── stsd.rs │ ├── stss.rs │ ├── stsz.rs │ ├── stts.rs │ ├── tfhd.rs │ ├── tkhd.rs │ ├── traf.rs │ ├── trak.rs │ ├── trex.rs │ ├── trun.rs │ ├── udta.rs │ └── wave.rs │ ├── demuxer.rs │ ├── fourcc.rs │ ├── fp.rs │ ├── lib.rs │ └── stream.rs ├── symphonia-format-mkv ├── Cargo.toml ├── README.md └── src │ ├── codecs.rs │ ├── demuxer.rs │ ├── ebml.rs │ ├── element_ids.rs │ ├── lacing.rs │ ├── lib.rs │ └── segment.rs ├── symphonia-format-ogg ├── Cargo.toml ├── README.md └── src │ ├── common.rs │ ├── demuxer.rs │ ├── lib.rs │ ├── logical.rs │ ├── mappings │ ├── flac.rs │ ├── mod.rs │ ├── opus.rs │ └── vorbis.rs │ ├── page.rs │ └── physical.rs ├── symphonia-format-riff ├── Cargo.toml ├── README.md └── src │ ├── aiff │ ├── chunks.rs │ └── mod.rs │ ├── common.rs │ ├── lib.rs │ └── wave │ ├── chunks.rs │ └── mod.rs ├── symphonia-format-wav ├── Cargo.toml ├── README.md └── src │ ├── chunks.rs │ └── lib.rs ├── symphonia-metadata ├── Cargo.toml ├── README.md └── src │ ├── flac.rs │ ├── id3v1.rs │ ├── id3v2 │ ├── frames.rs │ ├── mod.rs │ └── unsync.rs │ ├── itunes.rs │ ├── lib.rs │ ├── riff.rs │ └── vorbis.rs ├── symphonia-play ├── Cargo.toml ├── README.md └── src │ ├── main.rs │ ├── output.rs │ └── resampler.rs ├── symphonia-utils-xiph ├── Cargo.toml ├── README.md └── src │ ├── flac │ ├── metadata.rs │ └── mod.rs │ ├── lib.rs │ └── vorbis │ └── mod.rs └── symphonia ├── Cargo.toml ├── README.md ├── examples ├── README.md ├── basic-interleaved.rs └── getting-started.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ ├── decode_any.rs │ └── decode_mp3.rs └── src └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: {} 8 | 9 | jobs: 10 | check: 11 | name: Check ${{ matrix.os }} 12 | 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest, windows-latest] 17 | fail-fast: false 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Setup Toolchain 23 | run: rustup install stable && rustup default stable 24 | 25 | - name: Check default features 26 | run: cargo check --all 27 | 28 | - name: Check all features 29 | run: cargo check --all --all-features --exclude symphonia-play 30 | 31 | clippy: 32 | name: Clippy ${{ matrix.os }} 33 | 34 | runs-on: ${{ matrix.os }} 35 | strategy: 36 | matrix: 37 | os: [ubuntu-latest, macos-latest, windows-latest] 38 | fail-fast: false 39 | 40 | steps: 41 | - uses: actions/checkout@v4 42 | 43 | - name: Setup Toolchain 44 | run: rustup install stable && rustup default stable 45 | 46 | - name: Check default features 47 | run: cargo clippy --all -- -D warnings 48 | 49 | - name: Check all features 50 | run: cargo clippy --all --all-features --exclude symphonia-play -- -D warnings 51 | 52 | test: 53 | name: Test ${{ matrix.config.target }} on ${{ matrix.config.os }} 54 | 55 | env: 56 | CARGO: cargo 57 | CROSS_VERSION: v0.2.5 58 | 59 | runs-on: ${{ matrix.config.os }} 60 | strategy: 61 | matrix: 62 | config: 63 | - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, use-cross: false } 64 | - { os: macos-latest, target: aarch64-apple-darwin, use-cross: false } 65 | - { os: windows-latest, target: x86_64-pc-windows-msvc, use-cross: false } 66 | - { os: ubuntu-latest, target: powerpc-unknown-linux-gnu, use-cross: true } 67 | 68 | steps: 69 | - uses: actions/checkout@v4 70 | 71 | - name: Setup Toolchain 72 | run: rustup install stable && rustup default stable 73 | 74 | - name: Setup Cross 75 | if: matrix.config.use-cross 76 | run: | 77 | cargo install cross --locked 78 | echo "CARGO=cross" >> $GITHUB_ENV 79 | 80 | - name: Test default features 81 | run: ${{ env.CARGO }} test --target ${{ matrix.config.target }} --all --exclude symphonia-play 82 | 83 | - name: Test all features 84 | run: ${{ env.CARGO }} test --target ${{ matrix.config.target }} --all --all-features --exclude symphonia-play 85 | 86 | fmt: 87 | name: Rustfmt 88 | 89 | runs-on: ubuntu-latest 90 | 91 | steps: 92 | - uses: actions/checkout@v4 93 | 94 | - name: Setup Toolchain 95 | run: rustup install nightly --profile minimal && rustup default nightly && rustup component add rustfmt 96 | 97 | - name: Check formatting 98 | run: cargo fmt --all -- --check 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust/Cargo 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | 6 | # Ghostwriter 7 | *.md.backup 8 | 9 | # Visual Studio Code 10 | *.code-workspace 11 | .vscode 12 | 13 | # Profiling data 14 | perf.data 15 | perf.data.old 16 | 17 | # Development Test Vectors 18 | testvec/*/**/*.aac 19 | testvec/*/**/*.flac 20 | testvec/*/**/*.m4a 21 | testvec/*/**/*.mp3 22 | testvec/*/**/*.mp4 23 | testvec/*/**/*.ogg 24 | testvec/*/**/*.opus 25 | testvec/*/**/*.wav 26 | testvec/*/**/*.mov 27 | 28 | # Benchmarking Files 29 | benchtest/*.aac 30 | benchtest/*.flac 31 | benchtest/*.m4a 32 | benchtest/*.mp3 33 | benchtest/*.mp4 34 | benchtest/*.ogg 35 | benchtest/*.opus 36 | benchtest/*.wav 37 | 38 | # macOS 39 | .DS_Store 40 | .AppleDouble 41 | .LSOverride 42 | ._* 43 | 44 | # Linux 45 | *~ 46 | .directory 47 | 48 | # Windows 49 | Thumbs.db 50 | Thumbs.db:encryptable 51 | ehthumbs.db 52 | ehthumbs_vista.db 53 | *.stackdump 54 | [Dd]esktop.ini 55 | *.lnk 56 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # Primary Author & Maintainer 2 | 3 | Philip Deljanov 4 | 5 | # Contributors who wish to provide their name and email, please use the following format: 6 | # Name 7 | # 8 | # Please keep this section sorted in ascending order. 9 | 10 | Akiyuki Okayasu 11 | Ian Hobson 12 | Kostya Shishkov 13 | Thom Chiovoloni 14 | 15 | # Contributors who wish to use an online handle, please use the following format: 16 | # Handle [GitHub/GitLab/BitBucket URL] 17 | # 18 | # Please keep this section sorted in ascending order. 19 | 20 | aschey [https://github.com/aschey] 21 | BlackHoleFox [https://github.com/blackholefox] 22 | darksv [https://github.com/darksv] 23 | dedobbin [https://github.com/dedobbin] 24 | djugei [https://github.com/djugei] 25 | erikas-taroza [https://github.com/erikas-taroza] 26 | FelixMcFelix [https://github.com/FelixMcFelix] 27 | geckoxx [https://github.com/geckoxx] 28 | Herohtar [https://github.com/herohtar] 29 | nicholaswyoung [https://github.com/nicholaswyoung] 30 | richardmitic [https://github.com/richardmitic] 31 | terrorfisch [https://github.com/terrorfisch] 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "symphonia", 5 | "symphonia-bundle-flac", 6 | "symphonia-bundle-mp3", 7 | "symphonia-codec-aac", 8 | "symphonia-codec-adpcm", 9 | "symphonia-codec-alac", 10 | "symphonia-codec-opus", 11 | "symphonia-codec-pcm", 12 | "symphonia-codec-vorbis", 13 | "symphonia-codec-wavpack", 14 | "symphonia-core", 15 | "symphonia-format-isomp4", 16 | "symphonia-format-mkv", 17 | "symphonia-format-ogg", 18 | "symphonia-format-riff", 19 | "symphonia-format-wav", 20 | "symphonia-metadata", 21 | "symphonia-play", 22 | "symphonia-utils-xiph", 23 | "symphonia-check", 24 | ] 25 | -------------------------------------------------------------------------------- /benchtest/benchtest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import subprocess 5 | import os 6 | import glob 7 | from urllib.parse import urlparse 8 | 9 | def download_format(fmt, urls): 10 | for i, url in enumerate(filter(lambda url: url.endswith(fmt), urls)): 11 | out = os.path.basename(urlparse(url).path) 12 | if os.path.exists(out): 13 | print(f"{out} already downloaded") 14 | else: 15 | print(f"Downloading {out}") 16 | subprocess.call(['curl', '-Lo', out, url]) 17 | 18 | def benchmark_file(filepath, ffmpeg, symphonia): 19 | subprocess.check_call(['hyperfine', '-m', '20', f'{ffmpeg} -threads 1 -benchmark -v 0 -i {filepath} -f null -', f'{symphonia} --decode-only {filepath}']) 20 | 21 | def benchmark_format(fmt, ffmpeg, symphonia): 22 | for filepath in glob.iglob(f'*.{fmt}'): 23 | benchmark_file(filepath, ffmpeg, symphonia) 24 | 25 | parser = argparse.ArgumentParser(description='Benchtest symphonia-play against ffmpeg') 26 | parser.add_argument('formats', nargs='*', default=['flac', 'mp3', 'wav'], help='formats to test') 27 | parser.add_argument('-f', '--ffmpeg', default='ffmpeg', help='Path to ffmpeg executable') 28 | parser.add_argument('-s', '--symphonia', default='symphonia-play', help='Path to symphonia-play executable') 29 | args = parser.parse_args() 30 | 31 | urls = [ 32 | 'https://archive.org/download/MLKDream/MLKDream.flac', 33 | 'https://archive.org/download/MLKDream/MLKDream.mp3', 34 | 'https://archive.org/download/MLKDream/MLKDream.wav', 35 | 'https://archive.org/download/kdtu2015-01-07.cmc641.flac24/kdtu2015-01-07.cmc641-t01.flac', 36 | 'https://archive.org/download/kdtu2015-01-07.cmc641.flac24/kdtu2015-01-07.cmc641-t01.mp3', 37 | 'https://archive.org/download/tsp1993-08-07.flac16/tsp1993-08-07d2t01.flac', 38 | 'https://archive.org/download/tsp1993-08-07.flac16/tsp1993-08-07d2t01.mp3', 39 | 'https://archive.org/download/gds2004-10-16.matrix.flac/gds10-16-2004d2t10.flac', 40 | 'https://archive.org/download/gds2004-10-16.matrix.flac/gds10-16-2004d2t10.mp3', 41 | 'https://archive.org/download/tsp1998-06-01.flac16/tsp1998-06-01t02.flac', 42 | 'https://archive.org/download/tsp1998-06-01.flac16/tsp1998-06-01t02.mp3', 43 | 'https://archive.org/download/videogamemusic_201806/11-WelcomeStrangermainTitleextendedopllYm241324bit96khz.wav', 44 | 'https://archive.org/download/videogamemusic_201806/11-WelcomeStrangermainTitleextendedopllYm241324bit96khz.flac', 45 | ] 46 | 47 | for fmt in args.formats: 48 | download_format(fmt, urls) 49 | benchmark_format(fmt, args.ffmpeg, args.symphonia) 50 | 51 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | # The following lints are ALLOWED in all Symphonia crates. 2 | # 3 | # -------------------------------------------------------------------------------------------------- 4 | # 5 | # Lint: 6 | # clippy::manual_range_contains 7 | # 8 | # Justification: 9 | # Where possible, expressing an algorithm as closely as possible to the specification is 10 | # preferrable. Rewriting using range syntax provides marginal benefit, but could be confusing. 11 | # 12 | # -------------------------------------------------------------------------------------------------- 13 | # 14 | # Lint: 15 | # clippy::comparison_chain 16 | # 17 | # Justification: 18 | # There is a performance penalty associated with the recommended idiom. 19 | # 20 | # -------------------------------------------------------------------------------------------------- 21 | # 22 | # Lint: 23 | # clippy::excessive_precision 24 | # 25 | # Justification: 26 | # Floating point constants provided by specifications and papers often provide extra digits that 27 | # are truncated when using 32-bit floating point airthmetic. By keeping this precision, we can use 28 | # the values provided directly & without modification, and, when investigating accuracy issues, 29 | # we can easily switch to 64-bit floating point arithmetic and benefit from the higher precision. 30 | # 31 | # -------------------------------------------------------------------------------------------------- 32 | # 33 | # Lint: 34 | # clippy::identity_op 35 | # 36 | # Justification: 37 | # Sometimes the base or initial case of an unrolled multi-iteration/step mathematical expression 38 | # contains operations that are otherwise no-ops. Disable the warning for these expressions because 39 | # they're instructive to the reader and improve the code aesthetics. 40 | # 41 | # -------------------------------------------------------------------------------------------------- 42 | msrv = "1.53" -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # The following rustfmt.toml requires the Nightly toolchain. Please DO NOT run with the Stable 2 | # toolchain. 3 | 4 | # Stable Features 5 | max_width = 100 6 | use_small_heuristics = "Max" 7 | hard_tabs = false 8 | tab_spaces = 4 9 | newline_style = "Unix" 10 | 11 | # Unstable Features (requires Nightly) 12 | unstable_features = true 13 | control_brace_style = "ClosingNextLine" 14 | # wrap_comments = true 15 | # comment_width = 100 -------------------------------------------------------------------------------- /symphonia-bundle-flac/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-bundle-flac" 3 | version = "0.5.4" 4 | description = "Pure Rust FLAC demuxer and decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "flac"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 18 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 19 | symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" } 20 | 21 | [lints.rust] 22 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } -------------------------------------------------------------------------------- /symphonia-bundle-flac/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia FLAC Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-bundle-flac/badge.svg)](https://docs.rs/symphonia-bundle-flac) 4 | 5 | > Come for the fidelity, stay for the memory safety! 6 | 7 | FLAC decoder for Project Symphonia. 8 | 9 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 10 | 11 | ## License 12 | 13 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 14 | 15 | ## Acknowledgements 16 | 17 | * [The FLAC Reference Library](https://github.com/xiph/flac), for format specification and algorithm clarifications 18 | * [FFmpeg](https://github.com/FFmpeg/FFmpeg), for algorithm clarifications 19 | * [Claxon](https://github.com/ruuda/claxon), for inspiration on how to structure a decoder written in idiomatic Rust 20 | 21 | ## Contributing 22 | 23 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 24 | -------------------------------------------------------------------------------- /symphonia-bundle-flac/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | mod decoder; 18 | mod demuxer; 19 | mod frame; 20 | mod parser; 21 | mod validate; 22 | 23 | pub use decoder::FlacDecoder; 24 | pub use demuxer::FlacReader; 25 | -------------------------------------------------------------------------------- /symphonia-bundle-flac/src/validate.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use std::mem; 9 | use std::vec::Vec; 10 | 11 | use symphonia_core::audio::{AudioBuffer, Signal}; 12 | use symphonia_core::checksum::Md5; 13 | use symphonia_core::io::Monitor; 14 | 15 | /// `Validator` computes the MD5 checksum of an audio stream taking into account the peculiarities 16 | /// of FLAC's MD5 validation scheme. 17 | #[derive(Default)] 18 | pub struct Validator { 19 | state: Md5, 20 | buf: Vec, 21 | } 22 | 23 | impl Validator { 24 | /// Processes the audio buffer and updates the state of the validator. 25 | pub fn update(&mut self, buf: &AudioBuffer, bps: u32) { 26 | // The MD5 checksum is calculated on a buffer containing interleaved audio samples of the 27 | // correct sample width. While FLAC can encode and decode samples of arbitrary bit widths, 28 | // the samples in the buffer must be a multiple of 8-bits. 29 | // 30 | // Additionally, Symphonia's AudioBuffer's are in planar format, and the FLAC decoder works 31 | // internally on signed 32-bit samples exclusively. 32 | // 33 | // Therefore, to compute the checksum, the audio buffer samples must truncated to the 34 | // correct bit-width, interlaced, and converted to a little-endian byte buffer. The byte 35 | // buffer can then be passed to the MD5 algorithm for hashing. 36 | 37 | // Round the sample bit width up to the nearest byte. 38 | let bytes_per_sample = match bps { 39 | 0 => return, 40 | 1..=8 => 1, 41 | 9..=16 => 2, 42 | 17..=24 => 3, 43 | 25..=32 => 4, 44 | _ => unreachable!(), 45 | }; 46 | 47 | let n_channels = buf.spec().channels.count(); 48 | let n_frames = buf.frames(); 49 | 50 | // Calculate the total size of all the samples in bytes. 51 | let buf_len = n_channels * n_frames * bytes_per_sample; 52 | 53 | // Ensure the byte buffer length can accomodate all the samples. 54 | if self.buf.len() < buf_len { 55 | self.buf.resize(buf_len, 0u8); 56 | } 57 | 58 | // Populate the hash buffer with samples truncated to the correct width. A &[u8] slice of 59 | // all the samples in hash buffer will be returned. 60 | let buf_slice = match bytes_per_sample { 61 | 1 => copy_as_i8(buf, &mut self.buf, n_channels, n_frames), 62 | 2 => copy_as_i16(buf, &mut self.buf, n_channels, n_frames), 63 | 3 => copy_as_i24(buf, &mut self.buf, n_channels, n_frames), 64 | 4 => copy_as_i32(buf, &mut self.buf, n_channels, n_frames), 65 | _ => unreachable!(), 66 | }; 67 | 68 | // Update the MD5 state. 69 | self.state.process_buf_bytes(buf_slice); 70 | } 71 | 72 | /// Get the checksum. 73 | pub fn md5(&mut self) -> [u8; 16] { 74 | self.state.md5() 75 | } 76 | } 77 | 78 | fn copy_as_i24<'a>( 79 | samples: &AudioBuffer, 80 | buf: &'a mut [u8], 81 | n_channels: usize, 82 | n_frames: usize, 83 | ) -> &'a [u8] { 84 | const SIZE_OF_I24: usize = 24 / 8; 85 | 86 | for ch in 0..n_channels { 87 | for (out, sample) in 88 | buf.chunks_exact_mut(SIZE_OF_I24).skip(ch).step_by(n_channels).zip(samples.chan(ch)) 89 | { 90 | out.copy_from_slice(&sample.to_le_bytes()[0..SIZE_OF_I24]); 91 | } 92 | } 93 | 94 | &buf[..n_channels * n_frames * SIZE_OF_I24] 95 | } 96 | 97 | macro_rules! copy_as { 98 | ($name:ident, $type:ty) => { 99 | fn $name<'a>( 100 | samples: &AudioBuffer, 101 | buf: &'a mut [u8], 102 | n_channels: usize, 103 | n_frames: usize, 104 | ) -> &'a [u8] { 105 | for ch in 0..n_channels { 106 | for (out, sample) in buf 107 | .chunks_exact_mut(mem::size_of::<$type>()) 108 | .skip(ch) 109 | .step_by(n_channels) 110 | .zip(samples.chan(ch)) 111 | { 112 | out.copy_from_slice(&(*sample as $type).to_le_bytes()); 113 | } 114 | } 115 | 116 | &buf[..n_channels * n_frames * mem::size_of::<$type>()] 117 | } 118 | }; 119 | } 120 | 121 | copy_as!(copy_as_i8, i8); 122 | copy_as!(copy_as_i16, i16); 123 | copy_as!(copy_as_i32, i32); 124 | -------------------------------------------------------------------------------- /symphonia-bundle-mp3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-bundle-mp3" 3 | version = "0.5.4" 4 | description = "Pure Rust MP1, MP2, and MP3 demuxer and decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "mp3", "mpeg"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [features] 16 | default = ["mp1", "mp2", "mp3"] 17 | mp1 = [] 18 | mp2 = [] 19 | mp3 = [] 20 | 21 | [dependencies] 22 | log = "0.4" 23 | lazy_static = "1.4.0" 24 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 25 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } -------------------------------------------------------------------------------- /symphonia-bundle-mp3/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia MP3 (and MP1, MP2) Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-bundle-mp3/badge.svg)](https://docs.rs/symphonia-bundle-mp3) 4 | 5 | MPEG-1 and MPEG-2 Layer I, II, III (MP1, MP2, MP3) decoder for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## Support 10 | 11 | This decoder implements Layer I, II, III of MPEG-1 Part 3 (ISO/IEC 11172-3), and all the extensions and enhancements defined in MPEG-2 Part 3 (ISO/IEC 13818-3). 12 | 13 | ## Conformance 14 | 15 | The decoder will ideally conform to ISO/IEC 11172-4 and ISO/IEC 13818-4. 16 | 17 | ## License 18 | 19 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 20 | 21 | ## Contributing 22 | 23 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 24 | -------------------------------------------------------------------------------- /symphonia-bundle-mp3/src/layer12.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | /// Layer 1 and 2 shared scale factors. Derived from ISO/ISO 11172-3 Table 3-B.1. 9 | pub const LAYER12_SCALEFACTORS: [f32; 64] = [ 10 | 2.00000000000000, 11 | 1.58740105196820, 12 | 1.25992104989487, 13 | 1.00000000000000, 14 | 0.79370052598410, 15 | 0.62996052494744, 16 | 0.50000000000000, 17 | 0.39685026299205, 18 | 0.31498026247372, 19 | 0.25000000000000, 20 | 0.19842513149602, 21 | 0.15749013123686, 22 | 0.12500000000000, 23 | 0.09921256574801, 24 | 0.07874506561843, 25 | 0.06250000000000, 26 | 0.04960628287401, 27 | 0.03937253280921, 28 | 0.03125000000000, 29 | 0.02480314143700, 30 | 0.01968626640461, 31 | 0.01562500000000, 32 | 0.01240157071850, 33 | 0.00984313320230, 34 | 0.00781250000000, 35 | 0.00620078535925, 36 | 0.00492156660115, 37 | 0.00390625000000, 38 | 0.00310039267963, 39 | 0.00246078330058, 40 | 0.00195312500000, 41 | 0.00155019633981, 42 | 0.00123039165029, 43 | 0.00097656250000, 44 | 0.00077509816991, 45 | 0.00061519582514, 46 | 0.00048828125000, 47 | 0.00038754908495, 48 | 0.00030759791257, 49 | 0.00024414062500, 50 | 0.00019377454248, 51 | 0.00015379895629, 52 | 0.00012207031250, 53 | 0.00009688727124, 54 | 0.00007689947814, 55 | 0.00006103515625, 56 | 0.00004844363562, 57 | 0.00003844973907, 58 | 0.00003051757813, 59 | 0.00002422181781, 60 | 0.00001922486954, 61 | 0.00001525878906, 62 | 0.00001211090890, 63 | 0.00000961243477, 64 | 0.00000762939453, 65 | 0.00000605545445, 66 | 0.00000480621738, 67 | 0.00000381469727, 68 | 0.00000302772723, 69 | 0.00000240310869, 70 | 0.00000190734863, 71 | 0.00000151386361, 72 | 0.00000120155435, 73 | // This value is not in the table provided by ISO/IEC 11172-3. However, some files do use 63 as 74 | // a scalefactor index. To increase compatibility, a dummy scalefactor has been added. 75 | 0.00000000000000, 76 | ]; 77 | -------------------------------------------------------------------------------- /symphonia-bundle-mp3/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | // Shared modules. 18 | mod common; 19 | mod header; 20 | 21 | // Demuxer module. 22 | mod demuxer; 23 | 24 | // Decoder modules. 25 | #[cfg(any(feature = "mp1", feature = "mp2", feature = "mp3"))] 26 | mod decoder; 27 | #[cfg(any(feature = "mp1", feature = "mp2", feature = "mp3"))] 28 | mod synthesis; 29 | 30 | // Shared layer 1 & 2 decoder support module. 31 | #[cfg(any(feature = "mp1", feature = "mp2"))] 32 | mod layer12; 33 | 34 | // Layer-specific decoder support modules. 35 | #[cfg(feature = "mp1")] 36 | mod layer1; 37 | #[cfg(feature = "mp2")] 38 | mod layer2; 39 | #[cfg(feature = "mp3")] 40 | mod layer3; 41 | 42 | #[cfg(any(feature = "mp1", feature = "mp2", feature = "mp3"))] 43 | pub use decoder::MpaDecoder; 44 | pub use demuxer::MpaReader; 45 | 46 | // For SemVer compatibility in v0.5.x series. 47 | #[deprecated = "use `symphonia_bundle_mp3::MpaDecoder` instead"] 48 | #[cfg(any(feature = "mp1", feature = "mp2", feature = "mp3"))] 49 | pub type Mp3Decoder = MpaDecoder; 50 | 51 | #[deprecated = "use `symphonia_bundle_mp3::MpaReader` instead"] 52 | pub type Mp3Reader = MpaReader; 53 | -------------------------------------------------------------------------------- /symphonia-check/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-check" 3 | version = "0.5.4" 4 | description = "Project Symphonia QA tool." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | edition = "2018" 10 | publish = false 11 | 12 | [dependencies] 13 | clap = "3.1.0" 14 | log = { version = "0.4", features = ["release_max_level_info"] } 15 | pretty_env_logger = "0.4" 16 | symphonia = { version = "0.5.4", path = "../symphonia", features = ["all", "opt-simd"] } -------------------------------------------------------------------------------- /symphonia-check/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Check 2 | 3 | A utility to test the output of a file decoded by Symphonia against `ffmpeg` and other reference decoders. 4 | 5 | The currently supported reference decoders include: 6 | 7 | * `ffmpeg` 8 | * `flac` 9 | * `mpg123` (when provided by `libmad` aka. `mpg321`) 10 | * `oggdec` 11 | 12 | ## Prerequisites 13 | 14 | The reference decoder must must be installed and present in your `PATH`. 15 | 16 | ## Usage 17 | 18 | ```bash 19 | # Test a file, printing information on every packet with an error (no extra arguments). 20 | symphonia-check /path/to/file 21 | 22 | # Test a file, printing information on every sample with an error (--samples). 23 | symphonia-check --samples /path/to/file 24 | 25 | # Test a file, only printing the final test results (-q/--quiet). 26 | symphonia-check -q /path/to/file 27 | 28 | # Test a file, and abort the test on the first failed packet (-f/--first-fail). 29 | symphonia-check -f /path/to/file 30 | 31 | # Any of the above commands, without gapless playback enabled (--no-gapless). 32 | symphonia-check --no-gapless /path/to/file 33 | 34 | # Any of the above commands, using a specific reference decoder (--ref ). 35 | symphonia-check --ref flac /path/to/flac/file 36 | ``` 37 | 38 | ### Interpreting Results 39 | 40 | Most files will pass, however, `symphonia-check` is a very simple tool, and a failure **does not** necessarily mean an invalid decoding. All decoders, including the reference decoders, contain bugs that can cause differences when tested against Symphonia. 41 | 42 | Some scenarios can result in `symphonia-check` reporting large errors on almost all samples, yet when played sound okay. These scenarios are almost always false positives that can be caused by: 43 | 44 | * The reference decoder or Symphonia dropping differing amounts of samples when encountering corruption. 45 | * Testing a Symphonia decoder that does not support gapless playback without the `--no-gapless` flag. 46 | 47 | In the first scenario, it is useful to verify the file decodes without any warnings from `ffmpeg`. 48 | 49 | ```bash 50 | ffmpeg -v debug -i /path/to/file -f null - 51 | ``` 52 | 53 | Regardless, feel free to open an issue if you encounter a check failure. Please note that a sample file reproducing the failure is almost always required to triage the issue. 54 | 55 | ## License 56 | 57 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 58 | 59 | ## Contributing 60 | 61 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 62 | -------------------------------------------------------------------------------- /symphonia-codec-aac/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-aac" 3 | version = "0.5.4" 4 | description = "Pure Rust AAC decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov ", "Kostya Shishkov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "aac", "m4a"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | lazy_static = "1.4.0" 18 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-aac/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia AAC Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-codec-aac/badge.svg)](https://docs.rs/symphonia-codec-aac) 4 | 5 | Advanced Audio Coding (AAC) decoder for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## Support 10 | 11 | This decoder implements the low-complexity (LC) profile as defined in ISO/IEC 14496-3. 12 | 13 | ## Attribution 14 | 15 | Symphonia's AAC decoder was ported and relicensed from the [NihAV](https://nihav.org/) project with permission from the original author, Kostya Shishkov. The first commit with the original decoder is `3aeeb22`. 16 | 17 | ## License 18 | 19 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 20 | 21 | ## Contributing 22 | 23 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 24 | -------------------------------------------------------------------------------- /symphonia-codec-aac/src/aac/ics/gain.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // Previous Author: Kostya Shishkov 5 | // 6 | // This source file includes code originally written for the NihAV 7 | // project. With the author's permission, it has been relicensed for, 8 | // and ported to the Symphonia project. 9 | // 10 | // This Source Code Form is subject to the terms of the Mozilla Public 11 | // License, v. 2.0. If a copy of the MPL was not distributed with this 12 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 13 | 14 | use symphonia_core::errors::{unsupported_error, Result}; 15 | use symphonia_core::io::ReadBitsLtr; 16 | 17 | #[derive(Clone, Copy)] 18 | #[allow(dead_code)] 19 | pub struct GainControl { 20 | max_band: u8, 21 | } 22 | 23 | impl GainControl { 24 | pub fn read(bs: &mut B) -> Result> { 25 | let gain_control_data_present = bs.read_bool()?; 26 | 27 | if !gain_control_data_present { 28 | return Ok(None); 29 | } 30 | 31 | /* 32 | self.max_band = bs.read_bits_leq32(2)? as u8; 33 | if window_sequence == ONLY_LONG_SEQUENCE { 34 | for bd in 0..max_band 35 | ... 36 | } 37 | Ok(Some(Self { })) 38 | */ 39 | 40 | unsupported_error("aac: gain control data") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /symphonia-codec-aac/src/aac/ics/ltp.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // Previous Author: Kostya Shishkov 5 | // 6 | // This source file includes code originally written for the NihAV 7 | // project. With the author's permission, it has been relicensed for, 8 | // and ported to the Symphonia project. 9 | // 10 | // This Source Code Form is subject to the terms of the Mozilla Public 11 | // License, v. 2.0. If a copy of the MPL was not distributed with this 12 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 13 | 14 | use symphonia_core::errors::{unsupported_error, Result}; 15 | use symphonia_core::io::ReadBitsLtr; 16 | 17 | #[derive(Clone, Copy)] 18 | pub struct LtpData {} 19 | 20 | impl LtpData { 21 | pub fn read(bs: &mut B) -> Result> { 22 | let predictor_data_present = bs.read_bool()?; 23 | 24 | if !predictor_data_present { 25 | return Ok(None); 26 | } 27 | 28 | /* 29 | if is_main { 30 | let predictor_reset = bs.read_bit()?; 31 | if predictor_reset { 32 | let predictor_reset_group_number = bs.read_bits_leq32(5)?; 33 | } 34 | for sfb in 0..max_sfb.min(PRED_SFB_MAX) { 35 | prediction_used[sfb] = bs.read_bit()?; 36 | } 37 | } 38 | else { 39 | let ltp_data_present = bs.read_bit()?; 40 | if ltp_data_present { 41 | //ltp data 42 | } 43 | if common_window { 44 | let ltp_data_present = bs.read_bit()?; 45 | if ltp_data_present { 46 | //ltp data 47 | } 48 | } 49 | } 50 | Ok(Some(Self { })) 51 | */ 52 | 53 | unsupported_error("aac: predictor data") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /symphonia-codec-aac/src/aac/ics/pulse.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // Previous Author: Kostya Shishkov 5 | // 6 | // This source file includes code originally written for the NihAV 7 | // project. With the author's permission, it has been relicensed for, 8 | // and ported to the Symphonia project. 9 | // 10 | // This Source Code Form is subject to the terms of the Mozilla Public 11 | // License, v. 2.0. If a copy of the MPL was not distributed with this 12 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 13 | 14 | use symphonia_core::errors::Result; 15 | use symphonia_core::io::ReadBitsLtr; 16 | 17 | use crate::aac::common::{MAX_SFBS, MAX_WINDOWS}; 18 | 19 | #[inline(always)] 20 | fn iquant(val: f32) -> f32 { 21 | if val < 0.0 { 22 | -((-val).powf(4.0 / 3.0)) 23 | } 24 | else { 25 | val.powf(4.0 / 3.0) 26 | } 27 | } 28 | 29 | #[inline(always)] 30 | fn requant(val: f32, scale: f32) -> f32 { 31 | if scale == 0.0 { 32 | return 0.0; 33 | } 34 | let bval = val / scale; 35 | if bval >= 0.0 { 36 | val.powf(3.0 / 4.0) 37 | } 38 | else { 39 | -((-val).powf(3.0 / 4.0)) 40 | } 41 | } 42 | 43 | #[derive(Clone, Copy)] 44 | #[allow(dead_code)] 45 | pub struct Pulse { 46 | number_pulse: usize, 47 | pulse_start_sfb: usize, 48 | pulse_offset: [u8; 4], 49 | pulse_amp: [u8; 4], 50 | } 51 | 52 | impl Pulse { 53 | pub fn read(bs: &mut B) -> Result> { 54 | let pulse_data_present = bs.read_bool()?; 55 | 56 | if !pulse_data_present { 57 | return Ok(None); 58 | } 59 | 60 | let number_pulse = bs.read_bits_leq32(2)? as usize + 1; 61 | let pulse_start_sfb = bs.read_bits_leq32(6)? as usize; 62 | 63 | let mut pulse_offset: [u8; 4] = [0; 4]; 64 | let mut pulse_amp: [u8; 4] = [0; 4]; 65 | 66 | for i in 0..number_pulse { 67 | pulse_offset[i] = bs.read_bits_leq32(5)? as u8; 68 | pulse_amp[i] = bs.read_bits_leq32(4)? as u8; 69 | } 70 | 71 | Ok(Some(Self { number_pulse, pulse_start_sfb, pulse_offset, pulse_amp })) 72 | } 73 | 74 | pub fn synth( 75 | &self, 76 | bands: &[usize], 77 | scales: &[[f32; MAX_SFBS]; MAX_WINDOWS], 78 | coeffs: &mut [f32; 1024], 79 | ) { 80 | if self.pulse_start_sfb >= bands.len() - 1 { 81 | return; 82 | } 83 | 84 | let mut k = bands[self.pulse_start_sfb]; 85 | 86 | let mut band = self.pulse_start_sfb; 87 | 88 | for pno in 0..self.number_pulse { 89 | k += self.pulse_offset[pno] as usize; 90 | 91 | if k >= 1024 { 92 | return; 93 | } 94 | 95 | while bands[band + 1] <= k { 96 | band += 1; 97 | } 98 | 99 | let scale = scales[0][band]; 100 | 101 | let mut base = coeffs[k]; 102 | 103 | if base != 0.0 { 104 | base = requant(coeffs[k], scale); 105 | } 106 | 107 | if base > 0.0 { 108 | base += f32::from(self.pulse_amp[pno]); 109 | } 110 | else { 111 | base -= f32::from(self.pulse_amp[pno]); 112 | } 113 | coeffs[k] = iquant(base) * scale; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /symphonia-codec-aac/src/aac/window.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // Previous Author: Kostya Shishkov 5 | // 6 | // This source file includes code originally written for the NihAV 7 | // project. With the author's permission, it has been relicensed for, 8 | // and ported to the Symphonia project. 9 | // 10 | // This Source Code Form is subject to the terms of the Mozilla Public 11 | // License, v. 2.0. If a copy of the MPL was not distributed with this 12 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 13 | 14 | use std::f32::consts; 15 | 16 | /// Window types. 17 | #[derive(Debug, Clone, Copy, PartialEq)] 18 | pub enum WindowType { 19 | /// Simple sine window. 20 | Sine, 21 | /// Kaiser-Bessel derived window. 22 | KaiserBessel(f32), 23 | } 24 | 25 | /// Calculates window coefficients for the requested window type and size. 26 | /// 27 | /// Set `half` flag to calculate only the first half of the window. 28 | pub fn generate_window(mode: WindowType, scale: f32, size: usize, half: bool, dst: &mut [f32]) { 29 | match mode { 30 | WindowType::Sine => { 31 | let param = 32 | if half { consts::PI / ((2 * size) as f32) } else { consts::PI / (size as f32) }; 33 | for n in 0..size { 34 | dst[n] = (((n as f32) + 0.5) * param).sin() * scale; 35 | } 36 | } 37 | WindowType::KaiserBessel(alpha) => { 38 | let dlen = if half { size as f32 } else { (size as f32) * 0.5 }; 39 | let alpha2 = f64::from((alpha * consts::PI / dlen) * (alpha * consts::PI / dlen)); 40 | 41 | let mut kb: Vec = Vec::with_capacity(size); 42 | let mut sum = 0.0; 43 | for n in 0..size { 44 | let b = bessel_i0(((n * (size - n)) as f64) * alpha2); 45 | sum += b; 46 | kb.push(sum); 47 | } 48 | sum += 1.0; 49 | for n in 0..size { 50 | dst[n] = (kb[n] / sum).sqrt() as f32; 51 | } 52 | } 53 | }; 54 | } 55 | 56 | fn bessel_i0(inval: f64) -> f64 { 57 | let mut val: f64 = 1.0; 58 | for n in (1..64).rev() { 59 | val *= inval / f64::from(n * n); 60 | val += 1.0; 61 | } 62 | val 63 | } 64 | -------------------------------------------------------------------------------- /symphonia-codec-aac/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | // TODO: Remove this when refactoring AAC. 17 | #![allow(clippy::needless_range_loop)] 18 | 19 | mod aac; 20 | mod adts; 21 | mod common; 22 | 23 | pub use aac::AacDecoder; 24 | pub use adts::AdtsReader; 25 | -------------------------------------------------------------------------------- /symphonia-codec-aac/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use symphonia_codec_aac::{AacDecoder, AdtsReader}; 2 | use symphonia_core::codecs::{CodecParameters, Decoder, DecoderOptions, CODEC_TYPE_AAC}; 3 | use symphonia_core::errors; 4 | use symphonia_core::formats::{FormatOptions, FormatReader}; 5 | use symphonia_core::io::MediaSourceStream; 6 | 7 | fn test_decode(data: Vec) -> symphonia_core::errors::Result<()> { 8 | let data = std::io::Cursor::new(data); 9 | 10 | let source = MediaSourceStream::new(Box::new(data), Default::default()); 11 | 12 | let mut reader = AdtsReader::try_new(source, &FormatOptions::default())?; 13 | 14 | let mut decoder = AacDecoder::try_new( 15 | CodecParameters::new().for_codec(CODEC_TYPE_AAC), 16 | &DecoderOptions::default(), 17 | )?; 18 | 19 | loop { 20 | let packet = reader.next_packet()?; 21 | let _ = decoder.decode(&packet); 22 | } 23 | } 24 | 25 | #[test] 26 | fn invalid_channels_aac() { 27 | let file = vec![ 28 | 0xff, 0xf1, 0xaf, 0xce, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 29 | 0xaf, 30 | ]; 31 | 32 | let err = test_decode(file).unwrap_err(); 33 | 34 | assert!(matches!(err, errors::Error::Unsupported(_))); 35 | } 36 | -------------------------------------------------------------------------------- /symphonia-codec-adpcm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-adpcm" 3 | version = "0.5.4" 4 | description = "Pure Rust ADPCM audio decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov ", "Johannes Hackel "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "adpcm"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-adpcm/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia ADPCM Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-codec-adpcm/badge.svg)](https://docs.rs/symphonia-codec-adpcm) 4 | 5 | ADPCM audio decoders for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## Support 10 | 11 | The following ADPCM encodings are supported: 12 | 13 | * Microsoft ADPCM 14 | * ADPCM IMA WAV 15 | 16 | Only 4 bits per sample and only mono and stereo channels are supported. 17 | 18 | ## License 19 | 20 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 21 | 22 | ## Contributing 23 | 24 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 25 | -------------------------------------------------------------------------------- /symphonia-codec-adpcm/src/codec_ima.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | use symphonia_core::util::clamp::clamp_i16; 11 | 12 | use crate::common::{from_i16_shift, u16_to_i32, Nibble}; 13 | 14 | #[rustfmt::skip] 15 | const IMA_INDEX_TABLE: [i32; 16] = [ 16 | -1, -1, -1, -1, 2, 4, 6, 8, 17 | -1, -1, -1, -1, 2, 4, 6, 8, 18 | ]; 19 | 20 | #[rustfmt::skip] 21 | const IMA_STEP_TABLE: [i32; 89] = [ 22 | 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 23 | 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 24 | 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 25 | 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 26 | 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 27 | 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 28 | 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 29 | 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 30 | 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767, 31 | ]; 32 | 33 | /// `AdpcmImaBlockStatus` contains values to decode a block 34 | struct AdpcmImaBlockStatus { 35 | predictor: i32, 36 | step_index: i32, 37 | } 38 | 39 | impl AdpcmImaBlockStatus { 40 | fn read_preamble(stream: &mut B) -> Result { 41 | let predictor = u16_to_i32!(stream.read_u16()?); 42 | let step_index = stream.read_byte()? as i32; 43 | if step_index > 88 { 44 | return decode_error("adpcm (ima): invalid step index"); 45 | } 46 | //reserved byte 47 | let _ = stream.read_byte()?; 48 | let status = Self { predictor, step_index }; 49 | Ok(status) 50 | } 51 | 52 | fn expand_nibble(&mut self, byte: u8, nibble: Nibble) -> i32 { 53 | let nibble = nibble.get_nibble(byte); 54 | let step = IMA_STEP_TABLE[self.step_index as usize]; 55 | let sign = (nibble & 0x08) != 0; 56 | let delta = (nibble & 0x07) as i32; 57 | let diff = ((2 * delta + 1) * step) >> 3; 58 | let predictor = if sign { self.predictor - diff } else { self.predictor + diff }; 59 | self.predictor = clamp_i16(predictor) as i32; 60 | self.step_index = (self.step_index + IMA_INDEX_TABLE[nibble as usize]).clamp(0, 88); 61 | from_i16_shift!(self.predictor) 62 | } 63 | } 64 | 65 | pub(crate) fn decode_mono( 66 | stream: &mut B, 67 | buffer: &mut [i32], 68 | frames_per_block: usize, 69 | ) -> Result<()> { 70 | let data_bytes_per_channel = (frames_per_block - 1) / 2; 71 | let mut status = AdpcmImaBlockStatus::read_preamble(stream)?; 72 | buffer[0] = from_i16_shift!(status.predictor); 73 | for byte in 0..data_bytes_per_channel { 74 | let nibbles = stream.read_u8()?; 75 | buffer[1 + byte * 2] = status.expand_nibble(nibbles, Nibble::Lower); 76 | buffer[1 + byte * 2 + 1] = status.expand_nibble(nibbles, Nibble::Upper); 77 | } 78 | Ok(()) 79 | } 80 | 81 | pub(crate) fn decode_stereo( 82 | stream: &mut B, 83 | buffers: [&mut [i32]; 2], 84 | frames_per_block: usize, 85 | ) -> Result<()> { 86 | let data_bytes_per_channel = frames_per_block - 1; 87 | let mut status = 88 | [AdpcmImaBlockStatus::read_preamble(stream)?, AdpcmImaBlockStatus::read_preamble(stream)?]; 89 | buffers[0][0] = from_i16_shift!(status[0].predictor); 90 | buffers[1][0] = from_i16_shift!(status[1].predictor); 91 | for index in 0..data_bytes_per_channel { 92 | let channel = (index / 4) & 1; 93 | let offset = (index / 8) * 8; 94 | let byte = index % 4; 95 | let nibbles = stream.read_u8()?; 96 | buffers[channel][1 + offset + byte * 2] = 97 | status[channel].expand_nibble(nibbles, Nibble::Lower); 98 | buffers[channel][1 + offset + byte * 2 + 1] = 99 | status[channel].expand_nibble(nibbles, Nibble::Upper); 100 | } 101 | Ok(()) 102 | } 103 | -------------------------------------------------------------------------------- /symphonia-codec-adpcm/src/common.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | /// `Nibble` represents the lower or upper 4 bits of a byte 9 | pub(crate) enum Nibble { 10 | Upper, 11 | Lower, 12 | } 13 | 14 | impl Nibble { 15 | pub fn get_nibble(&self, byte: u8) -> u8 { 16 | match self { 17 | Nibble::Upper => byte >> 4, 18 | Nibble::Lower => byte & 0x0F, 19 | } 20 | } 21 | } 22 | 23 | macro_rules! u16_to_i32 { 24 | ($input:expr) => { 25 | $input as i16 as i32 26 | }; 27 | } 28 | 29 | macro_rules! from_i16_shift { 30 | ($input:expr) => { 31 | ($input as i32) << 16 32 | }; 33 | } 34 | 35 | pub(crate) use from_i16_shift; 36 | pub(crate) use u16_to_i32; 37 | -------------------------------------------------------------------------------- /symphonia-codec-alac/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-alac" 3 | version = "0.5.4" 4 | description = "Pure Rust ALAC decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "alac"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-alac/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia ALAC Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-codec-alac/badge.svg)](https://docs.rs/symphonia-codec-alac) 4 | 5 | Apple Lossless Audio Codec (ALAC) decoder for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-codec-opus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-opus" 3 | version = "0.0.1" 4 | description = "Pure Opus decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "opus"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-opus/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Opus Codec 2 | 3 | This is a placeholder crate for Project Symphonia's work-in-progress Opus decoder. 4 | 5 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 6 | 7 | ## License 8 | 9 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 10 | 11 | ## Contributing 12 | 13 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 14 | -------------------------------------------------------------------------------- /symphonia-codec-opus/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /symphonia-codec-pcm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-pcm" 3 | version = "0.5.4" 4 | description = "Pure Rust PCM audio decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "pcm"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-pcm/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia PCM Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-codec-pcm/badge.svg)](https://docs.rs/symphonia-codec-pcm) 4 | 5 | PCM audio decoders for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## Support 10 | 11 | The following PCM encodings are supported: 12 | 13 | * Signed 32-bit Little-Endian Interleaved 14 | * Signed 24-bit Little-Endian Interleaved 15 | * Signed 16-bit Little-Endian Interleaved 16 | * Signed 8-bit Interleaved 17 | * Signed 32-bit Big-Endian Interleaved 18 | * Signed 24-bit Big-Endian Interleaved 19 | * Signed 16-bit Big-Endian Interleaved 20 | * Unsigned 32-bit Little-Endian Interleaved 21 | * Unsigned 24-bit Little-Endian Interleaved 22 | * Unsigned 16-bit Little-Endian Interleaved 23 | * Unsigned 8-bit Interleaved 24 | * Unsigned 32-bit Big-Endian Interleaved 25 | * Unsigned 24-bit Big-Endian Interleaved 26 | * Unsigned 16-bit Big-Endian Interleaved 27 | * 32-bit Little-Endian Floating Point Interleaved 28 | * 64-bit Little-Endian Floating Point Interleaved 29 | * 32-bit Big-Endian Floating Point Interleaved 30 | * 64-bit Big-Endian Floating Point Interleaved 31 | * A-law 32 | * Mu-law 33 | 34 | ## License 35 | 36 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 37 | 38 | ## Contributing 39 | 40 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 41 | -------------------------------------------------------------------------------- /symphonia-codec-vorbis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-vorbis" 3 | version = "0.5.4" 4 | description = "Pure Rust Vorbis decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "vorbis"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 18 | symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" } -------------------------------------------------------------------------------- /symphonia-codec-vorbis/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Vorbis Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-codec-vorbis/badge.svg)](https://docs.rs/symphonia-codec-vorbis) 4 | 5 | Vorbis decoder for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-codec-vorbis/src/common.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | /// As defined in section 9.2.1 of the Vorbis I specification. 9 | /// 10 | /// The `ilog` function returns the position number (1 through n) of the highest set bit in the two’s 11 | /// complement integer value `x`. 12 | #[inline(always)] 13 | pub fn ilog(x: u32) -> u32 { 14 | 32 - x.leading_zeros() 15 | } 16 | 17 | pub struct BitSetIterator<'a> { 18 | bits: &'a [u32], 19 | pos: usize, 20 | count: usize, 21 | } 22 | 23 | impl<'a> Iterator for BitSetIterator<'a> { 24 | type Item = usize; 25 | 26 | fn next(&mut self) -> Option { 27 | if self.count == 0 { 28 | return None; 29 | } 30 | 31 | for bits in &self.bits[self.pos >> 5..] { 32 | let bits_read = self.pos & 0x1f; 33 | 34 | let offset = (bits >> bits_read).trailing_zeros() as usize; 35 | 36 | if offset < 32 - bits_read { 37 | self.pos += offset + 1; 38 | self.count -= 1; 39 | return Some(self.pos - 1); 40 | } 41 | else { 42 | self.pos += 32 - bits_read; 43 | } 44 | } 45 | 46 | None 47 | } 48 | } 49 | 50 | // TODO: When const generics allow division, switch to that. 51 | macro_rules! decl_bitset { 52 | ($name:ident, $size:expr) => { 53 | #[derive(Default)] 54 | pub struct $name { 55 | bits: [u32; $size >> 5], 56 | bit_count: usize, 57 | } 58 | 59 | impl $name { 60 | #[inline(always)] 61 | pub fn set(&mut self, idx: usize) { 62 | if !self.is_set(idx) { 63 | self.bits[idx >> 5] |= 1 << (idx & 0x1f); 64 | self.bit_count += 1; 65 | } 66 | } 67 | 68 | // #[inline(always)] 69 | // pub fn unset(&mut self, idx: usize) { 70 | // if self.is_set(idx) { 71 | // self.bits[idx >> 5] &= !(1 << (idx & 0x1f)); 72 | // self.bit_count -= 1; 73 | // } 74 | // } 75 | 76 | #[inline(always)] 77 | pub fn is_set(&self, idx: usize) -> bool { 78 | self.bits[idx >> 5] & (1 << (idx & 0x1f)) != 0 79 | } 80 | 81 | #[inline(always)] 82 | pub fn count(&self) -> usize { 83 | self.bit_count 84 | } 85 | 86 | #[inline(always)] 87 | pub fn iter(&self) -> BitSetIterator<'_> { 88 | BitSetIterator { bits: &self.bits, pos: 0, count: self.bit_count } 89 | } 90 | } 91 | }; 92 | } 93 | 94 | decl_bitset!(BitSet256, 256); 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::BitSet256; 99 | 100 | #[test] 101 | fn verify_bitset_count() { 102 | let mut bitset: BitSet256 = Default::default(); 103 | 104 | bitset.set(1); 105 | bitset.set(2); 106 | bitset.set(56); 107 | bitset.set(64); 108 | bitset.set(127); 109 | bitset.set(128); 110 | bitset.set(250); 111 | bitset.set(251); 112 | bitset.set(252); 113 | bitset.set(253); 114 | bitset.set(254); 115 | bitset.set(255); 116 | 117 | assert_eq!(bitset.count(), 12); 118 | } 119 | 120 | #[test] 121 | fn verify_bitset_iter() { 122 | let mut bitset: BitSet256 = Default::default(); 123 | 124 | assert_eq!(bitset.count(), 0); 125 | 126 | for _ in bitset.iter() { 127 | panic!("Should be empty!"); 128 | } 129 | 130 | bitset.set(1); 131 | bitset.set(2); 132 | bitset.set(56); 133 | bitset.set(64); 134 | bitset.set(127); 135 | bitset.set(128); 136 | bitset.set(250); 137 | bitset.set(251); 138 | bitset.set(252); 139 | bitset.set(253); 140 | bitset.set(254); 141 | bitset.set(255); 142 | 143 | let mut iter = bitset.iter(); 144 | assert_eq!(iter.next(), Some(1)); 145 | assert_eq!(iter.next(), Some(2)); 146 | assert_eq!(iter.next(), Some(56)); 147 | assert_eq!(iter.next(), Some(64)); 148 | assert_eq!(iter.next(), Some(127)); 149 | assert_eq!(iter.next(), Some(128)); 150 | assert_eq!(iter.next(), Some(250)); 151 | assert_eq!(iter.next(), Some(251)); 152 | assert_eq!(iter.next(), Some(252)); 153 | assert_eq!(iter.next(), Some(253)); 154 | assert_eq!(iter.next(), Some(254)); 155 | assert_eq!(iter.next(), Some(255)); 156 | assert_eq!(iter.next(), None); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /symphonia-codec-vorbis/src/window.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use std::f64::consts; 9 | 10 | /// For a given window size, generates the curve of the left-half of the window. 11 | fn generate_win_curve(bs: usize) -> Vec { 12 | let len = bs / 2; 13 | let denom = f64::from(len as u32); 14 | 15 | let mut slope = vec![0.0; len]; 16 | 17 | for (i, s) in slope.iter_mut().enumerate() { 18 | let num = f64::from(i as u32) + 0.5; 19 | let frac = consts::FRAC_PI_2 * (num / denom); 20 | *s = (consts::FRAC_PI_2 * frac.sin().powi(2)).sin() as f32 21 | } 22 | 23 | slope 24 | } 25 | 26 | pub struct Windows { 27 | /// Short block window left-half curve. 28 | pub short: Vec, 29 | /// Long block window left-half curve. 30 | pub long: Vec, 31 | } 32 | 33 | impl Windows { 34 | pub fn new(blocksize0: usize, blocksize1: usize) -> Self { 35 | let short = generate_win_curve(blocksize0); 36 | let long = generate_win_curve(blocksize1); 37 | Windows { short, long } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /symphonia-codec-wavpack/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-codec-wavpack" 3 | version = "0.0.1" 4 | description = "Pure Rust WacPack decoder (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "wavpack"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-codec-wavpack/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia WavPack Codec 2 | 3 | This is a placeholder crate for Project Symphonia's WavPack decoder. 4 | 5 | Please consider contributing! 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-codec-wavpack/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /symphonia-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-core" 3 | version = "0.5.4" 4 | description = "Project Symphonia shared structs, traits, and features." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "multimedia", "media"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [features] 16 | default = [] 17 | 18 | # SIMD support. 19 | opt-simd-sse = ["rustfft/sse"] 20 | opt-simd-avx = ["rustfft/avx"] 21 | opt-simd-neon = ["rustfft/neon"] 22 | 23 | # Enable all SIMD support. 24 | opt-simd = [ 25 | "opt-simd-sse", 26 | "opt-simd-avx", 27 | "opt-simd-neon", 28 | ] 29 | 30 | [dependencies] 31 | arrayvec = "0.7.1" 32 | bitflags = "1.2.1" 33 | bytemuck = "1.7" 34 | lazy_static = "1.4.0" 35 | log = "0.4" 36 | 37 | [dependencies.rustfft] 38 | version = "6.1.0" 39 | optional = true 40 | default-features = false -------------------------------------------------------------------------------- /symphonia-core/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Core 2 | 3 | [![Docs](https://docs.rs/symphonia-core/badge.svg)](https://docs.rs/symphonia-core) 4 | 5 | Core structs, traits, helpers, and more for Project Symphonia. 6 | 7 | **Note:** This crate should only be used if developing Project Symphonia decoders and demuxers. For other use cases please use the [`symphonia`](https://crates.io/crates/symphonia) crate. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-core/src/checksum/crc8.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use crate::io::Monitor; 9 | 10 | // Credit: This table was extracted from the reference FLAC decoder. 11 | #[rustfmt::skip] 12 | const CRC8_CCITT: [u8; 256] = 13 | [ 14 | 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 15 | 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 16 | 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 17 | 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 18 | 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 19 | 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 20 | 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 21 | 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 22 | 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 23 | 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 24 | 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 25 | 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 26 | 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 27 | 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 28 | 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 29 | 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3, 30 | ]; 31 | 32 | /// `Crc8Ccitt` implements the CRC-8 algorithm using the CCITT polynominal. 33 | /// 34 | /// * Polynomial = 0x07 35 | /// * RefIn = false 36 | /// * RefOut = false 37 | /// * XorOut = false 38 | pub struct Crc8Ccitt { 39 | state: u8, 40 | } 41 | 42 | impl Crc8Ccitt { 43 | /// Instantiate a `Crc8Ccitt` instance with an initial state. 44 | pub fn new(state: u8) -> Self { 45 | Crc8Ccitt { state } 46 | } 47 | 48 | /// Returns the computed CRC. 49 | pub fn crc(&self) -> u8 { 50 | self.state 51 | } 52 | } 53 | 54 | impl Monitor for Crc8Ccitt { 55 | #[inline(always)] 56 | fn process_byte(&mut self, byte: u8) { 57 | self.state = CRC8_CCITT[(self.state ^ byte) as usize]; 58 | } 59 | 60 | fn process_buf_bytes(&mut self, buf: &[u8]) { 61 | for byte in buf { 62 | self.state = CRC8_CCITT[(self.state ^ byte) as usize]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /symphonia-core/src/checksum/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! The `checksum` module provides implementations of common error-detecting codes and hashing 9 | //! algorithms. 10 | 11 | mod crc16; 12 | mod crc32; 13 | mod crc8; 14 | mod md5; 15 | 16 | pub use crc16::{Crc16Ansi, Crc16AnsiLe}; 17 | pub use crc32::Crc32; 18 | pub use crc8::Crc8Ccitt; 19 | pub use md5::Md5; 20 | -------------------------------------------------------------------------------- /symphonia-core/src/dsp/mdct/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! The `mdct` module implements the Modified Discrete Cosine Transform (MDCT). 9 | //! 10 | //! The MDCT in this module is implemented in-terms of a forward FFT. 11 | 12 | #[cfg(any(feature = "opt-simd-sse", feature = "opt-simd-avx", feature = "opt-simd-neon"))] 13 | mod simd; 14 | 15 | #[cfg(any(feature = "opt-simd-sse", feature = "opt-simd-avx", feature = "opt-simd-neon"))] 16 | pub use simd::*; 17 | 18 | #[cfg(not(any(feature = "opt-simd-sse", feature = "opt-simd-avx", feature = "opt-simd-neon")))] 19 | mod no_simd; 20 | #[cfg(not(any( 21 | feature = "opt-simd-sse", 22 | feature = "opt-simd-avx", 23 | feature = "opt-simd-neon" 24 | )))] 25 | pub use no_simd::*; 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | use std::f64; 31 | 32 | fn imdct_analytical(x: &[f32], y: &mut [f32], scale: f64) { 33 | assert!(y.len() == 2 * x.len()); 34 | 35 | // Generates 2N outputs from N inputs. 36 | let n_in = x.len(); 37 | let n_out = x.len() << 1; 38 | 39 | let pi_2n = f64::consts::PI / (2 * n_out) as f64; 40 | 41 | for (i, item) in y.iter_mut().enumerate().take(n_out) { 42 | let accum: f64 = x 43 | .iter() 44 | .copied() 45 | .map(f64::from) 46 | .enumerate() 47 | .take(n_in) 48 | .map(|(j, jtem)| jtem * (pi_2n * ((2 * i + 1 + n_in) * (2 * j + 1)) as f64).cos()) 49 | .sum(); 50 | 51 | *item = (scale * accum) as f32; 52 | } 53 | } 54 | 55 | #[test] 56 | fn verify_imdct() { 57 | #[rustfmt::skip] 58 | const TEST_VECTOR: [f32; 32] = [ 59 | 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 60 | 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 61 | 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 62 | 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 63 | ]; 64 | 65 | let mut actual = [0f32; 64]; 66 | let mut expected = [0f32; 64]; 67 | 68 | let scale = (2.0f64 / 64.0).sqrt(); 69 | 70 | imdct_analytical(&TEST_VECTOR, &mut expected, scale); 71 | 72 | let mut mdct = Imdct::new_scaled(32, scale); 73 | mdct.imdct(&TEST_VECTOR, &mut actual); 74 | 75 | for i in 0..64 { 76 | let delta = f64::from(actual[i]) - f64::from(expected[i]); 77 | assert!(delta.abs() < 0.00001); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /symphonia-core/src/dsp/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! The `dsp` module provides efficient implementations of common signal processing algorithms. 9 | 10 | pub mod complex; 11 | pub mod fft; 12 | pub mod mdct; 13 | -------------------------------------------------------------------------------- /symphonia-core/src/errors.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! The `errors` module defines the common error type. 9 | 10 | use std::error; 11 | use std::fmt; 12 | use std::io; 13 | use std::result; 14 | 15 | /// `SeekErrorKind` is a list of generic reasons why a seek may fail. 16 | #[derive(Debug)] 17 | pub enum SeekErrorKind { 18 | /// The stream is not seekable at all. 19 | Unseekable, 20 | /// The stream can only be seeked forward. 21 | ForwardOnly, 22 | /// The timestamp to seek to is out of range. 23 | OutOfRange, 24 | /// The track ID provided is invalid. 25 | InvalidTrack, 26 | } 27 | 28 | impl SeekErrorKind { 29 | fn as_str(&self) -> &'static str { 30 | match *self { 31 | SeekErrorKind::Unseekable => "stream is not seekable", 32 | SeekErrorKind::ForwardOnly => "stream can only be seeked forward", 33 | SeekErrorKind::OutOfRange => "requested seek timestamp is out-of-range for stream", 34 | SeekErrorKind::InvalidTrack => "invalid track id", 35 | } 36 | } 37 | } 38 | 39 | /// `Error` provides an enumeration of all possible errors reported by Symphonia. 40 | #[derive(Debug)] 41 | pub enum Error { 42 | /// An IO error occured while reading, writing, or seeking the stream. 43 | IoError(std::io::Error), 44 | /// The stream contained malformed data and could not be decoded or demuxed. 45 | DecodeError(&'static str), 46 | /// The stream could not be seeked. 47 | SeekError(SeekErrorKind), 48 | /// An unsupported container or codec feature was encounted. 49 | Unsupported(&'static str), 50 | /// A default or user-defined limit was reached while decoding or demuxing the stream. Limits 51 | /// are used to prevent denial-of-service attacks from malicious streams. 52 | LimitError(&'static str), 53 | /// The demuxer or decoder needs to be reset before continuing. 54 | ResetRequired, 55 | } 56 | 57 | impl fmt::Display for Error { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | match *self { 60 | Error::IoError(ref err) => err.fmt(f), 61 | Error::DecodeError(msg) => { 62 | write!(f, "malformed stream: {}", msg) 63 | } 64 | Error::SeekError(ref kind) => { 65 | write!(f, "seek error: {}", kind.as_str()) 66 | } 67 | Error::Unsupported(feature) => { 68 | write!(f, "unsupported feature: {}", feature) 69 | } 70 | Error::LimitError(constraint) => { 71 | write!(f, "limit reached: {}", constraint) 72 | } 73 | Error::ResetRequired => { 74 | write!(f, "decoder needs to be reset") 75 | } 76 | } 77 | } 78 | } 79 | 80 | impl std::error::Error for Error { 81 | fn cause(&self) -> Option<&dyn error::Error> { 82 | match *self { 83 | Error::IoError(ref err) => Some(err), 84 | Error::DecodeError(_) => None, 85 | Error::SeekError(_) => None, 86 | Error::Unsupported(_) => None, 87 | Error::LimitError(_) => None, 88 | Error::ResetRequired => None, 89 | } 90 | } 91 | } 92 | 93 | impl From for Error { 94 | fn from(err: io::Error) -> Error { 95 | Error::IoError(err) 96 | } 97 | } 98 | 99 | pub type Result = result::Result; 100 | 101 | /// Convenience function to create a decode error. 102 | pub fn decode_error(desc: &'static str) -> Result { 103 | Err(Error::DecodeError(desc)) 104 | } 105 | 106 | /// Convenience function to create a seek error. 107 | pub fn seek_error(kind: SeekErrorKind) -> Result { 108 | Err(Error::SeekError(kind)) 109 | } 110 | 111 | /// Convenience function to create an unsupport feature error. 112 | pub fn unsupported_error(feature: &'static str) -> Result { 113 | Err(Error::Unsupported(feature)) 114 | } 115 | 116 | /// Convenience function to create a limit error. 117 | pub fn limit_error(constraint: &'static str) -> Result { 118 | Err(Error::LimitError(constraint)) 119 | } 120 | 121 | /// Convenience function to create a reset required error. 122 | pub fn reset_error() -> Result { 123 | Err(Error::ResetRequired) 124 | } 125 | 126 | /// Convenience function to create an end-of-stream error. 127 | pub fn end_of_stream_error() -> Result { 128 | Err(Error::IoError(io::Error::new(io::ErrorKind::UnexpectedEof, "end of stream"))) 129 | } 130 | -------------------------------------------------------------------------------- /symphonia-core/src/io/monitor_stream.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use std::io; 9 | 10 | use super::ReadBytes; 11 | 12 | /// A `Monitor` provides a common interface to examine the operations observed be 13 | /// a [`MonitorStream`]. 14 | pub trait Monitor { 15 | fn process_byte(&mut self, byte: u8); 16 | 17 | #[inline(always)] 18 | fn process_double_bytes(&mut self, buf: [u8; 2]) { 19 | self.process_byte(buf[0]); 20 | self.process_byte(buf[1]); 21 | } 22 | 23 | #[inline(always)] 24 | fn process_triple_bytes(&mut self, buf: [u8; 3]) { 25 | self.process_byte(buf[0]); 26 | self.process_byte(buf[1]); 27 | self.process_byte(buf[2]); 28 | } 29 | 30 | #[inline(always)] 31 | fn process_quad_bytes(&mut self, buf: [u8; 4]) { 32 | self.process_byte(buf[0]); 33 | self.process_byte(buf[1]); 34 | self.process_byte(buf[2]); 35 | self.process_byte(buf[3]); 36 | } 37 | 38 | fn process_buf_bytes(&mut self, buf: &[u8]); 39 | } 40 | 41 | /// A `MonitorStream` is a passive stream that observes all operations performed on the inner 42 | /// stream and forwards an immutable reference of the result to a [`Monitor`]. 43 | pub struct MonitorStream { 44 | inner: B, 45 | monitor: M, 46 | } 47 | 48 | impl MonitorStream { 49 | pub fn new(inner: B, monitor: M) -> MonitorStream { 50 | MonitorStream { inner, monitor } 51 | } 52 | 53 | pub fn inner(&self) -> &B { 54 | &self.inner 55 | } 56 | 57 | pub fn inner_mut(&mut self) -> &mut B { 58 | &mut self.inner 59 | } 60 | 61 | pub fn into_inner(self) -> B { 62 | self.inner 63 | } 64 | 65 | pub fn monitor(&self) -> &M { 66 | &self.monitor 67 | } 68 | 69 | pub fn monitor_mut(&mut self) -> &mut M { 70 | &mut self.monitor 71 | } 72 | } 73 | 74 | impl ReadBytes for MonitorStream { 75 | #[inline(always)] 76 | fn read_byte(&mut self) -> io::Result { 77 | let byte = self.inner.read_byte()?; 78 | self.monitor.process_byte(byte); 79 | Ok(byte) 80 | } 81 | 82 | #[inline(always)] 83 | fn read_double_bytes(&mut self) -> io::Result<[u8; 2]> { 84 | let bytes = self.inner.read_double_bytes()?; 85 | self.monitor.process_double_bytes(bytes); 86 | Ok(bytes) 87 | } 88 | 89 | #[inline(always)] 90 | fn read_triple_bytes(&mut self) -> io::Result<[u8; 3]> { 91 | let bytes = self.inner.read_triple_bytes()?; 92 | self.monitor.process_triple_bytes(bytes); 93 | Ok(bytes) 94 | } 95 | 96 | #[inline(always)] 97 | fn read_quad_bytes(&mut self) -> io::Result<[u8; 4]> { 98 | let bytes = self.inner.read_quad_bytes()?; 99 | self.monitor.process_quad_bytes(bytes); 100 | Ok(bytes) 101 | } 102 | 103 | fn read_buf(&mut self, buf: &mut [u8]) -> io::Result { 104 | let len = self.inner.read_buf(buf)?; 105 | self.monitor.process_buf_bytes(&buf[0..len]); 106 | Ok(len) 107 | } 108 | 109 | fn read_buf_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { 110 | self.inner.read_buf_exact(buf)?; 111 | self.monitor.process_buf_bytes(buf); 112 | Ok(()) 113 | } 114 | 115 | fn scan_bytes_aligned<'a>( 116 | &mut self, 117 | pattern: &[u8], 118 | align: usize, 119 | buf: &'a mut [u8], 120 | ) -> io::Result<&'a mut [u8]> { 121 | let result = self.inner.scan_bytes_aligned(pattern, align, buf)?; 122 | self.monitor.process_buf_bytes(result); 123 | Ok(result) 124 | } 125 | 126 | fn ignore_bytes(&mut self, count: u64) -> io::Result<()> { 127 | self.inner.ignore_bytes(count) 128 | } 129 | 130 | #[inline(always)] 131 | fn pos(&self) -> u64 { 132 | self.inner.pos() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /symphonia-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | #![forbid(unsafe_code)] 8 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 9 | // justification. 10 | #![allow(clippy::comparison_chain)] 11 | #![allow(clippy::excessive_precision)] 12 | #![allow(clippy::identity_op)] 13 | #![allow(clippy::manual_range_contains)] 14 | 15 | pub mod audio; 16 | pub mod checksum; 17 | pub mod codecs; 18 | pub mod conv; 19 | pub mod dsp; 20 | pub mod errors; 21 | pub mod formats; 22 | pub mod io; 23 | pub mod meta; 24 | pub mod probe; 25 | pub mod sample; 26 | pub mod units; 27 | pub mod util; 28 | -------------------------------------------------------------------------------- /symphonia-format-caf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-caf" 3 | version = "0.5.4" 4 | description = "Pure Rust CAF demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Ian Hobson ", "Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "media", "demuxer", "caf"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 18 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 19 | -------------------------------------------------------------------------------- /symphonia-format-caf/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Core Audio Format demuxer 2 | 3 | CAF decoder for Project Symphonia. 4 | 5 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 6 | 7 | ## License 8 | 9 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 10 | 11 | ## Contributing 12 | 13 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 14 | -------------------------------------------------------------------------------- /symphonia-format-caf/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2024 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | mod chunks; 18 | mod demuxer; 19 | 20 | pub use demuxer::CafReader; 21 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-isomp4" 3 | version = "0.5.4" 4 | description = "Pure Rust ISO/MP4 demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "media", "demuxer", "mp4", "iso"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | encoding_rs = "0.8.17" 17 | log = "0.4" 18 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 19 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 20 | symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" } -------------------------------------------------------------------------------- /symphonia-format-isomp4/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia ISO/MP4 Demuxer 2 | 3 | [![Docs](https://docs.rs/symphonia-format-isomp4/badge.svg)](https://docs.rs/symphonia-format-isomp4) 4 | 5 | ISO/MP4 demuxer for Project Symphonia. 6 | 7 | **Note:** This crate is part of Project Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/alac.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::codecs::{CodecParameters, CODEC_TYPE_ALAC}; 9 | use symphonia_core::errors::{decode_error, unsupported_error, Result}; 10 | use symphonia_core::io::ReadBytes; 11 | 12 | use crate::atoms::{Atom, AtomHeader}; 13 | 14 | #[derive(Debug)] 15 | pub struct AlacAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | /// ALAC extra data (magic cookie). 19 | extra_data: Box<[u8]>, 20 | } 21 | 22 | impl Atom for AlacAtom { 23 | fn header(&self) -> AtomHeader { 24 | self.header 25 | } 26 | 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | let (version, flags) = AtomHeader::read_extra(reader)?; 29 | 30 | if version != 0 { 31 | return unsupported_error("isomp4 (alac): unsupported alac version"); 32 | } 33 | 34 | if flags != 0 { 35 | return decode_error("isomp4 (alac): flags not zero"); 36 | } 37 | 38 | if header.data_len <= AtomHeader::EXTRA_DATA_SIZE { 39 | return decode_error("isomp4 (alac): invalid alac atom length"); 40 | } 41 | 42 | // The ALAC magic cookie (aka extra data) is either 24 or 48 bytes long. 43 | let magic_len = match header.data_len - AtomHeader::EXTRA_DATA_SIZE { 44 | len @ 24 | len @ 48 => len as usize, 45 | _ => return decode_error("isomp4 (alac): invalid magic cookie length"), 46 | }; 47 | 48 | // Read the magic cookie. 49 | let extra_data = reader.read_boxed_slice_exact(magic_len)?; 50 | 51 | Ok(AlacAtom { header, extra_data }) 52 | } 53 | } 54 | 55 | impl AlacAtom { 56 | pub fn fill_codec_params(&self, codec_params: &mut CodecParameters) { 57 | codec_params.for_codec(CODEC_TYPE_ALAC).with_extra_data(self.extra_data.clone()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/co64.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Chunk offset atom (64-bit version). 14 | #[derive(Debug)] 15 | pub struct Co64Atom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | pub chunk_offsets: Vec, 19 | } 20 | 21 | impl Atom for Co64Atom { 22 | fn header(&self) -> AtomHeader { 23 | self.header 24 | } 25 | 26 | fn read(reader: &mut B, header: AtomHeader) -> Result { 27 | let (_, _) = AtomHeader::read_extra(reader)?; 28 | 29 | let entry_count = reader.read_be_u32()?; 30 | 31 | // TODO: Apply a limit. 32 | let mut chunk_offsets = Vec::with_capacity(entry_count as usize); 33 | 34 | for _ in 0..entry_count { 35 | chunk_offsets.push(reader.read_be_u64()?); 36 | } 37 | 38 | Ok(Co64Atom { header, chunk_offsets }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/ctts.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Composition time atom. 14 | #[derive(Debug)] 15 | pub struct CttsAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | } 19 | 20 | impl Atom for CttsAtom { 21 | fn header(&self) -> AtomHeader { 22 | self.header 23 | } 24 | 25 | fn read(_reader: &mut B, _header: AtomHeader) -> Result { 26 | todo!() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/edts.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, ElstAtom}; 12 | 13 | /// Edits atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct EdtsAtom { 17 | header: AtomHeader, 18 | pub elst: Option, 19 | } 20 | 21 | impl Atom for EdtsAtom { 22 | fn header(&self) -> AtomHeader { 23 | self.header 24 | } 25 | 26 | #[allow(clippy::single_match)] 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | let mut iter = AtomIterator::new(reader, header); 29 | 30 | let mut elst = None; 31 | 32 | while let Some(header) = iter.next()? { 33 | match header.atype { 34 | AtomType::EditList => { 35 | elst = Some(iter.read_atom::()?); 36 | } 37 | _ => (), 38 | } 39 | } 40 | 41 | Ok(EdtsAtom { header, elst }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/elst.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | use symphonia_core::util::bits; 11 | 12 | use crate::atoms::{Atom, AtomHeader}; 13 | 14 | /// Edit list entry. 15 | #[derive(Debug)] 16 | #[allow(dead_code)] 17 | pub struct ElstEntry { 18 | segment_duration: u64, 19 | media_time: i64, 20 | media_rate_int: i16, 21 | media_rate_frac: i16, 22 | } 23 | 24 | /// Edit list atom. 25 | #[derive(Debug)] 26 | #[allow(dead_code)] 27 | pub struct ElstAtom { 28 | header: AtomHeader, 29 | entries: Vec, 30 | } 31 | 32 | impl Atom for ElstAtom { 33 | fn header(&self) -> AtomHeader { 34 | self.header 35 | } 36 | 37 | fn read(reader: &mut B, header: AtomHeader) -> Result { 38 | let (version, _) = AtomHeader::read_extra(reader)?; 39 | 40 | // TODO: Apply a limit. 41 | let entry_count = reader.read_be_u32()?; 42 | 43 | let mut entries = Vec::new(); 44 | 45 | for _ in 0..entry_count { 46 | let (segment_duration, media_time) = match version { 47 | 0 => ( 48 | u64::from(reader.read_be_u32()?), 49 | i64::from(bits::sign_extend_leq32_to_i32(reader.read_be_u32()?, 32)), 50 | ), 51 | 1 => ( 52 | reader.read_be_u64()?, 53 | bits::sign_extend_leq64_to_i64(reader.read_be_u64()?, 64), 54 | ), 55 | _ => return decode_error("isomp4: invalid tkhd version"), 56 | }; 57 | 58 | let media_rate_int = bits::sign_extend_leq16_to_i16(reader.read_be_u16()?, 16); 59 | let media_rate_frac = bits::sign_extend_leq16_to_i16(reader.read_be_u16()?, 16); 60 | 61 | entries.push(ElstEntry { 62 | segment_duration, 63 | media_time, 64 | media_rate_int, 65 | media_rate_frac, 66 | }); 67 | } 68 | 69 | Ok(ElstAtom { header, entries }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/flac.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::codecs::{CodecParameters, VerificationCheck, CODEC_TYPE_FLAC}; 9 | use symphonia_core::errors::{decode_error, unsupported_error, Result}; 10 | use symphonia_core::io::{BufReader, ReadBytes}; 11 | 12 | use symphonia_utils_xiph::flac::metadata::{MetadataBlockHeader, MetadataBlockType, StreamInfo}; 13 | 14 | use crate::atoms::{Atom, AtomHeader}; 15 | 16 | #[derive(Debug)] 17 | pub struct FlacAtom { 18 | /// Atom header. 19 | header: AtomHeader, 20 | /// FLAC stream info block. 21 | stream_info: StreamInfo, 22 | /// FLAC extra data. 23 | extra_data: Box<[u8]>, 24 | } 25 | 26 | impl Atom for FlacAtom { 27 | fn header(&self) -> AtomHeader { 28 | self.header 29 | } 30 | 31 | fn read(reader: &mut B, header: AtomHeader) -> Result { 32 | let (version, flags) = AtomHeader::read_extra(reader)?; 33 | 34 | if version != 0 { 35 | return unsupported_error("isomp4 (flac): unsupported flac version"); 36 | } 37 | 38 | if flags != 0 { 39 | return decode_error("isomp4 (flac): flags not zero"); 40 | } 41 | 42 | // The first block must be the stream information block. 43 | let block_header = MetadataBlockHeader::read(reader)?; 44 | 45 | if block_header.block_type != MetadataBlockType::StreamInfo { 46 | return decode_error("isomp4 (flac): first block is not stream info"); 47 | } 48 | 49 | // Ensure the block length is correct for a stream information block before allocating a 50 | // buffer for it. 51 | if !StreamInfo::is_valid_size(u64::from(block_header.block_len)) { 52 | return decode_error("isomp4 (flac): invalid stream info block length"); 53 | } 54 | 55 | let extra_data = reader.read_boxed_slice_exact(block_header.block_len as usize)?; 56 | let stream_info = StreamInfo::read(&mut BufReader::new(&extra_data))?; 57 | 58 | Ok(FlacAtom { header, stream_info, extra_data }) 59 | } 60 | } 61 | 62 | impl FlacAtom { 63 | pub fn fill_codec_params(&self, codec_params: &mut CodecParameters) { 64 | codec_params 65 | .for_codec(CODEC_TYPE_FLAC) 66 | .with_sample_rate(self.stream_info.sample_rate) 67 | .with_bits_per_sample(self.stream_info.bits_per_sample) 68 | .with_channels(self.stream_info.channels) 69 | .with_packet_data_integrity(true) 70 | .with_extra_data(self.extra_data.clone()); 71 | 72 | if let Some(md5) = self.stream_info.md5 { 73 | codec_params.with_verification_code(VerificationCheck::Md5(md5)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/ftyp.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | use crate::fourcc::FourCc; 13 | 14 | /// File type atom. 15 | #[allow(dead_code)] 16 | #[derive(Debug)] 17 | pub struct FtypAtom { 18 | header: AtomHeader, 19 | pub major: FourCc, 20 | pub minor: [u8; 4], 21 | pub compatible: Vec, 22 | } 23 | 24 | impl Atom for FtypAtom { 25 | fn read(reader: &mut B, header: AtomHeader) -> Result { 26 | // The Ftyp atom must be have a data length that is known, and it must be a multiple of 4 27 | // since it only stores FourCCs. 28 | if header.data_len < 8 || header.data_len & 0x3 != 0 { 29 | return decode_error("isomp4: invalid ftyp data length"); 30 | } 31 | 32 | // Major 33 | let major = FourCc::new(reader.read_quad_bytes()?); 34 | 35 | // Minor 36 | let minor = reader.read_quad_bytes()?; 37 | 38 | // The remainder of the Ftyp atom contains the FourCCs of compatible brands. 39 | let n_brands = (header.data_len - 8) / 4; 40 | 41 | let mut compatible = Vec::new(); 42 | 43 | for _ in 0..n_brands { 44 | let brand = reader.read_quad_bytes()?; 45 | compatible.push(FourCc::new(brand)); 46 | } 47 | 48 | Ok(FtypAtom { header, major, minor, compatible }) 49 | } 50 | 51 | fn header(&self) -> AtomHeader { 52 | self.header 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/hdlr.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::{ 12 | atoms::{Atom, AtomHeader}, 13 | fourcc::FourCc, 14 | }; 15 | 16 | use log::warn; 17 | 18 | /// Handler type. 19 | #[derive(Debug, PartialEq, Eq)] 20 | pub enum HandlerType { 21 | /// Video handler. 22 | Video, 23 | /// Audio handler. 24 | Sound, 25 | /// Subtitle handler. 26 | Subtitle, 27 | /// Metadata handler. 28 | Metadata, 29 | /// Text handler. 30 | Text, 31 | /// Unknown handler type. 32 | Other([u8; 4]), 33 | } 34 | 35 | /// Handler atom. 36 | #[allow(dead_code)] 37 | #[derive(Debug)] 38 | pub struct HdlrAtom { 39 | /// Atom header. 40 | header: AtomHeader, 41 | /// Handler type. 42 | pub handler_type: HandlerType, 43 | /// Human-readable handler name. 44 | pub name: String, 45 | } 46 | 47 | impl Atom for HdlrAtom { 48 | fn header(&self) -> AtomHeader { 49 | self.header 50 | } 51 | 52 | fn read(reader: &mut B, header: AtomHeader) -> Result { 53 | let (_, _) = AtomHeader::read_extra(reader)?; 54 | 55 | // Always 0 for MP4, but for Quicktime this contains the component type. 56 | let _ = reader.read_quad_bytes()?; 57 | 58 | let handler_type = match &reader.read_quad_bytes()? { 59 | b"vide" => HandlerType::Video, 60 | b"soun" => HandlerType::Sound, 61 | b"meta" => HandlerType::Metadata, 62 | b"subt" => HandlerType::Subtitle, 63 | b"text" => HandlerType::Text, 64 | &hdlr => { 65 | warn!("unknown handler type {:?}", FourCc::new(hdlr)); 66 | HandlerType::Other(hdlr) 67 | } 68 | }; 69 | 70 | // These bytes are reserved for MP4, but for QuickTime they contain the component 71 | // manufacturer, flags, and flags mask. 72 | reader.ignore_bytes(4 * 3)?; 73 | 74 | // Human readable UTF-8 string of the track type. 75 | let buf = reader.read_boxed_slice_exact((header.data_len - 24) as usize)?; 76 | let name = String::from_utf8_lossy(&buf).to_string(); 77 | 78 | Ok(HdlrAtom { header, handler_type, name }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mdhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | fn parse_language(code: u16) -> String { 14 | // An ISO language code outside of these bounds is not valid. 15 | if code < 0x400 || code > 0x7fff { 16 | String::new() 17 | } 18 | else { 19 | let chars = [ 20 | ((code >> 10) & 0x1f) as u8 + 0x60, 21 | ((code >> 5) & 0x1f) as u8 + 0x60, 22 | ((code >> 0) & 0x1f) as u8 + 0x60, 23 | ]; 24 | 25 | String::from_utf8_lossy(&chars).to_string() 26 | } 27 | } 28 | 29 | /// Media header atom. 30 | #[derive(Debug)] 31 | pub struct MdhdAtom { 32 | /// Atom header. 33 | header: AtomHeader, 34 | /// Creation time. 35 | pub ctime: u64, 36 | /// Modification time. 37 | pub mtime: u64, 38 | /// Timescale. 39 | pub timescale: u32, 40 | /// Duration of the media in timescale units. 41 | pub duration: u64, 42 | /// Language. 43 | pub language: String, 44 | } 45 | 46 | impl Atom for MdhdAtom { 47 | fn header(&self) -> AtomHeader { 48 | self.header 49 | } 50 | 51 | fn read(reader: &mut B, header: AtomHeader) -> Result { 52 | let (version, _) = AtomHeader::read_extra(reader)?; 53 | 54 | let mut mdhd = MdhdAtom { 55 | header, 56 | ctime: 0, 57 | mtime: 0, 58 | timescale: 0, 59 | duration: 0, 60 | language: String::new(), 61 | }; 62 | 63 | match version { 64 | 0 => { 65 | mdhd.ctime = u64::from(reader.read_be_u32()?); 66 | mdhd.mtime = u64::from(reader.read_be_u32()?); 67 | mdhd.timescale = reader.read_be_u32()?; 68 | // 0xffff_ffff is a special case. 69 | mdhd.duration = match reader.read_be_u32()? { 70 | u32::MAX => u64::MAX, 71 | duration => u64::from(duration), 72 | }; 73 | } 74 | 1 => { 75 | mdhd.ctime = reader.read_be_u64()?; 76 | mdhd.mtime = reader.read_be_u64()?; 77 | mdhd.timescale = reader.read_be_u32()?; 78 | mdhd.duration = reader.read_be_u64()?; 79 | } 80 | _ => { 81 | return decode_error("isomp4: invalid mdhd version"); 82 | } 83 | } 84 | 85 | mdhd.language = parse_language(reader.read_be_u16()?); 86 | 87 | // Quality 88 | let _ = reader.read_be_u16()?; 89 | 90 | Ok(mdhd) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mdia.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, HdlrAtom, MdhdAtom, MinfAtom}; 12 | 13 | #[allow(dead_code)] 14 | #[derive(Debug)] 15 | pub struct MdiaAtom { 16 | header: AtomHeader, 17 | pub mdhd: MdhdAtom, 18 | pub hdlr: HdlrAtom, 19 | pub minf: MinfAtom, 20 | } 21 | 22 | impl Atom for MdiaAtom { 23 | fn header(&self) -> AtomHeader { 24 | self.header 25 | } 26 | 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | let mut iter = AtomIterator::new(reader, header); 29 | 30 | let mut mdhd = None; 31 | let mut hdlr = None; 32 | let mut minf = None; 33 | 34 | while let Some(header) = iter.next()? { 35 | match header.atype { 36 | AtomType::MediaHeader => { 37 | mdhd = Some(iter.read_atom::()?); 38 | } 39 | AtomType::Handler => { 40 | hdlr = Some(iter.read_atom::()?); 41 | } 42 | AtomType::MediaInfo => { 43 | minf = Some(iter.read_atom::()?); 44 | } 45 | _ => (), 46 | } 47 | } 48 | 49 | if mdhd.is_none() { 50 | return decode_error("isomp4: missing mdhd atom"); 51 | } 52 | 53 | if hdlr.is_none() { 54 | return decode_error("isomp4: missing hdlr atom"); 55 | } 56 | 57 | if minf.is_none() { 58 | return decode_error("isomp4: missing minf atom"); 59 | } 60 | 61 | Ok(MdiaAtom { header, mdhd: mdhd.unwrap(), hdlr: hdlr.unwrap(), minf: minf.unwrap() }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mehd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Movie extends header atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct MehdAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | /// Fragment duration. 20 | pub fragment_duration: u64, 21 | } 22 | 23 | impl Atom for MehdAtom { 24 | fn header(&self) -> AtomHeader { 25 | self.header 26 | } 27 | 28 | fn read(reader: &mut B, header: AtomHeader) -> Result { 29 | let (version, _) = AtomHeader::read_extra(reader)?; 30 | 31 | let fragment_duration = match version { 32 | 0 => u64::from(reader.read_be_u32()?), 33 | 1 => reader.read_be_u64()?, 34 | _ => { 35 | return decode_error("isomp4: invalid mehd version"); 36 | } 37 | }; 38 | 39 | Ok(MehdAtom { header, fragment_duration }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/meta.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use std::fmt::Debug; 9 | 10 | use symphonia_core::errors::Result; 11 | use symphonia_core::io::ReadBytes; 12 | use symphonia_core::meta::MetadataRevision; 13 | 14 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, IlstAtom}; 15 | 16 | /// User data atom. 17 | pub struct MetaAtom { 18 | /// Atom header. 19 | header: AtomHeader, 20 | /// Metadata revision. 21 | pub metadata: Option, 22 | } 23 | 24 | impl Debug for MetaAtom { 25 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 26 | write!(f, "(redacted)") 27 | } 28 | } 29 | 30 | impl MetaAtom { 31 | /// If metadata was read, consumes the metadata and returns it. 32 | pub fn take_metadata(&mut self) -> Option { 33 | self.metadata.take() 34 | } 35 | } 36 | 37 | impl Atom for MetaAtom { 38 | fn header(&self) -> AtomHeader { 39 | self.header 40 | } 41 | 42 | #[allow(clippy::single_match)] 43 | fn read(reader: &mut B, mut header: AtomHeader) -> Result { 44 | let (_, _) = AtomHeader::read_extra(reader)?; 45 | 46 | // AtomIterator doesn't know the extra data was read already, so the extra data size must be 47 | // subtrated from the atom's data length. 48 | header.data_len -= AtomHeader::EXTRA_DATA_SIZE; 49 | 50 | let mut iter = AtomIterator::new(reader, header); 51 | 52 | let mut metadata = None; 53 | 54 | while let Some(header) = iter.next()? { 55 | match header.atype { 56 | AtomType::MetaList => { 57 | metadata = Some(iter.read_atom::()?.metadata); 58 | } 59 | _ => (), 60 | } 61 | } 62 | 63 | Ok(MetaAtom { header, metadata }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mfhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Movie fragment header atom. 14 | #[derive(Debug)] 15 | pub struct MfhdAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | /// Sequence number associated with fragment. 19 | pub sequence_number: u32, 20 | } 21 | 22 | impl Atom for MfhdAtom { 23 | fn header(&self) -> AtomHeader { 24 | self.header 25 | } 26 | 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | let (_, _) = AtomHeader::read_extra(reader)?; 29 | 30 | let sequence_number = reader.read_be_u32()?; 31 | 32 | Ok(MfhdAtom { header, sequence_number }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/minf.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, SmhdAtom, StblAtom}; 12 | 13 | /// Media information atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct MinfAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | /// Sound media header atom. 20 | pub smhd: Option, 21 | /// Sample table atom. 22 | pub stbl: StblAtom, 23 | } 24 | 25 | impl Atom for MinfAtom { 26 | fn header(&self) -> AtomHeader { 27 | self.header 28 | } 29 | 30 | fn read(reader: &mut B, header: AtomHeader) -> Result { 31 | let mut iter = AtomIterator::new(reader, header); 32 | 33 | let mut smhd = None; 34 | let mut stbl = None; 35 | 36 | while let Some(header) = iter.next()? { 37 | match header.atype { 38 | AtomType::SoundMediaHeader => { 39 | smhd = Some(iter.read_atom::()?); 40 | } 41 | AtomType::SampleTable => { 42 | stbl = Some(iter.read_atom::()?); 43 | } 44 | _ => (), 45 | } 46 | } 47 | 48 | if stbl.is_none() { 49 | return decode_error("isomp4: missing stbl atom"); 50 | } 51 | 52 | Ok(MinfAtom { header, smhd, stbl: stbl.unwrap() }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/moof.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, MfhdAtom, TrafAtom}; 12 | 13 | /// Movie fragment atom. 14 | #[derive(Debug)] 15 | pub struct MoofAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | /// The position of the first byte of this moof atom. This is used as the anchor point for the 19 | /// subsequent track atoms. 20 | pub moof_base_pos: u64, 21 | /// Movie fragment header. 22 | pub mfhd: MfhdAtom, 23 | /// Track fragments. 24 | pub trafs: Vec, 25 | } 26 | 27 | impl Atom for MoofAtom { 28 | fn header(&self) -> AtomHeader { 29 | self.header 30 | } 31 | 32 | fn read(reader: &mut B, header: AtomHeader) -> Result { 33 | let moof_base_pos = reader.pos() - AtomHeader::HEADER_SIZE; 34 | 35 | let mut mfhd = None; 36 | let mut trafs = Vec::new(); 37 | 38 | let mut iter = AtomIterator::new(reader, header); 39 | 40 | while let Some(header) = iter.next()? { 41 | match header.atype { 42 | AtomType::MovieFragmentHeader => { 43 | mfhd = Some(iter.read_atom::()?); 44 | } 45 | AtomType::TrackFragment => { 46 | let traf = iter.read_atom::()?; 47 | trafs.push(traf); 48 | } 49 | _ => (), 50 | } 51 | } 52 | 53 | if mfhd.is_none() { 54 | return decode_error("isomp4: missing mfhd atom"); 55 | } 56 | 57 | Ok(MoofAtom { header, moof_base_pos, mfhd: mfhd.unwrap(), trafs }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/moov.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | use symphonia_core::meta::MetadataRevision; 11 | 12 | use crate::atoms::{ 13 | Atom, AtomHeader, AtomIterator, AtomType, MvexAtom, MvhdAtom, TrakAtom, UdtaAtom, 14 | }; 15 | 16 | use log::warn; 17 | 18 | /// Movie atom. 19 | #[allow(dead_code)] 20 | #[derive(Debug)] 21 | pub struct MoovAtom { 22 | /// Atom header. 23 | header: AtomHeader, 24 | /// Movie header atom. 25 | pub mvhd: MvhdAtom, 26 | /// Trak atoms. 27 | pub traks: Vec, 28 | /// Movie extends atom. The presence of this atom indicates a fragmented stream. 29 | pub mvex: Option, 30 | /// User data (usually metadata). 31 | pub udta: Option, 32 | } 33 | 34 | impl MoovAtom { 35 | /// If metadata was read, consumes the metadata and returns it. 36 | pub fn take_metadata(&mut self) -> Option { 37 | self.udta.as_mut().and_then(|udta| udta.take_metadata()) 38 | } 39 | 40 | /// Is the movie segmented. 41 | pub fn is_fragmented(&self) -> bool { 42 | self.mvex.is_some() 43 | } 44 | } 45 | 46 | impl Atom for MoovAtom { 47 | fn header(&self) -> AtomHeader { 48 | self.header 49 | } 50 | 51 | fn read(reader: &mut B, header: AtomHeader) -> Result { 52 | let mut iter = AtomIterator::new(reader, header); 53 | 54 | let mut mvhd = None; 55 | let mut traks = Vec::new(); 56 | let mut mvex = None; 57 | let mut udta = None; 58 | 59 | while let Some(header) = iter.next()? { 60 | match header.atype { 61 | AtomType::MovieHeader => { 62 | mvhd = Some(iter.read_atom::()?); 63 | } 64 | AtomType::Track => { 65 | let trak = iter.read_atom::()?; 66 | traks.push(trak); 67 | } 68 | AtomType::MovieExtends => { 69 | mvex = Some(iter.read_atom::()?); 70 | } 71 | AtomType::UserData => { 72 | udta = Some(iter.read_atom::()?); 73 | } 74 | _ => (), 75 | } 76 | } 77 | 78 | if mvhd.is_none() { 79 | return decode_error("isomp4: missing mvhd atom"); 80 | } 81 | 82 | // If fragmented, the mvex atom should contain a trex atom for each trak atom in moov. 83 | if let Some(mvex) = mvex.as_ref() { 84 | // For each trak, find a matching trex atom using the track id. 85 | for trak in traks.iter() { 86 | let found = mvex.trexs.iter().any(|trex| trex.track_id == trak.tkhd.id); 87 | 88 | if !found { 89 | warn!("missing trex atom for trak with id={}", trak.tkhd.id); 90 | } 91 | } 92 | } 93 | 94 | Ok(MoovAtom { header, mvhd: mvhd.unwrap(), traks, mvex, udta }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mvex.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, MehdAtom, TrexAtom}; 12 | 13 | /// Movie extends atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct MvexAtom { 17 | /// Atom header. 18 | pub header: AtomHeader, 19 | /// Movie extends header, optional. 20 | pub mehd: Option, 21 | /// Track extends box, one per track. 22 | pub trexs: Vec, 23 | } 24 | 25 | impl Atom for MvexAtom { 26 | fn header(&self) -> AtomHeader { 27 | self.header 28 | } 29 | 30 | fn read(reader: &mut B, header: AtomHeader) -> Result { 31 | let mut iter = AtomIterator::new(reader, header); 32 | 33 | let mut mehd = None; 34 | let mut trexs = Vec::new(); 35 | 36 | while let Some(header) = iter.next()? { 37 | match header.atype { 38 | AtomType::MovieExtendsHeader => { 39 | mehd = Some(iter.read_atom::()?); 40 | } 41 | AtomType::TrackExtends => { 42 | let trex = iter.read_atom::()?; 43 | trexs.push(trex); 44 | } 45 | _ => (), 46 | } 47 | } 48 | 49 | Ok(MvexAtom { header, mehd, trexs }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/mvhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | use crate::fp::FpU8; 13 | 14 | /// Movie header atom. 15 | #[derive(Debug)] 16 | pub struct MvhdAtom { 17 | /// Atom header. 18 | pub header: AtomHeader, 19 | /// The creation time. 20 | pub ctime: u64, 21 | /// The modification time. 22 | pub mtime: u64, 23 | /// Timescale for the movie expressed as the number of units per second. 24 | pub timescale: u32, 25 | /// The duration of the movie in `timescale` units. 26 | pub duration: u64, 27 | /// The preferred volume to play the movie. 28 | pub volume: FpU8, 29 | } 30 | 31 | impl Atom for MvhdAtom { 32 | fn header(&self) -> AtomHeader { 33 | self.header 34 | } 35 | 36 | fn read(reader: &mut B, header: AtomHeader) -> Result { 37 | let (version, _) = AtomHeader::read_extra(reader)?; 38 | 39 | let mut mvhd = MvhdAtom { 40 | header, 41 | ctime: 0, 42 | mtime: 0, 43 | timescale: 0, 44 | duration: 0, 45 | volume: Default::default(), 46 | }; 47 | 48 | // Version 0 uses 32-bit time values, verion 1 used 64-bit values. 49 | match version { 50 | 0 => { 51 | mvhd.ctime = u64::from(reader.read_be_u32()?); 52 | mvhd.mtime = u64::from(reader.read_be_u32()?); 53 | mvhd.timescale = reader.read_be_u32()?; 54 | // 0xffff_ffff is a special case. 55 | mvhd.duration = match reader.read_be_u32()? { 56 | u32::MAX => u64::MAX, 57 | duration => u64::from(duration), 58 | }; 59 | } 60 | 1 => { 61 | mvhd.ctime = reader.read_be_u64()?; 62 | mvhd.mtime = reader.read_be_u64()?; 63 | mvhd.timescale = reader.read_be_u32()?; 64 | mvhd.duration = reader.read_be_u64()?; 65 | } 66 | _ => return decode_error("isomp4: invalid mvhd version"), 67 | } 68 | 69 | // Ignore the preferred playback rate. 70 | let _ = reader.read_be_u32()?; 71 | 72 | // Preferred volume. 73 | mvhd.volume = FpU8::parse_raw(reader.read_be_u16()?); 74 | 75 | // Remaining fields are ignored. 76 | 77 | Ok(mvhd) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/opus.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::codecs::{CodecParameters, CODEC_TYPE_OPUS}; 9 | use symphonia_core::errors::{decode_error, unsupported_error, Result}; 10 | use symphonia_core::io::ReadBytes; 11 | 12 | use crate::atoms::{Atom, AtomHeader}; 13 | 14 | #[derive(Debug)] 15 | pub struct OpusAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | /// Opus extra data (identification header). 19 | extra_data: Box<[u8]>, 20 | } 21 | 22 | impl Atom for OpusAtom { 23 | fn header(&self) -> AtomHeader { 24 | self.header 25 | } 26 | 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | const OPUS_MAGIC: &[u8] = b"OpusHead"; 29 | const OPUS_MAGIC_LEN: usize = OPUS_MAGIC.len(); 30 | 31 | const MIN_OPUS_EXTRA_DATA_SIZE: usize = OPUS_MAGIC_LEN + 11; 32 | const MAX_OPUS_EXTRA_DATA_SIZE: usize = MIN_OPUS_EXTRA_DATA_SIZE + 257; 33 | 34 | // Offset of the Opus version number in the extra data. 35 | const OPUS_EXTRADATA_VERSION_OFFSET: usize = OPUS_MAGIC_LEN; 36 | 37 | // The dops atom contains an Opus identification header excluding the OpusHead magic 38 | // signature. Therefore, the atom data length should be atleast as long as the shortest 39 | // Opus identification header. 40 | let data_len = header.data_len as usize; 41 | 42 | if data_len < MIN_OPUS_EXTRA_DATA_SIZE - OPUS_MAGIC_LEN { 43 | return decode_error("isomp4 (opus): opus identification header too short"); 44 | } 45 | 46 | if data_len > MAX_OPUS_EXTRA_DATA_SIZE - OPUS_MAGIC_LEN { 47 | return decode_error("isomp4 (opus): opus identification header too large"); 48 | } 49 | 50 | let mut extra_data = vec![0; OPUS_MAGIC_LEN + data_len].into_boxed_slice(); 51 | 52 | // The Opus magic is excluded in the atom, but the extra data must start with it. 53 | extra_data[..OPUS_MAGIC_LEN].copy_from_slice(OPUS_MAGIC); 54 | 55 | // Read the extra data from the atom. 56 | reader.read_buf_exact(&mut extra_data[OPUS_MAGIC_LEN..])?; 57 | 58 | // Verify the version number is 0. 59 | if extra_data[OPUS_EXTRADATA_VERSION_OFFSET] != 0 { 60 | return unsupported_error("isomp4 (opus): unsupported opus version"); 61 | } 62 | 63 | Ok(OpusAtom { header, extra_data }) 64 | } 65 | } 66 | 67 | impl OpusAtom { 68 | pub fn fill_codec_params(&self, codec_params: &mut CodecParameters) { 69 | codec_params.for_codec(CODEC_TYPE_OPUS).with_extra_data(self.extra_data.clone()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/sidx.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | #[derive(Debug)] 14 | pub enum ReferenceType { 15 | Segment, 16 | Media, 17 | } 18 | 19 | #[allow(dead_code)] 20 | #[derive(Debug)] 21 | pub struct SidxReference { 22 | pub reference_type: ReferenceType, 23 | pub reference_size: u32, 24 | pub subsegment_duration: u32, 25 | // pub starts_with_sap: bool, 26 | // pub sap_type: u8, 27 | // pub sap_delta_time: u32, 28 | } 29 | 30 | /// Segment index atom. 31 | #[allow(dead_code)] 32 | #[derive(Debug)] 33 | pub struct SidxAtom { 34 | /// Atom header. 35 | header: AtomHeader, 36 | pub reference_id: u32, 37 | pub timescale: u32, 38 | pub earliest_pts: u64, 39 | pub first_offset: u64, 40 | pub references: Vec, 41 | } 42 | 43 | impl Atom for SidxAtom { 44 | fn header(&self) -> AtomHeader { 45 | self.header 46 | } 47 | 48 | fn read(reader: &mut B, header: AtomHeader) -> Result { 49 | // The anchor point for segment offsets is the first byte after this atom. 50 | let anchor = reader.pos() + header.data_len; 51 | 52 | let (version, _) = AtomHeader::read_extra(reader)?; 53 | 54 | let reference_id = reader.read_be_u32()?; 55 | let timescale = reader.read_be_u32()?; 56 | 57 | let (earliest_pts, first_offset) = match version { 58 | 0 => (u64::from(reader.read_be_u32()?), anchor + u64::from(reader.read_be_u32()?)), 59 | 1 => (reader.read_be_u64()?, anchor + reader.read_be_u64()?), 60 | _ => { 61 | return decode_error("isomp4: invalid sidx version"); 62 | } 63 | }; 64 | 65 | let _reserved = reader.read_be_u16()?; 66 | let reference_count = reader.read_be_u16()?; 67 | 68 | let mut references = Vec::new(); 69 | 70 | for _ in 0..reference_count { 71 | let reference = reader.read_be_u32()?; 72 | let subsegment_duration = reader.read_be_u32()?; 73 | 74 | let reference_type = match (reference & 0x8000_0000) != 0 { 75 | false => ReferenceType::Media, 76 | true => ReferenceType::Segment, 77 | }; 78 | 79 | let reference_size = reference & !0x8000_0000; 80 | 81 | // Ignore SAP 82 | let _ = reader.read_be_u32()?; 83 | 84 | references.push(SidxReference { reference_type, reference_size, subsegment_duration }); 85 | } 86 | 87 | Ok(SidxAtom { header, reference_id, timescale, earliest_pts, first_offset, references }) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/smhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | use crate::fp::FpI8; 13 | 14 | /// Sound header atom. 15 | #[allow(dead_code)] 16 | #[derive(Debug)] 17 | pub struct SmhdAtom { 18 | /// Atom header. 19 | header: AtomHeader, 20 | /// Stereo balance. 21 | pub balance: FpI8, 22 | } 23 | 24 | impl Atom for SmhdAtom { 25 | fn header(&self) -> AtomHeader { 26 | self.header 27 | } 28 | 29 | fn read(reader: &mut B, header: AtomHeader) -> Result { 30 | let (_, _) = AtomHeader::read_extra(reader)?; 31 | 32 | // Stereo balance 33 | let balance = FpI8::parse_raw(reader.read_be_u16()? as i16); 34 | 35 | // Reserved. 36 | let _ = reader.read_be_u16()?; 37 | 38 | Ok(SmhdAtom { header, balance }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stbl.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType}; 12 | use crate::atoms::{Co64Atom, StcoAtom, StscAtom, StsdAtom, StszAtom, SttsAtom}; 13 | 14 | use log::warn; 15 | 16 | /// Sample table atom. 17 | #[derive(Debug)] 18 | pub struct StblAtom { 19 | /// Atom header. 20 | header: AtomHeader, 21 | pub stsd: StsdAtom, 22 | pub stts: SttsAtom, 23 | pub stsc: StscAtom, 24 | pub stsz: StszAtom, 25 | pub stco: Option, 26 | pub co64: Option, 27 | } 28 | 29 | impl Atom for StblAtom { 30 | fn header(&self) -> AtomHeader { 31 | self.header 32 | } 33 | 34 | fn read(reader: &mut B, header: AtomHeader) -> Result { 35 | let mut iter = AtomIterator::new(reader, header); 36 | 37 | let mut stsd = None; 38 | let mut stts = None; 39 | let mut stsc = None; 40 | let mut stsz = None; 41 | let mut stco = None; 42 | let mut co64 = None; 43 | 44 | while let Some(header) = iter.next()? { 45 | match header.atype { 46 | AtomType::SampleDescription => { 47 | stsd = Some(iter.read_atom::()?); 48 | } 49 | AtomType::TimeToSample => { 50 | stts = Some(iter.read_atom::()?); 51 | } 52 | AtomType::CompositionTimeToSample => { 53 | // Composition time to sample atom is only required for video. 54 | warn!("ignoring ctts atom."); 55 | } 56 | AtomType::SyncSample => { 57 | // Sync sample atom is only required for video. 58 | warn!("ignoring stss atom."); 59 | } 60 | AtomType::SampleToChunk => { 61 | stsc = Some(iter.read_atom::()?); 62 | } 63 | AtomType::SampleSize => { 64 | stsz = Some(iter.read_atom::()?); 65 | } 66 | AtomType::ChunkOffset => { 67 | stco = Some(iter.read_atom::()?); 68 | } 69 | AtomType::ChunkOffset64 => { 70 | co64 = Some(iter.read_atom::()?); 71 | } 72 | _ => (), 73 | } 74 | } 75 | 76 | if stsd.is_none() { 77 | return decode_error("isomp4: missing stsd atom"); 78 | } 79 | 80 | if stts.is_none() { 81 | return decode_error("isomp4: missing stts atom"); 82 | } 83 | 84 | if stsc.is_none() { 85 | return decode_error("isomp4: missing stsc atom"); 86 | } 87 | 88 | if stsz.is_none() { 89 | return decode_error("isomp4: missing stsz atom"); 90 | } 91 | 92 | if stco.is_none() && co64.is_none() { 93 | // This is a spec. violation, but some m4a files appear to lack these atoms. 94 | warn!("missing stco or co64 atom"); 95 | } 96 | 97 | Ok(StblAtom { 98 | header, 99 | stsd: stsd.unwrap(), 100 | stts: stts.unwrap(), 101 | stsc: stsc.unwrap(), 102 | stsz: stsz.unwrap(), 103 | stco, 104 | co64, 105 | }) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stco.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Chunk offset atom (32-bit version). 14 | #[derive(Debug)] 15 | pub struct StcoAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | pub chunk_offsets: Vec, 19 | } 20 | 21 | impl Atom for StcoAtom { 22 | fn header(&self) -> AtomHeader { 23 | self.header 24 | } 25 | 26 | fn read(reader: &mut B, header: AtomHeader) -> Result { 27 | let (_, _) = AtomHeader::read_extra(reader)?; 28 | 29 | let entry_count = reader.read_be_u32()?; 30 | 31 | // TODO: Apply a limit. 32 | let mut chunk_offsets = Vec::with_capacity(entry_count as usize); 33 | 34 | for _ in 0..entry_count { 35 | chunk_offsets.push(reader.read_be_u32()?); 36 | } 37 | 38 | Ok(StcoAtom { header, chunk_offsets }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stsc.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | #[allow(dead_code)] 14 | #[derive(Debug)] 15 | pub struct StscEntry { 16 | pub first_chunk: u32, 17 | pub first_sample: u32, 18 | pub samples_per_chunk: u32, 19 | pub sample_desc_index: u32, 20 | } 21 | 22 | /// Sample to Chunk Atom 23 | #[derive(Debug)] 24 | pub struct StscAtom { 25 | /// Atom header. 26 | header: AtomHeader, 27 | /// Entries. 28 | pub entries: Vec, 29 | } 30 | 31 | impl StscAtom { 32 | /// Finds the `StscEntry` for the sample indicated by `sample_num`. Note, `sample_num` is indexed 33 | /// relative to the `StscAtom`. Complexity is O(log2 N). 34 | pub fn find_entry_for_sample(&self, sample_num: u32) -> Option<&StscEntry> { 35 | let mut left = 1; 36 | let mut right = self.entries.len(); 37 | 38 | while left < right { 39 | let mid = left + (right - left) / 2; 40 | 41 | let entry = self.entries.get(mid).unwrap(); 42 | 43 | if entry.first_sample < sample_num { 44 | left = mid + 1; 45 | } 46 | else { 47 | right = mid; 48 | } 49 | } 50 | 51 | // The index found above (left) is the exclusive upper bound of all entries where 52 | // first_sample < sample_num. Therefore, the entry to return has an index of left-1. The 53 | // index will never equal 0 so this is safe. If the table were empty, left == 1, thus calling 54 | // get with an index of 0, and safely returning None. 55 | self.entries.get(left - 1) 56 | } 57 | } 58 | 59 | impl Atom for StscAtom { 60 | fn header(&self) -> AtomHeader { 61 | self.header 62 | } 63 | 64 | fn read(reader: &mut B, header: AtomHeader) -> Result { 65 | let (_, _) = AtomHeader::read_extra(reader)?; 66 | 67 | let entry_count = reader.read_be_u32()?; 68 | 69 | // TODO: Apply a limit. 70 | let mut entries = Vec::with_capacity(entry_count as usize); 71 | 72 | for _ in 0..entry_count { 73 | entries.push(StscEntry { 74 | first_chunk: reader.read_be_u32()? - 1, 75 | first_sample: 0, 76 | samples_per_chunk: reader.read_be_u32()?, 77 | sample_desc_index: reader.read_be_u32()?, 78 | }); 79 | } 80 | 81 | // Post-process entries to check for errors and calculate the file sample. 82 | if entry_count > 0 { 83 | for i in 0..entry_count as usize - 1 { 84 | // Validate that first_chunk is monotonic across all entries. 85 | if entries[i + 1].first_chunk < entries[i].first_chunk { 86 | return decode_error("isomp4: stsc entry first chunk not monotonic"); 87 | } 88 | 89 | // Validate that samples per chunk is > 0. Could the entry be ignored? 90 | if entries[i].samples_per_chunk == 0 { 91 | return decode_error("isomp4: stsc entry has 0 samples per chunk"); 92 | } 93 | 94 | let n = entries[i + 1].first_chunk - entries[i].first_chunk; 95 | 96 | entries[i + 1].first_sample = 97 | entries[i].first_sample + (n * entries[i].samples_per_chunk); 98 | } 99 | 100 | // Validate that samples per chunk is > 0. Could the entry be ignored? 101 | if entries[entry_count as usize - 1].samples_per_chunk == 0 { 102 | return decode_error("isomp4: stsc entry has 0 samples per chunk"); 103 | } 104 | } 105 | 106 | Ok(StscAtom { header, entries }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stss.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | #[derive(Debug)] 14 | pub struct StssAtom { 15 | /// Atom header. 16 | header: AtomHeader, 17 | } 18 | 19 | impl Atom for StssAtom { 20 | fn header(&self) -> AtomHeader { 21 | self.header 22 | } 23 | 24 | fn read(_reader: &mut B, _header: AtomHeader) -> Result { 25 | todo!() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stsz.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | #[derive(Debug)] 14 | pub enum SampleSize { 15 | Constant(u32), 16 | Variable(Vec), 17 | } 18 | 19 | /// Sample Size Atom 20 | #[derive(Debug)] 21 | pub struct StszAtom { 22 | /// Atom header. 23 | header: AtomHeader, 24 | /// The total number of samples. 25 | pub sample_count: u32, 26 | /// A vector of `sample_count` sample sizes, or a constant size for all samples. 27 | pub sample_sizes: SampleSize, 28 | } 29 | 30 | impl Atom for StszAtom { 31 | fn header(&self) -> AtomHeader { 32 | self.header 33 | } 34 | 35 | fn read(reader: &mut B, header: AtomHeader) -> Result { 36 | let (_, _) = AtomHeader::read_extra(reader)?; 37 | 38 | let sample_size = reader.read_be_u32()?; 39 | let sample_count = reader.read_be_u32()?; 40 | 41 | let sample_sizes = if sample_size == 0 { 42 | // TODO: Apply a limit. 43 | let mut entries = Vec::with_capacity(sample_count as usize); 44 | 45 | for _ in 0..sample_count { 46 | entries.push(reader.read_be_u32()?); 47 | } 48 | 49 | SampleSize::Variable(entries) 50 | } 51 | else { 52 | SampleSize::Constant(sample_size) 53 | }; 54 | 55 | Ok(StszAtom { header, sample_count, sample_sizes }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/stts.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | #[derive(Debug)] 14 | pub struct SampleDurationEntry { 15 | pub sample_count: u32, 16 | pub sample_delta: u32, 17 | } 18 | 19 | /// Time-to-sample atom. 20 | #[derive(Debug)] 21 | pub struct SttsAtom { 22 | /// Atom header. 23 | header: AtomHeader, 24 | pub entries: Vec, 25 | pub total_duration: u64, 26 | } 27 | 28 | impl SttsAtom { 29 | /// Get the timestamp and duration for the sample indicated by `sample_num`. Note, `sample_num` 30 | /// is indexed relative to the `SttsAtom`. Complexity of this function in O(N). 31 | pub fn find_timing_for_sample(&self, sample_num: u32) -> Option<(u64, u32)> { 32 | let mut ts = 0; 33 | let mut next_entry_first_sample = 0; 34 | 35 | // The Stts atom compactly encodes a mapping between number of samples and sample duration. 36 | // Iterate through each entry until the entry containing the next sample is found. The next 37 | // packet timestamp is then the sum of the product of sample count and sample duration for 38 | // the n-1 iterated entries, plus the product of the number of consumed samples in the n-th 39 | // iterated entry and sample duration. 40 | for entry in &self.entries { 41 | next_entry_first_sample += entry.sample_count; 42 | 43 | if sample_num < next_entry_first_sample { 44 | let entry_sample_offset = sample_num + entry.sample_count - next_entry_first_sample; 45 | ts += u64::from(entry.sample_delta) * u64::from(entry_sample_offset); 46 | 47 | return Some((ts, entry.sample_delta)); 48 | } 49 | 50 | ts += u64::from(entry.sample_count) * u64::from(entry.sample_delta); 51 | } 52 | 53 | None 54 | } 55 | 56 | /// Get the sample that contains the timestamp indicated by `ts`. Note, the returned `sample_num` 57 | /// is indexed relative to the `SttsAtom`. Complexity of this function in O(N). 58 | pub fn find_sample_for_timestamp(&self, ts: u64) -> Option { 59 | let mut ts_accum = 0; 60 | let mut sample_num = 0; 61 | 62 | for entry in &self.entries { 63 | let delta = u64::from(entry.sample_delta) * u64::from(entry.sample_count); 64 | 65 | if ts_accum + delta > ts { 66 | sample_num += ((ts - ts_accum) / u64::from(entry.sample_delta)) as u32; 67 | return Some(sample_num); 68 | } 69 | 70 | ts_accum += delta; 71 | sample_num += entry.sample_count; 72 | } 73 | 74 | None 75 | } 76 | } 77 | 78 | impl Atom for SttsAtom { 79 | fn header(&self) -> AtomHeader { 80 | self.header 81 | } 82 | 83 | fn read(reader: &mut B, header: AtomHeader) -> Result { 84 | let (_, _) = AtomHeader::read_extra(reader)?; 85 | 86 | let entry_count = reader.read_be_u32()?; 87 | 88 | let mut total_duration = 0; 89 | 90 | // TODO: Limit table length. 91 | let mut entries = Vec::with_capacity(entry_count as usize); 92 | 93 | for _ in 0..entry_count { 94 | let sample_count = reader.read_be_u32()?; 95 | let sample_delta = reader.read_be_u32()?; 96 | 97 | total_duration += u64::from(sample_count) * u64::from(sample_delta); 98 | 99 | entries.push(SampleDurationEntry { sample_count, sample_delta }); 100 | } 101 | 102 | Ok(SttsAtom { header, entries, total_duration }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/tfhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Track fragment header atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct TfhdAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | pub track_id: u32, 20 | pub base_data_offset: Option, 21 | pub sample_desc_idx: Option, 22 | pub default_sample_duration: Option, 23 | pub default_sample_size: Option, 24 | pub default_sample_flags: Option, 25 | /// If true, there are no samples for this time duration. 26 | pub duration_is_empty: bool, 27 | /// If true, the base data offset for this track is the first byte of the parent containing moof 28 | /// atom. 29 | pub default_base_is_moof: bool, 30 | } 31 | 32 | impl Atom for TfhdAtom { 33 | fn header(&self) -> AtomHeader { 34 | self.header 35 | } 36 | 37 | fn read(reader: &mut B, header: AtomHeader) -> Result { 38 | let (_, flags) = AtomHeader::read_extra(reader)?; 39 | 40 | let track_id = reader.read_be_u32()?; 41 | 42 | let base_data_offset = match flags & 0x1 { 43 | 0 => None, 44 | _ => Some(reader.read_be_u64()?), 45 | }; 46 | 47 | let sample_desc_idx = match flags & 0x2 { 48 | 0 => None, 49 | _ => Some(reader.read_be_u32()?), 50 | }; 51 | 52 | let default_sample_duration = match flags & 0x8 { 53 | 0 => None, 54 | _ => Some(reader.read_be_u32()?), 55 | }; 56 | 57 | let default_sample_size = match flags & 0x10 { 58 | 0 => None, 59 | _ => Some(reader.read_be_u32()?), 60 | }; 61 | 62 | let default_sample_flags = match flags & 0x20 { 63 | 0 => None, 64 | _ => Some(reader.read_be_u32()?), 65 | }; 66 | 67 | let duration_is_empty = (flags & 0x1_0000) != 0; 68 | 69 | // The default-base-is-moof flag is ignored if the base-data-offset flag is set. 70 | let default_base_is_moof = (flags & 0x1 == 0) && (flags & 0x2_0000 != 0); 71 | 72 | Ok(TfhdAtom { 73 | header, 74 | track_id, 75 | base_data_offset, 76 | sample_desc_idx, 77 | default_sample_duration, 78 | default_sample_size, 79 | default_sample_flags, 80 | duration_is_empty, 81 | default_base_is_moof, 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/tkhd.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | use crate::fp::FpU8; 13 | 14 | /// Track header atom. 15 | #[allow(dead_code)] 16 | #[derive(Debug)] 17 | pub struct TkhdAtom { 18 | /// Atom header. 19 | header: AtomHeader, 20 | /// Track header flags. 21 | pub flags: u32, 22 | /// Creation time. 23 | pub ctime: u64, 24 | /// Modification time. 25 | pub mtime: u64, 26 | /// Track identifier. 27 | pub id: u32, 28 | /// Track duration in the timescale units specified in the movie header. This value is equal to 29 | /// the sum of the durations of all the track's edits. 30 | pub duration: u64, 31 | /// Layer. 32 | pub layer: u16, 33 | /// Grouping identifier. 34 | pub alternate_group: u16, 35 | /// Preferred volume for track playback. 36 | pub volume: FpU8, 37 | } 38 | 39 | impl Atom for TkhdAtom { 40 | fn header(&self) -> AtomHeader { 41 | self.header 42 | } 43 | 44 | fn read(reader: &mut B, header: AtomHeader) -> Result { 45 | let (version, flags) = AtomHeader::read_extra(reader)?; 46 | 47 | let mut tkhd = TkhdAtom { 48 | header, 49 | flags, 50 | ctime: 0, 51 | mtime: 0, 52 | id: 0, 53 | duration: 0, 54 | layer: 0, 55 | alternate_group: 0, 56 | volume: Default::default(), 57 | }; 58 | 59 | // Version 0 uses 32-bit time values, verion 1 used 64-bit values. 60 | match version { 61 | 0 => { 62 | tkhd.ctime = u64::from(reader.read_be_u32()?); 63 | tkhd.mtime = u64::from(reader.read_be_u32()?); 64 | tkhd.id = reader.read_be_u32()?; 65 | let _ = reader.read_be_u32()?; // Reserved 66 | tkhd.duration = u64::from(reader.read_be_u32()?); 67 | } 68 | 1 => { 69 | tkhd.ctime = reader.read_be_u64()?; 70 | tkhd.mtime = reader.read_be_u64()?; 71 | tkhd.id = reader.read_be_u32()?; 72 | let _ = reader.read_be_u32()?; // Reserved 73 | tkhd.duration = reader.read_be_u64()?; 74 | } 75 | _ => return decode_error("isomp4: invalid tkhd version"), 76 | } 77 | 78 | // Reserved 79 | let _ = reader.read_be_u64()?; 80 | 81 | tkhd.layer = reader.read_be_u16()?; 82 | tkhd.alternate_group = reader.read_be_u16()?; 83 | tkhd.volume = FpU8::parse_raw(reader.read_be_u16()?); 84 | 85 | // The remainder of the header is only useful for video tracks. 86 | 87 | Ok(tkhd) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/traf.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, TfhdAtom, TrunAtom}; 12 | 13 | /// Track fragment atom. 14 | #[derive(Debug)] 15 | pub struct TrafAtom { 16 | /// Atom header. 17 | header: AtomHeader, 18 | /// Track fragment header. 19 | pub tfhd: TfhdAtom, 20 | /// Track fragment sample runs. 21 | pub truns: Vec, 22 | /// The total number of samples in this track fragment. 23 | pub total_sample_count: u32, 24 | } 25 | 26 | impl Atom for TrafAtom { 27 | fn header(&self) -> AtomHeader { 28 | self.header 29 | } 30 | 31 | fn read(reader: &mut B, header: AtomHeader) -> Result { 32 | let mut tfhd = None; 33 | let mut truns = Vec::new(); 34 | 35 | let mut iter = AtomIterator::new(reader, header); 36 | 37 | let mut total_sample_count = 0; 38 | 39 | while let Some(header) = iter.next()? { 40 | match header.atype { 41 | AtomType::TrackFragmentHeader => { 42 | tfhd = Some(iter.read_atom::()?); 43 | } 44 | AtomType::TrackFragmentRun => { 45 | let trun = iter.read_atom::()?; 46 | 47 | // Increment the total sample count. 48 | total_sample_count += trun.sample_count; 49 | 50 | truns.push(trun); 51 | } 52 | _ => (), 53 | } 54 | } 55 | 56 | // Tfhd is mandatory. 57 | if tfhd.is_none() { 58 | return decode_error("isomp4: missing tfhd atom"); 59 | } 60 | 61 | Ok(TrafAtom { header, tfhd: tfhd.unwrap(), truns, total_sample_count }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/trak.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::{decode_error, Result}; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, EdtsAtom, MdiaAtom, TkhdAtom}; 12 | 13 | /// Track atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct TrakAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | /// Track header atom. 20 | pub tkhd: TkhdAtom, 21 | /// Optional, edit list atom. 22 | pub edts: Option, 23 | /// Media atom. 24 | pub mdia: MdiaAtom, 25 | } 26 | 27 | impl Atom for TrakAtom { 28 | fn header(&self) -> AtomHeader { 29 | self.header 30 | } 31 | 32 | fn read(reader: &mut B, header: AtomHeader) -> Result { 33 | let mut iter = AtomIterator::new(reader, header); 34 | 35 | let mut tkhd = None; 36 | let mut edts = None; 37 | let mut mdia = None; 38 | 39 | while let Some(header) = iter.next()? { 40 | match header.atype { 41 | AtomType::TrackHeader => { 42 | tkhd = Some(iter.read_atom::()?); 43 | } 44 | AtomType::Edit => { 45 | edts = Some(iter.read_atom::()?); 46 | } 47 | AtomType::Media => { 48 | mdia = Some(iter.read_atom::()?); 49 | } 50 | _ => (), 51 | } 52 | } 53 | 54 | if tkhd.is_none() { 55 | return decode_error("isomp4: missing tkhd atom"); 56 | } 57 | 58 | if mdia.is_none() { 59 | return decode_error("isomp4: missing mdia atom"); 60 | } 61 | 62 | Ok(TrakAtom { header, tkhd: tkhd.unwrap(), edts, mdia: mdia.unwrap() }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/trex.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader}; 12 | 13 | /// Track extends atom. 14 | #[allow(dead_code)] 15 | #[derive(Debug)] 16 | pub struct TrexAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | /// Track this atom describes. 20 | pub track_id: u32, 21 | /// Default sample description index. 22 | pub default_sample_desc_idx: u32, 23 | /// Default sample duration. 24 | pub default_sample_duration: u32, 25 | /// Default sample size. 26 | pub default_sample_size: u32, 27 | /// Default sample flags. 28 | pub default_sample_flags: u32, 29 | } 30 | 31 | impl Atom for TrexAtom { 32 | fn header(&self) -> AtomHeader { 33 | self.header 34 | } 35 | 36 | fn read(reader: &mut B, header: AtomHeader) -> Result { 37 | let (_, _) = AtomHeader::read_extra(reader)?; 38 | 39 | Ok(TrexAtom { 40 | header, 41 | track_id: reader.read_be_u32()?, 42 | default_sample_desc_idx: reader.read_be_u32()?, 43 | default_sample_duration: reader.read_be_u32()?, 44 | default_sample_size: reader.read_be_u32()?, 45 | default_sample_flags: reader.read_be_u32()?, 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/udta.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | use symphonia_core::meta::MetadataRevision; 11 | 12 | use crate::atoms::{Atom, AtomHeader, AtomIterator, AtomType, MetaAtom}; 13 | 14 | /// User data atom. 15 | #[derive(Debug)] 16 | pub struct UdtaAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | /// Metadata atom. 20 | pub meta: Option, 21 | } 22 | 23 | impl UdtaAtom { 24 | /// If metadata was read, consumes the metadata and returns it. 25 | pub fn take_metadata(&mut self) -> Option { 26 | self.meta.as_mut().and_then(|meta| meta.take_metadata()) 27 | } 28 | } 29 | 30 | impl Atom for UdtaAtom { 31 | fn header(&self) -> AtomHeader { 32 | self.header 33 | } 34 | 35 | #[allow(clippy::single_match)] 36 | fn read(reader: &mut B, header: AtomHeader) -> Result { 37 | let mut iter = AtomIterator::new(reader, header); 38 | 39 | let mut meta = None; 40 | 41 | while let Some(header) = iter.next()? { 42 | match header.atype { 43 | AtomType::Meta => { 44 | meta = Some(iter.read_atom::()?); 45 | } 46 | _ => (), 47 | } 48 | } 49 | 50 | Ok(UdtaAtom { header, meta }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/atoms/wave.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::errors::Result; 9 | use symphonia_core::io::ReadBytes; 10 | 11 | use crate::atoms::{Atom, AtomHeader, EsdsAtom}; 12 | 13 | use super::{AtomIterator, AtomType}; 14 | 15 | #[derive(Debug)] 16 | pub struct WaveAtom { 17 | /// Atom header. 18 | header: AtomHeader, 19 | pub esds: Option, 20 | } 21 | 22 | impl Atom for WaveAtom { 23 | fn header(&self) -> AtomHeader { 24 | self.header 25 | } 26 | 27 | fn read(reader: &mut B, header: AtomHeader) -> Result { 28 | let mut iter = AtomIterator::new(reader, header); 29 | 30 | let mut esds = None; 31 | 32 | while let Some(header) = iter.next()? { 33 | if header.atype == AtomType::Esds { 34 | esds = Some(iter.read_atom::()?); 35 | } 36 | } 37 | 38 | Ok(WaveAtom { header, esds }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/fourcc.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use std::fmt; 9 | 10 | /// Four character codes for typical Ftyps (reference: http://ftyps.com/). 11 | #[derive(PartialEq, Eq, Clone, Copy)] 12 | #[repr(transparent)] 13 | pub struct FourCc { 14 | val: [u8; 4], 15 | } 16 | 17 | impl FourCc { 18 | /// Construct a new FourCC code from the given byte array. 19 | pub fn new(val: [u8; 4]) -> Self { 20 | Self { val } 21 | } 22 | } 23 | 24 | impl fmt::Debug for FourCc { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | match std::str::from_utf8(&self.val) { 27 | Ok(name) => f.write_str(name), 28 | _ => write!(f, "{:x?}", self.val), 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/fp.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | /// An unsigned 16.16-bit fixed point value. 9 | #[derive(Copy, Clone, Debug, Default)] 10 | pub struct FpU16(u32); 11 | 12 | impl FpU16 { 13 | pub fn new(val: u16) -> Self { 14 | Self(u32::from(val) << 16) 15 | } 16 | 17 | pub fn parse_raw(val: u32) -> Self { 18 | Self(val) 19 | } 20 | } 21 | 22 | impl From for f64 { 23 | fn from(fp: FpU16) -> Self { 24 | f64::from(fp.0) / f64::from(1u32 << 16) 25 | } 26 | } 27 | 28 | /// An unsigned 8.8-bit fixed point value. 29 | #[derive(Copy, Clone, Debug, Default)] 30 | pub struct FpU8(u16); 31 | 32 | impl FpU8 { 33 | pub fn new(val: u8) -> Self { 34 | Self(u16::from(val) << 8) 35 | } 36 | 37 | pub fn parse_raw(val: u16) -> Self { 38 | Self(val) 39 | } 40 | } 41 | 42 | impl From for f64 { 43 | fn from(fp: FpU8) -> Self { 44 | f64::from(fp.0) / f64::from(1u16 << 8) 45 | } 46 | } 47 | 48 | impl From for f32 { 49 | fn from(fp: FpU8) -> Self { 50 | f32::from(fp.0) / f32::from(1u16 << 8) 51 | } 52 | } 53 | 54 | /// An unsigned 8.8-bit fixed point value. 55 | #[derive(Copy, Clone, Debug, Default)] 56 | pub struct FpI8(i16); 57 | 58 | impl FpI8 { 59 | pub fn new(val: i8) -> Self { 60 | Self(i16::from(val) * 0x100) 61 | } 62 | 63 | pub fn parse_raw(val: i16) -> Self { 64 | Self(val) 65 | } 66 | } 67 | 68 | impl From for f64 { 69 | fn from(fp: FpI8) -> Self { 70 | f64::from(fp.0) / f64::from(1u16 << 8) 71 | } 72 | } 73 | 74 | impl From for f32 { 75 | fn from(fp: FpI8) -> Self { 76 | f32::from(fp.0) / f32::from(1u16 << 8) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /symphonia-format-isomp4/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 10 | // justification. 11 | #![allow(clippy::comparison_chain)] 12 | #![allow(clippy::excessive_precision)] 13 | #![allow(clippy::identity_op)] 14 | #![allow(clippy::manual_range_contains)] 15 | 16 | mod atoms; 17 | mod demuxer; 18 | mod fourcc; 19 | mod fp; 20 | mod stream; 21 | 22 | pub use demuxer::IsoMp4Reader; 23 | -------------------------------------------------------------------------------- /symphonia-format-mkv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-mkv" 3 | version = "0.5.4" 4 | description = "Pure Rust MKV/WebM demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Dariusz Niedoba "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["media", "demuxer", "mkv", "matroska", "webm"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | lazy_static = "1.4.0" 18 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 19 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 20 | symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" } -------------------------------------------------------------------------------- /symphonia-format-mkv/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia MKV/WebM Demuxer 2 | 3 | MKV/WebM demuxer for Project Symphonia. 4 | 5 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 6 | 7 | ## License 8 | 9 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 10 | 11 | ## Contributing 12 | 13 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 14 | -------------------------------------------------------------------------------- /symphonia-format-mkv/src/codecs.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::codecs; 9 | use symphonia_core::codecs::CodecType; 10 | 11 | use crate::segment::TrackElement; 12 | 13 | pub(crate) fn codec_id_to_type(track: &TrackElement) -> Option { 14 | let bit_depth = track.audio.as_ref().and_then(|a| a.bit_depth); 15 | 16 | match track.codec_id.as_str() { 17 | "A_MPEG/L1" => Some(codecs::CODEC_TYPE_MP1), 18 | "A_MPEG/L2" => Some(codecs::CODEC_TYPE_MP2), 19 | "A_MPEG/L3" => Some(codecs::CODEC_TYPE_MP3), 20 | "A_FLAC" => Some(codecs::CODEC_TYPE_FLAC), 21 | "A_OPUS" => Some(codecs::CODEC_TYPE_OPUS), 22 | "A_VORBIS" => Some(codecs::CODEC_TYPE_VORBIS), 23 | "A_AAC/MPEG2/MAIN" | "A_AAC/MPEG2/LC" | "A_AAC/MPEG2/LC/SBR" | "A_AAC/MPEG2/SSR" 24 | | "A_AAC/MPEG4/MAIN" | "A_AAC/MPEG4/LC" | "A_AAC/MPEG4/LC/SBR" | "A_AAC/MPEG4/SSR" 25 | | "A_AAC/MPEG4/LTP" | "A_AAC" => Some(codecs::CODEC_TYPE_AAC), 26 | "A_PCM/INT/BIG" => match bit_depth? { 27 | 16 => Some(codecs::CODEC_TYPE_PCM_S16BE), 28 | 24 => Some(codecs::CODEC_TYPE_PCM_S24BE), 29 | 32 => Some(codecs::CODEC_TYPE_PCM_S32BE), 30 | _ => None, 31 | }, 32 | "A_PCM/INT/LIT" => match bit_depth? { 33 | 16 => Some(codecs::CODEC_TYPE_PCM_S16LE), 34 | 24 => Some(codecs::CODEC_TYPE_PCM_S24LE), 35 | 32 => Some(codecs::CODEC_TYPE_PCM_S32LE), 36 | _ => None, 37 | }, 38 | "A_PCM/FLOAT/IEEE" => match bit_depth? { 39 | 32 => Some(codecs::CODEC_TYPE_PCM_F32LE), 40 | 64 => Some(codecs::CODEC_TYPE_PCM_F64LE), 41 | _ => None, 42 | }, 43 | _ => { 44 | log::info!("unknown codec: {}", &track.codec_id); 45 | None 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /symphonia-format-mkv/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | mod codecs; 18 | mod demuxer; 19 | mod ebml; 20 | mod element_ids; 21 | mod lacing; 22 | mod segment; 23 | 24 | pub use crate::demuxer::MkvReader; 25 | -------------------------------------------------------------------------------- /symphonia-format-ogg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-ogg" 3 | version = "0.5.4" 4 | description = "Pure Rust OGG demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "media", "demuxer", "ogg"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 18 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 19 | symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" } 20 | 21 | [lints.rust] 22 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } -------------------------------------------------------------------------------- /symphonia-format-ogg/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia OGG Demuxer 2 | 3 | [![Docs](https://docs.rs/symphonia-format-ogg/badge.svg)](https://docs.rs/symphonia-format-ogg) 4 | 5 | OGG demuxer for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-format-ogg/src/common.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::meta::MetadataRevision; 9 | 10 | /// Side data variants. 11 | pub enum SideData { 12 | Metadata(MetadataRevision), 13 | } 14 | -------------------------------------------------------------------------------- /symphonia-format-ogg/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | mod common; 18 | mod demuxer; 19 | mod logical; 20 | mod mappings; 21 | mod page; 22 | mod physical; 23 | 24 | pub use demuxer::OggReader; 25 | -------------------------------------------------------------------------------- /symphonia-format-ogg/src/mappings/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use super::common::SideData; 9 | 10 | use symphonia_core::codecs::CodecParameters; 11 | use symphonia_core::errors::Result; 12 | 13 | mod flac; 14 | mod opus; 15 | mod vorbis; 16 | 17 | /// Detect a `Mapper` for a logical stream given the identification packet of the stream. 18 | pub fn detect(buf: &[u8]) -> Result>> { 19 | let mapper = flac::detect(buf)? 20 | .or(vorbis::detect(buf)?) 21 | .or(opus::detect(buf)?) 22 | .or_else(make_null_mapper); 23 | 24 | Ok(mapper) 25 | } 26 | 27 | /// Result of a packet map operation. 28 | pub enum MapResult { 29 | /// The packet contained side data. 30 | SideData { data: SideData }, 31 | /// The packet contained setup data. 32 | Setup, 33 | /// The packet contained stream data. 34 | StreamData { dur: u64 }, 35 | /// The packet contained unknown data. 36 | Unknown, 37 | } 38 | 39 | /// A `PacketParser` implements a packet parser that decodes the timestamp and duration for a 40 | /// packet. 41 | pub trait PacketParser: Send + Sync { 42 | fn parse_next_packet_dur(&mut self, packet: &[u8]) -> u64; 43 | } 44 | 45 | /// A `Mapper` implements packet-handling for a specific `Codec`. 46 | pub trait Mapper: Send + Sync { 47 | /// Gets the name of the mapper. 48 | fn name(&self) -> &'static str; 49 | 50 | /// Soft-reset the mapper after a discontinuity in packets. 51 | fn reset(&mut self); 52 | 53 | /// Gets an immutable reference `CodecParameters` for the stream belonging to this `Mapper`. If 54 | /// the stream is not ready then the set of parameters may be incomplete. 55 | fn codec_params(&self) -> &CodecParameters; 56 | 57 | /// Gets a mutable reference to the `CodecParameters` for the stream belonging to this `Mapper`. 58 | /// If the stream is not ready then the set of parameters may be incomplete. 59 | fn codec_params_mut(&mut self) -> &mut CodecParameters; 60 | 61 | /// Convert an absolute granular position to a timestamp. 62 | fn absgp_to_ts(&self, ts: u64) -> u64 { 63 | ts 64 | } 65 | 66 | /// Make a packet parser for parsing packet timing. 67 | fn make_parser(&self) -> Option>; 68 | 69 | /// Map a packet. 70 | fn map_packet(&mut self, packet: &[u8]) -> Result; 71 | 72 | /// Returns `true` if the stream can is ready for usage. If the stream is not ready then the 73 | /// mapper needs to consume more setup packets. 74 | fn is_ready(&self) -> bool { 75 | true 76 | } 77 | } 78 | 79 | fn make_null_mapper() -> Option> { 80 | Some(Box::new(NullMapper::new())) 81 | } 82 | 83 | struct NullMapper { 84 | params: CodecParameters, 85 | } 86 | 87 | impl NullMapper { 88 | fn new() -> Self { 89 | NullMapper { params: CodecParameters::new() } 90 | } 91 | } 92 | 93 | impl Mapper for NullMapper { 94 | fn name(&self) -> &'static str { 95 | "null" 96 | } 97 | 98 | fn codec_params(&self) -> &CodecParameters { 99 | &self.params 100 | } 101 | 102 | fn codec_params_mut(&mut self) -> &mut CodecParameters { 103 | &mut self.params 104 | } 105 | 106 | fn reset(&mut self) { 107 | // Nothing to do! 108 | } 109 | 110 | fn make_parser(&self) -> Option> { 111 | Some(Box::new(NullPacketParser {})) 112 | } 113 | 114 | fn map_packet(&mut self, _: &[u8]) -> Result { 115 | Ok(MapResult::Unknown) 116 | } 117 | } 118 | 119 | struct NullPacketParser {} 120 | 121 | impl PacketParser for NullPacketParser { 122 | fn parse_next_packet_dur(&mut self, _: &[u8]) -> u64 { 123 | 0 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /symphonia-format-riff/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-riff" 3 | version = "0.5.4" 4 | description = "Pure Rust RIFF demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov ", "dedobbin "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "media", "demuxer", "aiff", "wav"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [features] 16 | default = ["aiff", "wav"] 17 | aiff = [] 18 | wav = [] 19 | 20 | [dependencies] 21 | extended = "0.1.0" 22 | log = "0.4" 23 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 24 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } 25 | -------------------------------------------------------------------------------- /symphonia-format-riff/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia RIFF (AIFF, AVI, WAVE) Demuxer 2 | 3 | [![Docs](https://docs.rs/symphonia-format-riff/badge.svg)](https://docs.rs/symphonia-format-riff) 4 | 5 | AIFF/AVI/WAVE demuxer for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## Support 10 | 11 | This crate supports demuxing media containers based off the Resource Interchange File Format (RIFF). Specific format support may be enabled or disabled using feature flags. However, by default, all formats are enabled. 12 | 13 | | Format | Feature Flag | Default | 14 | |--------|--------------|---------| 15 | | AIFF | `aiff` | Yes | 16 | | WAVE | `wav` | Yes | 17 | 18 | ## License 19 | 20 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 21 | 22 | ## Contributing 23 | 24 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 25 | -------------------------------------------------------------------------------- /symphonia-format-riff/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | mod common; 18 | 19 | #[cfg(feature = "aiff")] 20 | mod aiff; 21 | #[cfg(feature = "wav")] 22 | mod wave; 23 | 24 | #[cfg(feature = "aiff")] 25 | pub use aiff::AiffReader; 26 | #[cfg(feature = "wav")] 27 | pub use wave::WavReader; 28 | -------------------------------------------------------------------------------- /symphonia-format-wav/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-format-wav" 3 | version = "0.5.4" 4 | description = "Pure Rust WAV demuxer (a part of project Symphonia)." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "media", "demuxer", "wav", "riff"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | log = "0.4" 17 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 18 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } -------------------------------------------------------------------------------- /symphonia-format-wav/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Wave Codec 2 | 3 | [![Docs](https://docs.rs/symphonia-format-wav/badge.svg)](https://docs.rs/symphonia-format-wav) 4 | 5 | WAV decoder for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-metadata/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-metadata" 3 | version = "0.5.4" 4 | description = "Project Symphonia multimedia tag and metadata readers." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["multimedia", "media", "metadata", "id3v1", "id3v2"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | encoding_rs = "0.8.17" 17 | lazy_static = "1.4.0" 18 | log = "0.4" 19 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } -------------------------------------------------------------------------------- /symphonia-metadata/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Metadata Utilities 2 | 3 | [![Docs](https://docs.rs/symphonia-metadata/badge.svg)](https://docs.rs/symphonia-metadata) 4 | 5 | Common metadata readers, helpers, and utilities for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-metadata/src/flac.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2024 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! Readers for FLAC comment and picture metadata blocks. 9 | 10 | use std::num::NonZeroU32; 11 | 12 | use symphonia_core::errors::{decode_error, Result}; 13 | use symphonia_core::io::ReadBytes; 14 | use symphonia_core::meta::{ColorMode, MetadataBuilder, Size, StandardTagKey, Tag, Value, Visual}; 15 | 16 | use crate::{id3v2, vorbis}; 17 | 18 | /// Converts a string of bytes to an ASCII string if all characters are within the printable ASCII 19 | /// range. If a null byte is encounted, the string terminates at that point. 20 | fn printable_ascii_to_string(bytes: &[u8]) -> Option { 21 | let mut result = String::with_capacity(bytes.len()); 22 | 23 | for c in bytes { 24 | match c { 25 | 0x00 => break, 26 | 0x20..=0x7e => result.push(char::from(*c)), 27 | _ => return None, 28 | } 29 | } 30 | 31 | Some(result) 32 | } 33 | 34 | /// Read a comment metadata block. 35 | pub fn read_comment_block( 36 | reader: &mut B, 37 | metadata: &mut MetadataBuilder, 38 | ) -> Result<()> { 39 | vorbis::read_comment_no_framing(reader, metadata) 40 | } 41 | 42 | /// Read a picture metadata block. 43 | pub fn read_picture_block( 44 | reader: &mut B, 45 | metadata: &mut MetadataBuilder, 46 | ) -> Result<()> { 47 | let type_enc = reader.read_be_u32()?; 48 | 49 | // Read the Media Type length in bytes. 50 | let media_type_len = reader.read_be_u32()? as usize; 51 | 52 | // Read the Media Type bytes 53 | let mut media_type_buf = vec![0u8; media_type_len]; 54 | reader.read_buf_exact(&mut media_type_buf)?; 55 | 56 | // Convert Media Type bytes to an ASCII string. Non-printable ASCII characters are invalid. 57 | let media_type = match printable_ascii_to_string(&media_type_buf) { 58 | Some(s) => s, 59 | None => return decode_error("meta (flac): picture mime-type contains invalid characters"), 60 | }; 61 | 62 | // Read the description length in bytes. 63 | let desc_len = reader.read_be_u32()? as usize; 64 | 65 | // Read the description bytes. 66 | let mut desc_buf = vec![0u8; desc_len]; 67 | reader.read_buf_exact(&mut desc_buf)?; 68 | 69 | let desc = String::from_utf8_lossy(&desc_buf); 70 | 71 | // Convert description bytes into a standard Vorbis DESCRIPTION tag. 72 | let tags = vec![Tag::new(Some(StandardTagKey::Description), "DESCRIPTION", Value::from(desc))]; 73 | 74 | // Read the width, and height of the visual. 75 | let width = reader.read_be_u32()?; 76 | let height = reader.read_be_u32()?; 77 | 78 | // If either the width or height is 0, then the size is invalid. 79 | let dimensions = if width > 0 && height > 0 { Some(Size { width, height }) } else { None }; 80 | 81 | // Read bits-per-pixel of the visual. 82 | let bits_per_pixel = NonZeroU32::new(reader.read_be_u32()?); 83 | 84 | // Indexed colours is only valid for image formats that use an indexed colour palette. If it is 85 | // 0, the image does not used indexed colours. 86 | let indexed_colours_enc = reader.read_be_u32()?; 87 | 88 | let color_mode = match indexed_colours_enc { 89 | 0 => Some(ColorMode::Discrete), 90 | _ => Some(ColorMode::Indexed(NonZeroU32::new(indexed_colours_enc).unwrap())), 91 | }; 92 | 93 | // Read the image data 94 | let data_len = reader.read_be_u32()? as usize; 95 | let data = reader.read_boxed_slice_exact(data_len)?; 96 | 97 | metadata.add_visual(Visual { 98 | media_type, 99 | dimensions, 100 | bits_per_pixel, 101 | color_mode, 102 | usage: id3v2::util::apic_picture_type_to_visual_key(type_enc), 103 | tags, 104 | data, 105 | }); 106 | 107 | Ok(()) 108 | } 109 | -------------------------------------------------------------------------------- /symphonia-metadata/src/itunes.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! iTunes metadata support. 9 | 10 | use symphonia_core::meta::StandardTagKey; 11 | 12 | use std::collections::HashMap; 13 | 14 | use lazy_static::lazy_static; 15 | 16 | lazy_static! { 17 | static ref ITUNES_TAG_MAP: HashMap<&'static str, StandardTagKey> = { 18 | let mut m = HashMap::new(); 19 | m.insert("com.apple.iTunes:ARTISTS", StandardTagKey::Artist); 20 | m.insert("com.apple.iTunes:ASIN", StandardTagKey::IdentAsin); 21 | m.insert("com.apple.iTunes:BARCODE", StandardTagKey::IdentBarcode); 22 | m.insert("com.apple.iTunes:CATALOGNUMBER", StandardTagKey::IdentCatalogNumber); 23 | m.insert("com.apple.iTunes:CONDUCTOR", StandardTagKey::Conductor); 24 | m.insert("com.apple.iTunes:DISCSUBTITLE", StandardTagKey::DiscSubtitle); 25 | m.insert("com.apple.iTunes:DJMIXER", StandardTagKey::MixDj); 26 | m.insert("com.apple.iTunes:ENGINEER", StandardTagKey::Engineer); 27 | m.insert("com.apple.iTunes:ISRC", StandardTagKey::IdentIsrc); 28 | m.insert("com.apple.iTunes:LABEL", StandardTagKey::Label); 29 | m.insert("com.apple.iTunes:LANGUAGE", StandardTagKey::Language); 30 | m.insert("com.apple.iTunes:LICENSE", StandardTagKey::License); 31 | m.insert("com.apple.iTunes:LYRICIST", StandardTagKey::Lyricist); 32 | m.insert("com.apple.iTunes:MEDIA", StandardTagKey::MediaFormat); 33 | m.insert("com.apple.iTunes:MIXER", StandardTagKey::MixEngineer); 34 | m.insert("com.apple.iTunes:MOOD", StandardTagKey::Mood); 35 | m.insert( 36 | "com.apple.iTunes:MusicBrainz Album Artist Id", 37 | StandardTagKey::MusicBrainzAlbumArtistId, 38 | ); 39 | m.insert("com.apple.iTunes:MusicBrainz Album Id", StandardTagKey::MusicBrainzAlbumId); 40 | m.insert( 41 | "com.apple.iTunes:MusicBrainz Album Release Country", 42 | StandardTagKey::ReleaseCountry, 43 | ); 44 | m.insert( 45 | "com.apple.iTunes:MusicBrainz Album Status", 46 | StandardTagKey::MusicBrainzReleaseStatus, 47 | ); 48 | m.insert("com.apple.iTunes:MusicBrainz Album Type", StandardTagKey::MusicBrainzReleaseType); 49 | m.insert("com.apple.iTunes:MusicBrainz Artist Id", StandardTagKey::MusicBrainzArtistId); 50 | m.insert( 51 | "com.apple.iTunes:MusicBrainz Release Group Id", 52 | StandardTagKey::MusicBrainzReleaseGroupId, 53 | ); 54 | m.insert( 55 | "com.apple.iTunes:MusicBrainz Release Track Id", 56 | StandardTagKey::MusicBrainzReleaseTrackId, 57 | ); 58 | m.insert("com.apple.iTunes:MusicBrainz Track Id", StandardTagKey::MusicBrainzTrackId); 59 | m.insert("com.apple.iTunes:MusicBrainz Work Id", StandardTagKey::MusicBrainzWorkId); 60 | m.insert("com.apple.iTunes:originaldate", StandardTagKey::OriginalDate); 61 | m.insert("com.apple.iTunes:PRODUCER", StandardTagKey::Producer); 62 | m.insert("com.apple.iTunes:REMIXER", StandardTagKey::Remixer); 63 | m.insert("com.apple.iTunes:SCRIPT", StandardTagKey::Script); 64 | m.insert("com.apple.iTunes:SUBTITLE", StandardTagKey::TrackSubtitle); 65 | m 66 | }; 67 | } 68 | 69 | /// Try to map the iTunes `tag` name to a `StandardTagKey`. 70 | pub fn std_key_from_tag(key: &str) -> Option { 71 | ITUNES_TAG_MAP.get(key).copied() 72 | } 73 | -------------------------------------------------------------------------------- /symphonia-metadata/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | #![warn(rust_2018_idioms)] 9 | #![forbid(unsafe_code)] 10 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 11 | // justification. 12 | #![allow(clippy::comparison_chain)] 13 | #![allow(clippy::excessive_precision)] 14 | #![allow(clippy::identity_op)] 15 | #![allow(clippy::manual_range_contains)] 16 | 17 | pub mod flac; 18 | pub mod id3v1; 19 | pub mod id3v2; 20 | pub mod itunes; 21 | pub mod riff; 22 | pub mod vorbis; 23 | -------------------------------------------------------------------------------- /symphonia-metadata/src/riff.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | //! A RIFF INFO metadata reader. 9 | 10 | use lazy_static::lazy_static; 11 | use std::collections::HashMap; 12 | use symphonia_core::meta::{StandardTagKey, Tag, Value}; 13 | 14 | lazy_static! { 15 | static ref RIFF_INFO_MAP: HashMap<&'static str, StandardTagKey> = { 16 | let mut m = HashMap::new(); 17 | m.insert("ages", StandardTagKey::Rating); 18 | m.insert("cmnt", StandardTagKey::Comment); 19 | // Is this the same as a cmnt? 20 | m.insert("comm", StandardTagKey::Comment); 21 | m.insert("dtim", StandardTagKey::OriginalDate); 22 | m.insert("genr", StandardTagKey::Genre); 23 | m.insert("iart", StandardTagKey::Artist); 24 | // Is this also the same as cmnt? 25 | m.insert("icmt", StandardTagKey::Comment); 26 | m.insert("icop", StandardTagKey::Copyright); 27 | m.insert("icrd", StandardTagKey::Date); 28 | m.insert("idit", StandardTagKey::OriginalDate); 29 | m.insert("ienc", StandardTagKey::EncodedBy); 30 | m.insert("ieng", StandardTagKey::Engineer); 31 | m.insert("ifrm", StandardTagKey::TrackTotal); 32 | m.insert("ignr", StandardTagKey::Genre); 33 | m.insert("ilng", StandardTagKey::Language); 34 | m.insert("imus", StandardTagKey::Composer); 35 | m.insert("inam", StandardTagKey::TrackTitle); 36 | m.insert("iprd", StandardTagKey::Album); 37 | m.insert("ipro", StandardTagKey::Producer); 38 | m.insert("iprt", StandardTagKey::TrackNumber); 39 | m.insert("irtd", StandardTagKey::Rating); 40 | m.insert("isft", StandardTagKey::Encoder); 41 | m.insert("isgn", StandardTagKey::Genre); 42 | m.insert("isrf", StandardTagKey::MediaFormat); 43 | m.insert("itch", StandardTagKey::EncodedBy); 44 | m.insert("iwri", StandardTagKey::Writer); 45 | m.insert("lang", StandardTagKey::Language); 46 | m.insert("prt1", StandardTagKey::TrackNumber); 47 | m.insert("prt2", StandardTagKey::TrackTotal); 48 | // Same as inam? 49 | m.insert("titl", StandardTagKey::TrackTitle); 50 | m.insert("torg", StandardTagKey::Label); 51 | m.insert("trck", StandardTagKey::TrackNumber); 52 | m.insert("tver", StandardTagKey::Version); 53 | m.insert("year", StandardTagKey::Date); 54 | m 55 | }; 56 | } 57 | 58 | /// Parse the RIFF INFO block into a `Tag` using the block's identifier tag and a slice 59 | /// containing the block's contents. 60 | pub fn parse(tag: [u8; 4], buf: &[u8]) -> Tag { 61 | // TODO: Key should be checked that it only contains ASCII characters. 62 | let key = String::from_utf8_lossy(&tag); 63 | let value = String::from_utf8_lossy(buf); 64 | 65 | // Attempt to assign a standardized tag key. 66 | let std_tag = RIFF_INFO_MAP.get(key.to_lowercase().as_str()).copied(); 67 | 68 | Tag::new(std_tag, &key, Value::from(value)) 69 | } 70 | -------------------------------------------------------------------------------- /symphonia-play/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-play" 3 | version = "0.5.4" 4 | description = "Project Symphonia audio player demo application." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | edition = "2018" 10 | publish = false 11 | default-run = "symphonia-play" 12 | 13 | [dependencies] 14 | clap = "3.1.0" 15 | lazy_static = "1.4.0" 16 | log = { version = "0.4", features = ["release_max_level_info"] } 17 | pretty_env_logger = "0.4" 18 | symphonia = { version = "0.5.4", path = "../symphonia", features = ["all", "opt-simd"] } 19 | 20 | [target.'cfg(target_os = "linux")'.dependencies] 21 | libpulse-binding = "2.5.0" 22 | libpulse-simple-binding = "2.5.0" 23 | 24 | [target.'cfg(not(target_os = "linux"))'.dependencies] 25 | arrayvec = "0.7.1" 26 | cpal = "0.13.3" 27 | rb = "0.3.2" 28 | rubato = "0.12.0" 29 | -------------------------------------------------------------------------------- /symphonia-play/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Play 2 | 3 | A simple audio player for testing Project Symphonia demuxers and decoders. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | # Play an audio file. 9 | symphonia-play /path/to/file 10 | 11 | # Play an audio file and verify the decoded audio whilst playing (some formats only). 12 | symphonia-play --verify /path/to/file 13 | 14 | # Seek the audio file to the desired timestamp and then play. 15 | symphonia-play -s /path/to/file 16 | 17 | # Play a specific track within the file. 18 | symphonia-play -t /path/to/file 19 | 20 | # Probe a file for streams and metadata (tags, visuals, etc.) 21 | symphonia-play --probe-only /path/to/file 22 | 23 | # Decode and verify if the decoded audio is valid, but do not play it (some formats only). 24 | symphonia-play --verify-only /path/to/file 25 | 26 | # Decode, but do not play or verify the decoded audio (benchmarking). 27 | symphonia-play --decode-only /path/to/file 28 | 29 | # Do any of the above, but get the encoded audio from standard input by using '-' as the file path. 30 | cat /path/to/file | symphonia-play - 31 | curl -s https://radio.station.com/stream | symphonia-play - 32 | youtube-dl -f 140 -o - | symphonia-play - 33 | yt-dlp -f 140 -o - | symphonia-play - 34 | ``` 35 | 36 | ## License 37 | 38 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 39 | 40 | ## Contributing 41 | 42 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 43 | -------------------------------------------------------------------------------- /symphonia-utils-xiph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia-utils-xiph" 3 | version = "0.5.4" 4 | description = "Project Symphonia utilities for Xiph codecs and formats." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "multimedia", "media", "xiph"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [dependencies] 16 | symphonia-core = { version = "0.5.4", path = "../symphonia-core" } 17 | symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } -------------------------------------------------------------------------------- /symphonia-utils-xiph/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Xiph Utilities 2 | 3 | [![Docs](https://docs.rs/symphonia-utils-xiph/badge.svg)](https://docs.rs/symphonia-utils-xiph) 4 | 5 | Common utilities for Xiph formats and codecs for Project Symphonia. 6 | 7 | **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. 8 | 9 | ## License 10 | 11 | Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. 12 | 13 | ## Contributing 14 | 15 | Symphonia is a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). 16 | -------------------------------------------------------------------------------- /symphonia-utils-xiph/src/flac/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | pub mod metadata; 9 | -------------------------------------------------------------------------------- /symphonia-utils-xiph/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their 9 | // justification. 10 | #![allow(clippy::comparison_chain)] 11 | #![allow(clippy::excessive_precision)] 12 | #![allow(clippy::identity_op)] 13 | #![allow(clippy::manual_range_contains)] 14 | 15 | pub mod flac; 16 | pub mod vorbis; 17 | -------------------------------------------------------------------------------- /symphonia-utils-xiph/src/vorbis/mod.rs: -------------------------------------------------------------------------------- 1 | // Symphonia 2 | // Copyright (c) 2019-2022 The Project Symphonia Developers. 3 | // 4 | // This Source Code Form is subject to the terms of the Mozilla Public 5 | // License, v. 2.0. If a copy of the MPL was not distributed with this 6 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | 8 | use symphonia_core::audio::Channels; 9 | 10 | /// Get the mapping 0 channel listing for the given number of channels. 11 | pub fn vorbis_channels_to_channels(num_channels: u8) -> Option { 12 | let channels = match num_channels { 13 | 1 => Channels::FRONT_LEFT, 14 | 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, 15 | 3 => Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT, 16 | 4 => { 17 | Channels::FRONT_LEFT 18 | | Channels::FRONT_RIGHT 19 | | Channels::REAR_LEFT 20 | | Channels::REAR_RIGHT 21 | } 22 | 5 => { 23 | Channels::FRONT_LEFT 24 | | Channels::FRONT_CENTRE 25 | | Channels::FRONT_RIGHT 26 | | Channels::REAR_LEFT 27 | | Channels::REAR_RIGHT 28 | } 29 | 6 => { 30 | Channels::FRONT_LEFT 31 | | Channels::FRONT_CENTRE 32 | | Channels::FRONT_RIGHT 33 | | Channels::REAR_LEFT 34 | | Channels::REAR_RIGHT 35 | | Channels::LFE1 36 | } 37 | 7 => { 38 | Channels::FRONT_LEFT 39 | | Channels::FRONT_CENTRE 40 | | Channels::FRONT_RIGHT 41 | | Channels::SIDE_LEFT 42 | | Channels::SIDE_RIGHT 43 | | Channels::REAR_CENTRE 44 | | Channels::LFE1 45 | } 46 | 8 => { 47 | Channels::FRONT_LEFT 48 | | Channels::FRONT_CENTRE 49 | | Channels::FRONT_RIGHT 50 | | Channels::SIDE_LEFT 51 | | Channels::SIDE_RIGHT 52 | | Channels::REAR_LEFT 53 | | Channels::REAR_RIGHT 54 | | Channels::LFE1 55 | } 56 | _ => return None, 57 | }; 58 | 59 | Some(channels) 60 | } 61 | -------------------------------------------------------------------------------- /symphonia/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symphonia" 3 | version = "0.5.4" 4 | description = "Pure Rust media container and audio decoding library." 5 | homepage = "https://github.com/pdeljanov/Symphonia" 6 | repository = "https://github.com/pdeljanov/Symphonia" 7 | authors = ["Philip Deljanov "] 8 | license = "MPL-2.0" 9 | readme = "README.md" 10 | categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] 11 | keywords = ["audio", "codec", "decoder", "multimedia", "media"] 12 | edition = "2018" 13 | rust-version = "1.53" 14 | 15 | [features] 16 | # Only royalty-free open standard codecs and formats are enabled by default. 17 | # TODO: Remove all defaults for v0.6.0. Features should be additive. 18 | default = ["adpcm", "flac", "mkv", "ogg", "pcm", "vorbis", "wav"] 19 | 20 | # Enable specific codecs and formats. 21 | # TODO: Use "dep:" after MSRV is raised to >= 1.60. 22 | aac = ["symphonia-codec-aac"] 23 | adpcm = ["symphonia-codec-adpcm"] 24 | alac = ["symphonia-codec-alac"] 25 | flac = ["symphonia-bundle-flac"] 26 | caf = ["symphonia-format-caf"] 27 | isomp4 = ["symphonia-format-isomp4"] 28 | mkv = ["symphonia-format-mkv"] 29 | mp1 = ["symphonia-bundle-mp3/mp1"] 30 | mp2 = ["symphonia-bundle-mp3/mp2"] 31 | mp3 = ["symphonia-bundle-mp3/mp3"] 32 | ogg = ["symphonia-format-ogg"] 33 | pcm = ["symphonia-codec-pcm"] 34 | aiff = ["symphonia-format-riff/aiff"] 35 | vorbis = ["symphonia-codec-vorbis"] 36 | wav = ["symphonia-format-riff/wav"] 37 | 38 | # MPEG audio codecs. 39 | mpa = ["mp1", "mp2", "mp3"] 40 | 41 | # Enable all supported codecs. 42 | all-codecs = [ 43 | "aac", 44 | "adpcm", 45 | "alac", 46 | "flac", 47 | "mp1", 48 | "mp2", 49 | "mp3", 50 | "pcm", 51 | "vorbis", 52 | ] 53 | 54 | # Enable all supported formats. 55 | all-formats = [ 56 | "caf", 57 | "isomp4", 58 | "mkv", 59 | "ogg", 60 | "aiff", 61 | "wav" 62 | ] 63 | 64 | # Enable all supported codecs and formats. 65 | all = [ 66 | "all-codecs", 67 | "all-formats", 68 | ] 69 | 70 | # SIMD support. 71 | opt-simd-sse = ["symphonia-core/opt-simd-sse"] 72 | opt-simd-avx = ["symphonia-core/opt-simd-avx"] 73 | opt-simd-neon = ["symphonia-core/opt-simd-neon"] 74 | 75 | # Enable all SIMD support. 76 | opt-simd = [ 77 | "opt-simd-sse", 78 | "opt-simd-avx", 79 | "opt-simd-neon", 80 | ] 81 | 82 | [dependencies] 83 | lazy_static = "1.4.0" 84 | 85 | [dependencies.symphonia-core] 86 | version = "0.5.4" 87 | path = "../symphonia-core" 88 | 89 | [dependencies.symphonia-metadata] 90 | version = "0.5.4" 91 | path = "../symphonia-metadata" 92 | 93 | [dependencies.symphonia-bundle-flac] 94 | version = "0.5.4" 95 | path = "../symphonia-bundle-flac" 96 | optional = true 97 | 98 | [dependencies.symphonia-bundle-mp3] 99 | version = "0.5.4" 100 | path = "../symphonia-bundle-mp3" 101 | optional = true 102 | # Standalone crate enables all MP1, MP2, and MP3 decoders by default. 103 | default-features = false 104 | 105 | [dependencies.symphonia-codec-aac] 106 | version = "0.5.4" 107 | path = "../symphonia-codec-aac" 108 | optional = true 109 | 110 | [dependencies.symphonia-codec-adpcm] 111 | version = "0.5.4" 112 | path = "../symphonia-codec-adpcm" 113 | optional = true 114 | 115 | [dependencies.symphonia-codec-alac] 116 | version = "0.5.4" 117 | path = "../symphonia-codec-alac" 118 | optional = true 119 | 120 | [dependencies.symphonia-codec-pcm] 121 | version = "0.5.4" 122 | path = "../symphonia-codec-pcm" 123 | optional = true 124 | 125 | [dependencies.symphonia-codec-vorbis] 126 | version = "0.5.4" 127 | path = "../symphonia-codec-vorbis" 128 | optional = true 129 | 130 | [dependencies.symphonia-format-riff] 131 | version = "0.5.4" 132 | path = "../symphonia-format-riff" 133 | optional = true 134 | # Standalone crate enables AIFF and WAVE by default. 135 | default-features = false 136 | 137 | [dependencies.symphonia-format-ogg] 138 | version = "0.5.4" 139 | path = "../symphonia-format-ogg" 140 | optional = true 141 | 142 | [dependencies.symphonia-format-isomp4] 143 | version = "0.5.4" 144 | path = "../symphonia-format-isomp4" 145 | optional = true 146 | 147 | [dependencies.symphonia-format-mkv] 148 | version = "0.5.4" 149 | path = "../symphonia-format-mkv" 150 | optional = true 151 | 152 | [dependencies.symphonia-format-caf] 153 | version = "0.5.4" 154 | path = "../symphonia-format-caf" 155 | optional = true 156 | 157 | # Show documentation with all features enabled on docs.rs 158 | [package.metadata.docs.rs] 159 | all-features = true 160 | -------------------------------------------------------------------------------- /symphonia/examples/README.md: -------------------------------------------------------------------------------- 1 | # Symphonia Examples 2 | 3 | | Example | Description | 4 | |------------------------|----------------------------------------------------------------| 5 | | `basic-interleaved.rs` | Decode a file and interleave the decoded samples for playback. | 6 | | `getting-started.rs` | The example from GETTING_STARTED.md. | 7 | -------------------------------------------------------------------------------- /symphonia/examples/basic-interleaved.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::path::Path; 4 | 5 | use symphonia::core::audio::SampleBuffer; 6 | use symphonia::core::codecs::DecoderOptions; 7 | use symphonia::core::errors::Error; 8 | use symphonia::core::formats::FormatOptions; 9 | use symphonia::core::io::MediaSourceStream; 10 | use symphonia::core::meta::MetadataOptions; 11 | use symphonia::core::probe::Hint; 12 | 13 | fn main() { 14 | // Get command line arguments. 15 | let args: Vec = env::args().collect(); 16 | 17 | // Create a media source. Note that the MediaSource trait is automatically implemented for File, 18 | // among other types. 19 | let file = Box::new(File::open(Path::new(&args[1])).unwrap()); 20 | 21 | // Create the media source stream using the boxed media source from above. 22 | let mss = MediaSourceStream::new(file, Default::default()); 23 | 24 | // Create a hint to help the format registry guess what format reader is appropriate. In this 25 | // example we'll leave it empty. 26 | let hint = Hint::new(); 27 | 28 | // Use the default options when reading and decoding. 29 | let format_opts: FormatOptions = Default::default(); 30 | let metadata_opts: MetadataOptions = Default::default(); 31 | let decoder_opts: DecoderOptions = Default::default(); 32 | 33 | // Probe the media source stream for a format. 34 | let probed = 35 | symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts).unwrap(); 36 | 37 | // Get the format reader yielded by the probe operation. 38 | let mut format = probed.format; 39 | 40 | // Get the default track. 41 | let track = format.default_track().unwrap(); 42 | 43 | // Create a decoder for the track. 44 | let mut decoder = 45 | symphonia::default::get_codecs().make(&track.codec_params, &decoder_opts).unwrap(); 46 | 47 | // Store the track identifier, we'll use it to filter packets. 48 | let track_id = track.id; 49 | 50 | let mut sample_count = 0; 51 | let mut sample_buf = None; 52 | 53 | loop { 54 | // Get the next packet from the format reader. 55 | let packet = format.next_packet().unwrap(); 56 | 57 | // If the packet does not belong to the selected track, skip it. 58 | if packet.track_id() != track_id { 59 | continue; 60 | } 61 | 62 | // Decode the packet into audio samples, ignoring any decode errors. 63 | match decoder.decode(&packet) { 64 | Ok(audio_buf) => { 65 | // The decoded audio samples may now be accessed via the audio buffer if per-channel 66 | // slices of samples in their native decoded format is desired. Use-cases where 67 | // the samples need to be accessed in an interleaved order or converted into 68 | // another sample format, or a byte buffer is required, are covered by copying the 69 | // audio buffer into a sample buffer or raw sample buffer, respectively. In the 70 | // example below, we will copy the audio buffer into a sample buffer in an 71 | // interleaved order while also converting to a f32 sample format. 72 | 73 | // If this is the *first* decoded packet, create a sample buffer matching the 74 | // decoded audio buffer format. 75 | if sample_buf.is_none() { 76 | // Get the audio buffer specification. 77 | let spec = *audio_buf.spec(); 78 | 79 | // Get the capacity of the decoded buffer. Note: This is capacity, not length! 80 | let duration = audio_buf.capacity() as u64; 81 | 82 | // Create the f32 sample buffer. 83 | sample_buf = Some(SampleBuffer::::new(duration, spec)); 84 | } 85 | 86 | // Copy the decoded audio buffer into the sample buffer in an interleaved format. 87 | if let Some(buf) = &mut sample_buf { 88 | buf.copy_interleaved_ref(audio_buf); 89 | 90 | // The samples may now be access via the `samples()` function. 91 | sample_count += buf.samples().len(); 92 | print!("\rDecoded {} samples", sample_count); 93 | } 94 | } 95 | Err(Error::DecodeError(_)) => (), 96 | Err(_) => break, 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /symphonia/examples/getting-started.rs: -------------------------------------------------------------------------------- 1 | use symphonia::core::codecs::{DecoderOptions, CODEC_TYPE_NULL}; 2 | use symphonia::core::errors::Error; 3 | use symphonia::core::formats::FormatOptions; 4 | use symphonia::core::io::MediaSourceStream; 5 | use symphonia::core::meta::MetadataOptions; 6 | use symphonia::core::probe::Hint; 7 | 8 | fn main() { 9 | // Get the first command line argument. 10 | let args: Vec = std::env::args().collect(); 11 | let path = args.get(1).expect("file path not provided"); 12 | 13 | // Open the media source. 14 | let src = std::fs::File::open(path).expect("failed to open media"); 15 | 16 | // Create the media source stream. 17 | let mss = MediaSourceStream::new(Box::new(src), Default::default()); 18 | 19 | // Create a probe hint using the file's extension. [Optional] 20 | let mut hint = Hint::new(); 21 | hint.with_extension("mp3"); 22 | 23 | // Use the default options for metadata and format readers. 24 | let meta_opts: MetadataOptions = Default::default(); 25 | let fmt_opts: FormatOptions = Default::default(); 26 | 27 | // Probe the media source. 28 | let probed = symphonia::default::get_probe() 29 | .format(&hint, mss, &fmt_opts, &meta_opts) 30 | .expect("unsupported format"); 31 | 32 | // Get the instantiated format reader. 33 | let mut format = probed.format; 34 | 35 | // Find the first audio track with a known (decodeable) codec. 36 | let track = format 37 | .tracks() 38 | .iter() 39 | .find(|t| t.codec_params.codec != CODEC_TYPE_NULL) 40 | .expect("no supported audio tracks"); 41 | 42 | // Use the default options for the decoder. 43 | let dec_opts: DecoderOptions = Default::default(); 44 | 45 | // Create a decoder for the track. 46 | let mut decoder = symphonia::default::get_codecs() 47 | .make(&track.codec_params, &dec_opts) 48 | .expect("unsupported codec"); 49 | 50 | // Store the track identifier, it will be used to filter packets. 51 | let track_id = track.id; 52 | 53 | // The decode loop. 54 | loop { 55 | // Get the next packet from the media format. 56 | let packet = match format.next_packet() { 57 | Ok(packet) => packet, 58 | Err(Error::ResetRequired) => { 59 | // The track list has been changed. Re-examine it and create a new set of decoders, 60 | // then restart the decode loop. This is an advanced feature and it is not 61 | // unreasonable to consider this "the end." As of v0.5.0, the only usage of this is 62 | // for chained OGG physical streams. 63 | unimplemented!(); 64 | } 65 | Err(err) => { 66 | // A unrecoverable error occurred, halt decoding. 67 | panic!("{}", err); 68 | } 69 | }; 70 | 71 | // Consume any new metadata that has been read since the last packet. 72 | while !format.metadata().is_latest() { 73 | // Pop the old head of the metadata queue. 74 | format.metadata().pop(); 75 | 76 | // Consume the new metadata at the head of the metadata queue. 77 | } 78 | 79 | // If the packet does not belong to the selected track, skip over it. 80 | if packet.track_id() != track_id { 81 | continue; 82 | } 83 | 84 | // Decode the packet into audio samples. 85 | match decoder.decode(&packet) { 86 | Ok(_decoded) => { 87 | // Consume the decoded audio samples (see below). 88 | } 89 | Err(Error::IoError(_)) => { 90 | // The packet failed to decode due to an IO error, skip the packet. 91 | continue; 92 | } 93 | Err(Error::DecodeError(_)) => { 94 | // The packet failed to decode due to invalid data, skip the packet. 95 | continue; 96 | } 97 | Err(err) => { 98 | // An unrecoverable error occurred, halt decoding. 99 | panic!("{}", err); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /symphonia/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /symphonia/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "symphonia-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.4" 14 | 15 | [dependencies.symphonia] 16 | path = ".." 17 | default-features = false 18 | features = ["all"] 19 | 20 | 21 | # Prevent this from interfering with workspaces 22 | [workspace] 23 | members = ["."] 24 | 25 | [[bin]] 26 | name = "decode_any" 27 | path = "fuzz_targets/decode_any.rs" 28 | test = false 29 | doc = false 30 | 31 | [[bin]] 32 | name = "decode_mp3" 33 | path = "fuzz_targets/decode_mp3.rs" 34 | test = false 35 | doc = false 36 | -------------------------------------------------------------------------------- /symphonia/fuzz/fuzz_targets/decode_any.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | use symphonia::core::codecs::DecoderOptions; 4 | use symphonia::core::formats::FormatOptions; 5 | use symphonia::core::meta::MetadataOptions; 6 | use symphonia::core::probe::Hint; 7 | 8 | fuzz_target!(|data: Vec| { 9 | let data = std::io::Cursor::new(data); 10 | 11 | let source = symphonia::core::io::MediaSourceStream::new(Box::new(data), Default::default()); 12 | 13 | match symphonia::default::get_probe().format( 14 | &Hint::new(), 15 | source, 16 | &FormatOptions::default(), 17 | &MetadataOptions::default(), 18 | ) { 19 | Ok(mut probed) => { 20 | let track = probed.format.default_track().unwrap(); 21 | 22 | let mut decoder = match symphonia::default::get_codecs() 23 | .make(&track.codec_params, &DecoderOptions::default()) 24 | { 25 | Ok(d) => d, 26 | Err(_) => return, 27 | }; 28 | 29 | loop { 30 | let packet = match probed.format.next_packet() { 31 | Ok(p) => p, 32 | Err(_) => return, 33 | }; 34 | let _ = decoder.decode(&packet); 35 | } 36 | } 37 | Err(_) => {} 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /symphonia/fuzz/fuzz_targets/decode_mp3.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | use symphonia::core::codecs::{CODEC_TYPE_MP3, CodecParameters, Decoder}; 4 | use symphonia::core::formats::Packet; 5 | use symphonia::default::codecs::MpaDecoder; 6 | 7 | fuzz_target!(|data: Vec| { 8 | let mut codec_params = CodecParameters::new(); 9 | codec_params.for_codec(CODEC_TYPE_MP3); 10 | 11 | let mut decoder = MpaDecoder::try_new(&codec_params, &Default::default()).unwrap(); 12 | 13 | let packet = Packet::new_from_boxed_slice(0, 0, 0, data.into_boxed_slice()); 14 | let _ = decoder.decode(&packet); 15 | }); 16 | --------------------------------------------------------------------------------