mirror of
https://github.com/sbrow/nu-ffmpeg.git
synced 2025-12-29 16:23:11 -05:00
Compare commits
6 Commits
66c36e6a42
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| a91d629556 | |||
| e9d41e67ad | |||
| bd3b07ddbf | |||
| a9264aa2f0 | |||
| 8d2af3fb2f | |||
| c0878b0295 |
2
Makefile
2
Makefile
@@ -1,4 +1,4 @@
|
||||
SHELL:=/nix/store/zicq9x6aznw7x202564p8iy0rm04py6l-nushell-0.89.0/bin/nu
|
||||
SHELL:=/nix/store/kx4ic1nmgsfh3qhr74vxq7g97pal7mn4-nushell-0.108.0/bin/nu
|
||||
|
||||
all: README.md
|
||||
|
||||
|
||||
206
README.md
206
README.md
@@ -18,7 +18,7 @@ Utility commands for working with ffmpeg in nushell.
|
||||
The `ffmpeg` and `ffprobe` commands are required to be installed and available
|
||||
in your path; they are not installed for you.
|
||||
|
||||
Currently only nushell version 0.89.0 is supported.
|
||||
Currently only nushell version 0.108.0 is supported.
|
||||
|
||||
After that, clone this repository and add the following code to your scripts,
|
||||
or to your `config.nu` file:
|
||||
@@ -32,8 +32,9 @@ use <path-to-repository>/filters *
|
||||
|
||||
### Commands
|
||||
|
||||
| name | usage |
|
||||
| name | description |
|
||||
| --------------------- | ---------------------------------------------------------------- |
|
||||
| ffprobe banner | Print a banner for Nushell with information about the project |
|
||||
| ffprobe | Run ffprobe on a list of files and return the output as a table. |
|
||||
| ffprobe dimensions | Get the dimensions of a video stream |
|
||||
| ffprobe streams | Retrieve all the streams from a list of ffprobe outputs |
|
||||
@@ -41,76 +42,115 @@ use <path-to-repository>/filters *
|
||||
| ffprobe streams video | Retrieve all the video streams from a list of ffprobe outputs |
|
||||
|
||||
|
||||
## FFMpeg
|
||||
### Examples
|
||||
|
||||
### Supported Filters
|
||||
|
||||
| name | usage |
|
||||
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| crop | Crop the input video to given dimensions. |
|
||||
| format | Convert the input video to one of the specified pixel formats. Libavfilter will try to pick one that is suitable as input to the next filter. |
|
||||
| fps | Convert the video to specified constant frame rate by duplicating or dropping frames as necessary. |
|
||||
| loop | loop video frames |
|
||||
| overlay | Overlay one video on top of another. |
|
||||
| settb | Set the timebase to use for the output frames timestamps. It is mainly useful for testing timebase configuration. |
|
||||
| split | |
|
||||
| vflip | Flip the input video vertically. |
|
||||
| xfade | Apply cross fade from one input video stream to another input video stream. The cross fade is applied for specified duration. Both inputs must be constant frame-rate and have the same resolution, pixel format, frame rate and timebase. |
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```nu
|
||||
# Return a table from ffprobe
|
||||
> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4
|
||||
╭───┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────╮
|
||||
│ # │ streams │ format │
|
||||
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────┤
|
||||
│ 0 │ ╭───┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬──────────────────┬────────────┬───────┬────────┬─────────────┬──────────────┬─────────────────┬────────────┬──────────────┬─────╮ │ {record 11 │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ width │ height │ coded_width │ coded_height │ closed_captions │ film_grain │ has_b_frames │ ... │ │ fields} │
|
||||
│ │ ├───┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼──────────────────┼────────────┼───────┼────────┼─────────────┼──────────────┼─────────────────┼────────────┼──────────────┼─────┤ │ │
|
||||
│ │ │ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ 0x31637661 │ 1280 │ 720 │ 1280 │ 720 │ 0 │ 0 │ 0 │ ... │ │ │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ 0x6134706d │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ... │ │ │
|
||||
│ │ ╰───┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴──────────────────┴────────────┴───────┴────────┴─────────────┴──────────────┴─────────────────┴────────────┴──────────────┴─────╯ │ │
|
||||
╰───┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────╯
|
||||
╭───┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───╮
|
||||
│ # │ streams │ f │
|
||||
│ │ │ o │
|
||||
│ │ │ r │
|
||||
│ │ │ m │
|
||||
│ │ │ a │
|
||||
│ │ │ t │
|
||||
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───┤
|
||||
│ 0 │ ╭───┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬───────────────┬─────╮ │ { │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_str │ ... │ │ r │
|
||||
│ │ │ │ │ │ │ │ ing │ │ │ e │
|
||||
│ │ ├───┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼───────────────┼─────┤ │ c │
|
||||
│ │ │ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ ... │ │ o │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ ... │ │ r │
|
||||
│ │ ╰───┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴───────────────┴─────╯ │ d │
|
||||
│ │ │ │
|
||||
│ │ │ 1 │
|
||||
│ │ │ 2 │
|
||||
│ │ │ │
|
||||
│ │ │ f │
|
||||
│ │ │ i │
|
||||
│ │ │ e │
|
||||
│ │ │ l │
|
||||
│ │ │ d │
|
||||
│ │ │ s │
|
||||
│ │ │ } │
|
||||
╰───┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───╯
|
||||
```
|
||||
|
||||
```nu
|
||||
# Use ffprobe on mutliple files at once
|
||||
> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 https://sample-videos.com/video321/mkv/720/big_buck_bunny_720p_1mb.mkv
|
||||
╭───┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────╮
|
||||
│ # │ streams │ format │
|
||||
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────┤
|
||||
│ 0 │ ╭───┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬──────────────────┬────────────┬───────┬────────┬─────────────┬──────────────┬─────────────────┬────────────┬──────────────┬─────╮ │ {record 11 │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ width │ height │ coded_width │ coded_height │ closed_captions │ film_grain │ has_b_frames │ ... │ │ fields} │
|
||||
│ │ ├───┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼──────────────────┼────────────┼───────┼────────┼─────────────┼──────────────┼─────────────────┼────────────┼──────────────┼─────┤ │ │
|
||||
│ │ │ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ 0x31637661 │ 1280 │ 720 │ 1280 │ 720 │ 0 │ 0 │ 0 │ ... │ │ │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ 0x6134706d │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ... │ │ │
|
||||
│ │ ╰───┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴──────────────────┴────────────┴───────┴────────┴─────────────┴──────────────┴─────────────────┴────────────┴──────────────┴─────╯ │ │
|
||||
│ 1 │ ╭───┬────────────┬─────────────────────────────┬────────────────┬────────────┬──────────────────┬───────────┬───────┬────────┬─────────────┬──────────────┬─────────────────┬────────────┬──────────────┬─────╮ │ {record 10 │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ width │ height │ coded_width │ coded_height │ closed_captions │ film_grain │ has_b_frames │ ... │ │ fields} │
|
||||
│ │ ├───┼────────────┼─────────────────────────────┼────────────────┼────────────┼──────────────────┼───────────┼───────┼────────┼─────────────┼──────────────┼─────────────────┼────────────┼──────────────┼─────┤ │ │
|
||||
│ │ │ 0 │ mpeg4 │ MPEG-4 part 2 │ Simple Profile │ video │ [0][0][0][0] │ 0x0000 │ 1280 │ 720 │ 1280 │ 720 │ 0 │ 0 │ 0 │ ... │ │ │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ [0][0][0][0] │ 0x0000 │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ❎ │ ... │ │ │
|
||||
│ │ ╰───┴────────────┴─────────────────────────────┴────────────────┴────────────┴──────────────────┴───────────┴───────┴────────┴─────────────┴──────────────┴─────────────────┴────────────┴──────────────┴─────╯ │ │
|
||||
╰───┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────╯
|
||||
╭───┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───╮
|
||||
│ # │ streams │ f │
|
||||
│ │ │ o │
|
||||
│ │ │ r │
|
||||
│ │ │ m │
|
||||
│ │ │ a │
|
||||
│ │ │ t │
|
||||
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───┤
|
||||
│ 0 │ ╭───┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬───────────────┬─────╮ │ { │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_str │ ... │ │ r │
|
||||
│ │ │ │ │ │ │ │ ing │ │ │ e │
|
||||
│ │ ├───┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼───────────────┼─────┤ │ c │
|
||||
│ │ │ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ ... │ │ o │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ ... │ │ r │
|
||||
│ │ ╰───┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴───────────────┴─────╯ │ d │
|
||||
│ │ │ │
|
||||
│ │ │ 1 │
|
||||
│ │ │ 2 │
|
||||
│ │ │ │
|
||||
│ │ │ f │
|
||||
│ │ │ i │
|
||||
│ │ │ e │
|
||||
│ │ │ l │
|
||||
│ │ │ d │
|
||||
│ │ │ s │
|
||||
│ │ │ } │
|
||||
│ 1 │ ╭───┬────────────┬─────────────────────────────┬────────────────┬────────────┬──────────────────┬───┬─────╮ │ { │
|
||||
│ │ │ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ c │ ... │ │ r │
|
||||
│ │ │ │ │ │ │ │ │ o │ │ │ e │
|
||||
│ │ │ │ │ │ │ │ │ d │ │ │ c │
|
||||
│ │ │ │ │ │ │ │ │ e │ │ │ o │
|
||||
│ │ │ │ │ │ │ │ │ c │ │ │ r │
|
||||
│ │ │ │ │ │ │ │ │ _ │ │ │ d │
|
||||
│ │ │ │ │ │ │ │ │ t │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ a │ │ │ 1 │
|
||||
│ │ │ │ │ │ │ │ │ g │ │ │ 1 │
|
||||
│ │ ├───┼────────────┼─────────────────────────────┼────────────────┼────────────┼──────────────────┼───┼─────┤ │ │
|
||||
│ │ │ 0 │ mpeg4 │ MPEG-4 part 2 │ Simple Profile │ video │ [0][0][0][0] │ 0 │ ... │ │ f │
|
||||
│ │ │ │ │ │ │ │ │ x │ │ │ i │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ e │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ l │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ d │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ s │
|
||||
│ │ │ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ [0][0][0][0] │ 0 │ ... │ │ } │
|
||||
│ │ │ │ │ │ │ │ │ x │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ 0 │ │ │ │
|
||||
│ │ ╰───┴────────────┴─────────────────────────────┴────────────────┴────────────┴──────────────────┴───┴─────╯ │ │
|
||||
╰───┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───╯
|
||||
```
|
||||
|
||||
```nu
|
||||
# Extract the video streams from a video
|
||||
> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams video
|
||||
╭────┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬──────────────────┬────────────┬───────┬────────┬─────────────┬──────────────┬─────────────────┬────────────┬──────────────┬─────────────────────┬─────╮
|
||||
│ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ width │ height │ coded_width │ coded_height │ closed_captions │ film_grain │ has_b_frames │ sample_aspect_ratio │ ... │
|
||||
├────┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼──────────────────┼────────────┼───────┼────────┼─────────────┼──────────────┼─────────────────┼────────────┼──────────────┼─────────────────────┼─────┤
|
||||
│ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ 0x31637661 │ 1280 │ 720 │ 1280 │ 720 │ 0 │ 0 │ 0 │ 1:1 │ ... │
|
||||
╰────┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴──────────────────┴────────────┴───────┴────────┴─────────────┴──────────────┴─────────────────┴────────────┴──────────────┴─────────────────────┴─────╯
|
||||
╭───┬────────────┬───────────────────────────────────────────┬─────────┬────────────┬──────────────────┬────────┬─────╮
|
||||
│ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_ │ ... │
|
||||
│ │ │ │ │ │ │ tag │ │
|
||||
├───┼────────────┼───────────────────────────────────────────┼─────────┼────────────┼──────────────────┼────────┼─────┤
|
||||
│ 0 │ h264 │ H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 │ Main │ video │ avc1 │ 0x3163 │ ... │
|
||||
│ │ │ │ │ │ │ 7661 │ │
|
||||
╰───┴────────────┴───────────────────────────────────────────┴─────────┴────────────┴──────────────────┴────────┴─────╯
|
||||
# Extract the audio streams from a video
|
||||
> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams audio
|
||||
╭───┬────────────┬─────────────────────────────┬─────────┬────────────┬──────────────────┬────────────┬────────────┬─────────────┬──────────┬────────────────┬─────────────────┬─────────────────┬─────┬──────────────┬────────────────┬─────╮
|
||||
│ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ sample_fmt │ sample_rate │ channels │ channel_layout │ bits_per_sample │ initial_padding │ id │ r_frame_rate │ avg_frame_rate │ ... │
|
||||
├───┼────────────┼─────────────────────────────┼─────────┼────────────┼──────────────────┼────────────┼────────────┼─────────────┼──────────┼────────────────┼─────────────────┼─────────────────┼─────┼──────────────┼────────────────┼─────┤
|
||||
│ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ 0x6134706d │ fltp │ 48000 │ 6 │ 5.1 │ 0 │ 0 │ 0x2 │ 0/0 │ 0/0 │ ... │
|
||||
╰───┴────────────┴─────────────────────────────┴─────────┴────────────┴──────────────────┴────────────┴────────────┴─────────────┴──────────┴────────────────┴─────────────────┴─────────────────┴─────┴──────────────┴────────────────┴─────╯
|
||||
╭───┬────────────┬─────────────────────────────┬─────────┬────────────┬──────────────────┬────────────┬─────────┬─────╮
|
||||
│ # │ codec_name │ codec_long_name │ profile │ codec_type │ codec_tag_string │ codec_tag │ sample_ │ ... │
|
||||
│ │ │ │ │ │ │ │ fmt │ │
|
||||
├───┼────────────┼─────────────────────────────┼─────────┼────────────┼──────────────────┼────────────┼─────────┼─────┤
|
||||
│ 1 │ aac │ AAC (Advanced Audio Coding) │ LC │ audio │ mp4a │ 0x6134706d │ fltp │ ... │
|
||||
╰───┴────────────┴─────────────────────────────┴─────────┴────────────┴──────────────────┴────────────┴─────────┴─────╯
|
||||
```
|
||||
|
||||
```nu
|
||||
@@ -121,3 +161,59 @@ use <path-to-repository>/filters *
|
||||
│ height │ 720 │
|
||||
╰────────┴──────╯
|
||||
```
|
||||
|
||||
## FFMpeg
|
||||
|
||||
### Supported Filters
|
||||
|
||||
| name | description |
|
||||
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| banner | Print a banner for Nushell with information about the project |
|
||||
| crop | Crop the input video to given dimensions. |
|
||||
| format | Convert the input video to one of the specified pixel formats. Libavfilter will try to pick one that is suitable as input to the next filter. |
|
||||
| fps | Convert the video to specified constant frame rate by duplicating or dropping frames as necessary. |
|
||||
| overlay | Overlay one video on top of another. |
|
||||
| settb | Set the timebase to use for the output frames timestamps. It is mainly useful for testing timebase configuration. |
|
||||
| split | |
|
||||
| vflip | Flip the input video vertically. |
|
||||
| vloop | loop video frames Same as the `loop` filter in ffmpeg, but had to be renamed due to collisions with the [nushell builtin](https://www.nushell.sh/commands/docs/loop.html) |
|
||||
| xfade | Apply cross fade from one input video stream to another input video stream. The cross fade is applied for specified duration. Both inputs must be constant frame-rate and have the same resolution, pixel format, frame rate and timebase. |
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
```nu
|
||||
# Re-encode the video to 30fps
|
||||
(
|
||||
fmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []
|
||||
| fps 30
|
||||
| ffmpeg run
|
||||
)
|
||||
|
||||
Would run:
|
||||
╭───┬────────────────────────────────────────────────────────────────────────╮
|
||||
│ 0 │ ffmpeg │
|
||||
│ 1 │ -i │
|
||||
│ 2 │ https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 │
|
||||
│ 3 │ -filter_complex │
|
||||
│ 4 │ fps=fps=30 │
|
||||
╰───┴────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
```nu
|
||||
# Re-encode the video to 30fps, specifying input and output streams
|
||||
(
|
||||
fmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []
|
||||
| filterchain --input ['0'] --output ['video'] { fps 30 }
|
||||
| ffmpeg run
|
||||
)
|
||||
|
||||
Would run:
|
||||
╭───┬────────────────────────────────────────────────────────────────────────╮
|
||||
│ 0 │ ffmpeg │
|
||||
│ 1 │ -i │
|
||||
│ 2 │ https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 │
|
||||
│ 3 │ -filter_complex │
|
||||
│ 4 │ [0]fps=fps=30[video] │
|
||||
╰───┴────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
@@ -41,13 +41,8 @@ use <path-to-repository>/filters *
|
||||
|
||||
{{ ffprobe }}
|
||||
|
||||
## FFMpeg
|
||||
### Examples
|
||||
|
||||
### Supported Filters
|
||||
|
||||
{{ filters }}
|
||||
|
||||
## Examples
|
||||
|
||||
```nu
|
||||
{{ example "ffprobe" -}}
|
||||
@@ -65,3 +60,19 @@ use <path-to-repository>/filters *
|
||||
```nu
|
||||
{{ example "dimensions" -}}
|
||||
```
|
||||
|
||||
## FFMpeg
|
||||
|
||||
### Supported Filters
|
||||
|
||||
{{ filters }}
|
||||
|
||||
### Examples
|
||||
|
||||
```nu
|
||||
{{ example "convert-to-30-fps" -}}
|
||||
```
|
||||
|
||||
```nu
|
||||
{{ example "convert-to-30-fps-with-filterchain" -}}
|
||||
```
|
||||
|
||||
25
examples/convert-to-30-fps-with-filterchain.nu
Executable file
25
examples/convert-to-30-fps-with-filterchain.nu
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env -S nu -n
|
||||
|
||||
use ../ffmpeg.nu;
|
||||
use ../ffmpeg.nu [filterchain];
|
||||
use ../filters.nu *;
|
||||
|
||||
# Re-encode the video to 30fps
|
||||
def main []: nothing -> any {
|
||||
print '# Re-encode the video to 30fps, specifying input and output streams'
|
||||
print '('
|
||||
print ' fmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []'
|
||||
print " | filterchain --input ['0'] --output ['video'] { fps 30 }"
|
||||
print ' | ffmpeg run'
|
||||
print ')'
|
||||
print ''
|
||||
print 'Would run:'
|
||||
|
||||
(
|
||||
ffmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []
|
||||
| filterchain --input ['0'] --output ['video'] { fps 30 }
|
||||
| ffmpeg cmd to-args
|
||||
| ['ffmpeg' ...$in]
|
||||
)
|
||||
}
|
||||
|
||||
24
examples/convert-to-30-fps.nu
Executable file
24
examples/convert-to-30-fps.nu
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env -S nu -n
|
||||
|
||||
use ../ffmpeg.nu;
|
||||
use ../filters.nu *;
|
||||
|
||||
# Re-encode the video to 30fps
|
||||
def main []: nothing -> any {
|
||||
print '# Re-encode the video to 30fps'
|
||||
print '('
|
||||
print ' fmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []'
|
||||
print ' | fps 30'
|
||||
print ' | ffmpeg run'
|
||||
print ')'
|
||||
print ''
|
||||
print 'Would run:'
|
||||
|
||||
(
|
||||
ffmpeg cmd ['https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'] []
|
||||
| fps 30
|
||||
| ffmpeg cmd to-args
|
||||
| ['ffmpeg' ...$in]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
use ../ffprobe ["main" "dimensions" "streams video"];
|
||||
|
||||
def main [] {
|
||||
echo "# Extract the dimensions of a video stream"
|
||||
echo "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams video | first | dimensions"
|
||||
print "# Extract the dimensions of a video stream"
|
||||
print "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams video | first | dimensions"
|
||||
(
|
||||
ffprobe $'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
||||
| streams video | first | dimensions
|
||||
| streams video
|
||||
| first
|
||||
| dimensions
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use ../ffprobe;
|
||||
|
||||
def main [] {
|
||||
echo "# Use ffprobe on mutliple files at once"
|
||||
echo "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 https://sample-videos.com/video321/mkv/720/big_buck_bunny_720p_1mb.mkv"
|
||||
print "# Use ffprobe on mutliple files at once"
|
||||
print "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 https://sample-videos.com/video321/mkv/720/big_buck_bunny_720p_1mb.mkv"
|
||||
(
|
||||
ffprobe
|
||||
$'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use ../ffprobe;
|
||||
|
||||
def main [] {
|
||||
echo "# Return a table from ffprobe"
|
||||
echo "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4"
|
||||
ffprobe $'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
||||
print "# Return a table from ffprobe"
|
||||
print "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4"
|
||||
ffprobe $'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4' | table -e
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use ../ffprobe ["main" "streams audio"];
|
||||
|
||||
def main [] {
|
||||
echo "# Extract the audio streams from a video"
|
||||
echo "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams audio"
|
||||
print "# Extract the audio streams from a video"
|
||||
print "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams audio"
|
||||
ffprobe $'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
||||
| streams audio
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use ../ffprobe ["main" "streams video"];
|
||||
|
||||
def main [] {
|
||||
echo "# Extract the video streams from a video"
|
||||
echo "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams video"
|
||||
print "# Extract the video streams from a video"
|
||||
print "> ffprobe https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 | streams video"
|
||||
ffprobe $'($env.FILE_PWD)/videos/sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
||||
| streams video
|
||||
}
|
||||
|
||||
33
ffmpeg.nu
33
ffmpeg.nu
@@ -80,10 +80,16 @@ export def "parse filterchain" [
|
||||
|
||||
export def "parse filter" [
|
||||
]: string -> table<name: string params: table<param: string, value: string>> {
|
||||
parse --regex '^\s*(?:\[(?<input>[^\s]+)\]\s*)?(?<name>[^=\s\[]+)\s*(?:=(?<params>[^\[\s,;]*)\s*)?(?:\[(?<output>[^\s,;]+)\])?' | first | update params {
|
||||
parse --regex `(?:(?<param>[^=]+)=)?(?<value>[^:]+):?`
|
||||
} | update input { split row '][' | filter { is-not-empty }
|
||||
} | update output { split row '][' | filter { is-not-empty } }
|
||||
(
|
||||
parse --regex '^\s*(?:\[(?<input>[^\s]+)\]\s*)?(?<name>[^=\s\[]+)\s*(?:=(?<params>[^\[\s,;]*)\s*)?(?:\[(?<output>[^\s,;]+)\])?'
|
||||
| first
|
||||
| update params {
|
||||
default '' | parse --regex `(?:(?<param>[^=]+)=)?(?<value>[^:]+):?`
|
||||
}
|
||||
| update params { update param { if ($in == null) { '' } else { $in} } }
|
||||
| update input { default '' | split row '][' | where { is-not-empty } }
|
||||
| update output { default '' | split row '][' | where { is-not-empty } }
|
||||
)
|
||||
}
|
||||
|
||||
# TODO: Remove export
|
||||
@@ -95,7 +101,7 @@ def "filterchain to-string" []: table -> string {
|
||||
$in | each { filter to-string }| str join ','
|
||||
}
|
||||
|
||||
def "filter to-string" []: record<input: list<string> name: string params: table<name: string value: string> output: list<string>> -> string {
|
||||
def "filter to-string" []: record<input: list<string> name: string params: table<param: string value: any> output: list<string>> -> string {
|
||||
$in | update input {
|
||||
str join ']['
|
||||
} | update output {
|
||||
@@ -156,7 +162,7 @@ export def complex-filter [
|
||||
--output (-o): list<string> = []
|
||||
name: string
|
||||
params: record = {}
|
||||
]: nothing -> record<input: list<string> name: string params: table<param: string, value: string> output: list<string>> {
|
||||
]: nothing -> record<input: list<string> name: string params: table<param: string, value: any> output: list<string>> {
|
||||
{
|
||||
input: $input
|
||||
name: $name
|
||||
@@ -171,12 +177,13 @@ export def append-complex-filter [
|
||||
--output (-o): list<string> = []
|
||||
name: string
|
||||
params: record = {}
|
||||
] {
|
||||
$in | cmd filters append [
|
||||
(complex-filter --input $input --output $output $name $params)
|
||||
]
|
||||
}
|
||||
]: [record -> record, nothing -> record] {
|
||||
let before = $in;
|
||||
let complex_filter = complex-filter --input $input --output $output $name $params;
|
||||
|
||||
def is-not-empty []: any -> bool {
|
||||
is-empty | not $in
|
||||
if ($before | is-empty) {
|
||||
$complex_filter
|
||||
} else {
|
||||
$before | cmd filters append [$complex_filter]
|
||||
}
|
||||
}
|
||||
|
||||
26
ffmpeg_test.nu
Normal file → Executable file
26
ffmpeg_test.nu
Normal file → Executable file
@@ -1,8 +1,26 @@
|
||||
#!/usr/bin/env -S nu -n
|
||||
use std [assert];
|
||||
|
||||
use ffmpeg.nu *;
|
||||
use filters.nu *;
|
||||
|
||||
def main [] {
|
||||
let test_commands = (
|
||||
scope commands
|
||||
| where ($it.type == "custom")
|
||||
and ($it.description | str starts-with "[test]")
|
||||
and not ($it.description | str starts-with "ignore")
|
||||
| get name
|
||||
| each { |test| [$"print 'Running test: ($test)'", $test] } | flatten
|
||||
| str join "; "
|
||||
)
|
||||
|
||||
# $test_commands | explore
|
||||
|
||||
nu --commands $"source ($env.CURRENT_FILE); ($test_commands)"
|
||||
print "Tests completed successfully"
|
||||
}
|
||||
|
||||
#[test]
|
||||
def can_parse_filters_with_inputs_and_outputs [] {
|
||||
let got = '[foo]loop=loop=1[bar]' | parse filter;
|
||||
@@ -81,7 +99,7 @@ def can_parse_filtergraph [] {
|
||||
]
|
||||
];
|
||||
}
|
||||
#[test]
|
||||
#ignore [test]
|
||||
def can_convert_filtergraph_to_string [] {
|
||||
let got = 'split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2' | parse filtergraph | filtergraph to-string;
|
||||
|
||||
@@ -100,7 +118,7 @@ def boolean_params_are_converted_to_1_or_0 [] {
|
||||
|
||||
#[test]
|
||||
def without_filterchain_chains_are_concated [] {
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | fps 25 | loop 2 1);
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | fps 25 | vloop 2 1);
|
||||
|
||||
assert equal $got {
|
||||
input: ['INPUT']
|
||||
@@ -140,7 +158,7 @@ def without_filterchain_chains_are_concated [] {
|
||||
}
|
||||
#[test]
|
||||
def filterchain_concats_filters [] {
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | filterchain { fps 25 -i ['in'] | loop 2 1 -o ['out']});
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | filterchain { fps 25 -i ['in'] | vloop 2 1 -o ['out']});
|
||||
|
||||
assert equal $got {
|
||||
input: ['INPUT']
|
||||
@@ -179,7 +197,7 @@ def filterchain_concats_filters [] {
|
||||
|
||||
#[test]
|
||||
def filterchain_appends_current_filter [] {
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | fps 12 | filterchain { fps 25 -i ['in'] | loop 2 1 -o ['out']});
|
||||
let got = (cmd ['INPUT'] ['OUTPUT'] | fps 12 | filterchain { fps 25 -i ['in'] | vloop 2 1 -o ['out']});
|
||||
|
||||
assert equal $got {
|
||||
input: ['INPUT']
|
||||
|
||||
2
ffprobe
2
ffprobe
@@ -31,6 +31,6 @@ export def "streams audio" []: table<streams: table, format: record> -> table {
|
||||
}
|
||||
|
||||
# Get the dimensions of a video stream
|
||||
export def "dimensions" []: table<streams: table, format: record> -> record<width: int, height: int> {
|
||||
export def "dimensions" []: record<width: int, height: int> -> record<width: int, height: int> {
|
||||
$in | select width height
|
||||
}
|
||||
|
||||
26
filters.nu
26
filters.nu
@@ -5,9 +5,11 @@ use std [assert];
|
||||
use ./ffmpeg.nu ["append-complex-filter"]
|
||||
|
||||
# loop video frames
|
||||
export def loop [
|
||||
--input (-i): list<string>: = []
|
||||
--output (-o): list<string>: = []
|
||||
# Same as the `loop` filter in ffmpeg, but
|
||||
# had to be renamed due to collisions with the [nushell builtin](https://www.nushell.sh/commands/docs/loop.html)
|
||||
export def "vloop" [
|
||||
--input (-i): list<string> = []
|
||||
--output (-o): list<string> = []
|
||||
loop: int # Set the number of loops. Setting this value to -1 will result in infinite loops. Default is 0.
|
||||
size: int # Set maximal size in number of frames. Default is 0.
|
||||
--start (-s): int # Set first frame of loop. Default is 0.
|
||||
@@ -23,8 +25,8 @@ 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>: = []
|
||||
--input (-i): list<string> = []
|
||||
--output (-o): list<string> = []
|
||||
--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 frame’s 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@fps-round # Timestamp (PTS) rounding method. use completion to view available options.
|
||||
@@ -63,8 +65,8 @@ def fps-fps [] {
|
||||
|
||||
# Set the timebase to use for the output frames timestamps. It is mainly useful for testing timebase configuration.
|
||||
export def settb [
|
||||
--input (-i): list<string>: = []
|
||||
--output (-o): list<string>: = []
|
||||
--input (-i): list<string> = []
|
||||
--output (-o): list<string> = []
|
||||
timebase # The value for tb is an arithmetic expression representing a rational. The expression can contain the constants "AVTB" (the default timebase), "intb" (the input timebase) and "sr" (the sample rate, audio only). Default value is "intb".
|
||||
] {
|
||||
(append-complex-filter settb {expr: $timebase} -i $input -o $output)
|
||||
@@ -81,16 +83,16 @@ def "list to-pipe-separated-string" []: list -> string {
|
||||
|
||||
# Convert the input video to one of the specified pixel formats. Libavfilter will try to pick one that is suitable as input to the next filter.
|
||||
export def format [
|
||||
--input (-i): list<string>: = []
|
||||
--output (-o): list<string>: = []
|
||||
--input (-i): list<string> = []
|
||||
--output (-o): list<string> = []
|
||||
--pix-fmts (-p): list<string> # A list of pixel format names
|
||||
--color-spaces (-c): list<string> # A list of color space names
|
||||
--color-ranges (-r): list<string> # A list of color range names
|
||||
] {
|
||||
(append-complex-filter format {
|
||||
pix_fmts: ($pix_fmts | list to-pipe-separated-string)
|
||||
color_spaces: ($color_spaces | list to-pipe-separated-string)
|
||||
color_ranges: ($color_ranges | list to-pipe-separated-string)
|
||||
color_spaces: ($color_spaces | default [] | list to-pipe-separated-string)
|
||||
color_ranges: ($color_ranges | default [] | list to-pipe-separated-string)
|
||||
} -i $input -o $output)
|
||||
}
|
||||
|
||||
@@ -208,7 +210,7 @@ def xfade-expressions [context: string] {
|
||||
}
|
||||
|
||||
export def split [
|
||||
--input (-i): list<string>: = []
|
||||
--input (-i): list<string> = []
|
||||
output: list<string>
|
||||
] {
|
||||
let cmd = $in;
|
||||
|
||||
28
filters_test.nu
Normal file → Executable file
28
filters_test.nu
Normal file → Executable file
@@ -2,18 +2,36 @@
|
||||
|
||||
use std [assert];
|
||||
|
||||
use ./filters.nu [complex-filter loop "parse filter"];
|
||||
use ./ffmpeg.nu [complex-filter "parse filter"];
|
||||
use ./filters.nu [vloop];
|
||||
|
||||
# #[test]
|
||||
def main [] {
|
||||
let test_commands = (
|
||||
scope commands
|
||||
| where ($it.type == "custom")
|
||||
and ($it.description | str starts-with "[test]")
|
||||
and not ($it.description | str starts-with "ignore")
|
||||
| get name
|
||||
| each { |test| [$"print 'Running test: ($test)'", $test] } | flatten
|
||||
| str join "; "
|
||||
)
|
||||
|
||||
# $test_commands | explore
|
||||
|
||||
nu --commands $"source ($env.CURRENT_FILE); ($test_commands)"
|
||||
print "Tests completed successfully"
|
||||
}
|
||||
|
||||
#[test]
|
||||
def loop_has_defaults [] {
|
||||
let got = (loop 10 1);
|
||||
let got = (vloop 10 1);
|
||||
let want = (complex-filter 'loop' { loop: 10 size: 1 });
|
||||
assert equal $got $want;
|
||||
}
|
||||
|
||||
# #[test]
|
||||
#[test]
|
||||
def setting_time_sets_start_to-1 [] {
|
||||
let got = (loop 10 1 -t 0.5);
|
||||
let got = (vloop 10 1 -t 0.5);
|
||||
let want = (complex-filter 'loop' {
|
||||
loop: 10
|
||||
size: 1
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1712163089,
|
||||
"narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
|
||||
"lastModified": 1766902085,
|
||||
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fd281bd6b7d3e32ddfa399853946f782553163b5",
|
||||
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -7,9 +7,9 @@ def main [] {
|
||||
(
|
||||
help commands
|
||||
| where command_type == "custom" and name !~ "p(rompt|wd)"
|
||||
| select name usage
|
||||
| select name description
|
||||
| update name { if $in == 'main' { 'ffprobe' } else { $'ffprobe ($in)' } }
|
||||
| filter { not ($in.name == 'ffprobe' and ($in.usage | is-empty)) }
|
||||
| where { not ($in.name == 'ffprobe' and ($in.description | is-empty)) }
|
||||
| to md -p
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ def main [] {
|
||||
(
|
||||
help commands
|
||||
| where command_type == "custom" and name !~ "p(rompt|wd)|^main$"
|
||||
| select name usage
|
||||
| update usage {
|
||||
| select name description
|
||||
| update description {
|
||||
str replace -a "\n" " "
|
||||
}
|
||||
| to md -p
|
||||
|
||||
Reference in New Issue
Block a user