diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index d693b4c..70fe1d1 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -16,9 +16,10 @@ jobs: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - name: Lint Code Base - uses: github/super-linter@v3 + uses: github/super-linter@v4 env: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # conventional-changelog's generated CHANGELOGs don't pass linting checks - https://github.com/conventional-changelog/conventional-changelog/issues/615 FILTER_REGEX_EXCLUDE: ./CHANGELOG.md + VALIDATE_TERRAFORM_TERRASCAN: false diff --git a/Cargo.lock b/Cargo.lock index 90f5534..be328c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -133,18 +135,18 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.119" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.119" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -153,9 +155,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" dependencies = [ "itoa", "ryu", @@ -170,9 +172,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" dependencies = [ "clap", "lazy_static", @@ -181,9 +183,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", @@ -194,9 +196,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.58" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 850ef84..f584aed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,13 @@ name = "webmencoder" version = "0.0.2" authors = ["Matt Burgess <549318+mattburgess@users.noreply.github.com>"] -edition = "2018" +edition = "2021" license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] glob = "0.3.0" -serde = { version = "1.0.119", features = ["derive"] } -serde_json = "1.0.61" -structopt = "0.3.21" +serde = { version = "1.0.130", features = ["derive"] } +serde_json = "1.0.72" +structopt = "0.3.25" diff --git a/README.md b/README.md index 9e8b200..1ee3b56 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ ## Reason For Existence -FFmpeg is an incredibly useful tool for converting source media into other formats that are more space-efficient. As well as there being a large number of choices to make regarding the output media format, target quality/bit-rates, etc. target-device compatibility can further constrain what choices can be made. The end result of this is that the FFmpeg command line can become quite a complex beast, and having to construct that on a source by source basis can be both frustrating and error prone. +FFmpeg is an incredibly useful tool for converting source media into other formats that are more space-efficient. As well as there being a large number of choices to make regarding the output media format, target quality/bit-rates, etc. target-device compatibility can further constrain what choices can be made. The end result of this is that the FFmpeg command-line can become quite a complex +beast, and having to construct that on a source by source basis can be both frustrating and error-prone. -This tool automates the process of invoking FFmpeg with the correct command line options based on the input media and the opinions described below. +This tool automates the process of invoking FFmpeg with the correct command-line options based on the input media and the opinions described below. ## Webmencoder's Opinions @@ -18,19 +19,24 @@ This tool automates the process of invoking FFmpeg with the correct command line ### Video Encoding -* Codec: VP9 - it's open and royalty-free. It also has good support in open source media players and web browsers. Whilst AV1 is [recommended](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs#Recommendations_for_high-quality_video_presentation), it lacks [hardware encoding support](https://en.wikipedia.org/wiki/Intel_Quick_Sync_Video), at least on recent Intel CPUs, and software encoding is still pretty slow in comparison to VP9. -* Encoding method: Hardware - best current practice suggests the use of 2-pass encoding to obtain reasonable video quality using software encoding. On my hardware, it takes on average 25% of the video duration to complete the 1st pass, and 250% to complete the 2nd pass, for a total duration of 275%. In contrast, hardware encoding completes in 16% of the video duration and requires only a single pass to achieve similar quality levels. Using software, it will take approximately 124 minutes to encode a 45 minute video compared to just 7 minutes using hardware. +* Codec: VP9 - it's open and royalty-free. It also has good support in open source media players and web browsers. Whilst AV1 is [recommended](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs#Recommendations_for_high-quality_video_presentation), it lacks [hardware encoding support](https://en.wikipedia.org/wiki/Intel_Quick_Sync_Video), at least on recent Intel CPUs, and +software encoding is still pretty slow in comparison to VP9. +* Encoding method: Hardware - best current practice suggests the use of 2-pass encoding to obtain reasonable video quality using software encoding. On my hardware, it takes on average 25% of the video duration to complete the 1st pass, and 250% to complete the 2nd pass, for a total duration of 275%. In contrast, hardware encoding completes in 16% of the video duration and requires only a single +pass to achieve similar quality levels. Using software, it will take approximately 124 minutes to encode a 45 minute video compared to just 7 minutes using hardware. * Resolution, frame-rate, etc.: Original media resolutions will be used for the encoded video. This leaves the client device in control of any up- or down-scaling required. ### Audio Encoding -* Codec: Vorbis - Although the WebM container format supports both Vorbis and Opus codecs, some of my existing devices [don't support Opus natively](https://developer.samsung.com/smarttv/develop/specifications/media-specifications/2018-tv-video-specifications.html), resulting in my NAS having to transcode media on the fly. If it wasn't for that issue, I'd probably have selected Opus instead, using the Fullband profile. -* Channels, sample rate, etc.: All audio streams will be included in the encoded media, using the same channel layout (7, 5.1, stereo, etc.) and sample rate as the original. The only exception to this is when the original media contains 7.1 channel DTS streams (e.g. from BluRay discs). As some of my existing devices [only support 5.1 channels in Vorbis streams](https://developer.samsung.com/smarttv/develop/specifications/media-specifications/2018-tv-video-specifications.html) these have to be converted to 5.1 channel streams, otherwise playback is impossible to watch due to its constant buffering and stuttering. If my devices _did_ support Opus, then this utility would also change `5.1(side)` streams (commonly found on DVD media) to `5.1` due to an [FFmpeg bug](https://trac.ffmpeg.org/ticket/5718). +* Codec: Vorbis - Although the WebM container format supports both Vorbis and Opus codecs, some of my existing devices [don't support Opus natively](https://developer.samsung.com/smarttv/develop/specifications/media-specifications/2018-tv-video-specifications.html), resulting in my NAS having to transcode media on the fly. If it wasn't for that issue, I'd probably have selected Opus instead, +using the Fullband profile. +* Channels, sample rate, etc.: All audio streams will be included in the encoded media, using the same channel layout (7, 5.1, stereo, etc.) and sample rate as the original. The only exception to this is when the original media contains 7.1 channel DTS streams (e.g. from BluRay discs). As some of my existing devices [only support 5.1 channels in Vorbis streams](https://developer.samsung.com/smarttv/develop/specifications/media-specifications/2018-tv-video-specifications.html) +these have to be converted to 5.1 channel streams, otherwise playback is impossible to watch due to its constant buffering and stuttering. If my devices _did_ support Opus, then this utility would also change `5.1(side)` streams (commonly found on DVD media) to `5.1` due to an [FFmpeg bug](https://trac.ffmpeg.org/ticket/5718). * Metadata: Language metadata will be added to each audio stream; this helps media players automatically select the correct stream based on a user's language preference settings. ### Subtitles -* All subtitles are currently skipped/stripped. Whilst retaining them would be in-keeping with `webmencoder`s general principle of preserving as much of the original media's contents and settings as possible, I'm yet to find a suitable open source tool that can extract subtitles from an MPEG file, OCR them, and convert them to `WebVTT` format for inclusion into the WebM container. If anyone knows of such a tool, I'll be happy to add subtitle support! +* All subtitles are currently skipped/stripped. Whilst retaining them would be in-keeping with `webmencoder`s general principle of preserving as much of the original media's contents and settings as possible, I'm yet to find a suitable open source tool that can extract subtitles from an MPEG file, OCR them, and convert them to `WebVTT` format for inclusion into the WebM container. If anyone knows +of such a tool, I'll be happy to add subtitle support! ## Usage @@ -49,12 +55,13 @@ OPTIONS: -q, --quality [default: 100] ``` -As you can see, running `webmencoder` is rather trivial. You simply give it an input directory and an output directory and it will recursively process all `.mpeg` files it finds and convert them to `.webm` files. You can optionally provide it with a quality setting, if you find the default to be either too poor in quality or results in files larger than you'd prefer. It's also possible to tell it what hardware device to use to perform the encoding in case the default isn't suitable. +As you can see, running `webmencoder` is rather trivial. You simply give it an input directory and an output directory and it will recursively process all `.mpeg` files it finds and convert them to `.webm` files. You can optionally provide it with a quality setting, if you find the default to be either too poor in quality or results in files larger than you'd prefer. It's also possible to tell it +what hardware device to use to perform the encoding in case the default isn't suitable. ## TODO * Error handling -* Allow audio stream id -> language mappings to be passed in at runtime instead of being hardcoded; these are unlikely to be consistent across input files +* Allow audio stream ID -> language mappings to be passed in at runtime instead of being hardcoded; these are unlikely to be consistent across input files ## License diff --git a/github.tf b/github.tf index 5aef5a0..86df02c 100644 --- a/github.tf +++ b/github.tf @@ -19,6 +19,7 @@ variable "github_token" { } resource "github_repository" "webmencoder" { + #ts:skip=AC_GITHUB_0002 repo is intentionally public name = "webmencoder" description = "An opinionated CLI for converting MPEG to WebM" has_issues = true diff --git a/src/main.rs b/src/main.rs index 08e257d..48642a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,7 @@ enum Stream { id: String, }, Subtitle, + Data, } #[derive(Debug, Deserialize)] @@ -45,8 +46,8 @@ fn default_channel_layout() -> String { "stereo".to_string() } -fn get_lang_from_id(id: &String) -> &str { - match id.as_str() { +fn get_lang_from_id(id: &str) -> &str { + match id { "0x80" => "eng", "0x81" => "fra", "0x82" => "spa", @@ -58,8 +59,8 @@ fn build_command( metadata: &Metadata, hw_device: &str, quality: u8, - input_vid: &std::path::PathBuf, - output_dir: &std::path::PathBuf, + input_vid: &std::path::Path, + output_dir: &std::path::Path, ) -> Result<(), std::io::Error> { let mut a_filters = Vec::new(); let mut mappings = Vec::new(); @@ -104,6 +105,8 @@ fn build_command( } Stream::Subtitle => {} + + Stream::Data => {} } } @@ -147,7 +150,7 @@ fn build_command( Ok(()) } -fn get_video_details(input_vid: &std::path::PathBuf) -> Result { +fn get_video_details(input_vid: &std::path::Path) -> Result { let ffprobe = Command::new("ffprobe") .args(&[ "-v", @@ -164,8 +167,8 @@ fn get_video_details(input_vid: &std::path::PathBuf) -> Result Result<(), std::io::Error> { @@ -174,8 +177,8 @@ fn convert_file( } fn convert_files( - input_dir: &std::path::PathBuf, - output_dir: &std::path::PathBuf, + input_dir: &std::path::Path, + output_dir: &std::path::Path, hw_device: &str, quality: u8, ) -> Result<(), std::io::Error> {