Skip to content

Commit

Permalink
fix: Handle DVDs with data streams
Browse files Browse the repository at this point in the history
Some DVDs have streams of type 'data'. Simply ignore these for now.

fix(build): Fix Markdown linting issues

fix(build): Use latest version of super-linter action

fix(build): Fix textlint errors

fix(build): Fix clippy warnings

fix(build): Bump dependencies

fix(build): Disable broken Terrascan linter
  • Loading branch information
mattburgess committed Nov 27, 2021
1 parent 9e7d32b commit 96d5877
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 14 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -49,12 +55,13 @@ OPTIONS:
-q, --quality <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

Expand Down
1 change: 1 addition & 0 deletions github.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 12 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum Stream {
id: String,
},
Subtitle,
Data,
}

#[derive(Debug, Deserialize)]
Expand All @@ -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",
Expand All @@ -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();
Expand Down Expand Up @@ -104,6 +105,8 @@ fn build_command(
}

Stream::Subtitle => {}

Stream::Data => {}
}
}

Expand Down Expand Up @@ -147,7 +150,7 @@ fn build_command(
Ok(())
}

fn get_video_details(input_vid: &std::path::PathBuf) -> Result<Metadata, std::io::Error> {
fn get_video_details(input_vid: &std::path::Path) -> Result<Metadata, std::io::Error> {
let ffprobe = Command::new("ffprobe")
.args(&[
"-v",
Expand All @@ -164,8 +167,8 @@ fn get_video_details(input_vid: &std::path::PathBuf) -> Result<Metadata, std::io
}

fn convert_file(
input_vid: &std::path::PathBuf,
output_dir: &std::path::PathBuf,
input_vid: &std::path::Path,
output_dir: &std::path::Path,
hw_device: &str,
quality: u8,
) -> Result<(), std::io::Error> {
Expand All @@ -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> {
Expand Down

0 comments on commit 96d5877

Please sign in to comment.