xdg-desktop-portal screencast接口获取图像数据并播放

调用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 管道,并在每次视频帧更新时调用一个回调函数来处理数据:

  1. 定义一个回调函数来处理视频帧。
  2. 创建一个 GstPipeline 并添加必要的元素。
  3. 配置 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; // 继续接收更多的帧
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值