рдХреНрдпреВрдПрдордПрд▓ рдореЗрдВ рд╣рд╛рд░реНрдбрд╡реЗрдпрд░ рддреНрд╡рд░рд┐рдд рд╡реАрдбрд┐рдпреЛ рдХреА рд╕рд░рд▓ рд╢реВрдиреНрдп-рдХреЙрдкреА рд░реЗрдВрдбрд░рд┐рдВрдЧ

рдкрд░рд┐рдЪрдп


рдЗрд╕ рд▓реЗрдЦ рдХрд╛ рдЙрджреНрджреЗрд╢реНрдп рдпрд╣ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдирд╛ рд╣реИ рдХрд┐ рдЖрдк рддреГрддреАрдп-рдкрдХреНрд╖ рд╡реАрдбрд┐рдпреЛ рдмрдлрд╝рд░ рдФрд░ QML рдХреЗ рд╕рд╛рде рдорд┐рддреНрд░ рдХреИрд╕реЗ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред рдореБрдЦреНрдп рд╡рд┐рдЪрд╛рд░ VideoOutput рдХреЗ рдорд╛рдирдХ QML рдШрдЯрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИред рдпрд╣ рдЖрдкрдХреЛ рддреГрддреАрдп-рдкрдХреНрд╖ рд╕реНрд░реЛрддреЛрдВ рд╕реЗ рджреВрд░ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдпрд╣ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдкреНрд░рд▓реЗрдЦрд┐рдд рд╣реИ рдФрд░ рдЗрд╕рдореЗрдВ GL_OES_EGL_image_external рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдмреИрдХрдПрдВрдб рд╣реИред


рдЬрдм рдореИрдВ рдХреНрдпреВрдЯреА рдореЗрдВ рдХреИрдорд░реЗ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЛ рдЪрд▓рд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рдерд╛, рдФрд░ рдПрдореНрдмреЗрдбреЗрдб рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдкрд░ рд╡реЗ 3-5 рдлреНрд░реЗрдо рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб рдХреА рдЧрддрд┐ рд╕реЗ рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдпрд╣ рд╡рд┐рдЪрд╛рд░ рдЕрдЪрд╛рдирдХ рдЙрддреНрдкрдиреНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реЛ рдЧрдпрд╛ рдХрд┐ рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░ рдХрд┐рд╕реА рднреА рд╢реВрдиреНрдп-рдХреЙрдкреА рдХрд╛ рдХреЛрдИ рд╕рд╡рд╛рд▓ рд╣реА рдирд╣реАрдВ рдерд╛, рд╣рд╛рд▓рд╛рдБрдХрд┐ рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдпрд╣ рд╕рдм рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИред рдирд┐рд╖реНрдкрдХреНрд╖рддрд╛ рдореЗрдВ, рдбреЗрд╕реНрдХрдЯреЙрдк рдкрд░, VideoOutput рдФрд░ рдХреИрдорд░рд╛ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рдЬреИрд╕рд╛ рдХрд┐ рдЕрдкреЗрдХреНрд╖рд┐рдд рдерд╛, рдЬрд▓реНрджреА рдФрд░ рдЕрдирд╛рд╡рд╢реНрдпрдХ рдирдХрд▓ рдХреЗ рдмрд┐рдирд╛ред рд▓реЗрдХрд┐рди рдореЗрд░реЗ рдХрд╛рд░реНрдп рдореЗрдВ, рд╡реАрдбрд┐рдпреЛ рдХреЛ рдХреИрдкреНрдЪрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдореМрдЬреВрджрд╛ рдХрдХреНрд╖рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рдерд╛, рдФрд░ рдореИрдВ рдПрдХ рддреГрддреАрдп-рдкрдХреНрд╖ рд╕реНрд░реЛрдд рд╕реЗ рд╡реАрдбрд┐рдпреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛, рдЬреЛ рдбрд┐рдХреЛрдбрд┐рдВрдЧ рд╡реАрдбрд┐рдпреЛ рдХреЗ рд▓рд┐рдП рдПрдХ рдордирдорд╛рдиреЗ рдврдВрдЧ рд╕реЗ Greareamer рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдХ рдлрд╝рд╛рдЗрд▓ рдпрд╛ RTSP рд╕реНрдЯреНрд░реАрдо, рдпрд╛ рдПрдХ рддреГрддреАрдп-рдкрдХреНрд╖ API рд╕реЗ рдЬрд┐рд╕реЗ рдЖрдзрд╛рд░ рдореЗрдВ рдПрдХреАрдХреГрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдХреНрдпреВрдЯреА рдХреА рдХрдХреНрд╖рд╛рдПрдВ рдХреБрдЫ рд╕рдВрджрд┐рдЧреНрдз рд╣реИрдВред рдЖрдк рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдПрдХ рдмрд╛рд░ рдлрд┐рд░ рд╕реЗ рдкрд╣рд┐рдпрд╛ рдХреЛ рдлрд┐рд░ рд╕реЗ рдордЬрдмреВрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЕрдкрдиреЗ рдШрдЯрдХ рдХреЛ рдУрдкрдирдЬреАрдПрд▓ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдбреНрд░рд╛рдЗрдВрдЧ рдХреЗ рд╕рд╛рде рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рддреБрд░рдВрдд рдПрдХ рдЬрд╛рдирдмреВрдЭрдХрд░ рдореГрдд рдЕрдВрдд рдФрд░ рдХрдард┐рди рддрд░реАрдХрд╛ рд▓рдЧ рд░рд╣рд╛ рдерд╛ред


рд╕рдм рдХреБрдЫ рдЗрд╕ рддрдереНрдп рдХреА рдУрд░ рд▓реЗ рдЧрдпрд╛ рдХрд┐ рдЖрдкрдХреЛ рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд╕рд┐рджреНрдзрд╛рдВрдд рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдПрдХ рдЫреЛрдЯрд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд▓рд┐рдЦреЗрдВред


рд╕рд┐рджреНрдзрд╛рдВрдд


VideoOutput рдХрд╕реНрдЯрдо рд╕реНрд░реЛрдд рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рдмрд╢рд░реНрддреЗ рдХрд┐


  1. рдкрд╛рд░рд┐рдд рд╡рд╕реНрддреБ рд╕реАрдзреЗ VideoSurface рд╕рдВрдкрддреНрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ QAbstractVideoSurface рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░ рд╕рдХрддреА рд╣реИ
  2. рдпрд╛ QVideoRendererControl [рд▓рд┐рдВрдХ] рдХреЗ рд╕рд╛рде MediaObject рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗред

рд╕реВрддреНрд░реЛрдВ рдФрд░ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдПрдХ рдЦреЛрдЬ рд╕реЗ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ QtMultimedia рдореЗрдВ рдПрдХ QAbstractVideoBuffer рд╡рд░реНрдЧ рд╣реИ рдЬреЛ QPixmap рд╕реЗ GLTexture рдФрд░ EGLImage рддрдХ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд╣реИрдВрдбрд▓ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИред рдЖрдЧреЗ рдХреА рдЦреЛрдЬреЛрдВ рдиреЗ рд╡реАрдбреЛрдиреЛрдбреЗ_рдЧреНрд▓рдЧ рдкреНрд▓рдЧрдЗрди рдХрд╛ рдиреЗрддреГрддреНрд╡ рдХрд┐рдпрд╛, рдЬреЛ рдЙрд╕ рдлреНрд░реЗрдо рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдирдореВрдирд╛ рдХреЗ рд╕рд╛рде shaplerExternalOES рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдпрд╛ рдерд╛ред рдЗрд╕рдХрд╛ рдорддрд▓рдм рдпрд╣ рд╣реИ рдХрд┐ рдЬрдм рдореИрдВ EGLImage рдХреЗ рд╕рд╛рде рдПрдХ QAbstractVideoBuffer рдмрдирд╛рдиреЗ рдХрд╛ рдкреНрд░рдмрдВрдзрди рдХрд░рддрд╛ рд╣реВрдВ, рддреЛ рдпрд╣ рдЗрд╕ рдмрдлрд░ рдХреЛ videnode_egl рдХреЛ рдкрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рддрд░реАрдХрд╛ рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рд░рд╣рддрд╛ рд╣реИред
рдФрд░ рдЕрдЧрд░ EGLImage рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рд╕рдорд░реНрдерд┐рдд рдирд╣реАрдВ рд╣реИ, рддреЛ рдЖрдк рдореЗрдореЛрд░реА рдХреЛ рд░реИрдк рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рд░реЗрдВрдбрд░рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рднреЗрдЬ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдЕрдзрд┐рдХрд╛рдВрд╢ рдкрд┐рдХреНрд╕реЗрд▓ рдлреЙрд░реНрдореЗрдЯ рдХреЗ рд▓рд┐рдП рд╢реЗрдб рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд▓рд╛рдЧреВ рд╣реИрдВред


рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


рдЙрджрд╛рд╣рд░рдг рд▓рдЧрднрдЧ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╡реАрдбрд┐рдпреЛ рдЕрд╡рд▓реЛрдХрди рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИред


Qt рдХреЛ рдбреЗрд╕реНрдХрдЯреЙрдк рдкрд░ OpenGL ES рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ Qt рдХреЛ рд╕рдВрдмрдВрдзрд┐рдд рдзреНрд╡рдЬ рдХреЗ рд╕рд╛рде рдкреБрдирд░реНрдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рдпрд╣ рдбреЗрд╕реНрдХрдЯреЙрдк рдХреЗ рд▓рд┐рдП рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реИред


рд╕рд╛рджрдЧреА рдХреЗ рд▓рд┐рдП, рд╣рдо рдкрд╣рд▓реА рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ, рдФрд░ рд╡реАрдбрд┐рдпреЛ рд╕реНрд░реЛрдд рдХреЗ рд░реВрдк рдореЗрдВ рд╕рд░рд▓ GStreamer рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд▓реЗрдВ:


v4l2src ! appsink 

рдПрдХ V4L2Source рд╡рд░реНрдЧ рдмрдирд╛рдПрдВ рдЬреЛ рдЗрд╕рдХреЗ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ QAbstractVideoSurface рдХреЛ рдлрд╝реНрд░реЗрдо рд╡рд┐рддрд░рд┐рдд рдХрд░реЗрдЧрд╛ред


 class V4L2Source : public QQuickItem { Q_OBJECT Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface) Q_PROPERTY(QString device MEMBER m_device READ device WRITE setDevice) Q_PROPERTY(QString caps MEMBER m_caps) public: V4L2Source(QQuickItem* parent = nullptr); virtual ~V4L2Source(); void setVideoSurface(QAbstractVideoSurface* surface); void setDevice(QString device); public slots: void start(); void stop(); private slots: void setWindow(QQuickWindow* win); void sync(); signals: void frameReady(); ... } 

рд╕рдм рдХреБрдЫ рдХрд╛рдлреА рддреБрдЪреНрдЫ рд╣реИ, рд╕реЗрдЯрд╡реЙрдЗрди () рд╕реНрд▓реЙрдЯ рдХреЛ рдЫреЛрдбрд╝рдХрд░ - рдЗрд╕реЗ QQuickItem :: windowChanged () рд╕рд┐рдЧреНрдирд▓ рдХреЛ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП QQuickWindow :: setSynchronizing () рдХреЛ рд╕реЗрдЯ рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред


рдЪреВрдВрдХрд┐ VideoOutput рдмреИрдХрдПрдВрдб рд╣рдореЗрд╢рд╛ рдирд╣реАрдВ рдЬрд╛рдирддрд╛ рдХрд┐ рдИрдЧрд▓реИрдореЗрдЬ рдХреЗ рд╕рд╛рде рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рдирд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ QAbstractVideoSurface рд╕реЗ рдкреВрдЫрдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рджрд┐рдП рдЧрдП QAbstractVideoBuffer рдХреЗ рд▓рд┐рдП рдХреМрди рд╕реЗ рдкреНрд░рд╛рд░реВрдк :: HandleType рд╕рдорд░реНрдерди рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддреЗ рд╣реИрдВ:


 void V4L2Source::setVideoSurface(QAbstractVideoSurface* surface) { if (m_surface != surface && m_surface && m_surface->isActive()) { m_surface->stop(); } m_surface = surface; if (surface ->supportedPixelFormats( QAbstractVideoBuffer::HandleType::EGLImageHandle) .size() > 0) { EGLImageSupported = true; } else { EGLImageSupported = false; } if (m_surface && m_device.length() > 0) { start(); } } 

рдЖрдЗрдП рд╣рдорд╛рд░реА рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдмрдирд╛рдПрдВ рдФрд░ рдЖрд╡рд╢реНрдпрдХ рдХреЙрд▓рдмреИрдХ рд╕реЗрдЯ рдХрд░реЗрдВ:


 GstAppSinkCallbacks V4L2Source::callbacks = {.eos = nullptr, .new_preroll = nullptr, .new_sample = &V4L2Source::on_new_sample}; V4L2Source::V4L2Source(QQuickItem* parent) : QQuickItem(parent) { m_surface = nullptr; connect(this, &QQuickItem::windowChanged, this, &V4L2Source::setWindow); pipeline = gst_pipeline_new("V4L2Source::pipeline"); v4l2src = gst_element_factory_make("v4l2src", nullptr); appsink = gst_element_factory_make("appsink", nullptr); GstPad* pad = gst_element_get_static_pad(appsink, "sink"); gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_BOTH, appsink_pad_probe, nullptr, nullptr); gst_object_unref(pad); gst_app_sink_set_callbacks(GST_APP_SINK(appsink), &callbacks, this, nullptr); gst_bin_add_many(GST_BIN(pipeline), v4l2src, appsink, nullptr); gst_element_link(v4l2src, appsink); context = g_main_context_new(); loop = g_main_loop_new(context, FALSE); } void V4L2Source::setWindow(QQuickWindow* win) { if (win) { connect(win, &QQuickWindow::beforeSynchronizing, this, &V4L2Source::sync, Qt::DirectConnection); } } GstFlowReturn V4L2Source::on_new_sample(GstAppSink* sink, gpointer data) { Q_UNUSED(sink) V4L2Source* self = (V4L2Source*)data; QMutexLocker locker(&self->mutex); self->ready = true; self->frameReady(); return GST_FLOW_OK; } // Request v4l2src allocator to add GstVideoMeta to buffers static GstPadProbeReturn appsink_pad_probe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { if (info->type & GST_PAD_PROBE_TYPE_QUERY_BOTH) { GstQuery* query = gst_pad_probe_info_get_query(info); if (GST_QUERY_TYPE(query) == GST_QUERY_ALLOCATION) { gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL); } } return GST_PAD_PROBE_OK; } 

рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдореЗрдВ, рдЖрдкрдХреА рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдорд╛рдирдХ рдХреЛрдб GMainContext рдФрд░ GMainLoop рджреНрд╡рд╛рд░рд╛ рдПрдХ рдЕрд▓рдЧ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред


рдпрд╣ Qt рдкрд░ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ :: SetWindow () рдореЗрдВ DirectConnection рдзреНрд╡рдЬ - рдпрд╣ рдЧрд╛рд░рдВрдЯреА рджреЗрддрд╛ рд╣реИ рдХрд┐ рдХреЙрд▓рдмреИрдХ рдХреЛ рд╕рд┐рдЧреНрдирд▓ рдХреЗ рд╕рдорд╛рди рдереНрд░реЗрдб рдореЗрдВ рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЬреЛ рд╣рдореЗрдВ рд╡рд░реНрддрдорд╛рди OpenGL рд╕рдВрджрд░реНрдн рддрдХ рдкрд╣реБрдВрдЪ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред


V4L2Source :: on_new_sample () рдЬрд┐рд╕реЗ v4l2src рд╕реЗ рдирдпрд╛ рдлрд╝реНрд░реЗрдо рдЖрдиреЗ рдкрд░ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдмрд╕ рддреИрдпрд╛рд░ рдзреНрд╡рдЬ рдХреЛ рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╡реАрдбрд┐рдпреЛрдСрдЯрдкреБрдЯ рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдВрдмрдВрдзрд┐рдд рд╕рд┐рдЧреНрдирд▓ рдХреЛ рдЯреНрд░рд┐рдЧрд░ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рд╕рд╛рдордЧреНрд░реА рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред


рдПрдкреНрд╕рд┐рдВрдХ рд╕рд┐рдВрдХ рдЬрд╛рдВрдЪ рдХреА рдЬрд░реВрд░рдд рд╣реИ рддрд╛рдХрд┐ v4l2src рдПрд▓реЛрдХреЗрдЯрд░ рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдмрдлрд░ рдкрд░ рд╡реАрдбрд┐рдпреЛ рдкреНрд░рд╛рд░реВрдк рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдореЗрдЯрд╛ рдЬрд╛рдирдХрд╛рд░реА рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рд╛ рдЬрд╛ рд╕рдХреЗред рдпрд╣ рдЙрди рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдЬрдм рдЪрд╛рд▓рдХ рдорд╛рдирдХ рдХреЗ рдЕрд▓рд╛рд╡рд╛ рд╣рдбрд╝рддрд╛рд▓ / рдСрдлрд╕реЗрдЯ рдХреЗ рд╕рд╛рде рд╡реАрдбрд┐рдпреЛ рдмрдлрд░ рдЬрд╛рд░реА рдХрд░рддрд╛ рд╣реИред


VideoOutput рдХреЗ рд▓рд┐рдП рд╡реАрдбрд┐рдпреЛ рдлреНрд░реЗрдо рдЕрдкрдбреЗрдЯ рд╕рд┐рдВрдХ () рд╕реНрд▓реЙрдЯ рдореЗрдВ рд╣реЛрддрд╛ рд╣реИ:


 // Make sure this callback is invoked from rendering thread void V4L2Source::sync() { { QMutexLocker locker(&mutex); if (!ready) { return; } // reset ready flag ready = false; } // pull available sample and convert GstBuffer into a QAbstractVideoBuffer GstSample* sample = gst_app_sink_pull_sample(GST_APP_SINK(appsink)); GstBuffer* buffer = gst_sample_get_buffer(sample); GstVideoMeta* videoMeta = gst_buffer_get_video_meta(buffer); // if memory is DMABUF and EGLImage is supported by the backend, // create video buffer with EGLImage handle videoFrame.reset(); if (EGLImageSupported && buffer_is_dmabuf(buffer)) { videoBuffer.reset(new GstDmaVideoBuffer(buffer, videoMeta)); } else { // TODO: support other memory types, probably GL textures? // just map memory videoBuffer.reset(new GstVideoBuffer(buffer, videoMeta)); } QSize size = QSize(videoMeta->width, videoMeta->height); QVideoFrame::PixelFormat format = gst_video_format_to_qvideoformat(videoMeta->format); videoFrame.reset(new QVideoFrame( static_cast<QAbstractVideoBuffer*>(videoBuffer.get()), size, format)); if (!m_surface->isActive()) { m_format = QVideoSurfaceFormat(size, format); Q_ASSERT(m_surface->start(m_format) == true); } m_surface->present(*videoFrame); gst_sample_unref(sample); } 

рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ, рд╣рдо рдРрдкреНрдкрд┐рдВрдХ рд╕реЗ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЙрдкрд▓рдмреНрдз рдЕрдВрддрд┐рдо рдмрдлрд░ рдХреЛ рд▓реЗрддреЗ рд╣реИрдВ, GstVideoMeta рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд▓реЗрд▓рд┐рд╕реНрдЯ рдХреЗ рд▓рд┐рдП рдСрдлрд╝рд╕реЗрдЯреНрд╕ рдФрд░ рд╕реНрдЯреНрд░рд╛рдЗрдбреНрд╕ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣реЗрдВ (рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ, рд╕рд╛рджрдЧреА рдХреЗ рд▓рд┐рдП, рдХрд┐рд╕реА рдХрд╛рд░рдг рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдореЗрдЯрд╛ рдирд╣реАрдВ рд╣реИ) рдФрд░ рд╡рд╛рдВрдЫрд┐рдд рд╕рд┐рд░ рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рде рдПрдХ QAbstractVideoBuffer рдмрдирд╛рдПрдВ: EGLImage (GstDmaVideoBuffer) рдпрд╛ рдХреЛрдИ рдирд╣реАрдВ (GstVideoBuffer)ред рдлрд┐рд░ рдЗрд╕реЗ QVideoFrame рдореЗрдВ рд▓рдкреЗрдЯреЗрдВ рдФрд░ рд░реЗрдВрдбрд░рд┐рдВрдЧ рдХрддрд╛рд░ рдореЗрдВ рд░рдЦреЗрдВред


GstDmaVideoBuffer рдФрд░ GstVideoBuffer рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЕрдкрдиреЗ рдЖрдк рдореЗрдВ рдХрд╛рдлреА рддреБрдЪреНрдЫ рд╣реИ:


 #define GST_BUFFER_GET_DMAFD(buffer, plane) \ (((plane) < gst_buffer_n_memory((buffer))) ? \ gst_dmabuf_memory_get_fd(gst_buffer_peek_memory((buffer), (plane))) : \ gst_dmabuf_memory_get_fd(gst_buffer_peek_memory((buffer), 0))) class GstDmaVideoBuffer : public QAbstractVideoBuffer { public: // This should be called from renderer thread GstDmaVideoBuffer(GstBuffer* buffer, GstVideoMeta* videoMeta) : QAbstractVideoBuffer(HandleType::EGLImageHandle), buffer(gst_buffer_ref(buffer)), m_videoMeta(videoMeta) { static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>( eglGetProcAddress("eglCreateImageKHR")); int idx = 0; EGLint attribs[MAX_ATTRIBUTES_COUNT]; attribs[idx++] = EGL_WIDTH; attribs[idx++] = m_videoMeta->width; attribs[idx++] = EGL_HEIGHT; attribs[idx++] = m_videoMeta->height; attribs[idx++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[idx++] = gst_video_format_to_drm_code(m_videoMeta->format); attribs[idx++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[idx++] = GST_BUFFER_GET_DMAFD(buffer, 0); attribs[idx++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attribs[idx++] = m_videoMeta->offset[0]; attribs[idx++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[idx++] = m_videoMeta->stride[0]; if (m_videoMeta->n_planes > 1) { attribs[idx++] = EGL_DMA_BUF_PLANE1_FD_EXT; attribs[idx++] = GST_BUFFER_GET_DMAFD(buffer, 1); attribs[idx++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; attribs[idx++] = m_videoMeta->offset[1]; attribs[idx++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; attribs[idx++] = m_videoMeta->stride[1]; } if (m_videoMeta->n_planes > 2) { attribs[idx++] = EGL_DMA_BUF_PLANE2_FD_EXT; attribs[idx++] = GST_BUFFER_GET_DMAFD(buffer, 2); attribs[idx++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; attribs[idx++] = m_videoMeta->offset[2]; attribs[idx++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; attribs[idx++] = m_videoMeta->stride[2]; } attribs[idx++] = EGL_NONE; auto m_qOpenGLContext = QOpenGLContext::currentContext(); QEGLNativeContext qEglContext = qvariant_cast<QEGLNativeContext>(m_qOpenGLContext->nativeHandle()); EGLDisplay dpy = qEglContext.display(); Q_ASSERT(dpy != EGL_NO_DISPLAY); image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, (EGLClientBuffer) nullptr, attribs); Q_ASSERT(image != EGL_NO_IMAGE_KHR); } ... // This should be called from renderer thread ~GstDmaVideoBuffer() override { static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>( eglGetProcAddress("eglDestroyImageKHR")); auto m_qOpenGLContext = QOpenGLContext::currentContext(); QEGLNativeContext qEglContext = qvariant_cast<QEGLNativeContext>(m_qOpenGLContext->nativeHandle()); EGLDisplay dpy = qEglContext.display(); Q_ASSERT(dpy != EGL_NO_DISPLAY); eglDestroyImageKHR(dpy, image); gst_buffer_unref(buffer); } private: EGLImage image; GstBuffer* buffer; GstVideoMeta* m_videoMeta; }; class GstVideoBuffer : public QAbstractPlanarVideoBuffer { public: GstVideoBuffer(GstBuffer* buffer, GstVideoMeta* videoMeta) : QAbstractPlanarVideoBuffer(HandleType::NoHandle), m_buffer(gst_buffer_ref(buffer)), m_videoMeta(videoMeta), m_mode(QAbstractVideoBuffer::MapMode::NotMapped) { } QVariant handle() const override { return QVariant(); } void release() override { } int map(MapMode mode, int* numBytes, int bytesPerLine[4], uchar* data[4]) override { int size = 0; const GstMapFlags flags = GstMapFlags(((mode & ReadOnly) ? GST_MAP_READ : 0) | ((mode & WriteOnly) ? GST_MAP_WRITE : 0)); if (mode == NotMapped || m_mode != NotMapped) { return 0; } else { for (int i = 0; i < m_videoMeta->n_planes; i++) { gst_video_meta_map(m_videoMeta, i, &m_mapInfo[i], (gpointer*)&data[i], &bytesPerLine[i], flags); size += m_mapInfo[i].size; } } m_mode = mode; *numBytes = size; return m_videoMeta->n_planes; } MapMode mapMode() const override { return m_mode; } void unmap() override { if (m_mode != NotMapped) { for (int i = 0; i < m_videoMeta->n_planes; i++) { gst_video_meta_unmap(m_videoMeta, i, &m_mapInfo[i]); } } m_mode = NotMapped; } ~GstVideoBuffer() override { unmap(); gst_buffer_unref(m_buffer); } private: GstBuffer* m_buffer; MapMode m_mode; GstVideoMeta* m_videoMeta; GstMapInfo m_mapInfo[4]; }; 

рдЗрд╕ рд╕рдм рдХреЗ рдмрд╛рдж, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдлреЙрд░реНрдо рдХрд╛ QML рдкреЗрдЬ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ:


 import QtQuick 2.10 import QtQuick.Window 2.10 import QtQuick.Layouts 1.10 import QtQuick.Controls 2.0 import QtMultimedia 5.10 import v4l2source 1.0 Window { visible: true width: 640 height: 480 title: qsTr("qml zero copy rendering") color: "black" CameraSource { id: camera device: "/dev/video0" onFrameReady: videoOutput.update() } VideoOutput { id: videoOutput source: camera anchors.fill: parent } onClosing: camera.stop() } 

рдирд┐рд╖реНрдХрд░реНрд╖


рдЗрд╕ рд▓реЗрдЦ рдХрд╛ рдЙрджреНрджреЗрд╢реНрдп рдпрд╣ рджрд┐рдЦрд╛рдирд╛ рдерд╛ рдХрд┐ рдореМрдЬреВрджрд╛ рдПрдкреАрдЖрдИ рдХреЛ рдХреИрд╕реЗ рдПрдХреАрдХреГрдд рдХрд┐рдпрд╛ рдЬрд╛рдП рдЬреЛ QML рдХреЗ рд╕рд╛рде рд╣рд╛рд░реНрдбрд╡реЗрдпрд░-рддреНрд╡рд░рд┐рдд рд╡реАрдбрд┐рдпреЛ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛ рдФрд░ рдореМрдЬреВрджрд╛ рдШрдЯрдХреЛрдВ рдХреЛ рдХреЙрдкреА рдХрд┐рдП рдмрд┐рдирд╛ (рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдпрд╛ рд╕рдмрд╕реЗ рдЦрд░рд╛рдм рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдПрдХ рдХреЗ рд╕рд╛рде, рд▓реЗрдХрд┐рди рдорд╣рдВрдЧреЗ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рд░реВрдкрд╛рдВрддрд░рдг рдХреЗ рдмрд┐рдирд╛ RGB рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП) рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред


рдХреЛрдб рд▓рд┐рдВрдХ


рд╕рдВрджрд░реНрдн


Source: https://habr.com/ru/post/hi486062/


All Articles