other_video_transcoding 0.1.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/LICENSE +1 -1
- data/README.md +21 -1
- data/bin/ask-ffmpeg-log +13 -9
- data/bin/other-transcode +453 -128
- data/other_video_transcoding-0.1.0.gem +0 -0
- data/other_video_transcoding-0.1.1.gem +0 -0
- data/other_video_transcoding-0.2.0.gem +0 -0
- data/other_video_transcoding-0.3.0.gem +0 -0
- data/other_video_transcoding-0.3.1.gem +0 -0
- data/other_video_transcoding.gemspec +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfd039fdab5dca779448614baaf5f0e99b3d7dc883f97693919a016224e4b7aa
|
4
|
+
data.tar.gz: c97d8899ea867a018059d1896c3608e3982f8677f8fe81c7506097171c7591e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1915b0bdacf95a0bdfe5e825497f9081571318aaf9423a10df614b0f5ab7a74949ebd6c6039c4ef09c5ce2d567dbe6a851019f996d64284a1ab0525a0a7c10e2
|
7
|
+
data.tar.gz: 64f23ade8dc74a15f8024313b66cd91b3298e80d4e39be391f47630ddd2aa812c67271bfa522794202dd33b4ce18b601c8cbf77a8b46078f288c050afd5eef3a
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,62 @@
|
|
2
2
|
|
3
3
|
This single document contains all of the notes created for each [release](https://github.com/donmelton/other_video_transcoding/releases).
|
4
4
|
|
5
|
+
## [0.3.2](https://github.com/donmelton/other_video_transcoding/releases/tag/0.3.2)
|
6
|
+
|
7
|
+
Friday, September 11, 2020
|
8
|
+
|
9
|
+
* Modify `other-transcode` to use a new `ffmpeg` Matroksa muxer option so the `-disposition` option is once again honored when using `ffmpeg` version 4.3 and later.
|
10
|
+
* Change the codec ID from the default of `hev1` to `hvc1` for HEVC video in MP4 output from `other-transcode` to enable playback in QuickTime on macOS. Via [ #50](https://github.com/donmelton/other_video_transcoding/issues/50).
|
11
|
+
* Convert added SRT format subtitles to MOV-compatible format in MP4 output from `other-transcode`. Via [ #55](https://github.com/donmelton/other_video_transcoding/issues/55).
|
12
|
+
|
13
|
+
## [0.3.1](https://github.com/donmelton/other_video_transcoding/releases/tag/0.3.1)
|
14
|
+
|
15
|
+
Tuesday, May 26, 2020
|
16
|
+
|
17
|
+
* Modify the `--preview-crop` option in `other-transcode` to show commands compatible with newer versions of `mpv`.
|
18
|
+
* No longer force a NTSC film frame rate for interlaced inputs in PAL MPEG-2 format.
|
19
|
+
* When using the `--dry-run` option in `other-transcode`, issue a warning instead of failing if the output or log files already exist.
|
20
|
+
* Add a link to another Docker container for Linux in the "README" document. Thanks, @ttyS0!
|
21
|
+
|
22
|
+
## [0.3.0](https://github.com/donmelton/other_video_transcoding/releases/tag/0.3.0)
|
23
|
+
|
24
|
+
Thursday, February 27, 2020
|
25
|
+
|
26
|
+
* Add a `--scan` option to `other-transcode`. This prints media information and then exits, allowing easy identification of track numbers and formats. Via [ #11](https://github.com/donmelton/other_video_transcoding/issues/11).
|
27
|
+
* Add a `--mono-bitrate` option to `other-transcode`. This sets the mono audio bitrate, which is otherwise 50% of the stereo bitrate.
|
28
|
+
* Raise the maximum bitrates for audio in AAC format to 320 Kbps for stereo and 256 Kbps for mono. The default birates remain the same.
|
29
|
+
* Add a `--all-eac3` option to `other-transcode`. This uses the Dolby Digital Plus (Enhanced AC-3) format for all transcoded audio. The behavior of the `--eac3` option, which uses Dolby Digital Plus for surround audio only, remains the same.
|
30
|
+
* Add a `--keep-ac3-stereo` option to `other-transcode`. This copies stereo and mono audio in AC-3 format even when the original source bitrate is above the output transcoding bitrate.
|
31
|
+
* Add a `--pass-dts` option to `other-transcode`. This enables passthrough of audio in DTS and DTS-ES formats. However, such audio also in surround format will still be transcoded if that audio is output to a stereo-width track.
|
32
|
+
* Add `--rc-maxrate` and `--rc-bufsize` options to `other-transcode`. These set the ratecontrol maximum rate and/or the buffer size as a multiple of the video bitrate target, but only for certain encoders and ratecontrol systems.
|
33
|
+
* Add a `--x264-mbtree` option to `other-transcode`. This uses macroblock-tree ratecontrol and disables AVBR if in use.
|
34
|
+
* In order to ensure compatible H.264 levels, limit the number of reference frames when using the `x264` encoder with slower presets.
|
35
|
+
* Remove the deprecated `--name` option of `other-transcode`.
|
36
|
+
* Add a link to a Docker container for Linux in the "README" document. Thanks, @ttyS0!
|
37
|
+
|
38
|
+
## [0.2.0](https://github.com/donmelton/other_video_transcoding/releases/tag/0.2.0)
|
39
|
+
|
40
|
+
Monday, January 13, 2020
|
41
|
+
|
42
|
+
* Allow `all` to be used as an argument to the `--add-audio` and `--add-subtitle` options of `other-transcode`, adding all audio tracks or all subtitle tracks. Via [ #3](https://github.com/donmelton/other_video_transcoding/issues/3).
|
43
|
+
* Add `original` as a width attribute to the `--main-audio` and `--add-audio` options of `other-transcode`. Unlike `stereo` and `surround`, this disables transcoding and always copies the selected track(s). Via [ #5](https://github.com/donmelton/other_video_transcoding/issues/5).
|
44
|
+
* Add a `--copy-video` option to `other-transcode`. This disables transcoding and copies the original video track to the output.
|
45
|
+
* No longer ignore any image-based subtitles added to MP4 output. Instead, let `ffmpeg` foolishly add DVD-style subtitles and (currently) fail when adding Blu-ray Disc-style subtitles.
|
46
|
+
* Deprecate the `--name` option of `other-transcode` because it doesn't make sense to name only the first output file from a tool which can take multiple inputs. The option still works for now, but using it issues a warning message. It will be removed in a future release.
|
47
|
+
* Remove warnings when other options disable the Nvidia video decoder, which could only happen if the `--burn-subtitle` or `--detelecine` options were used with the `--cuvid` option.
|
48
|
+
|
49
|
+
## [0.1.1](https://github.com/donmelton/other_video_transcoding/releases/tag/0.1.1)
|
50
|
+
|
51
|
+
Friday, January 3, 2020
|
52
|
+
|
53
|
+
* Prevent passing full or partial paths to the `--name` option of `other-transcode`.
|
54
|
+
* Hide the path prefix when naming the program in `--help` output and in usage errors for both `other-transcode` and `ask-ffmpeg-log`.
|
55
|
+
* In the "README" document:
|
56
|
+
* Add warnings to avoid installing within virtual machines and about the possible need to use `sudo`.
|
57
|
+
* Add a link to additional documentation on the wiki for installing `ffprobe`, `ffmpeg`, `mkvpropedit` and `mpv` on Windows.
|
58
|
+
* Also explain how to install those same programs on macOS using Homebrew.
|
59
|
+
* Update all copyright notices to the year 2020.
|
60
|
+
|
5
61
|
## [0.1.0](https://github.com/donmelton/other_video_transcoding/releases/tag/0.1.0)
|
6
62
|
|
7
63
|
Thursday, December 26, 2019
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -26,7 +26,7 @@ And many features are supported including:
|
|
26
26
|
* Hardware-based video decoding for improved performance
|
27
27
|
* Fallback to software video encoding when appropriate hardware is not available
|
28
28
|
* Optional automatic and reliable video cropping
|
29
|
-
* Adding audio and subtitle tracks by
|
29
|
+
* Adding audio and subtitle tracks by language or title
|
30
30
|
* [Dolby Digital Plus](https://en.wikipedia.org/wiki/Dolby_Digital_Plus) (Enhanced AC-3) audio encoding
|
31
31
|
* Burning image-based subtitles into video output to ease player compatibility
|
32
32
|
|
@@ -36,6 +36,8 @@ Additional documentation for this project is available in the [wiki](https://git
|
|
36
36
|
|
37
37
|
## Installation
|
38
38
|
|
39
|
+
_Avoid installing within [virtual machines](https://en.wikipedia.org/wiki/Virtual_machine) such as the [Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) since access to hardware video encoders may not be allowed, severely impacting performance._
|
40
|
+
|
39
41
|
These tools work on Windows, Linux and macOS. They're packaged as a Gem and require Ruby. See "[Installing Ruby](https://www.ruby-lang.org/en/documentation/installation/)" if you don't have it on your platform.
|
40
42
|
|
41
43
|
Use this command to install the package:
|
@@ -46,6 +48,8 @@ And this command to update it:
|
|
46
48
|
|
47
49
|
gem update other_video_transcoding
|
48
50
|
|
51
|
+
_The commands to install and update may need prefixing with_ `sudo` _on some platforms._
|
52
|
+
|
49
53
|
The `other-transcode` tool in this package requires other software to function properly, specifically these command line programs:
|
50
54
|
|
51
55
|
* `ffprobe`
|
@@ -56,6 +60,22 @@ Optional crop previewing also requires the `mpv` command line program.
|
|
56
60
|
|
57
61
|
See "[Download FFmpeg](https://ffmpeg.org/download.html)," "[MKVToolNix Downloads](https://mkvtoolnix.download/downloads.html)" and "[mpv Installation](https://mpv.io/installation/)" to find versions for your platform.
|
58
62
|
|
63
|
+
Additional documentation for installing these programs on Windows is available in the [wiki](https://github.com/donmelton/other_video_transcoding/wiki/Windows).
|
64
|
+
|
65
|
+
[Docker](https://en.wikipedia.org/wiki/Docker_(software)) containers for Linux, including installation instructions, are available here:
|
66
|
+
|
67
|
+
https://github.com/ttyS0/docker-transcode-nvidia
|
68
|
+
|
69
|
+
https://github.com/ttyS0/docker-transcode-vaapi
|
70
|
+
|
71
|
+
On macOS, all of these programs can be easily installed via [Homebrew](http://brew.sh/), an optional package manager:
|
72
|
+
|
73
|
+
brew install ffmpeg
|
74
|
+
brew install mkvtoolnix
|
75
|
+
brew install mpv
|
76
|
+
|
77
|
+
The `ffprobe` program is included within the `ffmpeg` package and the `mkvpropedit` program is included within the `mkvtoolnix` package.
|
78
|
+
|
59
79
|
## Usage
|
60
80
|
|
61
81
|
Each tool in this package has several command line options. The `other-transcode` tool is the most complex with over 50 of its own. Use `--help` to list the options available for a specific tool, along with brief instructions on their usage:
|
data/bin/ask-ffmpeg-log
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ask-ffmpeg-log
|
4
4
|
#
|
5
|
-
# Copyright (c) 2019 Don Melton
|
5
|
+
# Copyright (c) 2019-2020 Don Melton
|
6
6
|
#
|
7
7
|
|
8
8
|
require 'abbrev'
|
@@ -15,18 +15,18 @@ module Transcoding
|
|
15
15
|
|
16
16
|
class Command
|
17
17
|
def about
|
18
|
-
|
19
|
-
ask-ffmpeg-log 0.
|
20
|
-
Copyright (c) 2019 Don Melton
|
21
|
-
HERE
|
18
|
+
<<-HERE
|
19
|
+
ask-ffmpeg-log 0.3.2
|
20
|
+
Copyright (c) 2019-2020 Don Melton
|
21
|
+
HERE
|
22
22
|
end
|
23
23
|
|
24
24
|
def usage
|
25
|
-
|
25
|
+
<<-HERE
|
26
26
|
Report temporal information from ffmpeg-generated `.log` files
|
27
27
|
containing encoding statistics.
|
28
28
|
|
29
|
-
Usage: #{$PROGRAM_NAME} [OPTION]... [FILE|DIRECTORY]...
|
29
|
+
Usage: #{File.basename($PROGRAM_NAME)} [OPTION]... [FILE|DIRECTORY]...
|
30
30
|
|
31
31
|
Options:
|
32
32
|
--time sort results by time instead of speed
|
@@ -34,7 +34,7 @@ Options:
|
|
34
34
|
--tabular use tab character as field delimiter and suppress labels
|
35
35
|
-h, --help display this help and exit
|
36
36
|
--version output version information and exit
|
37
|
-
HERE
|
37
|
+
HERE
|
38
38
|
end
|
39
39
|
|
40
40
|
def initialize
|
@@ -65,11 +65,13 @@ HERE
|
|
65
65
|
end
|
66
66
|
|
67
67
|
fail UsageError, 'missing argument' if ARGV.empty?
|
68
|
+
|
68
69
|
ARGV.each { |arg| process_input arg }
|
69
70
|
complete
|
70
71
|
exit
|
71
72
|
rescue UsageError => e
|
72
|
-
Kernel.warn "#{$PROGRAM_NAME}: #{e}
|
73
|
+
Kernel.warn "#{$PROGRAM_NAME}: #{e}"
|
74
|
+
Kernel.warn "Try `#{File.basename($PROGRAM_NAME)} --help` for more information."
|
73
75
|
exit false
|
74
76
|
rescue StandardError => e
|
75
77
|
Kernel.warn "#{$PROGRAM_NAME}: #{e}"
|
@@ -91,10 +93,12 @@ HERE
|
|
91
93
|
if File.directory? input
|
92
94
|
logs = Dir[input + File::SEPARATOR + '*.log']
|
93
95
|
fail "does not contain `.log` files: #{input}" if logs.empty?
|
96
|
+
|
94
97
|
@logs += logs
|
95
98
|
@paths << input
|
96
99
|
else
|
97
100
|
fail "not a `.log` file: #{input}" unless File.extname(input) == '.log'
|
101
|
+
|
98
102
|
@logs << File.absolute_path(input)
|
99
103
|
@paths << File.dirname(input)
|
100
104
|
end
|
data/bin/other-transcode
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# other-transcode
|
4
4
|
#
|
5
|
-
# Copyright (c) 2019 Don Melton
|
5
|
+
# Copyright (c) 2019-2020 Don Melton
|
6
6
|
#
|
7
7
|
|
8
8
|
require 'English'
|
@@ -17,73 +17,72 @@ module Transcoding
|
|
17
17
|
|
18
18
|
class Command
|
19
19
|
def about
|
20
|
-
|
21
|
-
other-transcode 0.
|
22
|
-
Copyright (c) 2019 Don Melton
|
23
|
-
HERE
|
20
|
+
<<-HERE
|
21
|
+
other-transcode 0.3.2
|
22
|
+
Copyright (c) 2019-2020 Don Melton
|
23
|
+
HERE
|
24
24
|
end
|
25
25
|
|
26
26
|
def usage1
|
27
|
-
|
27
|
+
<<-HERE
|
28
28
|
Transcode Blu-ray Disc or DVD rip into a smaller, more portable format
|
29
29
|
while remaining high enough quality to be mistaken for the original.
|
30
30
|
|
31
|
-
Usage: #{$PROGRAM_NAME} [OPTION]... [FILE]...
|
31
|
+
Usage: #{File.basename($PROGRAM_NAME)} [OPTION]... [FILE]...
|
32
32
|
|
33
33
|
Creates Matroska `.mkv` format file in current working directory.
|
34
34
|
|
35
35
|
Automatically selects a platform-specific hardware video encoder.
|
36
36
|
|
37
|
-
HERE
|
37
|
+
HERE
|
38
38
|
end
|
39
39
|
|
40
40
|
def usage2
|
41
|
-
|
41
|
+
<<-HERE
|
42
42
|
Input options:
|
43
43
|
--position TIME, --duration TIME
|
44
44
|
start transcoding at position and/or limit to duration
|
45
45
|
in seconds[.milliseconds] or [HH:]MM:SS[.m...] format
|
46
46
|
|
47
|
-
HERE
|
47
|
+
HERE
|
48
48
|
end
|
49
49
|
|
50
50
|
def usage3
|
51
|
-
|
51
|
+
<<-HERE
|
52
52
|
Output options:
|
53
53
|
--debug increase diagnostic information
|
54
|
+
--scan print media information and exit
|
54
55
|
--preview-crop show commands to preview detected video crop and exit
|
55
|
-
HERE
|
56
|
+
HERE
|
56
57
|
end
|
57
58
|
|
58
59
|
def usage4
|
59
|
-
|
60
|
+
<<-HERE
|
60
61
|
--print-crop print only detected video crop geometry and exit
|
61
62
|
--mp4 output MP4 instead of Matroska `.mkv` format
|
62
|
-
--name STRING set output filename, excluding format extension
|
63
|
-
(default: based on input filename)
|
64
63
|
--copy-track-names
|
65
64
|
copy all input audio track names to output
|
66
|
-
HERE
|
65
|
+
HERE
|
67
66
|
end
|
68
67
|
|
69
68
|
def usage5
|
70
|
-
|
69
|
+
<<-HERE
|
71
70
|
--max-muxing-queue-size SIZE
|
72
71
|
set maximum number of packets to buffer when muxing
|
73
|
-
HERE
|
72
|
+
HERE
|
74
73
|
end
|
75
74
|
|
76
75
|
def usage6
|
77
|
-
|
76
|
+
<<-HERE
|
78
77
|
-n, --dry-run don't transcode, just show `ffmpeg` command and exit
|
79
78
|
|
80
79
|
Video options:
|
81
80
|
--hevc use HEVC version of platform-specific video encoder
|
82
|
-
HERE
|
81
|
+
HERE
|
83
82
|
end
|
84
83
|
|
85
84
|
def usage7
|
86
|
-
|
85
|
+
<<-HERE
|
87
86
|
--vt use Apple Video Toolbox encoder
|
88
87
|
--nvenc use Nvidia video encoder
|
89
88
|
--qsv use Intel Quick Sync video encoder
|
@@ -102,11 +101,11 @@ HERE
|
|
102
101
|
--cuvid use Nvidia video decoder
|
103
102
|
for H.264, VC-1, MPEG-2 and other formats
|
104
103
|
(ignores scope set by `--decode`)
|
105
|
-
HERE
|
104
|
+
HERE
|
106
105
|
end
|
107
106
|
|
108
107
|
def usage8
|
109
|
-
|
108
|
+
<<-HERE
|
110
109
|
--target [2160p=|1080p=|720p=|480p=]BITRATE
|
111
110
|
set video bitrate target (default: based on input)
|
112
111
|
or target for specific input resolution
|
@@ -114,11 +113,11 @@ HERE
|
|
114
113
|
set video crop geometry (default: none)
|
115
114
|
or automatically detect it
|
116
115
|
--720p fit video within 1280x720 pixel bounds
|
117
|
-
HERE
|
116
|
+
HERE
|
118
117
|
end
|
119
118
|
|
120
119
|
def usage9
|
121
|
-
|
120
|
+
<<-HERE
|
122
121
|
--1080p " " " 1920x1080 " "
|
123
122
|
--deinterlace reduce interlace artifacts without changing frame rate
|
124
123
|
(applied automatically for some inputs)
|
@@ -127,11 +126,15 @@ HERE
|
|
127
126
|
--detelecine drop duplicate frames to restore original frame rate
|
128
127
|
(disables any deinterlacing and forced frame rate)
|
129
128
|
--no-filters disable any automatic adjustments via filters
|
130
|
-
HERE
|
129
|
+
HERE
|
131
130
|
end
|
132
131
|
|
133
132
|
def usage10
|
134
|
-
|
133
|
+
<<-HERE
|
134
|
+
--rc-maxrate FACTOR, --rc-bufsize FACTOR
|
135
|
+
set ratecontrol maximum rate and/or buffer size
|
136
|
+
as multiple of video bitrate target
|
137
|
+
--copy-video disable transcoding and copy original video track
|
135
138
|
|
136
139
|
Apple Video Toolbox encoder options:
|
137
140
|
--vt-allow-sw allow software encoding
|
@@ -172,22 +175,36 @@ Video Acceleration API encoder options:
|
|
172
175
|
|
173
176
|
x264 software video encoder options:
|
174
177
|
--x264-avbr use average variable bitrate (AVBR) ratecontrol
|
178
|
+
--x264-mbtree use macroblock-tree ratecontrol (disables AVBR if in use)
|
175
179
|
--x264-quick increase encoding speed by 70-80%
|
176
180
|
with no easily perceptible loss in video quality
|
177
181
|
(avoids quality problems with some encoder presets)
|
178
|
-
HERE
|
182
|
+
HERE
|
179
183
|
end
|
180
184
|
|
181
185
|
def usage11
|
182
|
-
|
186
|
+
<<-HERE
|
183
187
|
|
184
188
|
Audio options:
|
185
189
|
--main-audio TRACK[=WIDTH]
|
186
190
|
select main audio track by number (default: 1)
|
187
191
|
with optional width (default: surround)
|
188
|
-
|
192
|
+
HERE
|
193
|
+
end
|
194
|
+
|
195
|
+
def usage12
|
196
|
+
<<-HERE
|
197
|
+
(use `original` to disable transcoding)
|
198
|
+
HERE
|
199
|
+
end
|
200
|
+
|
201
|
+
def usage13
|
202
|
+
<<-HERE
|
203
|
+
--add-audio TRACK|all|LANGUAGE|STRING[=WIDTH]
|
189
204
|
add single audio track by number
|
190
205
|
including main audio track
|
206
|
+
or all audio tracks
|
207
|
+
excluding main audio track
|
191
208
|
or audio tracks by language code
|
192
209
|
excluding main audio track
|
193
210
|
(in ISO 639-2 format, e.g.: `eng`)
|
@@ -195,22 +212,61 @@ Audio options:
|
|
195
212
|
excluding main audio track
|
196
213
|
(comparison is case-insensitve)
|
197
214
|
with optional width (default: stereo)
|
215
|
+
HERE
|
216
|
+
end
|
217
|
+
|
218
|
+
def usage14
|
219
|
+
<<-HERE
|
220
|
+
(use `original` to disable transcoding)
|
221
|
+
HERE
|
222
|
+
end
|
223
|
+
|
224
|
+
def usage15
|
225
|
+
<<-HERE
|
198
226
|
--surround-bitrate BITRATE
|
199
227
|
set surround audio bitrate (default: 640)
|
200
228
|
--stereo-bitrate BITRATE
|
201
229
|
set stereo audio bitrate (default: 256)
|
230
|
+
HERE
|
231
|
+
end
|
232
|
+
|
233
|
+
def usage16
|
234
|
+
<<-HERE
|
235
|
+
--mono-bitrate BITRATE
|
236
|
+
set mono audio bitrate (default: 50% of stereo bitrate)
|
237
|
+
HERE
|
238
|
+
end
|
239
|
+
|
240
|
+
def usage17
|
241
|
+
<<-HERE
|
202
242
|
--eac3 use Enhanced AC-3 format for surround audio
|
243
|
+
HERE
|
244
|
+
end
|
245
|
+
|
246
|
+
def usage18
|
247
|
+
<<-HERE
|
248
|
+
--all-eac3 " " " " " all audio
|
249
|
+
--keep-ac3-stereo
|
250
|
+
copy stereo and mono audio in AC-3 format
|
251
|
+
even when orginal bitrate is above transcoding bitrate
|
252
|
+
--pass-dts enable passthrough of audio in DTS and DTS-ES formats
|
253
|
+
HERE
|
254
|
+
end
|
255
|
+
|
256
|
+
def usage19
|
257
|
+
<<-HERE
|
203
258
|
|
204
259
|
Subtitle options:
|
205
|
-
--add-subtitle TRACK[=forced]|auto|LANGUAGE|STRING
|
260
|
+
--add-subtitle TRACK[=forced]|auto|all|LANGUAGE|STRING
|
206
261
|
add single subtitle track by number
|
207
262
|
optionally setting forced disposition
|
208
263
|
or enable automatic addition of forced subtitle
|
209
|
-
or add subtitle tracks
|
264
|
+
or add all subtitle tracks
|
265
|
+
or subtitle tracks by language code
|
210
266
|
(in ISO 639-2 format, e.g.: `eng`)
|
211
267
|
or subtitle tracks with titles containing string
|
212
268
|
(comparison is case-insensitve)
|
213
|
-
(
|
269
|
+
(variations exclude any burned track)
|
214
270
|
--burn-subtitle TRACK|auto
|
215
271
|
burn subtitle track by number into video
|
216
272
|
or enable automatic burning of forced subtitle
|
@@ -223,17 +279,18 @@ Other options:
|
|
223
279
|
--version output version information and exit
|
224
280
|
|
225
281
|
Requires `ffprobe`, `ffmpeg` and `mkvpropedit`.
|
226
|
-
HERE
|
282
|
+
HERE
|
227
283
|
end
|
228
284
|
|
229
285
|
def initialize
|
230
286
|
@position = nil
|
231
287
|
@duration = nil
|
232
288
|
@debug = false
|
289
|
+
@scan = false
|
233
290
|
@detect = false
|
234
291
|
@preview = false
|
235
292
|
@format = :mkv
|
236
|
-
@
|
293
|
+
@mkv_options = []
|
237
294
|
@copy_track_names = false
|
238
295
|
@max_muxing_queue_size = nil
|
239
296
|
@dry_run = false
|
@@ -255,6 +312,8 @@ HERE
|
|
255
312
|
@rate = nil
|
256
313
|
@detelecine = false
|
257
314
|
@enable_filters = true
|
315
|
+
@maxrate = nil
|
316
|
+
@bufsize = nil
|
258
317
|
@vt_allow_sw = false
|
259
318
|
@nvenc_spatial_aq = nil
|
260
319
|
@nvenc_temporal_aq = nil
|
@@ -270,6 +329,7 @@ HERE
|
|
270
329
|
@amf_bframes = nil
|
271
330
|
@vaapi_compression = nil
|
272
331
|
@x264_avbr = false
|
332
|
+
@x264_mbtree = false
|
273
333
|
@x264_quick = false
|
274
334
|
@audio_selections = [{
|
275
335
|
:track => 1,
|
@@ -279,8 +339,11 @@ HERE
|
|
279
339
|
}]
|
280
340
|
@surround_bitrate = 640
|
281
341
|
@stereo_bitrate = 256
|
342
|
+
@mono_bitrate = nil
|
282
343
|
@surround_encoder = 'ac3'
|
283
344
|
@stereo_encoder = nil
|
345
|
+
@keep_ac3_stereo = false
|
346
|
+
@pass_dts = false
|
284
347
|
@subtitle_selections = []
|
285
348
|
@auto_add_subtitle = false
|
286
349
|
@burn_subtitle_track = 0
|
@@ -295,12 +358,16 @@ HERE
|
|
295
358
|
case arg
|
296
359
|
when 'full'
|
297
360
|
puts usage1 + usage2 + usage3 + usage4 + usage5 + usage6 +
|
298
|
-
usage7 + usage8 + usage9 + usage10 + usage11
|
361
|
+
usage7 + usage8 + usage9 + usage10 + usage11 + usage12 +
|
362
|
+
usage13 + usage14 + usage15 + usage16 + usage17 +
|
363
|
+
usage18 + usage19
|
299
364
|
when 'more'
|
300
365
|
puts usage1 + usage2 + usage3 + usage4 + usage6 + usage7 +
|
301
|
-
usage8 + usage9 + usage11
|
366
|
+
usage8 + usage9 + usage11 + usage13 + usage15 + usage16 +
|
367
|
+
usage17 + usage18 + usage19
|
302
368
|
else
|
303
|
-
puts usage1 + usage3 + usage6 + usage8 + usage11
|
369
|
+
puts usage1 + usage3 + usage6 + usage8 + usage11 + usage13 +
|
370
|
+
usage15 + usage17 + usage19
|
304
371
|
end
|
305
372
|
|
306
373
|
exit
|
@@ -316,11 +383,13 @@ HERE
|
|
316
383
|
end
|
317
384
|
|
318
385
|
fail UsageError, 'missing argument' if ARGV.empty?
|
386
|
+
|
319
387
|
configure ARGV.first
|
320
388
|
ARGV.each { |arg| process_input arg }
|
321
389
|
exit
|
322
390
|
rescue UsageError => e
|
323
|
-
Kernel.warn "#{$PROGRAM_NAME}: #{e}
|
391
|
+
Kernel.warn "#{$PROGRAM_NAME}: #{e}"
|
392
|
+
Kernel.warn "Try `#{File.basename($PROGRAM_NAME)} --help` for more information."
|
324
393
|
exit false
|
325
394
|
rescue StandardError => e
|
326
395
|
Kernel.warn "#{$PROGRAM_NAME}: #{e}"
|
@@ -343,6 +412,10 @@ HERE
|
|
343
412
|
@debug = true
|
344
413
|
end
|
345
414
|
|
415
|
+
opts.on '--scan' do
|
416
|
+
@scan = true
|
417
|
+
end
|
418
|
+
|
346
419
|
opts.on '--preview-crop' do
|
347
420
|
@detect = true
|
348
421
|
@preview = true
|
@@ -357,10 +430,6 @@ HERE
|
|
357
430
|
@format = :mp4
|
358
431
|
end
|
359
432
|
|
360
|
-
opts.on '--name ARG' do |arg|
|
361
|
-
@name = arg
|
362
|
-
end
|
363
|
-
|
364
433
|
opts.on '--copy-track-names' do
|
365
434
|
@copy_track_names = true
|
366
435
|
end
|
@@ -410,10 +479,12 @@ HERE
|
|
410
479
|
|
411
480
|
opts.on '--[no-]10-bit' do |arg|
|
412
481
|
@ten_bit = arg
|
482
|
+
@encoder = nil if @encoder == 'copy'
|
413
483
|
end
|
414
484
|
|
415
485
|
opts.on '--preset ARG' do |arg|
|
416
486
|
@preset = arg
|
487
|
+
@encoder = nil if @encoder == 'copy'
|
417
488
|
end
|
418
489
|
|
419
490
|
opts.on '--decode ARG' do |arg|
|
@@ -450,6 +521,8 @@ HERE
|
|
450
521
|
else
|
451
522
|
@target = [arg.to_i, 1].max
|
452
523
|
end
|
524
|
+
|
525
|
+
@encoder = nil if @encoder == 'copy'
|
453
526
|
end
|
454
527
|
|
455
528
|
opts.on '--crop ARG' do |arg|
|
@@ -461,22 +534,27 @@ HERE
|
|
461
534
|
else
|
462
535
|
fail UsageError, "invalid crop geometry: #{arg}"
|
463
536
|
end
|
537
|
+
|
538
|
+
@encoder = nil if @encoder == 'copy'
|
464
539
|
end
|
465
540
|
|
466
541
|
opts.on '--720p' do
|
467
542
|
@max_width = 1280
|
468
543
|
@max_height = 720
|
544
|
+
@encoder = nil if @encoder == 'copy'
|
469
545
|
end
|
470
546
|
|
471
547
|
opts.on '--1080p' do
|
472
548
|
@max_width = 1920
|
473
549
|
@max_height = 1080
|
550
|
+
@encoder = nil if @encoder == 'copy'
|
474
551
|
end
|
475
552
|
|
476
553
|
opts.on '--deinterlace' do
|
477
554
|
@deinterlace = true
|
478
555
|
@detelecine = false
|
479
556
|
@enable_filters = false
|
557
|
+
@encoder = nil if @encoder == 'copy' and @decoder_type != :cuvid
|
480
558
|
end
|
481
559
|
|
482
560
|
opts.on '--rate ARG' do |arg|
|
@@ -499,6 +577,7 @@ HERE
|
|
499
577
|
|
500
578
|
@detelecine = false
|
501
579
|
@enable_filters = false
|
580
|
+
@encoder = nil if @encoder == 'copy'
|
502
581
|
end
|
503
582
|
|
504
583
|
opts.on '--detelecine' do
|
@@ -506,12 +585,36 @@ HERE
|
|
506
585
|
@deinterlace = false
|
507
586
|
@rate = nil
|
508
587
|
@enable_filters = false
|
588
|
+
@encoder = nil if @encoder == 'copy'
|
509
589
|
end
|
510
590
|
|
511
591
|
opts.on '--no-filters' do
|
512
592
|
@enable_filters = false
|
513
593
|
end
|
514
594
|
|
595
|
+
opts.on '--rc-maxrate ARG', Float do |arg|
|
596
|
+
@maxrate = arg
|
597
|
+
@encoder = nil if @encoder == 'copy'
|
598
|
+
end
|
599
|
+
|
600
|
+
opts.on '--rc-bufsize ARG', Float do |arg|
|
601
|
+
@bufsize = arg
|
602
|
+
@encoder = nil if @encoder == 'copy'
|
603
|
+
end
|
604
|
+
|
605
|
+
opts.on '--copy-video' do
|
606
|
+
@encoder = 'copy'
|
607
|
+
@hevc = false
|
608
|
+
@ten_bit = nil
|
609
|
+
@preset = nil
|
610
|
+
@target = nil
|
611
|
+
@crop = nil
|
612
|
+
@rate = nil
|
613
|
+
@detelecine = false
|
614
|
+
@enable_filters = false
|
615
|
+
@burn_subtitle_track = 0
|
616
|
+
end
|
617
|
+
|
515
618
|
opts.on '--vt-allow-sw' do
|
516
619
|
@encoder = @hevc ? 'hevc_videotoolbox' : 'h264_videotoolbox'
|
517
620
|
@vt_allow_sw = true
|
@@ -592,6 +695,14 @@ HERE
|
|
592
695
|
@encoder = 'libx264'
|
593
696
|
@hevc = false
|
594
697
|
@x264_avbr = true
|
698
|
+
@x264_mbtree = false
|
699
|
+
end
|
700
|
+
|
701
|
+
opts.on '--x264-mbtree' do
|
702
|
+
@encoder = 'libx264'
|
703
|
+
@hevc = false
|
704
|
+
@x264_mbtree = true
|
705
|
+
@x264_avbr = false
|
595
706
|
end
|
596
707
|
|
597
708
|
opts.on '--x264-quick' do
|
@@ -602,7 +713,7 @@ HERE
|
|
602
713
|
end
|
603
714
|
|
604
715
|
opts.on '--main-audio ARG' do |arg|
|
605
|
-
if arg =~ /^([0-9]+)(?:=(surround|
|
716
|
+
if arg =~ /^([0-9]+)(?:=(stereo|surround|original))?$/
|
606
717
|
@audio_selections[0][:track] = $1.to_i
|
607
718
|
@audio_selections[0][:width] = $2.to_sym unless $2.nil?
|
608
719
|
else
|
@@ -611,7 +722,7 @@ HERE
|
|
611
722
|
end
|
612
723
|
|
613
724
|
opts.on '--add-audio ARG' do |arg|
|
614
|
-
if arg =~ /^([^=]+)(?:=(surround|
|
725
|
+
if arg =~ /^([^=]+)(?:=(stereo|surround|original))?$/
|
615
726
|
scope = $1
|
616
727
|
width = $2
|
617
728
|
|
@@ -646,10 +757,27 @@ HERE
|
|
646
757
|
@stereo_bitrate = arg
|
647
758
|
end
|
648
759
|
|
760
|
+
opts.on '--mono-bitrate ARG', Integer do |arg|
|
761
|
+
@mono_bitrate = arg
|
762
|
+
end
|
763
|
+
|
649
764
|
opts.on '--eac3' do
|
650
765
|
@surround_encoder = 'eac3'
|
651
766
|
end
|
652
767
|
|
768
|
+
opts.on '--all-eac3' do
|
769
|
+
@surround_encoder = 'eac3'
|
770
|
+
@stereo_encoder = 'eac3'
|
771
|
+
end
|
772
|
+
|
773
|
+
opts.on '--keep-ac3-stereo' do
|
774
|
+
@keep_ac3_stereo = true
|
775
|
+
end
|
776
|
+
|
777
|
+
opts.on '--pass-dts' do
|
778
|
+
@pass_dts = true
|
779
|
+
end
|
780
|
+
|
653
781
|
opts.on '--add-subtitle ARG' do |arg|
|
654
782
|
if arg =~ /^([0-9]+)(?:=(forced))?$|^(auto)$|^([a-z]{3})$|^(.*)$/
|
655
783
|
@subtitle_selections += [{
|
@@ -675,6 +803,8 @@ HERE
|
|
675
803
|
else
|
676
804
|
fail UsageError, "invalid subtitle track: #{arg}"
|
677
805
|
end
|
806
|
+
|
807
|
+
@encoder = nil if @encoder == 'copy'
|
678
808
|
end
|
679
809
|
end
|
680
810
|
|
@@ -699,7 +829,13 @@ HERE
|
|
699
829
|
@audio_selections.uniq!
|
700
830
|
@subtitle_selections.uniq!
|
701
831
|
@surround_bitrate = [[@surround_bitrate, 256].max, (@surround_encoder == 'ac3' ? 640 : 768)].min
|
702
|
-
@stereo_bitrate = [[@stereo_bitrate, 128].max,
|
832
|
+
@stereo_bitrate = [[@stereo_bitrate, 128].max, (@stereo_encoder == 'eac3' ? 768 : 320)].min
|
833
|
+
|
834
|
+
if @mono_bitrate.nil?
|
835
|
+
@mono_bitrate = @stereo_bitrate / 2
|
836
|
+
else
|
837
|
+
@mono_bitrate = [[@mono_bitrate, 64].max, (@stereo_encoder == 'eac3' ? 768 : 256)].min
|
838
|
+
end
|
703
839
|
|
704
840
|
[
|
705
841
|
['ffprobe', '-loglevel', 'quiet', '-version'],
|
@@ -709,7 +845,7 @@ HERE
|
|
709
845
|
verify_tool_availability command
|
710
846
|
end
|
711
847
|
|
712
|
-
return if @detect
|
848
|
+
return if @scan or @detect
|
713
849
|
|
714
850
|
encoders = find_encoders
|
715
851
|
|
@@ -734,7 +870,7 @@ HERE
|
|
734
870
|
else
|
735
871
|
@encoder.sub!(/^h264/, 'hevc') if @hevc
|
736
872
|
|
737
|
-
unless @dry_run or encoders =~ /#{@encoder}/
|
873
|
+
unless @dry_run or @encoder == 'copy' or encoders =~ /#{@encoder}/
|
738
874
|
fail "video encoder not available: #{@encoder}"
|
739
875
|
end
|
740
876
|
end
|
@@ -752,6 +888,11 @@ HERE
|
|
752
888
|
@stereo_encoder = 'aac'
|
753
889
|
end
|
754
890
|
end
|
891
|
+
|
892
|
+
if @format == :mkv
|
893
|
+
capabilities = get_muxer_capabilities
|
894
|
+
@mkv_options = ['-default_mode', 'passthrough'] if capabilities =~ /passthrough/
|
895
|
+
end
|
755
896
|
end
|
756
897
|
|
757
898
|
def verify_tool_availability(command)
|
@@ -829,22 +970,51 @@ HERE
|
|
829
970
|
$CHILD_STATUS.exitstatus == 0
|
830
971
|
end
|
831
972
|
|
973
|
+
def get_muxer_capabilities
|
974
|
+
Kernel.warn 'Getting muxer capabilities...'
|
975
|
+
output = ''
|
976
|
+
|
977
|
+
begin
|
978
|
+
IO.popen([
|
979
|
+
'ffmpeg',
|
980
|
+
'-loglevel', 'quiet',
|
981
|
+
'-h', 'muxer=matroska'
|
982
|
+
], :err=>[:child, :out]) do |io|
|
983
|
+
io.each do |line|
|
984
|
+
Kernel.warn line if @debug
|
985
|
+
output += line
|
986
|
+
end
|
987
|
+
end
|
988
|
+
rescue SystemCallError => e
|
989
|
+
raise "getting muxer capabilities failed: #{e}"
|
990
|
+
end
|
991
|
+
|
992
|
+
fail 'getting muxer capabilities failed' unless $CHILD_STATUS.exitstatus == 0
|
993
|
+
|
994
|
+
output
|
995
|
+
end
|
996
|
+
|
832
997
|
def process_input(path)
|
833
998
|
seconds = Time.now.tv_sec
|
834
999
|
|
835
|
-
unless @detect
|
836
|
-
output_path =
|
837
|
-
|
1000
|
+
unless @scan or @detect
|
1001
|
+
output_path = File.basename(path, '.*') + '.' + @format.to_s
|
1002
|
+
fail_or_warn "output file already exists: #{output_path}" if File.exist? output_path
|
838
1003
|
|
839
1004
|
log_path = output_path + '.log'
|
840
|
-
|
1005
|
+
fail_or_warn "log file already exists: #{log_path}" if File.exist? log_path
|
841
1006
|
|
842
1007
|
tmp_log_path = "_ffmpeg_#{rand(10000..99999)}_#{$PROCESS_ID}.#{@format.to_s}.log"
|
843
|
-
|
1008
|
+
fail_or_warn "log file already exists: #{tmp_log_path}" if File.exist? tmp_log_path
|
844
1009
|
end
|
845
1010
|
|
846
1011
|
media_info = scan_media(path)
|
847
1012
|
|
1013
|
+
if @scan
|
1014
|
+
print_media_info media_info
|
1015
|
+
return
|
1016
|
+
end
|
1017
|
+
|
848
1018
|
video, burn_subtitle = get_video_streams(media_info)
|
849
1019
|
fail "video track not found: #{path}" if video.nil?
|
850
1020
|
|
@@ -855,7 +1025,7 @@ HERE
|
|
855
1025
|
crop = detect_crop(media_info, video)
|
856
1026
|
|
857
1027
|
if @detect
|
858
|
-
present_crop
|
1028
|
+
present_crop crop, path
|
859
1029
|
return
|
860
1030
|
else
|
861
1031
|
Kernel.warn "crop = #{crop[:width]}:#{crop[:height]}:#{crop[:x]}:#{crop[:y]}"
|
@@ -888,13 +1058,13 @@ HERE
|
|
888
1058
|
'-stats'
|
889
1059
|
] + time_options +
|
890
1060
|
decode_options + [
|
891
|
-
'-i',
|
1061
|
+
'-i', path
|
892
1062
|
] + (@max_muxing_queue_size.nil? ? [] : ['-max_muxing_queue_size', @max_muxing_queue_size.to_s]) +
|
893
1063
|
encode_options +
|
894
1064
|
get_audio_options(media_info) +
|
895
1065
|
get_subtitle_options(media_info, burn_subtitle) + [
|
896
1066
|
'-metadata:g', 'title='
|
897
|
-
] + (@format == :
|
1067
|
+
] + (@format == :mkv ? @mkv_options : ['-movflags', 'disable_chpl']) + [
|
898
1068
|
output_path
|
899
1069
|
]
|
900
1070
|
|
@@ -939,17 +1109,25 @@ HERE
|
|
939
1109
|
FileUtils.mv tmp_log_path, log_path
|
940
1110
|
end
|
941
1111
|
|
942
|
-
assemble_log
|
1112
|
+
assemble_log log_path, output
|
943
1113
|
|
944
1114
|
if @format == :mp4
|
945
1115
|
Kernel.warn 'Done.'
|
946
1116
|
else
|
947
|
-
add_track_statistics_tags
|
1117
|
+
add_track_statistics_tags output_path
|
948
1118
|
end
|
949
1119
|
|
950
1120
|
Kernel.warn "\nElapsed time: #{seconds_to_time(Time.now.tv_sec - seconds)}\n\n"
|
951
1121
|
end
|
952
1122
|
|
1123
|
+
def fail_or_warn(message)
|
1124
|
+
if @dry_run
|
1125
|
+
Kernel.warn "#{$PROGRAM_NAME}: #{message}"
|
1126
|
+
else
|
1127
|
+
fail message
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
953
1131
|
def scan_media(path)
|
954
1132
|
Kernel.warn 'Scanning media...'
|
955
1133
|
output = ''
|
@@ -984,6 +1162,93 @@ HERE
|
|
984
1162
|
media_info
|
985
1163
|
end
|
986
1164
|
|
1165
|
+
def print_media_info(media_info)
|
1166
|
+
video = nil
|
1167
|
+
audio_streams = []
|
1168
|
+
subtitles = []
|
1169
|
+
|
1170
|
+
media_info['streams'].each do |stream|
|
1171
|
+
case stream['codec_type']
|
1172
|
+
when 'video'
|
1173
|
+
video = stream if video.nil?
|
1174
|
+
when 'audio'
|
1175
|
+
audio_streams += [stream]
|
1176
|
+
when 'subtitle'
|
1177
|
+
subtitles += [stream]
|
1178
|
+
end
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
puts media_info['format']['filename']
|
1182
|
+
size = "#{video['width']} x #{video['height']}"
|
1183
|
+
print " format = #{video['codec_name']} / #{size} / #{video['avg_frame_rate']} fps"
|
1184
|
+
bitrate = get_bitrate(video)
|
1185
|
+
puts bitrate.nil? ? '' : " / #{bitrate} Kbps"
|
1186
|
+
duration = media_info['format']['duration'].to_f
|
1187
|
+
time = seconds_to_time(duration.to_i)
|
1188
|
+
milliseconds = duration.to_s.sub(/^[0-9]+(\.[0-9]+)$/, '\1')
|
1189
|
+
time += milliseconds unless milliseconds == '.0'
|
1190
|
+
puts " duration = #{time}"
|
1191
|
+
index = 0
|
1192
|
+
|
1193
|
+
audio_streams.each do |stream|
|
1194
|
+
index += 1
|
1195
|
+
puts "\##{index} audio:"
|
1196
|
+
codec_name = stream['codec_name']
|
1197
|
+
print " format = #{codec_name}"
|
1198
|
+
|
1199
|
+
if codec_name == 'dts'
|
1200
|
+
profile = stream.fetch('profile', 'DTS')
|
1201
|
+
print " (#{profile})" unless profile == 'DTS'
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
print ' / '
|
1205
|
+
layout = stream.fetch('channel_layout', '')
|
1206
|
+
|
1207
|
+
if layout.empty?
|
1208
|
+
channels = stream['channels'].to_i
|
1209
|
+
print "#{channels} " + (channels > 1 ? 'channels' : 'channel')
|
1210
|
+
else
|
1211
|
+
print "#{layout}"
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
bitrate = get_bitrate(stream)
|
1215
|
+
puts bitrate.nil? ? '' : " / #{bitrate} Kbps"
|
1216
|
+
puts " language = #{stream.fetch('tags', {}).fetch('language', '')}"
|
1217
|
+
title = stream.fetch('tags', {}).fetch('title', '')
|
1218
|
+
puts " title = #{title}" unless title.empty?
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
index = 0
|
1222
|
+
|
1223
|
+
subtitles.each do |stream|
|
1224
|
+
index += 1
|
1225
|
+
puts "\##{index} subtitle:"
|
1226
|
+
print " format = #{stream['codec_name']}"
|
1227
|
+
frames = stream.fetch('tags', {}).fetch('NUMBER_OF_FRAMES-eng', '')
|
1228
|
+
puts frames.empty? ? '' : " / #{frames} " + (frames == 1 ? 'frame' : 'frames')
|
1229
|
+
puts " language = #{stream.fetch('tags', {}).fetch('language', '')}"
|
1230
|
+
title = stream.fetch('tags', {}).fetch('title', '')
|
1231
|
+
puts " title = #{title}" unless title.empty?
|
1232
|
+
default = (stream['disposition']['default'] == 1)
|
1233
|
+
forced = (stream['disposition']['forced'] == 1)
|
1234
|
+
|
1235
|
+
if default or forced
|
1236
|
+
puts ' flags = ' +
|
1237
|
+
(default ? 'default' : '') +
|
1238
|
+
((default and forced) ? ' / ' : '') +
|
1239
|
+
(forced ? 'forced' : '')
|
1240
|
+
end
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def get_bitrate(stream)
|
1245
|
+
bitrate = stream.fetch('bit_rate', '')
|
1246
|
+
bitrate = stream.fetch('tags', {}).fetch('BPS-eng', '') if bitrate.empty?
|
1247
|
+
return nil if bitrate.empty?
|
1248
|
+
|
1249
|
+
bitrate.to_i / 1000
|
1250
|
+
end
|
1251
|
+
|
987
1252
|
def detect_crop(media_info, video)
|
988
1253
|
Kernel.warn 'Detecting crop...'
|
989
1254
|
duration = media_info['format']['duration'].to_f
|
@@ -1106,10 +1371,10 @@ HERE
|
|
1106
1371
|
drawbox_string = "#{crop[:x]}:#{crop[:y]}:#{crop[:width]}:#{crop[:height]}"
|
1107
1372
|
puts
|
1108
1373
|
puts escape_command([
|
1109
|
-
'mpv', '--no-audio',
|
1374
|
+
'mpv', '--no-audio', "--vf=lavfi=[drawbox=#{drawbox_string}:invert:1]", path
|
1110
1375
|
])
|
1111
1376
|
puts escape_command([
|
1112
|
-
'mpv', '--no-audio',
|
1377
|
+
'mpv', '--no-audio', "--vf=crop=#{crop_string}", path
|
1113
1378
|
])
|
1114
1379
|
puts
|
1115
1380
|
puts escape_command([
|
@@ -1239,13 +1504,7 @@ HERE
|
|
1239
1504
|
overlay_filter = nil
|
1240
1505
|
else
|
1241
1506
|
overlay_filter = "[0:#{burn_subtitle['index']}]overlay"
|
1242
|
-
|
1243
|
-
unless cuvid_decoder.nil?
|
1244
|
-
Kernel.warn '**********'
|
1245
|
-
Kernel.warn "burning subtitle disables video decoder: #{cuvid_decoder}"
|
1246
|
-
Kernel.warn '**********'
|
1247
|
-
cuvid_decoder = nil
|
1248
|
-
end
|
1507
|
+
cuvid_decoder = nil
|
1249
1508
|
end
|
1250
1509
|
|
1251
1510
|
deinterlace = @deinterlace
|
@@ -1255,7 +1514,7 @@ HERE
|
|
1255
1514
|
if video['avg_frame_rate'] == '30000/1001' or video['field_order'] != 'progressive'
|
1256
1515
|
deinterlace = true
|
1257
1516
|
|
1258
|
-
if video['codec_name'] == 'mpeg2video'
|
1517
|
+
if video['codec_name'] == 'mpeg2video' and video['avg_frame_rate'] != '25/1'
|
1259
1518
|
rate = '24000/1001'
|
1260
1519
|
end
|
1261
1520
|
end
|
@@ -1265,7 +1524,7 @@ HERE
|
|
1265
1524
|
|
1266
1525
|
if deinterlace
|
1267
1526
|
if cuvid_decoder.nil?
|
1268
|
-
frame_rate_filter = 'yadif=deint=interlaced'
|
1527
|
+
frame_rate_filter = 'yadif=deint=interlaced' unless @encoder == 'copy'
|
1269
1528
|
else
|
1270
1529
|
cuvid_options += ['-deint:v', 'adaptive']
|
1271
1530
|
end
|
@@ -1278,14 +1537,8 @@ HERE
|
|
1278
1537
|
end
|
1279
1538
|
|
1280
1539
|
if @detelecine
|
1281
|
-
unless cuvid_decoder.nil?
|
1282
|
-
Kernel.warn '**********'
|
1283
|
-
Kernel.warn "detelecine disables video decoder: #{cuvid_decoder}"
|
1284
|
-
Kernel.warn '**********'
|
1285
|
-
cuvid_decoder = nil
|
1286
|
-
end
|
1287
|
-
|
1288
1540
|
frame_rate_filter = 'fieldmatch=order=tff:combmatch=none,decimate'
|
1541
|
+
cuvid_decoder = nil
|
1289
1542
|
end
|
1290
1543
|
|
1291
1544
|
width = video['width'].to_i
|
@@ -1373,12 +1626,16 @@ HERE
|
|
1373
1626
|
conversion_filter = nil
|
1374
1627
|
end
|
1375
1628
|
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1629
|
+
if @encoder == 'copy'
|
1630
|
+
filter = ''
|
1631
|
+
else
|
1632
|
+
filter = overlay_filter.nil? ? '' : overlay_filter
|
1633
|
+
filter += frame_rate_filter.nil? ? '' : ",#{frame_rate_filter}"
|
1634
|
+
filter += crop_filter.nil? ? '' : ",#{crop_filter}"
|
1635
|
+
filter += scale_filter.nil? ? '' : ",#{scale_filter}"
|
1636
|
+
filter += conversion_filter.nil? ? '' : ",#{conversion_filter}"
|
1637
|
+
filter.sub!(/^,/, '')
|
1638
|
+
end
|
1382
1639
|
|
1383
1640
|
if overlay_filter.nil?
|
1384
1641
|
encode_options = [
|
@@ -1412,15 +1669,19 @@ HERE
|
|
1412
1669
|
if width > 1920 or height > 1080
|
1413
1670
|
bitrate = @target_2160p
|
1414
1671
|
max_bitrate = 40000
|
1672
|
+
max_dpb_mbs = 184320
|
1415
1673
|
elsif width > 1280 or height > 720
|
1416
1674
|
bitrate = @target_1080p
|
1417
1675
|
max_bitrate = 20000
|
1676
|
+
max_dpb_mbs = 32768
|
1418
1677
|
elsif width > 720 or height > 576
|
1419
1678
|
bitrate = @target_720p
|
1420
1679
|
max_bitrate = 10000
|
1680
|
+
max_dpb_mbs = 18000
|
1421
1681
|
else
|
1422
1682
|
bitrate = @target_480p
|
1423
1683
|
max_bitrate = 5000
|
1684
|
+
max_dpb_mbs = 8100
|
1424
1685
|
|
1425
1686
|
unless hdr
|
1426
1687
|
color_primaries = pal ? 'bt470bg' : 'smpte170m'
|
@@ -1430,7 +1691,22 @@ HERE
|
|
1430
1691
|
|
1431
1692
|
bitrate = @target unless @target.nil?
|
1432
1693
|
bitrate = [bitrate, max_bitrate].min
|
1433
|
-
maxrate =
|
1694
|
+
maxrate = 0
|
1695
|
+
bufsize = 0
|
1696
|
+
|
1697
|
+
if @encoder =~ /(nvenc|hevc_qsv|libx26[45])$/
|
1698
|
+
if @maxrate.nil?
|
1699
|
+
maxrate = bitrate * 3
|
1700
|
+
else
|
1701
|
+
maxrate = [[(bitrate * @maxrate).to_i, (bitrate * 1.5).to_i].max, bitrate * 3].min
|
1702
|
+
end
|
1703
|
+
|
1704
|
+
if @bufsize.nil?
|
1705
|
+
bufsize = maxrate if @encoder =~ /^libx26[45]$/
|
1706
|
+
else
|
1707
|
+
bufsize = [[(bitrate * @bufsize).to_i, bitrate].max, bitrate * 4].min
|
1708
|
+
end
|
1709
|
+
end
|
1434
1710
|
|
1435
1711
|
if @preset.nil? or @preset == 'none'
|
1436
1712
|
preset = nil
|
@@ -1457,12 +1733,17 @@ HERE
|
|
1457
1733
|
end
|
1458
1734
|
|
1459
1735
|
fail "invalid preset for encoder: #{@preset}" unless valid
|
1736
|
+
|
1460
1737
|
preset = @preset
|
1461
1738
|
end
|
1462
1739
|
|
1463
1740
|
Kernel.warn 'Stream mapping:'
|
1464
|
-
text = "#{sprintf("%2d", video['index'])} = #{@encoder}
|
1465
|
-
|
1741
|
+
text = "#{sprintf("%2d", video['index'])} = #{@encoder}"
|
1742
|
+
|
1743
|
+
unless @encoder == 'copy'
|
1744
|
+
text += " / #{bitrate} Kbps"
|
1745
|
+
text += " / #{preset}" unless preset.nil?
|
1746
|
+
end
|
1466
1747
|
|
1467
1748
|
unless burn_subtitle.nil?
|
1468
1749
|
text += " / #{sprintf("%d", burn_subtitle['index'])} = #{burn_subtitle['codec_name']} / burn"
|
@@ -1471,9 +1752,9 @@ HERE
|
|
1471
1752
|
Kernel.warn text
|
1472
1753
|
encode_options += ['-c:v', @encoder]
|
1473
1754
|
encode_options += ['-pix_fmt:v', (@encoder =~ /(nvenc|qsv)$/ ? 'p010le' : 'yuv420p10le')] if @ten_bit
|
1474
|
-
encode_options += ['-b:v', "#{bitrate}k"]
|
1475
|
-
encode_options += ['-maxrate:v', "#{maxrate}k"] if
|
1476
|
-
encode_options += ['-bufsize:v', "#{
|
1755
|
+
encode_options += ['-b:v', "#{bitrate}k"] unless @encoder == 'copy'
|
1756
|
+
encode_options += ['-maxrate:v', "#{maxrate}k"] if maxrate > 0
|
1757
|
+
encode_options += ['-bufsize:v', "#{bufsize}k"] if bufsize > 0
|
1477
1758
|
encode_options += ['-preset:v', preset] unless preset.nil?
|
1478
1759
|
encode_options += ['-allow_sw:v', '1'] if @encoder =~ /videotoolbox$/ and @vt_allow_sw
|
1479
1760
|
|
@@ -1527,14 +1808,31 @@ HERE
|
|
1527
1808
|
|
1528
1809
|
if @encoder == 'libx264'
|
1529
1810
|
encode_options += ['-x264opts:v', 'ratetol=inf'] if @x264_avbr
|
1530
|
-
encode_options += ['-mbtree:v', '0']
|
1811
|
+
encode_options += ['-mbtree:v', '0'] unless @x264_mbtree
|
1531
1812
|
|
1532
|
-
if @preset.nil?
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1813
|
+
if @preset.nil?
|
1814
|
+
if @x264_quick
|
1815
|
+
encode_options += [
|
1816
|
+
'-refs:v', '1',
|
1817
|
+
'-rc-lookahead:v', '30',
|
1818
|
+
'-partitions:v', 'none'
|
1819
|
+
]
|
1820
|
+
end
|
1821
|
+
else
|
1822
|
+
max_refs = [(max_dpb_mbs / (((width + 15) / 16) * ((height + 15) / 16))), 16].min
|
1823
|
+
|
1824
|
+
case @preset
|
1825
|
+
when 'slow'
|
1826
|
+
refs = 5
|
1827
|
+
when 'slower'
|
1828
|
+
refs = 8
|
1829
|
+
when 'veryslow', 'placebo'
|
1830
|
+
refs = 16
|
1831
|
+
else
|
1832
|
+
refs = 0
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
encode_options += ['-refs:v', max_refs.to_s] if refs > max_refs
|
1538
1836
|
end
|
1539
1837
|
end
|
1540
1838
|
|
@@ -1542,14 +1840,21 @@ HERE
|
|
1542
1840
|
encode_options += ['-profile:v', 'high'] if @encoder =~ /^(h264_nvenc|h264_amf|libx264)$/
|
1543
1841
|
end
|
1544
1842
|
|
1843
|
+
unless @encoder == 'copy'
|
1844
|
+
encode_options += [
|
1845
|
+
'-color_primaries:v', color_primaries,
|
1846
|
+
'-color_trc:v', color_trc,
|
1847
|
+
'-colorspace:v', colorspace
|
1848
|
+
]
|
1849
|
+
end
|
1850
|
+
|
1545
1851
|
encode_options += [
|
1546
|
-
'-color_primaries:v', color_primaries,
|
1547
|
-
'-color_trc:v', color_trc,
|
1548
|
-
'-colorspace:v', colorspace,
|
1549
1852
|
'-metadata:s:v', 'title=',
|
1550
1853
|
'-disposition:v', 'default'
|
1551
1854
|
]
|
1552
1855
|
|
1856
|
+
encode_options += ['-tag:v', 'hvc1'] if @format == :mp4 and @hevc
|
1857
|
+
|
1553
1858
|
[decode_options, encode_options]
|
1554
1859
|
end
|
1555
1860
|
|
@@ -1575,7 +1880,14 @@ HERE
|
|
1575
1880
|
audio_tracks = [{
|
1576
1881
|
:stream => main_audio,
|
1577
1882
|
:width => width,
|
1578
|
-
:bitrate => width
|
1883
|
+
:bitrate => case width
|
1884
|
+
when :stereo
|
1885
|
+
@stereo_bitrate
|
1886
|
+
when :surround
|
1887
|
+
@surround_bitrate
|
1888
|
+
when :original
|
1889
|
+
nil
|
1890
|
+
end
|
1579
1891
|
}]
|
1580
1892
|
|
1581
1893
|
titles = {}
|
@@ -1588,7 +1900,15 @@ HERE
|
|
1588
1900
|
end
|
1589
1901
|
|
1590
1902
|
width = selection[:width]
|
1591
|
-
|
1903
|
+
|
1904
|
+
bitrate = case width
|
1905
|
+
when :stereo
|
1906
|
+
@stereo_bitrate
|
1907
|
+
when :surround
|
1908
|
+
@surround_bitrate
|
1909
|
+
when :original
|
1910
|
+
nil
|
1911
|
+
end
|
1592
1912
|
|
1593
1913
|
unless selection[:track].nil?
|
1594
1914
|
audio_track = 0
|
@@ -1614,7 +1934,8 @@ HERE
|
|
1614
1934
|
media_info['streams'].each do |stream|
|
1615
1935
|
next if stream['codec_type'] != 'audio'
|
1616
1936
|
|
1617
|
-
if
|
1937
|
+
if (selection[:language] == 'all' or
|
1938
|
+
stream.fetch('tags', {}).fetch('language', '') == selection[:language]) and
|
1618
1939
|
stream['index'] != main_audio['index']
|
1619
1940
|
audio_tracks += [{
|
1620
1941
|
:stream => stream,
|
@@ -1658,28 +1979,37 @@ HERE
|
|
1658
1979
|
bitrate = nil
|
1659
1980
|
channels = nil
|
1660
1981
|
|
1661
|
-
if track[:width] == :
|
1662
|
-
if codec_name == @surround_encoder or codec_name == 'ac3'
|
1982
|
+
if track[:width] == :original
|
1663
1983
|
encoder = 'copy'
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1984
|
+
else
|
1985
|
+
dts = (codec_name == 'dts' and track[:stream].fetch('profile', 'DTS') =~ /^DTS(?:-ES)?$/)
|
1986
|
+
|
1987
|
+
if track[:width] == :surround
|
1988
|
+
if codec_name == @surround_encoder or
|
1989
|
+
codec_name == 'ac3' or
|
1990
|
+
(@pass_dts and dts)
|
1991
|
+
encoder = 'copy'
|
1992
|
+
elsif input_channels > 2
|
1993
|
+
encoder = @surround_encoder
|
1994
|
+
bitrate = @surround_bitrate
|
1995
|
+
end
|
1667
1996
|
end
|
1668
|
-
end
|
1669
1997
|
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1998
|
+
if encoder.nil?
|
1999
|
+
if input_channels <= 2 and (codec_name == 'aac' or
|
2000
|
+
((codec_name == @surround_encoder or codec_name == 'ac3') and
|
2001
|
+
(@keep_ac3_stereo or (track[:stream]['bit_rate'].to_i / 1000) <= @stereo_bitrate)) or
|
2002
|
+
(@pass_dts and dts))
|
2003
|
+
encoder = 'copy'
|
2004
|
+
else
|
2005
|
+
encoder = @stereo_encoder
|
2006
|
+
bitrate = @stereo_bitrate
|
1678
2007
|
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
2008
|
+
if input_channels > 2
|
2009
|
+
channels = 2
|
2010
|
+
elsif input_channels == 1
|
2011
|
+
bitrate = @mono_bitrate
|
2012
|
+
end
|
1683
2013
|
end
|
1684
2014
|
end
|
1685
2015
|
end
|
@@ -1762,7 +2092,8 @@ HERE
|
|
1762
2092
|
media_info['streams'].each do |stream|
|
1763
2093
|
next if stream['codec_type'] != 'subtitle'
|
1764
2094
|
|
1765
|
-
if
|
2095
|
+
if (selection[:language] == 'all' or
|
2096
|
+
stream.fetch('tags', {}).fetch('language', '') == selection[:language])
|
1766
2097
|
subtitles += [stream]
|
1767
2098
|
end
|
1768
2099
|
end
|
@@ -1779,10 +2110,7 @@ HERE
|
|
1779
2110
|
end
|
1780
2111
|
end
|
1781
2112
|
|
1782
|
-
unless force_subtitle.nil?
|
1783
|
-
subtitles = [force_subtitle] + subtitles
|
1784
|
-
end
|
1785
|
-
|
2113
|
+
subtitles = [force_subtitle] + subtitles unless force_subtitle.nil?
|
1786
2114
|
subtitles.uniq!
|
1787
2115
|
options = []
|
1788
2116
|
index = 0
|
@@ -1790,9 +2118,6 @@ HERE
|
|
1790
2118
|
subtitles.each do |subtitle|
|
1791
2119
|
next if (not burn_subtitle.nil?) and burn_subtitle['index'] == subtitle['index']
|
1792
2120
|
|
1793
|
-
next if @format == :mp4 and
|
1794
|
-
(subtitle['codec_name'] == 'hdmv_pgs_subtitle' or subtitle['codec_name'] == 'dvd_subtitle')
|
1795
|
-
|
1796
2121
|
force = (index == 0 and not force_subtitle.nil?)
|
1797
2122
|
text = "#{sprintf("%2d", subtitle['index'])} = #{subtitle['codec_name']}"
|
1798
2123
|
text += ' / force' if force
|
@@ -1802,7 +2127,7 @@ HERE
|
|
1802
2127
|
|
1803
2128
|
options += [
|
1804
2129
|
'-map', "0:#{subtitle['index']}",
|
1805
|
-
"-c:s:#{index}", 'copy',
|
2130
|
+
"-c:s:#{index}", ((@format == :mp4 and subtitle['codec_name'] == 'subrip') ? 'mov_text' : 'copy'),
|
1806
2131
|
"-disposition:s:#{index}", (force ? 'default+forced' : '0')
|
1807
2132
|
]
|
1808
2133
|
|