diff --git a/src/rtmp_manager.cpp b/src/rtmp_manager.cpp index 407a756..282546d 100644 --- a/src/rtmp_manager.cpp +++ b/src/rtmp_manager.cpp @@ -96,15 +96,18 @@ GstElement *RTMPManager::create_pipeline(const Camera &cam, StreamType type) std::string stream_name = cam.name + stream_type_suffix(type); + // ✅ 注意:只用 sync/async std::string pipeline_str = "v4l2src device=" + cam.device + " ! video/x-raw,format=NV12,width=" + std::to_string(width) + ",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) + "/1 ! queue max-size-buffers=1 leaky=downstream " "! mpph264enc bps=" + std::to_string(bitrate) + " gop=" + std::to_string(fps) + - " ! h264parse ! flvmux streamable=true name=mux " + " ! h264parse config-interval=-1 " + "! flvmux streamable=true name=mux " + "! queue leaky=downstream max-size-buffers=0 max-size-time=2000000000 max-size-bytes=0 " "! rtmpsink location=\"rtmp://127.0.0.1/live/" + - stream_name + " live=1\" sync=false async-handling=true do-timestamp=true"; + stream_name + " live=1\" sync=false async=false"; LOG_INFO("[RTMP] Creating pipeline for '" + stream_name + "': " + pipeline_str); @@ -274,7 +277,7 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) gst_element_set_state(pipeline, GST_STATE_PLAYING); bool first_frame = false; - const auto t0 = std::chrono::steady_clock::now(); + const auto start_time = std::chrono::steady_clock::now(); while (true) { @@ -287,8 +290,9 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) break; } + // 首帧超时保护 if (!first_frame && - std::chrono::duration_cast(std::chrono::steady_clock::now() - t0).count() > 5) + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count() > 5) { status.running = false; status.last_result = StreamResult::TIMEOUT; @@ -298,8 +302,7 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) } GstMessage *msg = gst_bus_timed_pop_filtered( - bus, 100 * GST_MSECOND, - static_cast(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED)); + bus, 200 * GST_MSECOND, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED)); if (!msg) continue; @@ -322,13 +325,20 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) case GST_MESSAGE_ERROR: { GError *err = nullptr; - gst_message_parse_error(msg, &err, nullptr); + gchar *debug = nullptr; + gst_message_parse_error(msg, &err, &debug); + status.running = false; status.last_result = StreamResult::CONNECTION_FAIL; status.last_error = err ? err->message : "GStreamer error"; + LOG_ERROR("[RTMP] Stream error from '" + cam.name + "': " + status.last_error); - gst_element_set_state(pipeline, GST_STATE_NULL); // ✅ 防止卡死 + + // ✅ 立即停止 pipeline,避免阻塞 + gst_element_set_state(pipeline, GST_STATE_NULL); + if (err) g_error_free(err); + if (debug) g_free(debug); try_set_start(status); break; } @@ -340,8 +350,6 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) try_set_start(status); break; } - default: - break; } gst_message_unref(msg); @@ -350,15 +358,9 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx) break; } - if (pipeline) - { - gst_element_set_state(pipeline, GST_STATE_NULL); - gst_object_unref(pipeline); - } - if (bus) - { - gst_object_unref(bus); - } + gst_element_set_state(pipeline, GST_STATE_NULL); + if (bus) gst_object_unref(bus); + gst_object_unref(pipeline); ctx->status.running = false; }