1
This commit is contained in:
parent
c592e1b7cb
commit
005529a9a0
@ -96,15 +96,18 @@ GstElement *RTMPManager::create_pipeline(const Camera &cam, StreamType type)
|
|||||||
|
|
||||||
std::string stream_name = cam.name + stream_type_suffix(type);
|
std::string stream_name = cam.name + stream_type_suffix(type);
|
||||||
|
|
||||||
|
// ✅ 注意:只用 sync/async
|
||||||
std::string pipeline_str = "v4l2src device=" + cam.device +
|
std::string pipeline_str = "v4l2src device=" + cam.device +
|
||||||
" ! video/x-raw,format=NV12,width=" + std::to_string(width) +
|
" ! video/x-raw,format=NV12,width=" + std::to_string(width) +
|
||||||
",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) +
|
",height=" + std::to_string(height) + ",framerate=" + std::to_string(fps) +
|
||||||
"/1 ! queue max-size-buffers=1 leaky=downstream "
|
"/1 ! queue max-size-buffers=1 leaky=downstream "
|
||||||
"! mpph264enc bps=" +
|
"! mpph264enc bps=" +
|
||||||
std::to_string(bitrate) + " gop=" + std::to_string(fps) +
|
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/" +
|
"! 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);
|
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);
|
gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||||
|
|
||||||
bool first_frame = false;
|
bool first_frame = false;
|
||||||
const auto t0 = std::chrono::steady_clock::now();
|
const auto start_time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -287,8 +290,9 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 首帧超时保护
|
||||||
if (!first_frame &&
|
if (!first_frame &&
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - t0).count() > 5)
|
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count() > 5)
|
||||||
{
|
{
|
||||||
status.running = false;
|
status.running = false;
|
||||||
status.last_result = StreamResult::TIMEOUT;
|
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(
|
GstMessage *msg = gst_bus_timed_pop_filtered(
|
||||||
bus, 100 * GST_MSECOND,
|
bus, 200 * GST_MSECOND, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED));
|
||||||
static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED));
|
|
||||||
|
|
||||||
if (!msg) continue;
|
if (!msg) continue;
|
||||||
|
|
||||||
@ -322,13 +325,20 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
|
|||||||
case GST_MESSAGE_ERROR:
|
case GST_MESSAGE_ERROR:
|
||||||
{
|
{
|
||||||
GError *err = nullptr;
|
GError *err = nullptr;
|
||||||
gst_message_parse_error(msg, &err, nullptr);
|
gchar *debug = nullptr;
|
||||||
|
gst_message_parse_error(msg, &err, &debug);
|
||||||
|
|
||||||
status.running = false;
|
status.running = false;
|
||||||
status.last_result = StreamResult::CONNECTION_FAIL;
|
status.last_result = StreamResult::CONNECTION_FAIL;
|
||||||
status.last_error = err ? err->message : "GStreamer error";
|
status.last_error = err ? err->message : "GStreamer error";
|
||||||
|
|
||||||
LOG_ERROR("[RTMP] Stream error from '" + cam.name + "': " + status.last_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 (err) g_error_free(err);
|
||||||
|
if (debug) g_free(debug);
|
||||||
try_set_start(status);
|
try_set_start(status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -340,8 +350,6 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
|
|||||||
try_set_start(status);
|
try_set_start(status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_message_unref(msg);
|
gst_message_unref(msg);
|
||||||
@ -350,15 +358,9 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state(pipeline, GST_STATE_NULL);
|
gst_element_set_state(pipeline, GST_STATE_NULL);
|
||||||
|
if (bus) gst_object_unref(bus);
|
||||||
gst_object_unref(pipeline);
|
gst_object_unref(pipeline);
|
||||||
}
|
|
||||||
if (bus)
|
|
||||||
{
|
|
||||||
gst_object_unref(bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->status.running = false;
|
ctx->status.running = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user