Skip to content

Commit

Permalink
Two Pass Encoding Class (#10)
Browse files Browse the repository at this point in the history
* working move to class

* class and script changes

* simplify script. class updates.

* Dockerfile update. MR comments.

* scipt improvements and comments

twopass class handles streams better and returns a file size

* comments and timestamp work

* changes for config file timestamps

* readme update
  • Loading branch information
zfleeman committed Jan 25, 2024
1 parent bb1643f commit 2af6051
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 199 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ RUN apk add --no-cache ffmpeg
RUN pip install ffmpeg-python
RUN mkdir -p /usr/app/out/
WORKDIR /usr/app/
COPY discord.py .
ENTRYPOINT ["python", "-u", "discord.py", "-o", "/usr/app/out/"]
COPY . .
ENTRYPOINT ["python", "-u", "ffmpeg4discord.py", "-o", "/usr/app/out/"]
54 changes: 42 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# ffmpeg video conversion for Discord
This is a Python script that takes a video file as its input and encodes it to be less than 8MB unless specified otherwise. You can change the file's name in a way that trims the file to a timestamped section. This script is useful when sharing large NVIDIA ShadowPlay clips quickly, without the need for a visual editor.
# Target File Size Video Compression for Discord using `ffmpeg`
This repository houses scripts and applications to assist with compressing a video file to a desired size.

This script supports a few ffmpeg video filters, like cropping and resolution scaling. It can be used in a variety of different audio/video workflows. It also showcases a 2-pass encoding methodology for the `ffmpeg-python` library, which is not well-documented on the web.
The `ffmpeg4discord.py` script takes a video file as its input and encodes it to be less than 8MB unless specified otherwise. Discord's free-tier file size sharing limit is 8MB. You can change the file's name in a way that trims the file to a timestamped section. I created this script to share large NVIDIA ShadowPlay clips on Discord quickly, without the need for a visual editor.

The `TwoPass()` Class showcases a 2-pass encoding methodology for the `ffmpeg-python` library, which is not well-documented on the web. The Class also supports a few ffmpeg video filters, like cropping and resolution scaling. It can be used in a variety of different audio/video workflows.

## Usage
You must first have `ffmpeg` installed on your system. `ffmpeg` needs to be registered in your PATH.
Expand All @@ -12,7 +14,7 @@ Install the required Python package, `ffmpeg-python` with:

Call the script with:

```python "C:/path/to/discord.py" cool_clip.mp4```
```python "C:/path/to/ffmpeg4discord.py" cool_clip.mp4```

The included Batch file for Windows users, `encode.bat`, allows for drag and drop functionality. Be sure to edit the Batch file before dragging your video files on top of it.

Expand All @@ -26,25 +28,51 @@ The included Batch file for Windows users, `encode.bat`, allows for drag and dro

## Optional Arguments
- `-o`, `--output`
- default: `current working directory`
- default: the current working directory
- If there is a folder that you want all of your smaller clips to land in, specify it with this argument.
- `-s`, `--filesize`
- default: `8.0`
- Increase or descrease this value if you want to compress your video to something other than the 8MB Discord limit.
- Increase or decrease this value if you want to compress your video to something other than the 8MB Discord limit.
- `-a`, `--audio-br`
- default: `96`
- You can change this value if you want to increase or decrease your audio bitrate. Lowering it will allow for a slight increase in the compressed file's video bitrate.
- `-r`, `--resolution`
- Example: `"1280x720"`
- Example: `1280x720`
- Modify this value to change the output resolution of your video file. I'd recommend lowering your output resolution by 1.5x or 2x. Example: `1920x1080` video should get an output resolution of `1280x720`
- `-c`, `--crop`
- `-x`, `--crop`
- Example: `255x0x1410x1080`
- From the top-left of your video, this example goes 255 pixels to the right, 0 pixels down, and it carves out a 1410x1080 section of the video.
- [ffmpeg crop documentation](https://ffmpeg.org/ffmpeg-filters.html#Examples-61)
- `--config`
- Example: `custom_run_config.json`
- Path to a json file containing the configuration for the above parameters. This config file takes precedence over all of the other flags.

### JSON Configuration
If your encoding job will always be the same, you can reference a JSON configuration file instead of passing a long list of arguments to the command line.

```
{
"target_filesize": 8.0,
"audio_br": 96,
"crop": "",
"resolution": "",
"codec": "libx264",
"times": {
"from": "00:00:00",
"to": "00:00:40"
}
}
```

Notes:
- All of the keys except for `"from"` and `"to"` must always be present. Those entries can be deleted if you do not have a timestamp entry for the given field. Examples:
- `"times": {}` -> if you do not wish to trim the start and stop time of the file. This falls back to the [file name formatting](https://github.com/zfleeman/ffmpeg4discord#file-name-formatting).
- `"times": {"from": "00:00:10"}` -> trim the clip from `00:00:10` to the end of the file
- `"times": {"to": "00:00:20"}` -> trim the clip from the beginning of the file up to `00:00:20`
- You can set `audio_br` to `null` if you want to maintain the clips audio bitrate.

## Docker Usage

Using the docker image is very similar to the basic python example, above. You need to volume mount your input file and the output directory. The output directory is hard coded into the Dockerfile's `ENTRYPOINT` line as the `/usr/app/out/` directory in the container. After the `docker run` options and flags, you need to specify your filename and the optional flags specified in the Python example, above.
Using the docker image is very similar to the basic python example, above. You need to volume mount your input file and the output directory. The output directory is hard coded into the Dockerfile's `ENTRYPOINT` line as the `/usr/app/out/` directory in the container. After the `docker run` options and flags, you need to specify your file name and the optional flags specified in the Python example, above.

```
docker run \
Expand All @@ -54,17 +82,19 @@ docker run \
000100.mp4 -s 20 -r 1280x720
```

If you want to use a JSON configuration file, be sure to mount it into your container.

## Detailed Example

```
python D:/ffmpeg4discord/discord.py 000050-000145.mp4 \
python D:/ffmpeg4discord/ffmpeg4discord.py 000050-000145.mp4 \
-c 1280x0x2560x1440 \
-r 1920x1080 \
-s 50 \
-a 48 \
-o D:/shadowplay/
```

The example above takes a 5120x1440 resolution video as its input. The script trims the video from 00:00:50 to 00:01:45 (specified in the [filename](https://github.com/zfleeman/ffmpeg4discord#file-name-formatting)). It crops a 2560x1440 section starting at 1280 pixels from the top-left and 0 pixels down (`-c`). The output file will be located in `D:/shadowplay/` (`-o`) with a new resolution of 1920x1080 (`-r`), and it will be 50MB (`-s`). The audio bitrate will be reduced to 48k (`-a`) as well, but that's probably going to sound terrible.
The example above takes a 5120x1440 resolution video as its input. The script trims the video from 00:00:50 to 00:01:45 (specified in the [file name](https://github.com/zfleeman/ffmpeg4discord#file-name-formatting)). It crops a 2560x1440 section starting at 1280 pixels from the top-left and 0 pixels down (`-c`). The output file will be located in `D:/shadowplay/` (`-o`) with a new resolution of 1920x1080 (`-r`), and it will be 50MB (`-s`). The audio bitrate will be reduced to 48k (`-a`) as well, but that's probably going to sound terrible.

![](https://i.imgur.com/WJXA723.png)
11 changes: 11 additions & 0 deletions conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"target_filesize": 8.0,
"audio_br": 96,
"crop": "",
"resolution": "",
"codec": "libx264",
"times": {
"from": "00:00:00",
"to": "00:00:40"
}
}
184 changes: 0 additions & 184 deletions discord.py

This file was deleted.

3 changes: 2 additions & 1 deletion encode.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@echo off
Set filename=%1
python "C:/path/to/discord.py" %filename% -o "C:/output/folder/"
python "C:/path/to/ffmpeg4discord.py" %filename% -o "C:/output/folder/"
DEL "ffmpeg2*"
PAUSE
21 changes: 21 additions & 0 deletions ffmpeg4discord.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
from utils.arguments import get_args
from twopass import TwoPass

# get args from the command line
args = get_args()

# instantiate the TwoPass class and save our target file size for comparison in the loop
twopass = TwoPass(**args)
end_fs = args["target_filesize"]

while twopass.run() >= end_fs:
print(
f"\nThe output file size ({round(twopass.output_filesize, 2)}MB) is still above the target of {end_fs}MB.\nRestarting...\n"
)
os.remove(twopass.output_filename)

# adjust the class's target file size to set a lower bitrate for the next run
twopass.target_filesize -= 0.2

print(f"\nSUCCESS!!\nThe smaller file ({round(twopass.output_filesize, 2)}MB) is located at {twopass.output_filename}")
1 change: 1 addition & 0 deletions twopass/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .twopass import TwoPass
Loading

0 comments on commit 2af6051

Please sign in to comment.