Skip to content

Commit

Permalink
Merge pull request #2 from HbHbNr/options
Browse files Browse the repository at this point in the history
Options
  • Loading branch information
HbHbNr authored Feb 19, 2024
2 parents 830fefa + 6eaf4b5 commit ab7a672
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 43 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/codequality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Code quality

on: [push]

jobs:

runtests:
runs-on: ubuntu-latest
steps:
- name: Install BATS and FFmpeg
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends bats ffmpeg
- uses: actions/checkout@v3
- name: Run BATS tests
run: |
bats test/test.bats
41 changes: 32 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,60 @@
[![GPL 3.0](https://hbhbnr.github.io/badges/license-GPL--3.0-blue.svg)](LICENSE)
[![GitHub Workflow Status](https://github.com/hbhbnr/binary2video/actions/workflows/codequality.yml/badge.svg)](https://github.com/hbhbnr/binary2video/actions/workflows/codequality.yml)

# binary2video
Convert binary files to videos, e.g. MP4 or WebM, and back.
Convert binary files to videos, e.g. MP4/WebM/AVI/MKV, and back. Supports h.264, VP9, FFV1 level 1 and 3.

## Usage

### binary2video

```
binary2video [-f fps] [-w width] [-h height] <infile> <outfile>
binary2video [-f fps] [-w width] [-h height] [-v] infile outfile
-f fps Set framerate, i.e. frame per second; default 1.
-f fps Set framerate, i.e. frames per second; default 1.
-w width Set width, default 320.
-h height Set height, default 240.
<infile> The name and path of the binary file which should be converted into
-c codec Set video codec: h264, vp9, ffv1, or ffv1l3. Default is vp9.
-v Be verbose.
infile The name and path of the binary file which should be converted into
a video.
<outfile> The name and path of the video file which should be created. The suffix
outfile The name and path of the video file which should be created. The suffix
of the file will determine the video container type. The container must
be supported by FFmpeg, for example .mp4, .webm, .avi, etc.
be supported by FFmpeg, for example .mp4, .webm, .avi, .mkv, etc. Not
all video codecs work with all container formats, though.
```

### video2binary

```
video2binary <infile> <outfile>
video2binary [-v] infile outfile
-v Be verbose.
<infile> The name and path of the video file from which the original binary file
infile The name and path of the video file from which the original binary file
should be extracted.
<outfile> The name and path of the binary file to which the original data should be
outfile The name and path of the binary file to which the original data should be
restored to.
```

## Compatibility matrix

| Container vs. Codec | **h264** | **vp9** | **ffv1** | **ffv1l3** |
|--------------------:|:--------:|:-------:|:--------:|:----------:|
| **.mp4** ||| | |
| **.webm** | || | |
| **.avi** |||||
| **.mkv** |||||

Other containers have not been tested and may also work.

## Internal process

### Conversion from binary file to video file
Expand Down Expand Up @@ -60,5 +82,6 @@ Run the unit tests with

## References
* FFmpeg Formats Documentation: [Demuxers - rawvideo](https://ffmpeg.org/ffmpeg-formats.html#rawvideo)
* FFmpeg FFV1: [FFV1 encoding cheatsheet](https://trac.ffmpeg.org/wiki/Encode/FFV1)
* AntumDeluge: [List of Lossless FFmpeg Video Encoders](https://antumdeluge.wordpress.com/lossless-ffmpeg-video-encoders/)
* Fufu Fang: [Converting-Arbitrary-Data-To-Video](https://github.com/fangfufu/Converting-Arbitrary-Data-To-Video)
99 changes: 81 additions & 18 deletions binary2video
Original file line number Diff line number Diff line change
@@ -1,34 +1,67 @@
#!/usr/bin/env bash

usage_and_exit () {
echo "Usage: binary2video [-f fps] [-w width] [-h height] <infile> <outfile>"
echo "Usage: binary2video [-f fps] [-w width] [-h height] [-c (vp9 | h264 | ffv1 | ffv1l3)] [-v] infile outfile"
echo " Defaults: fps=1 width=320 height=240 codec=vp9"
exit $1
}
check_executable_or_exit() {
if [ ! -x $1 ]; then
echo "error: no $(basename $1) at $gzip"
exit 2
fi
}

if [ $# = 1 -a x$1 = 'x--help' ]; then
if [ \( $# -eq 1 \) -a \( "$1" = '--help' \) ]; then
usage_and_exit 0
fi

# check needed binaries
gzip=/usr/bin/gzip
check_executable_or_exit $gzip
ffmpeg=/usr/bin/ffmpeg
check_executable_or_exit $ffmpeg

# check parameters
width=320
height=240
framerate=1
while getopts 'f:w:h:' option; do
case $option in
codec=vp9
verbose=0
while getopts 'f:w:h:c:v' option; do
case "$option" in
f) framerate="$OPTARG";;
w) width="$OPTARG";;
h) height="$OPTARG";;
c) codec="$OPTARG";;
v) verbose=1;;
?) usage_and_exit 1;;
esac
done
shift $(($OPTIND - 1))
# echo $framerate $width $height $1 $2
# exit 0

# verify codec
case "$codec" in
h264) ;;
vp9) ;;
ffv1) ;;
ffv1l3) ;;
*)
echo "error: unknown codec '$codec'"
usage_and_exit 3
;;
esac

if [ $# != 2 ]; then
usage_and_exit 1
fi
if [ ! -r "$1" ]; then
echo "error: file '$1' does not exist or is not readable"
exit 4
fi
if [ $verbose = 1 ]; then
echo "parameters: fps=$framerate width=$width height=$height codec=$codec"
fi

tmpfile_gzipped=$(mktemp --tmpdir binary2video.gz.XXXXXXXXXX)
tmpfile_padded=$(mktemp --tmpdir binary2video.padded.XXXXXXXXXX)
Expand All @@ -37,27 +70,57 @@ infile="$1"
outfile="$2"
pixel_format=rgb24
blocksize=$(($width * $height * 3))
echo "blocksize is $blocksize bytes"

md5sum ${infile}
if [ $verbose = 1 ]; then
echo "blocksize is $blocksize bytes"
echo -n "input file MD5: "
md5sum "${infile}" | cut -d\ -f1
fi

$gzip --fast --to-stdout ${infile} > ${tmpfile_gzipped}
$gzip --fast --to-stdout "${infile}" > ${tmpfile_gzipped}
size_gzipped=$(stat --format=%s ${tmpfile_gzipped})
echo "gzipped file has $size_gzipped bytes"
if [ $verbose = 1 ]; then
echo "gzipped file has $size_gzipped bytes"
fi

padding_needed=$(($blocksize - ($size_gzipped % $blocksize)))
echo "padding needed: $padding_needed bytes"
if [ $verbose = 1 ]; then
echo "padding needed: $padding_needed bytes"
fi

(cat ${tmpfile_gzipped}; dd if=/dev/zero bs=$padding_needed count=1 status=none) >> ${tmpfile_padded}
size_padded=$(stat --format=%s ${tmpfile_padded})
echo "padded file has $size_padded bytes"
if [ $verbose = 1 ]; then
echo "padded file has $size_padded bytes"
fi

$ffmpeg -y -hide_banner -loglevel error \
-f rawvideo -pixel_format $pixel_format -video_size "${width}x${height}" -framerate ${framerate} -i "${tmpfile_padded}" \
-c:v libvpx-vp9 -lossless 1 \
"${outfile}"
if [ $codec = "vp9" ]; then
$ffmpeg -y -hide_banner -loglevel error \
-f rawvideo -pixel_format $pixel_format -video_size "${width}x${height}" -framerate ${framerate} -i "${tmpfile_padded}" \
-c:v libvpx-vp9 -lossless 1 \
"${outfile}"
elif [ $codec = "h264" ]; then
$ffmpeg -y -hide_banner -loglevel error \
-f rawvideo -pixel_format $pixel_format -video_size "${width}x${height}" -framerate ${framerate} -i "${tmpfile_padded}" \
-c:v libx264rgb -preset ultrafast -qp 0 \
"${outfile}"
elif [ $codec = "ffv1" ]; then
$ffmpeg -y -hide_banner -loglevel error \
-f rawvideo -pixel_format $pixel_format -video_size "${width}x${height}" -framerate ${framerate} -i "${tmpfile_padded}" \
-c:v ffv1 -level 1 -coder 1 -context 1 -g 1 \
"${outfile}"
elif [ $codec = "ffv1l3" ]; then
$ffmpeg -y -hide_banner -loglevel error \
-f rawvideo -pixel_format $pixel_format -video_size "${width}x${height}" -framerate ${framerate} -i "${tmpfile_padded}" \
-c:v ffv1 -level 3 -threads 8 -coder 1 -context 1 -g 1 -slices 24 -slicecrc 1 \
"${outfile}"
else
echo "error: unknown codec '$codec'"
usage_and_exit 5
fi

echo "output file has $(stat --format=%s ${outfile}) bytes"
if [ $verbose = 1 ]; then
echo "output file has $(stat --format=%s "${outfile}") bytes"
fi

rm ${tmpfile_gzipped}
rm ${tmpfile_padded}
1 change: 1 addition & 0 deletions test/filename with (brackets).txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
filename with (brackets)
1 change: 1 addition & 0 deletions test/filename with spaces.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
filename with spaces
72 changes: 64 additions & 8 deletions test/test.bats
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
teardown() {
rm -f test/LICENSE.{mp4,webm,avi} test/LICENSE_restored
rm -f test/9.{mp4,webm,avi} test/9.txt_restored
rm -f test/42.{mp4,webm,avi} test/42.txt_restored
rm -f test/16777216.{mp4,webm,avi} test/16777216.txt_restored
rm -f test/LICENSE.{mp4,webm,avi,mkv} test/LICENSE_restored
rm -f test/9.{mp4,webm,avi,mkv} test/9.txt_restored
rm -f test/42.{mp4,webm,avi,mkv} test/42.txt_restored
rm -f test/16777216.{mp4,webm,avi,mkv} test/16777216.txt_restored
rm -f test/'filename with spaces'.{mp4,webm,avi,mkv} test/'filename with spaces'.txt_restored
rm -f test/'filename with (brackets)'.{mp4,webm,avi,mkv} test/'filename with (brackets)'.txt_restored
}

@test "can run binary2video" {
Expand All @@ -29,7 +31,7 @@ teardown() {
[[ "${lines[0]}" == "Usage:"* ]]
}

@test "convert to MP4 and restore file LICENSE" {
@test "convert to MP4/default and restore file LICENSE" {
echo "********** convert **********"
./binary2video LICENSE test/LICENSE.mp4
echo "********** restore **********"
Expand All @@ -38,7 +40,7 @@ teardown() {
cmp --silent LICENSE test/LICENSE_restored
}

@test "convert To WebM and restore file test/9.txt" {
@test "convert To WebM/default and restore file test/9.txt" {
echo "********** convert **********"
./binary2video test/9.txt test/9.webm
echo "********** restore **********"
Expand All @@ -47,7 +49,7 @@ teardown() {
cmp --silent test/9.txt test/9.txt_restored
}

@test "convert to AVI and restore file test/42.txt" {
@test "convert to AVI/default and restore file test/42.txt" {
echo "********** convert **********"
./binary2video test/42.txt test/42.avi
echo "********** restore **********"
Expand All @@ -56,11 +58,65 @@ teardown() {
cmp --silent test/42.txt test/42.txt_restored
}

@test "convert to MP4 and restore file test/16777216.txt" {
@test "convert to MP4/default and restore file test/16777216.txt" {
echo "********** convert **********"
./binary2video test/16777216.txt test/16777216.mp4
echo "********** restore **********"
./video2binary test/16777216.mp4 test/16777216.txt_restored
echo "********** compare **********"
cmp --silent test/16777216.txt test/16777216.txt_restored
}

@test "convert to MP4/default and restore file 'test/filename with spaces.txt'" {
echo "********** convert **********"
./binary2video 'test/filename with spaces.txt' 'test/filename with spaces.mp4'
echo "********** restore **********"
./video2binary 'test/filename with spaces.mp4' 'test/filename with spaces.txt_restored'
echo "********** compare **********"
cmp --silent 'test/filename with spaces.txt' 'test/filename with spaces.txt_restored'
}

@test "convert to MP4/default and restore file 'test/filename with (brackets).txt'" {
echo "********** convert **********"
./binary2video 'test/filename with (brackets).txt' 'test/filename with (brackets).mp4'
echo "********** restore **********"
./video2binary 'test/filename with (brackets).mp4' 'test/filename with (brackets).txt_restored'
echo "********** compare **********"
cmp --silent 'test/filename with (brackets).txt' 'test/filename with (brackets).txt_restored'
}

@test "convert to MKV/vp9 and restore file LICENSE" {
echo "********** convert **********"
./binary2video -c vp9 LICENSE test/LICENSE.mkv
echo "********** restore **********"
./video2binary test/LICENSE.mkv test/LICENSE_restored
echo "********** compare **********"
cmp --silent LICENSE test/LICENSE_restored
}

@test "convert to MKV/h264 and restore file LICENSE" {
echo "********** convert **********"
./binary2video -c h264 LICENSE test/LICENSE.mkv
echo "********** restore **********"
./video2binary test/LICENSE.mkv test/LICENSE_restored
echo "********** compare **********"
cmp --silent LICENSE test/LICENSE_restored
}

@test "convert to MKV/ffv1 and restore file LICENSE" {
echo "********** convert **********"
./binary2video -c ffv1 LICENSE test/LICENSE.mkv
echo "********** restore **********"
./video2binary test/LICENSE.mkv test/LICENSE_restored
echo "********** compare **********"
cmp --silent LICENSE test/LICENSE_restored
}

@test "convert to MKV/ffv1l3 and restore file LICENSE" {
echo "********** convert **********"
./binary2video -c ffv1l3 LICENSE test/LICENSE.mkv
echo "********** restore **********"
./video2binary test/LICENSE.mkv test/LICENSE_restored
echo "********** compare **********"
cmp --silent LICENSE test/LICENSE_restored
}
Loading

0 comments on commit ab7a672

Please sign in to comment.