Files
dvr/http_server.cpp
2026-02-08 08:58:29 +08:00

220 lines
7.5 KiB
C++

#include "http_server.h"
#include "camera.h"
QHash<unsigned long, Camera*> HttpServer::sockets;
HttpServer& HttpServer::instance() {
static HttpServer server;
return server;
}
void HttpServer::start(const QString &host, int port) {
hk_net_sdk_init(NULL); // 全局初始化一次
mg_mgr_init(&mgr);
mg_log_set(MG_LL_INFO);
std::string url = "http://" + host.toStdString() + ":" + std::to_string(port);
struct mg_connection *conn = mg_http_listen(&mgr, url.c_str(), ev_handler, this);
if (conn == NULL) {
qCritical().noquote() << "无法启动服务:" << QString::fromStdString(url);
return;
}
qInfo().noquote() << "服务运行中:" << QString::fromStdString(url);
qInfo() << "按 Ctrl+C 停止服务!";
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this]() {
mg_mgr_poll(&mgr, 0);
});
timer->start(100);
}
void HttpServer::stop() {
hk_net_sdk_uninit(); // 全局反初始化一次
if (!timer) return;
if (timer) {
timer->stop();
delete timer;
timer = nullptr;
}
mg_mgr_free(&mgr);
qInfo() << "服务器已停止!";
}
void HttpServer::ev_handler(struct mg_connection *conn, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
std::string method(hm->method.buf, hm->method.len);
if (mg_match(hm->uri, mg_str("/dvr/t"), NULL)) {
mg_ws_upgrade(conn, hm, NULL);
return;
}
QString data = QString::fromUtf8(hm->body.buf, hm->body.len);
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &parseError);
if (parseError.error != QJsonParseError::NoError) {
mg_http_reply(conn, 400, "Content-Type: application/json\r\n", "{}");
return;
}
if (!doc.isObject()) return;
// 已设置和特殊预置位
if (mg_match(hm->uri, mg_str("/dvr/camera/preset/list"), NULL)) {
QJsonObject packet = doc.object();
CameraInfo cameraInfo = CameraInfo::parseFrom(packet);
Camera* camera = Camera::of(cameraInfo);
bool ok = camera->login();
if (ok) {
std::vector<STRU_PRESET_CONFIG> presets = camera->presets();
QJsonArray list;
for (const auto& preset : presets) {
list.append(QJsonObject({{"id", static_cast<int>(preset.ui_no)}, {"special", preset.ui_especial == 1}, {"setted", preset.ui_enable == 1}}));
}
QByteArray buffer = QJsonDocument(list).toJson(QJsonDocument::Compact);
mg_http_reply(conn, 200, "Content-Type: application/json\r\n", buffer);
} else {
mg_http_reply(conn, 401, "Content-Type: application/json\r\n", "{}");
}
}
// 设置预置位
if (mg_match(hm->uri, mg_str("/dvr/camera/preset"), NULL)) {
QJsonObject packet = doc.object();
CameraInfo cameraInfo = CameraInfo::parseFrom(packet);
Camera* camera = Camera::of(cameraInfo);
bool ok = camera->login();
if (ok) {
ok = camera->addPreset(packet);
mg_http_reply(conn, ok ? 200 : 403, "Content-Type: application/json\r\n", "{}");
} else {
mg_http_reply(conn, 401, "Content-Type: application/json\r\n", "{}");
}
}
// 关联可见光IP
if (mg_match(hm->uri, mg_str("/dvr/camera/getViCameraIP"), NULL)) {
QJsonObject packet = doc.object();
CameraInfo cameraInfo = CameraInfo::parseFrom(packet);
Camera* camera = Camera::of(cameraInfo);
bool ok = camera->login();
if (ok) {
QString ip = camera->getViCameraIP();
if (ip.isEmpty()) {
mg_http_reply(conn, 404, "Content-Type: application/json\r\n", "{}");
} else {
mg_http_reply(conn, 200, "Content-Type: application/json\r\n", ip.toUtf8().constData());
}
} else {
mg_http_reply(conn, 401, "Content-Type: application/json\r\n", "{}");
}
}
// 抓图
if (mg_match(hm->uri, mg_str("/dvr/camera/capture"), NULL)) {
QJsonObject packet = doc.object();
CameraInfo cameraInfo = CameraInfo::parseFrom(packet);
Camera* camera = Camera::of(cameraInfo);
bool ok = camera->login();
if (ok) {
CaptureData result = camera->capture();
if (result.ok) {
QByteArray data = result.image;
qint64 dataSize = data.size();
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "Content-Type: application/octet-stream\r\n");
mg_printf(conn, "Content-Length: %lld\r\n", dataSize);
mg_printf(conn, "Connection: close\r\n");
mg_printf(conn, "Cache-Control: no-cache\r\n");
mg_printf(conn, "Accept-Ranges: bytes\r\n");
mg_printf(conn, "\r\n");
mg_send(conn, "", 0);
const int BUFFER_SIZE = 256 * 1024;
qint64 totalSent = 0;
const char* rawData = data.constData();
while (totalSent < dataSize) {
qint64 remaining = dataSize - totalSent;
qint64 chunkSize = qMin(static_cast<qint64>(BUFFER_SIZE), remaining);
mg_send(conn, rawData + totalSent, chunkSize);
totalSent += chunkSize;
if (totalSent % (4 * BUFFER_SIZE) == 0) {
QCoreApplication::processEvents();
}
}
} else {
mg_http_reply(conn, 500, "Content-Type: application/json\r\n", "{}");
}
} else {
mg_http_reply(conn, 401, "Content-Type: application/json\r\n", "{}");
}
}
}
if (ev == MG_EV_WS_MSG) {
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
QString data = QString::fromUtf8(wm->data.buf, wm->data.len);
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &parseError);
if (parseError.error != QJsonParseError::NoError) {
return;
}
if (doc.isObject()) {
QJsonObject packet = doc.object();
QString event = packet["event"].toString();
if (event == "camera:connect") {
QJsonObject payload = packet["payload"].toObject();
CameraInfo cameraInfo = CameraInfo::parseFrom(payload);
Camera* camera = Camera::of(cameraInfo);
bool ok = camera->login();
if (ok) {
camera->attach(conn);
camera->startRealtimeTemperatureCallback();
sockets.insert(conn->id, camera);
}
}
}
}
if (ev == MG_EV_CLOSE) {
if (conn->is_websocket) {
Camera* camera = sockets.take(conn->id);
if (camera) {
camera->unattach();
camera->stopRealtimeTemperatureCallback();
}
}
}
}
HttpServer::~HttpServer() {
stop();
}