Skip to content

Commit

Permalink
Better access (#19)
Browse files Browse the repository at this point in the history
* implemented a small web frontend to get a video file, next i just need to convert it to a stream of matrices and pass it to hyperwall

* initial implementation of using hyperwall through web ui

* finished implementing file uploading and added some more convenience scripts

* depends

* depends

* depends
  • Loading branch information
Mast3rwaf1z authored Dec 1, 2024
1 parent ae6eed4 commit a6c9b08
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 34 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build project
name: Build and test project


on:
Expand All @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: install dependencies
run: sudo apt-get update && sudo apt-get -y install cmake libopencv-dev libspdlog-dev libargparse-dev
run: sudo apt-get update && sudo apt-get -y install cmake libopencv-dev libspdlog-dev libargparse-dev libcpp-httplib-dev
- uses: actions/cache@v4
with:
path: build
Expand Down
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ find_package(spdlog REQUIRED)
# catch2
find_package(Catch2 REQUIRED)

foreach(exe Sender Receiver)
# httplib
find_package(httplib REQUIRED)

# init all executables
foreach(exe Sender Receiver SenderWeb)
add_executable(${exe} src/${exe}.cpp ${PROJECT_IMPLEMENTATIONS})

# Linking
target_link_libraries(${exe}
${OpenCV_LIBS}
argparse::argparse
spdlog::spdlog
httplib::httplib
)
endforeach()

Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
spdlog
argparse
catch2_3
httplib
];
};
}) {
Expand Down
10 changes: 10 additions & 0 deletions include/Utils.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#pragma once

#include <string>
#include <vector>

namespace Util {
Expand All @@ -19,4 +21,12 @@ constexpr std::vector<T> range(const int start, const int end) {
return values;
}

constexpr std::tuple<int, int> split_resolution(const std::string res) {
return {
std::stoi(res.substr(0, res.find("x"))),
std::stoi(res.substr(res.find("x")+1, res.size()))
};
}


}
52 changes: 52 additions & 0 deletions include/Web/Server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <httplib.h>
#include <spdlog/spdlog.h>

namespace Web {

class Server {
httplib::Server server;
public:

Server() {
server.set_logger([](httplib::Request req, httplib::Response res){
std::stringstream ss;
ss << "Method: " << req.method << " | ";
ss << "Path: " << req.path << " | ";
ss << "Status: " << res.status << " | ";

switch(((res.status + 50) / 100) * 100) {
case 100:
case 300:
spdlog::warn(ss.str());
break;
case 200:
spdlog::info(ss.str());
break;
case 400:
case 500:
spdlog::error(ss.str());
}
});
}
void run(std::string host, int port) {
spdlog::info("Running server: {}:{}", host, port);
server.listen(host, port);
}

void add(std::string path, std::string content) {
server.Get(path, [content](httplib::Request req, httplib::Response& res) {
res.set_content(content, "text/html");
});
}
void add_page(std::string path, std::ifstream file) {
std::stringstream ss;
ss << file.rdbuf();
return add(path, ss.str());
}
template<typename F>
void Post (std::string path, F f) {
server.Post(path, f);
}
};

}
6 changes: 6 additions & 0 deletions scripts/ffplay-single.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

while true; do
ffplay rtsp://${1}:8554/frame/${2}/${3} -fs -autoexit -window_title "frame-${2}x${3}"
sleep 1
done
9 changes: 4 additions & 5 deletions scripts/tmux-ffplay.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env bash

for ((x=0;x<$1;x++)); do
for ((y=0;y<$2;y++)); do
tmux new-session -d ./build/Receiver ${x}x${y}
done
for ((x=0;x<$2;x++)); do
for ((y=0;y<$3;y++)); do
tmux new-session -d ./scripts/ffplay-single.sh $1 $x $y
done
done
23 changes: 12 additions & 11 deletions src/Receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@

#include "Utils.hpp"

std::string split_res(std::string res, std::string direction) {
if (direction == "x") {
return res.substr(0, res.find("x"));
cv::VideoCapture await_capture(std::string path) {
while(true) {
cv::VideoCapture capture(path);
if (capture.isOpened())
return capture;
}
return res.substr(res.find("x")+1, res.size());
}

int main(int argc, char* argv[]) {
Expand All @@ -38,10 +39,8 @@ int main(int argc, char* argv[]) {
exit(EXIT_FAILURE);
}
if(!parser.get<bool>("--test")) {
cv::VideoCapture capture(
std::format("rtsp://0.0.0.0:8554/frame/{}/{}",
split_res(parser.get("coordinate"), "x"),
split_res(parser.get("coordinate"), "y")));
const auto [x, y] = Util::split_resolution(parser.get("coordinate"));
cv::VideoCapture capture = await_capture(std::format("rtsp://0.0.0.0:8554/frame/{}/{}", x, y));
while(true) {
cv::Mat frame;
capture.read(frame);
Expand All @@ -57,11 +56,13 @@ int main(int argc, char* argv[]) {
else {
// not particularly good
std::vector<std::thread> threads;
for(const auto& x : Util::range(stoi(split_res(parser.get("coordinate"), "x")))) {
for(const auto& y : Util::range(stoi(split_res(parser.get("coordinate"), "y")))) {
const auto [X, Y] = Util::split_resolution(parser.get("coordinate"));
for(const auto& x : Util::range(X)) {

for(const auto& y : Util::range(Y)) {
threads.push_back(std::thread{[x, y](){
spdlog::info("Running thread: {} {}", x, y);
auto capture = cv::VideoCapture(std::format("rtsp://0.0.0.0:8554/frame/{}/{}", x, y));
auto capture = await_capture(std::format("rtsp://0.0.0.0:8554/frame/{}/{}", x, y));
while(true) {
cv::Mat frame;
capture.read(frame);
Expand Down
18 changes: 3 additions & 15 deletions src/Sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@
#include "Hyperwall.hpp"
#include "Sources/FileSource.hpp"
#include "Sources/WebcamSource.hpp"

std::string split_res(std::string res, std::string direction) {
if (direction == "x") {
return res.substr(0, res.find("x"));
}
return res.substr(res.find("x")+1, res.size());
}
#include "Utils.hpp"

int main(int argc, char* argv[]) {
auto loglevel = (int)spdlog::level::info;
Expand Down Expand Up @@ -61,14 +55,8 @@ int main(int argc, char* argv[]) {
spdlog::debug("Generating settings");

Hyperwall::Settings settings(
{
stoi(split_res(parser.get("--resolution"), "x")),
stoi(split_res(parser.get("--resolution"), "y"))
},
{
stoi(split_res(parser.get("--dimensions"), "x")),
stoi(split_res(parser.get("--dimensions"), "y"))
},
Util::split_resolution(parser.get("--resolution")),
Util::split_resolution(parser.get("--dimensions")),
parser.get("--rtsp-server"),
parser.get("--bitrate"),
parser.get<int>("--framerate")
Expand Down
147 changes: 147 additions & 0 deletions src/SenderWeb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#include <filesystem>
#include <fstream>
#include <opencv2/videoio.hpp>
#include <spdlog/spdlog.h>

#include "Hyperwall.hpp"
#include "Sources/FileSource.hpp"
#include "Utils.hpp"
#include "Web/Server.hpp"

std::vector<std::thread> threads;
bool running;

std::string layout(std::string input) {
std::string style = R"(
<style>
html {
font-family: sans-serif;
}
body {
margin-left: 5%;
margin-right: 5%;
text-align: center;
}
form {
text-align: right;
}
section {
text-align: center;
width: 400px;
display: inline-flex;
}
footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
}
</style>
)";
return std::format(
R"(
<!DOCTYPE html>
<html>
{}
<head>
<title>Hyperwall</title>
<link rel="icon" type="image/png" href="https://avatars.githubusercontent.com/u/126147587?s200&amp;v=4">
</head>
<body>
<img style="position:fixed; left: 6%; top: 1%;" src="https://avatars.githubusercontent.com/u/126147587?s=200&amp;v=4" width="100" height="100">
<h1>Hyperwall</h1>
<br><br>
<hr>
{}
</body>
<footer>
Available on <a href="https://github.com/aalborg-supercomputer-klubben/hyperwall">Github</a> | Created By: <a href="https://skade.dev">Mast3r_waf1z</a>
</footer>
</html>
)", style, input);
}

int main(int argc, char* argv[]) {
Web::Server server;

server.add("/", layout(R"(
<section>
<form id="videoUploadForm" method="POST" enctype="multipart/form-data" action="/run">
<label for="videoFile">Choose a video file: </label>
<input type="file" id="videoFile" name="videoFile" accept="video/*" required>
<br><br>
<label for="dimensions">dimensions: </label>
<input type="text" id="dimensions" name="dimensions" placeholder="2x2">
<br><br>
<label for="resolution">resolution: </label>
<input type="text" id="resolution" name="resolution" placeholder="1920x1080">
<br><br>
<button type="submit">Run</button>
</form>
</section>
)"));

server.Post("/run", [](httplib::Request request, httplib::Response& response){
if(!request.has_file("videoFile")) {
response.status = 400;
response.set_content("No file uploaded.", "text/plain");
return;
}
std::tuple<int, int> dimensions, resolution;
if(request.has_file("dimensions") && request.get_file_value("dimensions").content.contains("x")) {
auto dim = request.get_file_value("dimensions");
dimensions = Util::split_resolution(dim.content);
}
else {
spdlog::warn("No dim provided");
dimensions = {2, 2};
}
if (request.has_file("resolution") && request.get_file_value("resolution").content.contains("x")) {
auto res = request.get_file_value("resolution");
resolution = Util::split_resolution(res.content);
}
else {
spdlog::warn("No res provided");
resolution = {1920, 1080};
}


const auto& file = request.get_file_value("videoFile");
spdlog::info("Got file: {}", file.filename);

if (running) {
response.status = 400;
response.set_content("Failed, hyperwall is already running", "text/plain");
return;
}
auto filename = std::format("./tmp/{}", file.filename);

std::ofstream ofs("./tmp/" + file.filename, std::ios::binary);
if (ofs) {
ofs.write(file.content.data(), file.content.size());
ofs.close();
ofs.flush();
}

threads.push_back(std::thread{[&filename, dimensions, resolution](){
running = true;
Hyperwall::FileSource source(filename);
Hyperwall::Settings settings(
resolution,
dimensions
);
Hyperwall::Hyperwall hyperwall(source, settings);
hyperwall.run();
std::filesystem::remove(filename);
running = false;
}});

response.set_content(layout(R"(
Successfully uploaded video!<br>
<a href="/">Go back</a>
)"), "text/html");
});

server.run("0.0.0.0", 8000);
}

0 comments on commit a6c9b08

Please sign in to comment.