完成GStreamer工作流最小核心,采集视频数据

This commit is contained in:
GuanYuankai 2025-10-21 05:56:09 +00:00
parent a4d926713f
commit f42ee41573
3 changed files with 154 additions and 37 deletions

View File

@ -4,7 +4,11 @@
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include"
"/usr/include",
"/usr/include/gstreamer-1.0",
"/usr/include/glib-2.0",
"/usr/lib/aarch64-linux-gnu/glib-2.0/include"
// (GStreamer GLib, GLib .include )
],
"defines": [
"CROW_USE_BOOST"

View File

@ -14,7 +14,6 @@ find_package(SQLite3 REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GST REQUIRED gstreamer-1.0 gstreamer-app-1.0)
find_package(OpenCV REQUIRED)
# Crow CMake
# Crow 使 Boost Asio
add_subdirectory(src/vendor/crow)
@ -117,42 +116,23 @@ target_link_libraries(test PRIVATE
)
# 1. 定义新的可执行文件 (我们将把代码放在 src/streamer/ 目录下)
# add_executable(edge_streamer
# src/streamer/main.cpp
# src/streamer/StreamManager.cpp
# )
add_executable(edge_streamer
src/streamer/main.cpp
)
# # 2.
# target_link_libraries(edge_streamer PRIVATE
# # GStreamer (来自我们新增的 pkg_check_modules)
# ${GST_LIBRARIES}
#
target_link_libraries(edge_streamer PRIVATE
# --- GStreamer (来自 pkg_check_modules) ---
${GST_LIBRARIES}
# Crow
# nlohmann_json
# PahoMqttCpp::paho-mqttpp3
# Boost::system
# Boost::thread
# rknn_api
# rknnrt
# pthread
# ssl
# crypto
# )
pthread
)
# # 3.
# target_include_directories(edge_streamer PRIVATE
# #
# ${CMAKE_CURRENT_SOURCE_DIR}/src/streamer/include
# ${CMAKE_CURRENT_SOURCE_DIR}/src/vendor
# # GStreamer
# ${GST_INCLUDE_DIRS}
# ${Boost_INCLUDE_DIRS}
# # RKNN C-API 的头文件 (来自我们 Dockerfile /usr/local/include)
# /usr/local/include
# )
#
target_include_directories(edge_streamer PRIVATE
#
${CMAKE_CURRENT_SOURCE_DIR}/src/streamer/include
# --- GStreamer (来自 pkg_check_modules) ---
${GST_INCLUDE_DIRS}
)

133
src/streamer/main.cpp Normal file
View File

@ -0,0 +1,133 @@
/*
* : src/streamer/main.cpp
* : 0.1 -
* : RTSP, 使MPP硬件解码, C++
*/
#include <gst/gst.h>
#include <gst/app/gstappsink.h> // 包含 appsink 的头文件
#include <glib.h>
// 这是一个简单的结构体,用于在回调间传递数据
typedef struct {
GMainLoop *loop;
GstElement *pipeline;
} AppData;
/**
* @brief
* * GStreamer 'appsink' 线
*
* * @param appsink appsink
* @param user_data
* @return GstFlowReturn 线
*/
static GstFlowReturn
on_new_sample (GstAppSink * appsink, gpointer user_data)
{
// 帧计数
static guint frame_count = 0;
// 从 appsink 中拉取 GstSample
// GstSample 是 GStreamer 中包含数据 (GstBuffer) 和元数据 (GstCaps) 的容器
GstSample *sample = gst_app_sink_pull_sample (appsink);
if (sample == NULL) {
g_warning ("Failed to pull sample.");
return GST_FLOW_ERROR;
}
// --- 核心采集点 ---
// 此时,'sample' 变量中就装着解码后的原始视频帧 (NV12 格式)
// 这是我们未来插入 ONNX/RGA/编码 的地方。
frame_count++;
if (frame_count % 100 == 0) {
g_print ("Hardware decoder acquired frame %u\n", frame_count);
// (可选的调试: 打印帧的详细信息)
// GstBuffer *buffer = gst_sample_get_buffer(sample);
// GstCaps *caps = gst_sample_get_caps(sample);
// g_print(" Buffer size: %lu, Caps: %s\n",
// gst_buffer_get_size(buffer),
// gst_caps_to_string(caps));
}
// **重要**: 释放 'sample'
// 必须释放它,否则将导致内存泄漏。
gst_sample_unref (sample);
return GST_FLOW_OK;
}
int
main (int argc, char *argv[])
{
AppData data;
GstElement *appsink;
// 初始化 GStreamer 和 GMainLoop
gst_init (&argc, &argv);
data.loop = g_main_loop_new (NULL, FALSE);
// 构建采集管线 (Pipeline String)
// **注意**: 您的摄像头可能不是H.264如果是H.265,请使用 rtph265depay ! h265parse
// **关键**: appsink 的配置
// name=sink: 给它一个名字 "sink",方便我们后面找到它
// emit-signals=true: **必须**设置为 true它才会触发 'new-sample' 信号
// max-buffers=5: 设置一个小的内部缓冲区
// drop=true: 如果C++处理不过来 (缓冲区满了),就丢弃旧的帧 (防止内存溢出)
const gchar *pipeline_str =
"rtspsrc location=rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301 latency=300 protocols=tcp ! "
"application/x-rtp, media=(string)video ! "
"rtpjitterbuffer latency=100 ! "
"rtph265depay ! h265parse ! "
"queue ! "
"mppvideodec ! "
"video/x-raw,format=NV12 ! "
"appsink name=sink emit-signals=true max-buffers=5 drop=true";
g_print ("Using pipeline:\n%s\n", pipeline_str);
data.pipeline = gst_parse_launch (pipeline_str, NULL);
if (!data.pipeline) {
g_printerr ("Failed to parse ingest pipeline.\n");
return -1;
}
// 获取 'appsink' 元件
// 我们通过在管线字符串中设置的 'name=sink' 来找到它
appsink = gst_bin_get_by_name (GST_BIN (data.pipeline), "sink");
if (!appsink) {
g_printerr("Failed to get appsink element.\n");
gst_object_unref(data.pipeline);
return -1;
}
// 连接 'new-sample' 信号到我们的 C++ 回调函数
// 这是整个程序最关键的“粘合”步骤
g_signal_connect (appsink, "new-sample", G_CALLBACK (on_new_sample), &data);
// appsink 已经设置好,我们可以释放对它的临时引用
g_object_unref (appsink);
// 启动管线
gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
g_print ("Acquisition pipeline started. Waiting for frames...\n");
// 运行主循环
// GStreamer 的所有工作都在后台线程中进行
// 主循环会阻塞在这里,直到 g_main_loop_quit() 被调用或程序被中断
g_main_loop_run (data.loop);
// 7. (程序退出时) 清理
g_print ("Exiting acquisition service...\n");
gst_element_set_state (data.pipeline, GST_STATE_NULL);
gst_object_unref (data.pipeline);
g_main_loop_unref (data.loop);
return 0;
}