mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-06 20:53:00 -05:00
Merge pull request #5227 from nabbi/fix/b74-zms-raw-frame-boundary
fix: write the multipart boundary for stored JPEGs sent by nph-zms
This commit is contained in:
2 files changed
+61
No files matched your search
@@ -371,7 +371,11 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
||||
// Calculate how long it takes to actually send the frame
|
||||
TimePoint send_start_time = std::chrono::steady_clock::now();
|
||||
|
||||
// Same part headers as sendFrame(Image *): send_raw is only set for
|
||||
// STREAM_JPEG, which is multipart.
|
||||
if (
|
||||
(0 > fputs("--" BOUNDARY "\r\nContent-Type: image/jpeg\r\n", stdout))
|
||||
||
|
||||
(0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %.6f\r\n\r\n",
|
||||
img_buffer_size, std::chrono::duration_cast<FPSeconds>(timestamp.time_since_epoch()).count()))
|
||||
||
|
||||
|
||||
@@ -23,9 +23,15 @@
|
||||
#include "zm_monitor.h"
|
||||
#include "zm_zone.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE("MonitorStreamBufferLevel") {
|
||||
SECTION("zero buffer count does not divide by zero") {
|
||||
// Regression for zoneminder/zoneminder#4936: processCommand() could run on
|
||||
@@ -107,11 +113,32 @@ class TestMonitor : public Monitor {
|
||||
// which needs the database. Hand one in directly instead.
|
||||
class TestMonitorStream : public MonitorStream {
|
||||
public:
|
||||
using MonitorStream::sendFrame;
|
||||
void setMonitor(const std::shared_ptr<Monitor> &p_monitor) {
|
||||
monitor = p_monitor;
|
||||
}
|
||||
};
|
||||
|
||||
// What a call writes to stdout, through a temporary file.
|
||||
template <typename F>
|
||||
std::string CaptureStdout(F f) {
|
||||
char path[] = "/tmp/zm_test_stdout_XXXXXX";
|
||||
int fd = mkstemp(path);
|
||||
REQUIRE(fd >= 0);
|
||||
fflush(stdout);
|
||||
int saved = dup(fileno(stdout));
|
||||
dup2(fd, fileno(stdout));
|
||||
f();
|
||||
fflush(stdout);
|
||||
dup2(saved, fileno(stdout));
|
||||
close(saved);
|
||||
close(fd);
|
||||
std::ifstream in(path, std::ios::binary);
|
||||
std::string out((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
unlink(path);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("MonitorStream releases its monitor when destroyed", "[Stream]") {
|
||||
@@ -135,3 +162,33 @@ TEST_CASE("MonitorStream releases its monitor when destroyed", "[Stream]") {
|
||||
|
||||
REQUIRE(watched.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("MonitorStream sends stored JPEGs as multipart parts", "[Stream]") {
|
||||
// Buffered playback at scale 100 without zoom sends the swap file as it is
|
||||
// (sendFrame(filepath)'s raw path). Each part needs the boundary and its
|
||||
// Content-Type like the frames sendFrame(Image *) writes, or the
|
||||
// multipart/x-mixed-replace stream breaks during rewind or step.
|
||||
EnsureConfig();
|
||||
config.timestamp_on_capture = true;
|
||||
|
||||
char jpeg_path[] = "/tmp/zm_test_swap_XXXXXX";
|
||||
int fd = mkstemp(jpeg_path);
|
||||
REQUIRE(fd >= 0);
|
||||
const std::string jpeg = "\xff\xd8not really a jpeg\xff\xd9";
|
||||
REQUIRE(write(fd, jpeg.data(), jpeg.size()) == static_cast<ssize_t>(jpeg.size()));
|
||||
close(fd);
|
||||
|
||||
TestMonitorStream stream;
|
||||
stream.setStreamType(StreamBase::STREAM_JPEG);
|
||||
bool sent = false;
|
||||
const std::string out = CaptureStdout([&] {
|
||||
sent = stream.sendFrame(std::string(jpeg_path), SystemTimePoint(Seconds(1700000000)));
|
||||
});
|
||||
unlink(jpeg_path);
|
||||
|
||||
REQUIRE(sent);
|
||||
const std::string head = "--" BOUNDARY "\r\nContent-Type: image/jpeg\r\nContent-Length: " +
|
||||
std::to_string(jpeg.size()) + "\r\n";
|
||||
REQUIRE(out.compare(0, head.size(), head) == 0);
|
||||
REQUIRE(out.find(jpeg) != std::string::npos);
|
||||
}
|
||||
Reference in new issue
Block a user