feat: Added more filters, including crop.

This commit is contained in:
Spencer Brower
2024-01-21 23:10:37 -05:00
parent 2686c545a3
commit 25a3868dca
4 changed files with 127 additions and 31 deletions

41
examples.nu Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env nu
use ffmpeg.nu;
use filters.nu *;
use filters.nu; # overlay filter can't be used with a * immport
def main [] {
# ffmpeg -i INPUT -filter_complex "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
(
ffmpeg cmd ['INPUT'] ['OUTPUT']
| split ['main' 'tmp']
| ffmpeg filterchain { crop --height 'ih/2' -i ['tmp'] | vflip ['flip'] }
| filters overlay -i ['main' 'flip'] -x 0 -y 'H/2'
| ffmpeg run --dry-run
)
# ffprobe 'https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
# ffprobe 'https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4' | streams video
# Re-encode the video to 30fps
#(
# command ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4']
# | fps 30
# | command to-args
# | ['ffmpeg', ...$in]
#)
# Re-encode the video to 30fps, specifying inputs and outputs
# (
# ffmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4']
# | filterchain ['0'] { fps 30 } ['video']
# | command to-args
# | ['ffmpeg', ...$in]
# )
#(ffmpeg cmd ['INPUT'] ['OUTPUT'] | fps 25 | loop 2 1 | get filters | to nuon);
#ffmpeg cmd ['INPUT'] ['OUTPUT'] | ffmpeg filterchain ['in'] ['out'] { fps 25 | loop 2 1 } | ffmpeg cmd to-args
#ffmpeg cmd ['INPUT'] ['OUTPUT'] | ffmpeg filterchain { fps 25 -i ['in'] | loop 2 1 -o ['out'] } | ffmpeg cmd to-args
}

View File

@@ -21,6 +21,18 @@ export def "cmd to-args" []: record -> list<string> {
]
}
# Run the given command
# Serves as a sink for a filter pipeline.
export def "run" [
--dry-run (-d) # Print the command that would be run
] {
if $dry_run {
[ffmpeg, ...($in | cmd to-args)]
} else {
ffmpeg ...($in | cmd to-args)
}
}
export def "cmd filters append" [
complex_filter: list<record>
]: record -> record {
@@ -70,8 +82,11 @@ def "filter to-string" []: record<input: list<string> name: string params: table
} | update output {
str join ']['
} | update params {
each { format '{param}={value}' } | str join ':' | str replace -ar '(?<=^|:)=' ''
} | format '[{input}]{name}={params}[{output}]' | str replace -ar '\[\]|=(?=[\[,;])' ''
each { update value { match ($in | describe) {
'bool' => (if ($in) { '1' } else { '0' })
_ => $in,
} } | format pattern '{param}={value}' } | str join ':' | str replace -ar '(?<=^|:)=' ''
} | format pattern '[{input}]{name}={params}[{output}]' | str replace -ar '\[\]|=(?=[\[,;])' ''
}
# Set the input and outputs of a filter chain

View File

@@ -90,6 +90,14 @@ def can_convert_filtergraph_to_string [] {
assert equal $got $want;
}
#[test]
def boolean_params_are_converted_to_1_or_0 [] {
let got = (cmd ['INPUT'] ['OUTPUT'] | crop -k | run -d | get 4);
let want = 'crop=w=iw:x=0:y=0:keep_aspect=1:exact=0';
assert equal $got $want;
}
#[test]
def without_filterchain_chains_are_concated [] {
let got = (cmd ['INPUT'] ['OUTPUT'] | fps 25 | loop 2 1);
@@ -102,18 +110,18 @@ def without_filterchain_chains_are_concated [] {
input: []
name: 'fps'
params: [
{param: 'fps' value: 25}
]
output: []
}
{
input: []
name: 'settb'
params: [
{param: 'expr' value: '1/25'}
{param: 'fps' value: '25'}
]
output: []
}
#{
# input: []
# name: 'settb'
# params: [
# {param: 'expr' value: '1/25'}
# ]
# output: []
#}
]
[{
input: []
@@ -141,18 +149,18 @@ def filterchain_concats_filters [] {
input: ['in']
name: 'fps'
params: [
{param: 'fps' value: 25}
]
output: []
}
{
input: []
name: 'settb'
params: [
{param: 'expr' value: '1/25'}
{param: 'fps' value: '25'}
]
output: []
}
#{
# input: []
# name: 'settb'
# params: [
# {param: 'expr' value: '1/25'}
# ]
# output: []
#}
{
input: []
name: 'loop'

View File

@@ -23,22 +23,30 @@ export def loop [
]
}
# Convert the video to specified constant frame rate by duplicating or dropping frames as necessary.
export def fps [
--input (-i): list<string>: = []
--output (-o): list<string>: = []
--round (-r): string
fps: int
--start-time (-s) # Assume the first PTS should be the given value, in seconds.
# This allows for padding/trimming at the start of stream. By default, no assumption is made about the first frames expected PTS, so no padding or trimming is done. For example, this could be set to 0 to pad the beginning with duplicates of the first frame if a video stream starts after the audio stream or to trim any frames with a negative PTS.
--round (-r): string # Timestamp (PTS) rounding method.
# Possible values are: "zero", "inf", "down", "up", "near".
--eof-action (-e): string # Action performed when reading the last frame. Possible values are: "round", "pass"
fps: string = '25' # The desired output frame rate. It accepts expressions containing the following constants:
# "source_fps": The inputs frame rate
# "ntsc": NTSC frame rate of 30000/1001
# "pal": PAL frame rate of 25.0
# "film": Film frame rate of 24.0
# "ntsc_film": NTSC-film frame rate of 24000/1001
] {
cmd filters append [
(complex-filter fps {fps: $fps round: $round} -i $input)
(complex-filter settb {expr: $'1/($fps)' } -o $output)
]
}
export def split [
--input (-i): list<string>: = []
--output (-o): string
output: list<string>
] {
let cmd = $in;
@@ -51,24 +59,48 @@ export def split [
}) -i $input -o $output)]
}
# TODO: Finish
# Crop the input video to given dimensions.
export def crop [
--input: list<string> = []
--input (-i): list<string> = []
--width (-w): string = 'iw'
--height (-h): string
--keep-aspect (-k) # Force the output display aspect ratio to be the same of the input
--exact (-e) # If enabled, subsampled videos will be cropped at exact width/height/x/y as specified
x = '0'
y = '0'
] {
cmd filters append [
(complex-filter crop {w: $width h: $height x: $x, y: $y} -i $input)
(complex-filter crop {
w: $width
h: $height
x: $x
y: $y
keep_aspect: $keep_aspect
exact: $exact
} -i $input)
]
}
# Flip the input video vertically.
export def vflip [
--input: list<string> = []
output: list<string>
] {
cmd filters append [
(complex-filter vflip -i $input -o $output)
]
}
#TODO: Finish
export def vflip [
...output: string
# Overlay one video on top of another.
export def overlay [
-x: int = 0
-y: int = 0
--output: list<string>
input: list<string>
] {
cmd filters append [
(complex-filter vflip -o $output)
(complex-filter overlay -i $input -o $output {x: $x y: $y})
]
}