调用OpenPipewireRemote获取数据并播放
Streams streams = qdbus_cast<Streams>(results.value(QLatin1String("streams")));
qWarning() << "streams.size:"<<streams.size();
for (const auto &stream : streams) {
QDBusMessage message = QDBusMessage::createMethodCall(desktopPortalService(),
desktopPortalPath(),
QLatin1String("org.freedesktop.portal.ScreenCast"),
QLatin1String("OpenPipeWireRemote"));
message << QVariant::fromValue(QDBusObjectPath(m_session)) << QVariantMap();
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
pendingCall.waitForFinished();
QDBusPendingReply<QDBusUnixFileDescriptor> reply = pendingCall.reply();
if (reply.isError()) {
qWarning() << "Failed to get fd for node_id " << stream.node_id;
}
QString gstLaunch = QString("pipewiresrc fd=%1 path=%2 ! videoconvert ! xvimagesink").arg(reply.value().fileDescriptor()).arg(stream.node_id);
GstElement *element = gst_parse_launch(gstLaunch.toUtf8(), nullptr);
gst_element_set_state(element, GST_STATE_PLAYING);
}
处理视频帧数据
如果你希望在视频流更新时进行一些处理,比如保存视频帧、进行图像分析或其他操作,你可以使用 GStreamer 的 appsink 元素来实现这一点。appsink 允许你从管道中获取数据,并在你的应用程序中处理这些数据。
以下是一个简单的示例,展示了如何设置一个带有 appsink 的 GStreamer 管道,并在每次视频帧更新时调用一个回调函数来处理数据:
- 定义一个回调函数来处理视频帧。
- 创建一个 GstPipeline 并添加必要的元素。
- 配置 appsink 以触发回调函数。
为了将 GstBuffer 中的数据赋值给 VideoFrame 结构体,我们需要从 GstBuffer 中提取出压缩的 H.264 数据,并根据需要填充 VideoFrame 的其他字段。下面是具体的步骤:
#include <gst/app/gstappsink.h>
// ...
// 定义用户数据结构,可以包含任何你需要的数据
struct UserData {
// 可以在这里添加你需要的数据成员
};
// ...
// 初始化用户数据
UserData* userData = new UserData();
// 构建管道字符串
QString gstLaunch = QStringLiteral("pipewiresrc fd=%1 path=%2 ! videoconvert ! appsink name=mysink")
.arg(reply.value().fileDescriptor())
.arg(stream.nodeId);
GstElement *pipeline = gst_parse_launch(gstLaunch.toUtf8().constData(), nullptr);
if (!pipeline) {
qCritical("Failed to create pipeline.");
return;
}
// 获取 appsink 元素
GstElement *appsink = gst_bin_get_by_name(GST_BIN(pipeline), "mysink");
if (!appsink) {
qCritical("Failed to get appsink element.");
return;
}
// 设置 appsink 的模式为按需模式
gst_app_sink_set_emit_signals(GST_APP_SINK(appsink), TRUE);
gst_app_sink_set_max_buffers(GST_APP_SINK(appsink), 1);
gst_app_sink_set_drop(GST_APP_SINK(appsink), TRUE);
// 连接信号到回调函数
g_signal_connect(appsink, "new-sample", G_CALLBACK(on_new_sample), userData);
// 开始管道
gst_element_set_state(pipeline, GST_STATE_PLAYING);
定义回调函数
定义一个回调函数,该函数将在每次 appsink 接收到新缓冲区时被调用。这个函数需要处理 GstBuffer 数据,并执行你需要的操作。
从 GstBuffer 中获取 H.264 压缩数据。
获取视频帧的尺寸。
检查是否是关键帧(keyframe)。
获取呈现时间戳。
将这些信息填充到 VideoFrame 结构体中
下面是一个示例代码,展示如何从 GstBuffer 中提取所需的信息,并填充到 VideoFrame 结构体中:
#include <gst/video/videooverlay.h>
#include <gst/video/gstvideometa.h>
#include <gst/gst.h>
gboolean on_new_sample(GstElement * appsink, gpointer user_data)
{
GstSample *sample;
GstBuffer *buffer;
GstMapInfo map;
GstVideoInfo vinfo;
GstVideoFrame vframe;
guint width, height;
gboolean is_keyframe;
guint64 timestamp;
struct VideoFrame *video_frame;
// 定义用户数据结构,可以包含任何你需要的数据
struct UserData {
VideoFrame *videoFrame;
};
// 尝试从 appsink 获取一个新的样本
sample = gst_app_sink_pull_sample(GST_APP_SINK(appsink));
if (sample) {
buffer = gst_sample_get_buffer(sample);
if (buffer) {
// 映射缓冲区中的数据
if (gst_buffer_map(buffer, &map, GST_MAP_READ)) {
// 获取视频信息
if (gst_buffer_parse_video_info(buffer, &vinfo)) {
// 获取宽度和高度
width = vinfo.width;
height = vinfo.height;
// 获取是否为关键帧
is_keyframe = (GST_VIDEO_INFO_Fields(&vinfo) & GST_VIDEO_FRAME_FLAG_KEY) != 0;
// 获取时间戳
timestamp = GST_BUFFER_PTS(buffer);
// 创建 VideoFrame 结构体实例
video_frame = static_cast<VideoFrame*>(user_data);
// 填充 VideoFrame 结构体
video_frame->size = QSize(width, height);
video_frame->data = QByteArray(reinterpret_cast<const char*>(map.data), map.size);
video_frame->damage = QRegion(); // 填充默认值
video_frame->isKeyFrame = is_keyframe;
video_frame->presentationTimeStamp = std::chrono::system_clock::now() + std::chrono::nanoseconds(timestamp);
// 打印一些信息
g_print("Received new video frame with size %dx%d and is keyframe? %s\n",
width, height, is_keyframe ? "yes" : "no");
// 不要忘记取消映射
gst_buffer_unmap(buffer, &map);
} else {
g_warning("Failed to parse video info from buffer.");
}
} else {
g_warning("Failed to map buffer.");
}
// 不要忘记释放资源
gst_sample_unref(sample);
} else {
g_warning("Failed to get buffer from sample.");
}
} else {
g_warning("Failed to get sample from appsink.");
}
return TRUE; // 继续接收更多的帧
}