This commit is contained in:
cxh 2025-10-15 10:30:36 +08:00
parent cb37ca9eb3
commit 4de32c3487
2 changed files with 99 additions and 132 deletions

View File

@ -7,79 +7,79 @@
"client_id": "20004_vehmedia", "client_id": "20004_vehmedia",
"username": "20004_4A:69:BE:32:59:AE", "username": "20004_4A:69:BE:32:59:AE",
"password": "31d6bb29f177d5bf8560756c0f0e63c63fd412e52c5b9ea59476024eab893884a5f34f0637e0fe3ad42b802c16edb6feb37cde613957c3540c060c07b230cb0aa6b4547bb86fcae43d484179d3a11a1969a2f367ec0ceede4c10510757a89927af4c2d0c0484476be3241a9ff9242e7401f3fbcd824b5cfb19674663b7045e32dd2f97b4", "password": "31d6bb29f177d5bf8560756c0f0e63c63fd412e52c5b9ea59476024eab893884a5f34f0637e0fe3ad42b802c16edb6feb37cde613957c3540c060c07b230cb0aa6b4547bb86fcae43d484179d3a11a1969a2f367ec0ceede4c10510757a89927af4c2d0c0484476be3241a9ff9242e7401f3fbcd824b5cfb19674663b7045e32dd2f97b4",
"mqtt_heart_threshold": 2000 "mqtt_heart_threshold": 1000
}, },
"cameras": [ "cameras": [
{ {
"device": "/dev/video11", "device": "/dev/video0",
"name": "AHD1", "name": "AHD1",
"enabled": true, "enabled": true,
"resolution": "720p", "resolution": "960p",
"width": 1280, "width": 1280,
"height": 720, "height": 960,
"fps": 30
},
{
"device": "/dev/video12",
"name": "AHD2",
"enabled": false,
"resolution": "720p",
"width": 1280,
"height": 720,
"fps": 30
},
{
"device": "/dev/video13",
"name": "AHD3",
"enabled": false,
"resolution": "720p",
"width": 1280,
"height": 720,
"fps": 30
},
{
"device": "/dev/video14",
"name": "AHD4",
"enabled": false,
"resolution": "720p",
"width": 1280,
"height": 720,
"fps": 30
},
{
"device": "/dev/video3",
"name": "AHD5",
"enabled": false,
"resolution": "1080p",
"width": 1920,
"height": 1080,
"fps": 30
},
{
"device": "/dev/video2",
"name": "AHD6",
"enabled": false,
"resolution": "1080p",
"width": 1920,
"height": 1080,
"fps": 30 "fps": 30
}, },
{ {
"device": "/dev/video1", "device": "/dev/video1",
"name": "AHD7", "name": "AHD2",
"enabled": false, "enabled": true,
"resolution": "1080p", "resolution": "960p",
"width": 1920, "width": 1280,
"height": 1080, "height": 960,
"fps": 30 "fps": 30
}, },
{ {
"device": "/dev/video0", "device": "/dev/video2",
"name": "AHD3",
"enabled": true,
"resolution": "960p",
"width": 1280,
"height": 960,
"fps": 30
},
{
"device": "/dev/video3",
"name": "AHD4",
"enabled": true,
"resolution": "960p",
"width": 1280,
"height": 960,
"fps": 30
},
{
"device": "/dev/video11",
"name": "AHD5",
"enabled": true,
"resolution": "960p",
"width": 1280,
"height": 960,
"fps": 30
},
{
"device": "/dev/video12",
"name": "AHD6",
"enabled": true,
"resolution": "960p",
"width": 1280,
"height": 960,
"fps": 30
},
{
"device": "/dev/video13",
"name": "AHD7",
"enabled": true,
"resolution": "960p",
"width": 1280,
"height": 960,
"fps": 30
},
{
"device": "/dev/video14",
"name": "AHD8", "name": "AHD8",
"enabled": false, "enabled": true,
"resolution": "1080p", "resolution": "960p",
"width": 1920, "width": 1280,
"height": 1080, "height": 960,
"fps": 30 "fps": 30
} }
] ]

View File

@ -82,11 +82,24 @@ void RTMPManager::stream_loop(Camera cam, StreamType type)
std::string key = make_stream_key(cam.name, type); std::string key = make_stream_key(cam.name, type);
LOG_INFO("[RTMP] Stream loop started for " + key); LOG_INFO("[RTMP] Stream loop started for " + key);
const int MAX_RETRIES = 5; GstElement *pipeline = create_pipeline(cam, type);
int retry_count = 0; if (!pipeline)
{
update_status(key, {false, StreamResult::PIPELINE_ERROR, "Failed to create pipeline"});
LOG_ERROR("[RTMP] " + key + " pipeline creation failed. Exiting stream loop.");
return;
}
GstBus *bus = gst_element_get_bus(pipeline);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
update_status(key, {true, StreamResult::OK, ""});
LOG_INFO("[RTMP] " + key + " is streaming.");
while (true) while (true)
{ {
GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
{ {
std::lock_guard<std::mutex> lock(streams_mutex); std::lock_guard<std::mutex> lock(streams_mutex);
auto it = streams.find(key); auto it = streams.find(key);
@ -94,88 +107,42 @@ void RTMPManager::stream_loop(Camera cam, StreamType type)
break; break;
} }
GstElement *pipeline = create_pipeline(cam, type); if (!msg)
if (!pipeline)
{
update_status(key, {false, StreamResult::PIPELINE_ERROR, "Failed to create pipeline"});
std::this_thread::sleep_for(std::chrono::seconds(3));
if (++retry_count > MAX_RETRIES)
break;
continue; continue;
}
GstBus *bus = gst_element_get_bus(pipeline); if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR)
gst_element_set_state(pipeline, GST_STATE_PLAYING);
update_status(key, {true, StreamResult::OK, ""});
LOG_INFO("[RTMP] " + key + " is streaming.");
bool stop_flag = false;
GstMessage *msg = nullptr;
while (true)
{ {
msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GError *err = nullptr;
static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); gchar *debug = nullptr;
gst_message_parse_error(msg, &err, &debug);
{ std::string err_msg = err ? err->message : "Unknown GStreamer error";
std::lock_guard<std::mutex> lock(streams_mutex); LOG_ERROR("[RTMP] Error in " + key + ": " + err_msg);
auto it = streams.find(key); update_status(key, {false, StreamResult::CONNECTION_FAIL, err_msg});
if (it == streams.end() || !it->second->running.load()) if (err)
{ g_error_free(err);
stop_flag = true; if (debug)
break; g_free(debug);
}
}
if (!msg)
continue;
if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR)
{
GError *err = nullptr;
gchar *debug = nullptr;
gst_message_parse_error(msg, &err, &debug);
std::string err_msg = err ? err->message : "Unknown GStreamer error";
LOG_ERROR("[RTMP] Error in " + key + ": " + err_msg);
update_status(key, {false, StreamResult::CONNECTION_FAIL, err_msg});
if (err)
g_error_free(err);
if (debug)
g_free(debug);
stop_flag = true;
}
else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS)
{
LOG_WARN("[RTMP] EOS on " + key);
update_status(key, {false, StreamResult::EOS_RECEIVED, "EOS"});
stop_flag = true;
}
gst_message_unref(msg); gst_message_unref(msg);
if (stop_flag) break; // 出错立即退出
break;
} }
else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS)
gst_element_set_state(pipeline, GST_STATE_NULL);
if (bus)
gst_object_unref(bus);
gst_object_unref(pipeline);
if (!stop_flag)
break;
if (++retry_count > MAX_RETRIES)
{ {
LOG_ERROR("[RTMP] " + key + " reached max retries. Giving up."); LOG_WARN("[RTMP] EOS on " + key);
break; update_status(key, {false, StreamResult::EOS_RECEIVED, "EOS"});
gst_message_unref(msg);
break; // EOS 退出
} }
LOG_WARN("[RTMP] Reconnecting " + key + " in 3s..."); gst_message_unref(msg);
std::this_thread::sleep_for(std::chrono::seconds(3));
} }
update_status(key, {false, StreamResult::UNKNOWN, "Stream loop exited"}); gst_element_set_state(pipeline, GST_STATE_NULL);
if (bus)
gst_object_unref(bus);
gst_object_unref(pipeline);
LOG_INFO("[RTMP] Stream loop ended for " + key); LOG_INFO("[RTMP] Stream loop ended for " + key);
update_status(key, {false, StreamResult::UNKNOWN, "Stream loop exited"});
} }
void RTMPManager::start_camera(const Camera &cam, StreamType type) void RTMPManager::start_camera(const Camera &cam, StreamType type)