#include "http_server.h" #include "camera.h" QHash 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 presets = camera->presets(); QJsonArray list; for (const auto& preset : presets) { list.append(QJsonObject({{"id", static_cast(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(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(); }