diff --git a/src/rtmp_manager.cpp b/src/rtmp_manager.cpp index d8a0943..a89e29c 100644 --- a/src/rtmp_manager.cpp +++ b/src/rtmp_manager.cpp @@ -111,8 +111,7 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) while (ctx->running) { // 检查设备是否存在 - struct stat st; - if (stat(cam.device.c_str(), &st) != 0) + if (!device_exists(cam.device)) { ctx->status.running = false; ctx->status.last_error = "Device not found: " + cam.device; @@ -122,7 +121,7 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) continue; } - // 创建 pipeline + // 创建 GStreamer 管线 GstElement *pipeline = create_pipeline(cam); if (!pipeline) { @@ -134,19 +133,44 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) continue; } + gst_element_set_name(pipeline, key.c_str()); GstBus *bus = gst_element_get_bus(pipeline); + + // ======== Pad Probe 探测帧 ========== + bool got_frame = false; + GstElement *src = gst_bin_get_by_name(GST_BIN(pipeline), "v4l2src0"); + if (!src) src = gst_bin_get_by_interface(GST_BIN(pipeline), GST_TYPE_ELEMENT); + + if (src) + { + GstPad *pad = gst_element_get_static_pad(src, "src"); + if (pad) + { + gst_pad_add_probe( + pad, GST_PAD_PROBE_TYPE_BUFFER, + [](GstPad *, GstPadProbeInfo *, gpointer user_data) -> GstPadProbeReturn + { + auto *flag = static_cast(user_data); + *flag = true; // 收到帧数据 + return GST_PAD_PROBE_OK; + }, + &got_frame, nullptr); + gst_object_unref(pad); + } + gst_object_unref(src); + } + + // ======== 启动推流 ======== gst_element_set_state(pipeline, GST_STATE_PLAYING); LOG_INFO("[RTMP] Starting stream: " + key); bool confirmed_running = false; - bool got_frame = false; bool need_restart = false; - auto start_time = std::chrono::steady_clock::now(); // ======== 等待 pipeline 确认启动 ======== { - bool got_first_frame = false; + bool reached_playing = false; auto check_start = std::chrono::steady_clock::now(); while (std::chrono::duration_cast(std::chrono::steady_clock::now() - check_start) @@ -156,18 +180,18 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) gst_element_get_state(pipeline, &state, nullptr, 200 * GST_MSECOND); if (state == GST_STATE_PLAYING) { - got_first_frame = true; + reached_playing = true; break; } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - if (got_first_frame) + if (reached_playing) { ctx->status.running = true; ctx->status.last_error.clear(); confirmed_running = true; - LOG_INFO("[RTMP] " + key + " confirmed running."); + LOG_INFO("[RTMP] " + key + " confirmed running (PLAYING)."); } else { @@ -189,12 +213,13 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) // ======== 主循环监听消息 ======== while (ctx->running) { - GstMessage *msg = gst_bus_timed_pop_filtered( - bus, 200 * GST_MSECOND, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_ELEMENT)); + GstMessage *msg = gst_bus_timed_pop_filtered(bus, 200 * GST_MSECOND, + (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); - // 检查是否超时无帧 + // 检查是否超时无帧(5 秒) auto elapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); + if (!got_frame && elapsed > 5) { ctx->status.running = false; @@ -208,16 +233,6 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) switch (GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_ELEMENT: - // 检测 fpsdisplaysink 输出帧 - if (gst_message_has_name(msg, "fpsprobe")) - { - got_frame = true; - ctx->status.running = true; - ctx->status.last_error.clear(); - } - break; - case GST_MESSAGE_ERROR: { GError *err = nullptr; @@ -229,14 +244,12 @@ void RTMPManager::stream_loop(Camera cam, StreamContext *ctx) need_restart = true; break; } - case GST_MESSAGE_EOS: ctx->status.running = false; ctx->status.last_error = "End of stream (EOS)"; LOG_WARN("[RTMP] " + key + " reached EOS"); need_restart = true; break; - default: break; }