Rip mp4 videos from logfiles with ffmpeg
Signed-off-by: Ravago Jones <ravagojones@gmail.com>
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
Change-Id: I64d784721a9e3a45ef9eec05e7c95628f795232b
diff --git a/WORKSPACE b/WORKSPACE
index 752cdf8..40f7cc3 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -575,6 +575,23 @@
)
http_archive(
+ name = "ffmpeg",
+ build_file_content = """
+load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
+
+native_binary(
+ name = "ffmpeg",
+ src = "ffmpeg-6.0.1-amd64-static/ffmpeg",
+ out = "ffmpeg",
+ visibility = ["//visibility:public"],
+ target_compatible_with = ["@platforms//cpu:x86_64", "@platforms//os:linux"],
+)
+ """,
+ sha256 = "28268bf402f1083833ea269331587f60a242848880073be8016501d864bd07a5",
+ url = "https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-6.0.1-amd64-static.tar.xz",
+)
+
+http_archive(
name = "apache2",
build_file = "@//debian:apache2.BUILD",
sha256 = "98b0ad6d911751ba0aa486429e6278f995e7bbabd928c7d3d44c888fa2bf371b",
@@ -1655,10 +1672,11 @@
# https://github.com/hedronvision/bazel-compile-commands-extractor
http_archive(
name = "hedron_compile_commands",
- strip_prefix = "bazel-compile-commands-extractor-daae6f40adfa5fdb7c89684cbe4d88b691c63b2d",
# Replace the commit hash (daae6f40adfa5fdb7c89684cbe4d88b691c63b2d) in both places (below) with the latest (https://github.com/hedronvision/bazel-compile-commands-extractor/commits/main), rather than using the stale one here.
# Even better, set up Renovate and let it do the work for you (see "Suggestion: Updates" in the README).
+ sha256 = "43451a32bf271e7ba4635a07f7996d535501f066c0fe8feab04fb0c91dd5986e",
+ strip_prefix = "bazel-compile-commands-extractor-daae6f40adfa5fdb7c89684cbe4d88b691c63b2d",
url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/daae6f40adfa5fdb7c89684cbe4d88b691c63b2d.tar.gz",
# When you first run this tool, it'll recommend a sha256 hash to put here with a message like: "DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = ..."
)
diff --git a/tools/dependency_rewrite b/tools/dependency_rewrite
index 2d2e610..1cd117a 100644
--- a/tools/dependency_rewrite
+++ b/tools/dependency_rewrite
@@ -17,6 +17,7 @@
rewrite downloads.sourceforge.net/(.*) software.frc971.org/Build-Dependencies/downloads.sourceforge.net/$1
rewrite cdn.cypress.io/(.*) software.frc971.org/Build-Dependencies/cdn.cypress.io/$1
rewrite www.googleapis.com/(.*) software.frc971.org/Build-Dependencies/www.googleapis.com/$1
+rewrite www.johnvansickle.com/(.*) software.frc971.org/Build-Dependencies/www.johnvansickle.com/$1
allow crates.io
allow golang.org
allow go.dev
diff --git a/y2023/vision/BUILD b/y2023/vision/BUILD
index 88c1f54..97ce003 100644
--- a/y2023/vision/BUILD
+++ b/y2023/vision/BUILD
@@ -410,3 +410,32 @@
"//aos/events:shm_event_loop",
],
)
+
+cc_binary(
+ name = "video_ripper",
+ srcs = [
+ "video_ripper.cc",
+ ],
+ data = [
+ "@ffmpeg",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos:init",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_reader",
+ "//frc971/vision:vision_fbs",
+ "@com_github_gflags_gflags//:gflags",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
+sh_binary(
+ name = "video_tiler",
+ srcs = ["video_tiler.sh"],
+ data = [
+ "@bazel_tools//tools/bash/runfiles",
+ "@ffmpeg",
+ ],
+)
diff --git a/y2023/vision/video_ripper.cc b/y2023/vision/video_ripper.cc
new file mode 100644
index 0000000..3c93139
--- /dev/null
+++ b/y2023/vision/video_ripper.cc
@@ -0,0 +1,80 @@
+#include <unistd.h>
+
+#include <cstdlib>
+
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+#include "aos/events/logging/log_reader.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/init.h"
+#include "aos/scoped/scoped_fd.h"
+#include "frc971/vision/vision_generated.h"
+
+DEFINE_string(channel, "/camera", "Channel name for the image.");
+DEFINE_int32(width, 1280, "Width of the image");
+DEFINE_int32(height, 720, "Height of the image");
+DEFINE_string(ffmpeg_binary, "external/ffmpeg/ffmpeg",
+ "The path to the ffmpeg binary");
+DEFINE_string(output_path, "video_ripper_output.mp4",
+ "The path to output the mp4 video");
+DEFINE_bool(flip, true, "If true, rotate the video 180 deg.");
+
+// Replays a log and dumps the contents of /camera frc971.vision.CameraImage
+// directly to stdout.
+int main(int argc, char *argv[]) {
+ aos::InitGoogle(&argc, &argv);
+ const std::vector<aos::logger::LogFile> logfiles =
+ aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
+ CHECK(!logfiles.empty());
+
+ // Start ffmpeg
+ std::stringstream command;
+ command << FLAGS_ffmpeg_binary;
+ command << " -framerate 30 -f rawvideo -pix_fmt yuyv422";
+ command << " -s " << FLAGS_width << "x" << FLAGS_height;
+ command << " -i pipe:";
+ command << " -c:v libx264 -f mp4";
+ if (FLAGS_flip) {
+ command << " -vf rotate=PI";
+ }
+ command << " \"" << FLAGS_output_path << "\"";
+
+ FILE *stream = popen(command.str().c_str(), "w");
+
+ const std::string replay_node = logfiles.at(0).logger_node;
+ LOG(INFO) << "Replaying as \"" << replay_node << "\"";
+
+ aos::logger::LogReader reader(logfiles, nullptr);
+ aos::SimulatedEventLoopFactory factory(reader.configuration());
+ reader.RegisterWithoutStarting(&factory);
+
+ const aos::Node *node =
+ (replay_node.empty() ||
+ !aos::configuration::MultiNode(reader.configuration()))
+ ? nullptr
+ : aos::configuration::GetNode(reader.configuration(), replay_node);
+
+ std::unique_ptr<aos::EventLoop> event_loop;
+ factory.GetNodeEventLoopFactory(node)->OnStartup([&stream, &event_loop,
+ &reader, node]() {
+ event_loop =
+ reader.event_loop_factory()->MakeEventLoop("video_ripper", node);
+ event_loop->MakeWatcher(
+ FLAGS_channel, [&stream](const frc971::vision::CameraImage &image) {
+ CHECK_EQ(FLAGS_width, image.cols())
+ << "Image width needs to match the images in the logfile";
+ CHECK_EQ(FLAGS_height, image.rows())
+ << "Image width needs to match the images in the logfile";
+
+ const size_t bytes_written =
+ write(fileno(stream), image.data()->data(), image.data()->size());
+ PCHECK(bytes_written == image.data()->size());
+ });
+ });
+
+ reader.event_loop_factory()->Run();
+ reader.Deregister();
+
+ pclose(stream);
+}
diff --git a/y2023/vision/video_tiler.sh b/y2023/vision/video_tiler.sh
new file mode 100755
index 0000000..5ad3e59
--- /dev/null
+++ b/y2023/vision/video_tiler.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# --- begin runfiles.bash initialization ---
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+
+
+# Copied from http://trac.ffmpeg.org/wiki/Create%20a%20mosaic%20out%20of%20several%20input%20videos
+
+$(rlocation ffmpeg/ffmpeg)\
+ -i $1 -i $2 -i $3 -i $4\
+ -filter_complex "
+ nullsrc=size=2560x1440 [base];
+ [0:v] setpts=PTS-STARTPTS, scale=1280x720 [upperleft];
+ [1:v] setpts=PTS-STARTPTS, scale=1280x720 [upperright];
+ [2:v] setpts=PTS-STARTPTS, scale=1280x720 [lowerleft];
+ [3:v] setpts=PTS-STARTPTS, scale=1280x720 [lowerright];
+ [base][upperleft] overlay=shortest=1 [tmp1];
+ [tmp1][upperright] overlay=shortest=1:x=1280 [tmp2];
+ [tmp2][lowerleft] overlay=shortest=1:y=720 [tmp3];
+ [tmp3][lowerright] overlay=shortest=1:x=1280:y=720
+ " \
+ -c:v libx264 ~/video_tiler_out.mp4