From 0b12b510443d0db42f9c22d794e34b3c3f8ef646 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 28 Nov 2024 16:47:24 +0100 Subject: [PATCH] stm-st-stm32mp: gstreamer: sync gstreamer recipes with v1.22.12 This commit removes all outdated gstreamer recipes and synchronizes it with the latest v1.22.12 from the meta-st-openstlinux layer, based on the openstlinux-6.6-yocto-scarthgap-mpu-v24.11.06 tag for Yocto 5.0 (scarthgap). https://onedigi.atlassian.net/browse/DEL-9381 Signed-off-by: Arturo Buzarra --- ...av-disable-decoder-direct-rendering-.patch | 37 - .../gstreamer1.0-libav_1.20.%.bbappend | 3 - ...r-Port-to-the-new-DRM-Dumb-Allocator.patch | 490 +++++ ...01-waylandsink-add-dmabuf-bufferpool.patch | 1001 ----------- ...emove-redefine-of-GST_CAPS_FEATURE_M.patch | 50 + ...x-error-when-mmapping-dmabuf-buffers.patch | 33 - ...p-modifying-the-display-GstVideoInfo.patch | 157 ++ ...wrong-width-when-creating-dmabuf-dum.patch | 36 - ...-Force-a-redraw-on-resolution-change.patch | 40 + ...ot-hardcode-dmabuf-bufferpool-info-s.patch | 46 - ...the-baseclass-know-when-frames-are-d.patch | 51 + ...ease-max-buffers-to-32-to-enable-dma.patch | 45 - ...sink-Refactor-internal-pool-handling.patch | 349 ++++ ...ink-always-select-dmabuf-buffer-pool.patch | 46 - ...ink-Fix-display-wl_window-pool-leaks.patch | 31 + ...aylandsink-do-not-destroy-pool-twice.patch | 36 - ...andle-video-meta-inside-the-importer.patch | 174 ++ ...0009-waylandsink-Uprank-to-secondary.patch | 28 - ...format-Fix-sign-issue-for-DRM-fourcc.patch | 93 + ...sink-set-video-alignment-to-32-bytes.patch | 43 - ...fferpool-Add-DRM-Dumb-buffer-support.patch | 268 +++ ...nsink-Add-DRM-Dumb-allocator-support.patch | 412 +++++ ...back-to-shm-if-display-does-not-supp.patch | 41 - ...te-doc-cache-for-waylandsink-changes.patch | 58 + ...protocol-does-not-work-in-fullscreen.patch | 58 - ...ndsink-silently-drop-erroneous-frame.patch | 99 -- ...INK-use-card0a-as-default-drm-device.patch | 27 + ...dsink-add-waylandpool-on-meson-build.patch | 29 - ...0015-waylandsink-Uprank-to-secondary.patch | 27 + .../0016-Add-new-gtkwaylandsink-element.patch | 1571 ---------------- ...allocator-correct-WL-API-declaration.patch | 26 + ...andsink-Destroy-GstWlWindow-when-par.patch | 29 + ...shm-pool-wrongly-selected-with-some-.patch | 53 - ...SINK-use-card0-as-default-drm-device.patch | 26 + ...o-not-use-drm-dumb-pool-with-DMAbuf-.patch | 66 + ...base-class-for-stateless-vp8-encoder.patch | 651 +++++++ ...s-Add-V4L2-VP8-stateless-encode-uAPI.patch | 131 ++ ...24-v4l2codecs-Add-v4l2-encoder-class.patch | 1583 +++++++++++++++++ ...odecs-Add-V4L2-stateless-VP8-encoder.patch | 813 +++++++++ ...-Register-V4L2-stateless-Vp8-encoder.patch | 77 + ...disable-frame-dropping-while-redraw.patch} | 21 +- ...8-v4l2codecs-add-key-frame-signaling.patch | 107 ++ ...ACK-disable-frame-dropping-while-red.patch | 63 + ...ancel-pending-redraw-callback-on-pau.patch | 63 + ...decs-fix-support-of-unaligned-videos.patch | 35 + ...k-Fix-rendering-of-unaligned-content.patch | 53 + ...cs-add-NV12M-format-to-gstv4l2format.patch | 25 + ...ix-gst_v4l2_encoder_set_src_fmt-call.patch | 52 + ...ecs-add-support-of-encoding-from-RGB.patch | 51 + ...-add-colorimetry-support-at-sink-pad.patch | 193 ++ ...decs-fix-support-of-unaligned-videos.patch | 29 + ...-h264parser-Define-level-enum-values.patch | 210 +++ ...p-naming-consistency-in-GST_H264_LEV.patch | 71 + ...er-No-need-to-set-num_planes-of-plan.patch | 34 + ...cs-add-NV12M-format-to-gstv4l2format.patch | 26 + ...-Add-V4L2-H264-stateless-encode-uAPI.patch | 124 ++ ...ase-class-for-stateless-h264-encoder.patch | 645 +++++++ ...er-Add-h264-code-to-v4l2-encoder-cla.patch | 61 + ...decs-Add-V4L2-stateless-H264-encoder.patch | 1210 +++++++++++++ ...Register-V4L2-stateless-H264-encoder.patch | 40 + ...ncoders-Implement-propose-allocation.patch | 83 + ...t-Allow-matching-planar-format-varia.patch | 71 + ...2codecs-encoder-Save-the-sink-format.patch | 38 + ...codecs-format-Prefer-mplane-variants.patch | 29 + ...-encoder-Implement-zero-copy-support.patch | 517 ++++++ .../0055-codecs-h264enc-Fix-indentation.patch | 27 + ...6-v4l2codecs-h264enc-Fix-indentation.patch | 362 ++++ ...nc-Cleanup-caps-colorimetry-framerat.patch | 351 ++++ ...enc-add-CABAC-enable-disable-control.patch | 154 ++ ...nc-Don-t-call-virtual-negotiate-dire.patch | 29 + ...c-Don-t-call-virtual-negotiate-direc.patch | 29 + ...decs-h264enc-Add-profile-negotiation.patch | 150 ++ ...s-h264encoder-Fix-H.264-QP-max-value.patch | 27 + ...-h264dec-Remove-dead-and-broken-code.patch | 142 ++ ...ntroduce-a-PID-based-rate-controller.patch | 355 ++++ ...der-Use-the-PID-base-rate-controller.patch | 578 ++++++ ...nc-Rewrite-profile-level-negotiation.patch | 286 +++ ...enc-De-emulate-SPS-and-PPS-bitstream.patch | 86 + ...-vp8-fix-support-of-unaligned-videos.patch | 47 + ...ce-to-add-math-as-library-dependency.patch | 36 + ...2codecs-encoder-add-rotation-support.patch | 248 +++ ...l2codecs-sync-to-kernel-v6.6-headers.patch | 1197 +++++++++++++ ...4l2codecs-vp8-add-webp-image-support.patch | 252 +++ ...trigger-message-for-failed-com-marke.patch | 47 + ...cator-fix-stride-with-planar-formats.patch | 32 + ...nk-Fix-rendering-of-unaligned-conten.patch | 29 + ...nk-match-drm-kernel-driver-alignment.patch | 53 + ...nk-match-drm-kernel-driver-alignment.patch | 53 + ...gst_buffer_pool_config_set_params-to.patch | 29 + ...dd-gst_buffer_pool_config_set_params.patch | 29 + ...ig-buffer-pool-with-query-size-when-.patch | 39 + ...onfig-buffer-pool-with-query-size-wh.patch | 35 + .../gstreamer1.0-plugins-bad_1.20.%.bbappend | 41 - .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 98 + ...-allocators-Add-a-DRM-Dumb-Allocator.patch | 955 ++++++++++ ...disable-any-default-video-processing.patch | 34 - ...w-frame-copy-from-different-dimensio.patch | 78 + ...disable-any-default-video-processing.patch | 34 - ...streamer1.0-plugins-base_1.22.12.bbappend} | 6 +- ...streamer1.0-plugins-good_1.22.12.bbappend} | 5 +- ..._BASE_SINK_FLOW_DROPPED-return-value.patch | 69 + .../gstreamer/gstreamer1.0_1.22.12.bbappend | 5 + 102 files changed, 15382 insertions(+), 3330 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav/0001-gstreamer1.0-libav-disable-decoder-direct-rendering-.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav_1.20.%.bbappend create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-waylandsink-add-dmabuf-bufferpool.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-waylandsink-fix-error-when-mmapping-dmabuf-buffers.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-fix-wrong-width-when-creating-dmabuf-dum.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-waylandsink-do-not-hardcode-dmabuf-bufferpool-info-s.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-increase-max-buffers-to-32-to-enable-dma.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-Refactor-internal-pool-handling.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-always-select-dmabuf-buffer-pool.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-waylandsink-do-not-destroy-pool-twice.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-waylandsink-Uprank-to-secondary.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-waylandsink-set-video-alignment-to-32-bytes.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-waylandsink-fallback-to-shm-if-display-does-not-supp.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-bad-Update-doc-cache-for-waylandsink-changes.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-waylandsink-XDG-protocol-does-not-work-in-fullscreen.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0013-waylandsink-silently-drop-erroneous-frame.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-waylandsink-add-waylandpool-on-meson-build.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0015-waylandsink-Uprank-to-secondary.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-Add-new-gtkwaylandsink-element.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-gstwlshmallocator-correct-WL-API-declaration.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-waylandsink-fix-shm-pool-wrongly-selected-with-some-.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0024-v4l2codecs-Add-v4l2-encoder-class.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch rename meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/{0008-waylandsink-HACK-disable-frame-dropping-while-redraw.patch => 0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch} (83%) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0028-v4l2codecs-add-key-frame-signaling.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0030-gtkwaylandsink-cancel-pending-redraw-callback-on-pau.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0031-v4l2codecs-fix-support-of-unaligned-videos.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0033-waylandsink-Fix-rendering-of-unaligned-content.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0035-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0036-v4l2codecs-fix-gst_v4l2_encoder_set_src_fmt-call.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0037-v4l2codecs-add-support-of-encoding-from-RGB.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0038-v4l2codecs-add-colorimetry-support-at-sink-pad.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0040-Revert-v4l2codecs-fix-support-of-unaligned-videos.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0041-h264parser-Define-level-enum-values.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0042-codecparsers-keep-naming-consistency-in-GST_H264_LEV.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0043-v4l2codecs-encoder-No-need-to-set-num_planes-of-plan.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0044-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0045-v4l2codecs-Add-V4L2-H264-stateless-encode-uAPI.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0046-codecs-Add-base-class-for-stateless-h264-encoder.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0047-v4l2codecs-encoder-Add-h264-code-to-v4l2-encoder-cla.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0048-v4l2codecs-Add-V4L2-stateless-H264-encoder.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0049-v4l2codecs-Register-V4L2-stateless-H264-encoder.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0050-v4l2codecs-encoders-Implement-propose-allocation.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0051-v4l2codecs-format-Allow-matching-planar-format-varia.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0052-v4l2codecs-encoder-Save-the-sink-format.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0053-v4l2codecs-format-Prefer-mplane-variants.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0054-v4l2codecs-encoder-Implement-zero-copy-support.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0055-codecs-h264enc-Fix-indentation.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0056-v4l2codecs-h264enc-Fix-indentation.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0057-v4l2codecs-h264enc-Cleanup-caps-colorimetry-framerat.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0058-v4l2codecs-h264enc-add-CABAC-enable-disable-control.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0059-v4l2codecs-h264enc-Don-t-call-virtual-negotiate-dire.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0060-v4l2codecs-vp8enc-Don-t-call-virtual-negotiate-direc.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0061-v4l2codecs-h264enc-Add-profile-negotiation.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0062-codecs-h264encoder-Fix-H.264-QP-max-value.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0063-v4l2codecs-h264dec-Remove-dead-and-broken-code.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0064-codecs-Introduce-a-PID-based-rate-controller.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0065-codecs-h264encoder-Use-the-PID-base-rate-controller.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0066-v4l2codecs-h264enc-Rewrite-profile-level-negotiation.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0067-v4l2codecs-h264enc-De-emulate-SPS-and-PPS-bitstream.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0068-v4l2codecs-vp8-fix-support-of-unaligned-videos.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0069-Force-to-add-math-as-library-dependency.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-encoder-add-rotation-support.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-sync-to-kernel-v6.6-headers.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0071-v4l2codecs-vp8-add-webp-image-support.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0072-jpegparse-don-t-trigger-message-for-failed-com-marke.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0073-kmsallocator-fix-stride-with-planar-formats.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0074-Revert-waylandsink-Fix-rendering-of-unaligned-conten.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0075-waylandsink-match-drm-kernel-driver-alignment.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0076-gtkwaylandsink-match-drm-kernel-driver-alignment.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0077-waylandsink-Add-gst_buffer_pool_config_set_params-to.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0079-waylandsink-config-buffer-pool-with-query-size-when-.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.20.%.bbappend create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-allocators-Add-a-DRM-Dumb-Allocator.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-playbin2-disable-any-default-video-processing.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-DRAFT-video-Allow-frame-copy-from-different-dimensio.patch delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-playbin3-disable-any-default-video-processing.patch rename meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/{gstreamer1.0-plugins-base_1.20.%.bbappend => gstreamer1.0-plugins-base_1.22.12.bbappend} (87%) rename meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/{gstreamer1.0-plugins-good_1.20.%.bbappend => gstreamer1.0-plugins-good_1.22.12.bbappend} (76%) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0/0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0_1.22.12.bbappend diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav/0001-gstreamer1.0-libav-disable-decoder-direct-rendering-.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav/0001-gstreamer1.0-libav-disable-decoder-direct-rendering-.patch deleted file mode 100644 index 3fb32e087..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav/0001-gstreamer1.0-libav-disable-decoder-direct-rendering-.patch +++ /dev/null @@ -1,37 +0,0 @@ -From a01f2526663eee1d98f2e133d076a5dcb556d1ea Mon Sep 17 00:00:00 2001 -From: Christophe Priouzeau -Date: Tue, 26 Apr 2022 11:30:31 +0200 -Subject: [PATCH] gstreamer1.0-libav: disable decoder direct rendering by - default - -DMA-buf 0-copy path and direct-rendering mode are not yet compatible -because of stride and offset issues. -Nevertheless performances are better than with direct-rendering enabled -and virtual memory path. - -This fix is required to reach 30fps on display driver side -with 30fps VGA video content. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/libav/gstavviddec.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ext/libav/gstavviddec.c b/ext/libav/gstavviddec.c -index f5197fb..fde599b 100644 ---- a/ext/libav/gstavviddec.c -+++ b/ext/libav/gstavviddec.c -@@ -41,7 +41,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE); - - #define DEFAULT_LOWRES 0 - #define DEFAULT_SKIPFRAME 0 --#define DEFAULT_DIRECT_RENDERING TRUE -+#define DEFAULT_DIRECT_RENDERING FALSE - #define DEFAULT_MAX_THREADS 0 - #define DEFAULT_OUTPUT_CORRUPT TRUE - #define REQUIRED_POOL_MAX_BUFFERS 32 --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav_1.20.%.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav_1.20.%.bbappend deleted file mode 100644 index 0e9942fc7..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-libav_1.20.%.bbappend +++ /dev/null @@ -1,3 +0,0 @@ -FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" - -SRC_URI:append = " file://0001-gstreamer1.0-libav-disable-decoder-direct-rendering-.patch " diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch new file mode 100644 index 000000000..d061c081d --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch @@ -0,0 +1,490 @@ +From bc7c1d8febed84d33f0e49272b7707059485d4b4 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Tue, 28 Mar 2023 18:24:40 +0200 +Subject: [PATCH 01/17] kmsallocator: Port to the new DRM Dumb Allocator + +This ports the KMS allocator to use the DRM Dumb allocator from the allocators +library. + +Part-of: +Upstream-Status: Backport +--- + sys/kms/gstkmsallocator.c | 237 +++++++++++++------------------------- + sys/kms/gstkmsallocator.h | 5 +- + 2 files changed, 84 insertions(+), 158 deletions(-) + +diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c +index 6687f3b..3d9ee0a 100644 +--- a/sys/kms/gstkmsallocator.c ++++ b/sys/kms/gstkmsallocator.c +@@ -38,6 +38,7 @@ + #include + + #include ++#include + + #include "gstkmsallocator.h" + #include "gstkmsutils.h" +@@ -51,20 +52,12 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + + #define GST_KMS_MEMORY_TYPE "KMSMemory" + +-struct kms_bo +-{ +- void *ptr; +- size_t size; +- unsigned handle; +- unsigned int refs; +-}; +- + struct _GstKMSAllocatorPrivate + { + int fd; + /* protected by GstKMSAllocator object lock */ + GList *mem_cache; +- GstAllocator *dmabuf_alloc; ++ GstAllocator *dumb_alloc; + }; + + #define parent_class gst_kms_allocator_parent_class +@@ -101,92 +94,40 @@ check_fd (GstKMSAllocator * alloc) + return alloc->priv->fd > -1; + } + +-static void +-gst_kms_allocator_memory_reset (GstKMSAllocator * allocator, GstKMSMemory * mem) +-{ +- int err; +- struct drm_mode_destroy_dumb arg = { 0, }; +- +- if (!check_fd (allocator)) +- return; +- +- if (mem->fb_id) { +- GST_DEBUG_OBJECT (allocator, "removing fb id %d", mem->fb_id); +- drmModeRmFB (allocator->priv->fd, mem->fb_id); +- mem->fb_id = 0; +- } +- +- if (!mem->bo) +- return; +- +- if (mem->bo->ptr != NULL) { +- GST_WARNING_OBJECT (allocator, "destroying mapped bo (refcount=%d)", +- mem->bo->refs); +- munmap (mem->bo->ptr, mem->bo->size); +- mem->bo->ptr = NULL; +- } +- +- arg.handle = mem->bo->handle; +- +- err = drmIoctl (allocator->priv->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg); +- if (err) +- GST_WARNING_OBJECT (allocator, +- "Failed to destroy dumb buffer object: %s %d", +- g_strerror (errno), errno); +- +- g_free (mem->bo); +- mem->bo = NULL; +-} +- + static gboolean + gst_kms_allocator_memory_create (GstKMSAllocator * allocator, + GstKMSMemory * kmsmem, GstVideoInfo * vinfo) + { +- gint i, ret, h; +- struct drm_mode_create_dumb arg = { 0, }; +- guint32 fmt; ++ gint i, h; + gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo); + gsize offs = 0; ++ guint32 pitch; + + if (kmsmem->bo) + return TRUE; + +- if (!check_fd (allocator)) +- return FALSE; +- +- kmsmem->bo = g_malloc0 (sizeof (*kmsmem->bo)); ++ kmsmem->bo = gst_drm_dumb_allocator_alloc (allocator->priv->dumb_alloc, ++ gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo)), ++ GST_VIDEO_INFO_WIDTH (vinfo), GST_VIDEO_INFO_HEIGHT (vinfo), &pitch); + if (!kmsmem->bo) +- return FALSE; +- +- fmt = gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo)); +- arg.bpp = gst_drm_bpp_from_drm (fmt); +- arg.width = GST_VIDEO_INFO_WIDTH (vinfo); +- h = GST_VIDEO_INFO_HEIGHT (vinfo); +- arg.height = gst_drm_height_from_drm (fmt, h); +- +- ret = drmIoctl (allocator->priv->fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg); +- if (ret) + goto create_failed; + +- if (!arg.pitch) ++ if (!pitch) + goto done; + ++ h = GST_VIDEO_INFO_HEIGHT (vinfo); + for (i = 0; i < num_planes; i++) { +- guint32 pitch; +- +- if (!arg.pitch) +- continue; ++ guint32 stride; + + /* Overwrite the video info's stride and offset using the pitch calculcated + * by the kms driver. */ +- pitch = gst_video_format_info_extrapolate_stride (vinfo->finfo, i, +- arg.pitch); ++ stride = gst_video_format_info_extrapolate_stride (vinfo->finfo, i, pitch); + GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = pitch; + GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = offs; + + /* Note that we cannot negotiate special padding betweem each planes, + * hence using the display height here. */ +- offs += pitch * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, h); ++ offs += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, h); + + GST_DEBUG_OBJECT (allocator, "Created BO plane %i with stride %i and " + "offset %" G_GSIZE_FORMAT, i, +@@ -198,10 +139,6 @@ gst_kms_allocator_memory_create (GstKMSAllocator * allocator, + GST_VIDEO_INFO_SIZE (vinfo) = offs; + + done: +- kmsmem->bo->handle = arg.handle; +- /* will be used a memory maxsize */ +- kmsmem->bo->size = arg.size; +- + /* Validate the size to prevent overflow */ + if (kmsmem->bo->size < GST_VIDEO_INFO_SIZE (vinfo)) { + GST_ERROR_OBJECT (allocator, +@@ -218,8 +155,6 @@ create_failed: + { + GST_ERROR_OBJECT (allocator, "Failed to create buffer object: %s (%d)", + g_strerror (errno), errno); +- g_free (kmsmem->bo); +- kmsmem->bo = NULL; + return FALSE; + } + } +@@ -233,7 +168,16 @@ gst_kms_allocator_free (GstAllocator * allocator, GstMemory * mem) + alloc = GST_KMS_ALLOCATOR (allocator); + kmsmem = (GstKMSMemory *) mem; + +- gst_kms_allocator_memory_reset (alloc, kmsmem); ++ if (check_fd (alloc) && kmsmem->fb_id) { ++ GST_DEBUG_OBJECT (allocator, "removing fb id %d", kmsmem->fb_id); ++ drmModeRmFB (alloc->priv->fd, kmsmem->fb_id); ++ kmsmem->fb_id = 0; ++ } ++ ++ if (kmsmem->bo) { ++ gst_memory_unref (kmsmem->bo); ++ kmsmem->bo = NULL; ++ } + g_slice_free (GstKMSMemory, kmsmem); + } + +@@ -276,6 +220,22 @@ gst_kms_allocator_get_property (GObject * object, guint prop_id, + } + } + ++static void ++gst_kms_allocator_constructed (GObject * obj) ++{ ++ GstKMSAllocator *alloc; ++ ++ alloc = GST_KMS_ALLOCATOR (obj); ++ ++ /* Should be called after the properties are set */ ++ g_assert (check_fd (alloc)); ++ alloc->priv->dumb_alloc = ++ gst_drm_dumb_allocator_new_with_fd (alloc->priv->fd); ++ ++ /* Its already opened and we already checked for dumb allocation support */ ++ g_assert (alloc->priv->dumb_alloc); ++} ++ + static void + gst_kms_allocator_finalize (GObject * obj) + { +@@ -285,8 +245,8 @@ gst_kms_allocator_finalize (GObject * obj) + + gst_kms_allocator_clear_cache (GST_ALLOCATOR (alloc)); + +- if (alloc->priv->dmabuf_alloc) +- gst_object_unref (alloc->priv->dmabuf_alloc); ++ if (alloc->priv->dumb_alloc) ++ gst_object_unref (alloc->priv->dumb_alloc); + + if (check_fd (alloc)) + close (alloc->priv->fd); +@@ -307,11 +267,12 @@ gst_kms_allocator_class_init (GstKMSAllocatorClass * klass) + + gobject_class->set_property = gst_kms_allocator_set_property; + gobject_class->get_property = gst_kms_allocator_get_property; ++ gobject_class->constructed = gst_kms_allocator_constructed; + gobject_class->finalize = gst_kms_allocator_finalize; + + g_props[PROP_DRM_FD] = g_param_spec_int ("drm-fd", "DRM fd", + "DRM file descriptor", -1, G_MAXINT, -1, +- G_PARAM_READWRITE | G_PARAM_CONSTRUCT); ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (gobject_class, PROP_N, g_props); + } +@@ -320,46 +281,20 @@ static gpointer + gst_kms_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) + { + GstKMSMemory *kmsmem; +- GstKMSAllocator *alloc; +- int err; +- gpointer out; +- struct drm_mode_map_dumb arg = { 0, }; +- +- alloc = (GstKMSAllocator *) mem->allocator; +- +- if (!check_fd (alloc)) +- return NULL; + + kmsmem = (GstKMSMemory *) mem; + if (!kmsmem->bo) + return NULL; + +- /* Reuse existing buffer object mapping if possible */ +- if (kmsmem->bo->ptr != NULL) { ++ if (kmsmem->bo_map.data) + goto out; +- } + +- arg.handle = kmsmem->bo->handle; +- +- err = drmIoctl (alloc->priv->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); +- if (err) { +- GST_ERROR_OBJECT (alloc, "Failed to get offset of buffer object: %s %d", +- g_strerror (errno), errno); +- return NULL; +- } +- +- out = mmap (0, kmsmem->bo->size, +- PROT_READ | PROT_WRITE, MAP_SHARED, alloc->priv->fd, arg.offset); +- if (out == MAP_FAILED) { +- GST_ERROR_OBJECT (alloc, "Failed to map dumb buffer object: %s %d", +- g_strerror (errno), errno); ++ if (!gst_memory_map (kmsmem->bo, &kmsmem->bo_map, flags)) + return NULL; +- } +- kmsmem->bo->ptr = out; + + out: +- g_atomic_int_inc (&kmsmem->bo->refs); +- return kmsmem->bo->ptr; ++ g_atomic_int_inc (&kmsmem->bo_map_refs); ++ return kmsmem->bo_map.data; + } + + static void +@@ -367,16 +302,13 @@ gst_kms_memory_unmap (GstMemory * mem) + { + GstKMSMemory *kmsmem; + +- if (!check_fd ((GstKMSAllocator *) mem->allocator)) +- return; +- + kmsmem = (GstKMSMemory *) mem; + if (!kmsmem->bo) + return; + +- if (g_atomic_int_dec_and_test (&kmsmem->bo->refs)) { +- munmap (kmsmem->bo->ptr, kmsmem->bo->size); +- kmsmem->bo->ptr = NULL; ++ if (g_atomic_int_dec_and_test (&kmsmem->bo_map_refs)) { ++ gst_memory_unmap (kmsmem->bo, &kmsmem->bo_map); ++ kmsmem->bo_map.data = NULL; + } + } + +@@ -414,11 +346,12 @@ gst_kms_allocator_new (int fd) + * which are relative to the GstBuffer start. */ + static gboolean + gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem, +- gsize in_offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo) ++ gsize in_offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo, ++ guint32 bo_handles[4]) + { + gint i, ret; + gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo); +- guint32 w, h, fmt, bo_handles[4] = { 0, }; ++ guint32 w, h, fmt; + guint32 pitches[4] = { 0, }; + guint32 offsets[4] = { 0, }; + +@@ -430,11 +363,6 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem, + fmt = gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo)); + + for (i = 0; i < num_planes; i++) { +- if (kmsmem->bo) +- bo_handles[i] = kmsmem->bo->handle; +- else +- bo_handles[i] = kmsmem->gem_handle[i]; +- + pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i); + offsets[i] = in_offsets[i]; + } +@@ -459,6 +387,8 @@ gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo) + GstKMSAllocator *alloc; + GstKMSMemory *kmsmem; + GstMemory *mem; ++ guint32 bo_handle[4] = { 0, }; ++ gint i; + + kmsmem = g_slice_new0 (GstKMSMemory); + if (!kmsmem) +@@ -474,9 +404,13 @@ gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo) + } + + gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL, +- kmsmem->bo->size, 0, 0, GST_VIDEO_INFO_SIZE (vinfo)); ++ kmsmem->bo->maxsize, 0, 0, GST_VIDEO_INFO_SIZE (vinfo)); ++ ++ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (vinfo); i++) ++ bo_handle[i] = gst_drm_dumb_memory_get_handle (kmsmem->bo); + +- if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo)) ++ if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo, ++ bo_handle)) + goto fail; + + return mem; +@@ -495,6 +429,7 @@ gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds, + GstKMSMemory *kmsmem; + GstMemory *mem; + gint i, ret; ++ guint32 gem_handle[4] = { 0, }; + + g_return_val_if_fail (n_planes <= GST_VIDEO_MAX_PLANES, FALSE); + +@@ -508,42 +443,41 @@ gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds, + + alloc = GST_KMS_ALLOCATOR (allocator); + for (i = 0; i < n_planes; i++) { +- ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i], +- &kmsmem->gem_handle[i]); ++ ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i], &gem_handle[i]); + if (ret) + goto import_fd_failed; + } + +- if (!gst_kms_allocator_add_fb (alloc, kmsmem, offsets, vinfo)) ++ if (!gst_kms_allocator_add_fb (alloc, kmsmem, offsets, vinfo, gem_handle)) + goto failed; + ++done: + for (i = 0; i < n_planes; i++) { +- struct drm_gem_close arg = { kmsmem->gem_handle[i], }; ++ struct drm_gem_close arg = { gem_handle[i], }; + gint err; + ++ if (!gem_handle[i]) ++ continue; ++ + err = drmIoctl (alloc->priv->fd, DRM_IOCTL_GEM_CLOSE, &arg); + if (err) + GST_WARNING_OBJECT (allocator, + "Failed to close GEM handle: %s %d", g_strerror (errno), errno); +- +- kmsmem->gem_handle[i] = 0; + } + + return kmsmem; + + /* ERRORS */ + import_fd_failed: +- { +- GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)", +- prime_fds[i], g_strerror (errno), errno); +- /* fallback */ +- } ++ GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)", ++ prime_fds[i], g_strerror (errno), errno); ++ /* fallthrough */ + + failed: +- { +- gst_memory_unref (mem); +- return NULL; +- } ++ gst_memory_unref (mem); ++ mem = NULL; ++ kmsmem = NULL; ++ goto done; + } + + GstMemory * +@@ -552,30 +486,21 @@ gst_kms_allocator_dmabuf_export (GstAllocator * allocator, GstMemory * _kmsmem) + GstKMSMemory *kmsmem = (GstKMSMemory *) _kmsmem; + GstKMSAllocator *alloc = GST_KMS_ALLOCATOR (allocator); + GstMemory *mem; +- gint ret; +- gint prime_fd; + + /* We can only export DUMB buffers */ + g_return_val_if_fail (kmsmem->bo, NULL); + +- +- ret = drmPrimeHandleToFD (alloc->priv->fd, kmsmem->bo->handle, +- DRM_CLOEXEC | DRM_RDWR, &prime_fd); +- if (ret) ++ mem = gst_drm_dumb_memory_export_dmabuf (kmsmem->bo); ++ if (!mem) + goto export_fd_failed; + +- if (G_UNLIKELY (alloc->priv->dmabuf_alloc == NULL)) +- alloc->priv->dmabuf_alloc = gst_dmabuf_allocator_new (); +- +- mem = gst_dmabuf_allocator_alloc (alloc->priv->dmabuf_alloc, prime_fd, +- gst_memory_get_sizes (_kmsmem, NULL, NULL)); +- + /* Populate the cache so KMSSink can find the kmsmem back when it receives + * one of these DMABuf. This call takes ownership of the kmsmem. */ + gst_kms_allocator_cache (allocator, mem, _kmsmem); + +- GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d", kmsmem->bo->handle, +- prime_fd); ++ GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d", ++ gst_drm_dumb_memory_get_handle (kmsmem->bo), ++ gst_dmabuf_memory_get_fd (mem)); + + return mem; + +@@ -583,7 +508,7 @@ gst_kms_allocator_dmabuf_export (GstAllocator * allocator, GstMemory * _kmsmem) + export_fd_failed: + { + GST_ERROR_OBJECT (alloc, "Failed to export bo handle %d: %s (%d)", +- kmsmem->bo->handle, g_strerror (errno), ret); ++ gst_drm_dumb_memory_get_handle (kmsmem->bo), g_strerror (errno), errno); + return NULL; + } + } +diff --git a/sys/kms/gstkmsallocator.h b/sys/kms/gstkmsallocator.h +index 9d00126..258efba 100644 +--- a/sys/kms/gstkmsallocator.h ++++ b/sys/kms/gstkmsallocator.h +@@ -56,8 +56,9 @@ struct _GstKMSMemory + GstMemory parent; + + guint32 fb_id; +- guint32 gem_handle[GST_VIDEO_MAX_PLANES]; +- struct kms_bo *bo; ++ GstMemory *bo; ++ GstMapInfo bo_map; ++ gint bo_map_refs; + }; + + struct _GstKMSAllocator +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-waylandsink-add-dmabuf-bufferpool.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-waylandsink-add-dmabuf-bufferpool.patch deleted file mode 100644 index 7e02f2388..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-waylandsink-add-dmabuf-bufferpool.patch +++ /dev/null @@ -1,1001 +0,0 @@ -From ddeb2fe4af5c62235c31bba257354258ad14d7a1 Mon Sep 17 00:00:00 2001 -From: Pierre-Yves MORDRET -Date: Thu, 5 Sep 2019 16:19:22 +0200 -Subject: [PATCH 01/14] waylandsink: add dmabuf bufferpool - -Add support of DMA-buf allocated buffers through use of -drm/kms kernel driver ioctl DRM_IOCTL_MODE_CREATE_DUMB. -This pool is selected if video/x-raw(memory:DMABuf) -is set in caps. - -Upstream-Status: Inappropriate [DEY specific] - -Change-Id: I0507752a1a64ee2c657b73d8c802c44fd707f49f -Signed-off-by: Hugues Fruchet ---- - ext/wayland/gstwaylandsink.c | 108 +++++- - ext/wayland/waylandpool.c | 640 +++++++++++++++++++++++++++++++++++ - ext/wayland/waylandpool.h | 105 ++++++ - ext/wayland/wldisplay.c | 3 + - ext/wayland/wldisplay.h | 9 +- - 5 files changed, 853 insertions(+), 12 deletions(-) - create mode 100644 ext/wayland/waylandpool.c - create mode 100644 ext/wayland/waylandpool.h - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 0761304..20df1bf 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -47,6 +47,7 @@ - #include "wlbuffer.h" - #include "wlshmallocator.h" - #include "wllinuxdmabuf.h" -+#include "waylandpool.h" - - #include - #include -@@ -557,6 +558,36 @@ gst_wayland_create_pool (GstWaylandSink * sink, GstCaps * caps) - return pool; - } - -+static GstBufferPool * -+gst_wayland_create_dmabuf_pool (GstWaylandSink * sink, GstCaps * caps) -+{ -+ GstBufferPool *pool = NULL; -+ GstStructure *structure; -+ gsize size = sink->video_info.size; -+ GstAllocator *alloc; -+ -+ /* create a new DMABuf pool */ -+ pool = gst_wayland_buffer_pool_new (sink->display); -+ if (!pool) { -+ GST_DEBUG_OBJECT (sink, "Failed to create new pool"); -+ return NULL; -+ } -+ -+ structure = gst_buffer_pool_get_config (pool); -+ gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0); -+ gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms); -+ alloc = gst_dmabuf_allocator_new (); -+ gst_buffer_pool_config_set_allocator (structure, alloc, NULL); -+ if (!gst_buffer_pool_set_config (pool, structure)) { -+ GST_DEBUG_OBJECT (sink, "failed setting config"); -+ gst_object_unref (pool); -+ return NULL; -+ } -+ g_object_unref (alloc); -+ -+ return pool; -+} -+ - static gboolean - gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) - { -@@ -575,15 +606,9 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) - format = GST_VIDEO_INFO_FORMAT (&sink->video_info); - sink->video_info_changed = TRUE; - -- /* create a new pool for the new caps */ -- if (sink->pool) -- gst_object_unref (sink->pool); -- sink->pool = gst_wayland_create_pool (sink, caps); -- - use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), - GST_CAPS_FEATURE_MEMORY_DMABUF); - -- /* validate the format base on the memory type. */ - if (use_dmabuf) { - if (!gst_wl_display_check_format_for_dmabuf (sink->display, format)) - goto unsupported_format; -@@ -591,6 +616,14 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) - goto unsupported_format; - } - -+ /* create a new pool for the new caps */ -+ if (sink->pool) -+ gst_object_unref (sink->pool); -+ if (use_dmabuf) -+ sink->pool = gst_wayland_create_dmabuf_pool (sink, caps); -+ else -+ sink->pool = gst_wayland_create_pool (sink, caps); -+ - sink->use_dmabuf = use_dmabuf; - - return TRUE; -@@ -610,7 +643,7 @@ unsupported_format: - } - - static gboolean --gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) -+gst_wayland_sink_propose_shm_allocation (GstBaseSink * bsink, GstQuery * query) - { - GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); - GstCaps *caps; -@@ -635,6 +668,67 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) - return TRUE; - } - -+static gboolean -+gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) -+{ -+ GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); -+ GstCaps *caps; -+ GstBufferPool *pool = NULL; -+ GstStructure *config; -+ guint size; -+ gboolean need_pool; -+ GstAllocator *alloc; -+ GstCaps *pcaps; -+ guint config_min_buf, config_max_buf; -+ -+ if (!sink->use_dmabuf) -+ return gst_wayland_sink_propose_shm_allocation(bsink, query); -+ -+ /* -+ * propose DMA-buf allocator... -+ */ -+ gst_query_parse_allocation (query, &caps, &need_pool); -+ -+ if (need_pool) { -+ /* Fill query with DMABuf pool characteristics, -+ * to do so create a pool, get its characteristics -+ * to fill query and free it... -+ */ -+ pool = gst_wayland_create_dmabuf_pool (sink, caps); -+ if (!pool) -+ goto no_pool; -+ -+ config = gst_buffer_pool_get_config (pool); -+ gst_buffer_pool_config_get_params (config, &pcaps, &size, -+ &config_min_buf, &config_max_buf); -+ gst_query_add_allocation_pool (query, pool, size, -+ config_min_buf, config_max_buf); -+ g_object_unref (pool); -+ } -+ -+ /* -+ * FIXME is there a case where !need_pool and we fill query with an -+ * allocator and alignment ? -+ */ -+ alloc = gst_dmabuf_allocator_new (); -+ gst_query_add_allocation_param (query, alloc, NULL); -+ gst_object_unref (alloc); -+ -+ /* we also support video metadata (alignment) */ -+ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); -+ -+ GST_WARNING_OBJECT (bsink, "Add dmabuf allocator"); -+ -+ return TRUE; -+ -+ /* ERRORS */ -+no_pool: -+ { -+ GST_DEBUG_OBJECT (bsink, "failed to propose the needed pool"); -+ return FALSE; -+ } -+} -+ - static void - frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time) - { -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -new file mode 100644 -index 0000000..70e40b4 ---- /dev/null -+++ b/ext/wayland/waylandpool.c -@@ -0,0 +1,640 @@ -+/* GStreamer -+ * Copyright (C) 2012 Intel Corporation -+ * Copyright (C) 2012 Sreerenj Balachandran -+ * Copyright (C) 2014 Collabora Ltd. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "waylandpool.h" -+#include "wldisplay.h" -+#include "wlvideoformat.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); -+#define GST_CAT_DEFAULT gstwayland_debug -+ -+/* wl metadata */ -+GType -+gst_wl_meta_api_get_type (void) -+{ -+ static volatile GType type; -+ static const gchar *tags[] = -+ { "memory", "size", "colorspace", "orientation", NULL }; -+ if (g_once_init_enter (&type)) { -+ GType _type = gst_meta_api_type_register ("GstWlMetaAPI", tags); -+ g_once_init_leave (&type, _type); -+ } -+ return type; -+} -+ -+static void -+gst_wl_meta_free (GstWlMeta * meta, GstBuffer * buffer) -+{ -+ GstMemory *gmem; -+ GST_DEBUG ("destroying wl_buffer %p", meta->wbuffer); -+ -+ gmem = gst_buffer_get_memory (buffer, 0); -+ -+ if ((gmem != NULL) && gst_is_dmabuf_memory (gmem)) { -+ struct drm_mode_destroy_dumb destroy_arg; -+ int prime_fd = gst_dmabuf_memory_get_fd (gmem); -+ -+ memset (&destroy_arg, 0, sizeof destroy_arg); -+ drmPrimeFDToHandle (meta->drm_fd, prime_fd, &destroy_arg.handle); -+ drmIoctl (meta->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); -+ close (prime_fd); -+ } else { -+ if (meta->data) -+ munmap (meta->data, meta->size); -+ } -+ gst_memory_unref (gmem); -+ -+ if (meta->wbuffer) -+ wl_buffer_destroy (meta->wbuffer); -+} -+ -+static gboolean gst_wl_meta_init(GstMeta *meta, G_GNUC_UNUSED gpointer params, G_GNUC_UNUSED GstBuffer *buffer) -+{ -+ /* Just to avoid a warning */ -+ return TRUE; -+} -+ -+const GstMetaInfo * -+gst_wl_meta_get_info (void) -+{ -+ static const GstMetaInfo *wl_meta_info = NULL; -+ if (g_once_init_enter (&wl_meta_info)) { -+ const GstMetaInfo *meta = -+ gst_meta_register (GST_WL_META_API_TYPE, "GstWlMeta", -+ sizeof (GstWlMeta), (GstMetaInitFunction) gst_wl_meta_init, -+ (GstMetaFreeFunction) gst_wl_meta_free, -+ (GstMetaTransformFunction) NULL); -+ g_once_init_leave (&wl_meta_info, meta); -+ } -+ return wl_meta_info; -+} -+ -+/* bufferpool */ -+static void gst_wayland_buffer_pool_finalize (GObject * object); -+static gboolean gst_wayland_buffer_pool_set_config (GstBufferPool * pool, -+ GstStructure * config); -+static gboolean gst_wayland_buffer_pool_start (GstBufferPool * pool); -+static gboolean gst_wayland_buffer_pool_stop (GstBufferPool * pool); -+static GstFlowReturn gst_wayland_buffer_pool_alloc (GstBufferPool * pool, -+ GstBuffer ** buffer, GstBufferPoolAcquireParams * params); -+ -+#define gst_wayland_buffer_pool_parent_class parent_class -+G_DEFINE_TYPE (GstWaylandBufferPool, gst_wayland_buffer_pool, -+ GST_TYPE_BUFFER_POOL); -+ -+static const gchar ** -+gst_wayland_buffer_pool_get_options (GstBufferPool * pool) -+{ -+ static const gchar *options[] = { -+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL -+ }; -+ return options; -+} -+ -+ -+static void -+gst_wayland_buffer_pool_class_init (GstWaylandBufferPoolClass * klass) -+{ -+ GObjectClass *gobject_class = (GObjectClass *) klass; -+ GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; -+ gobject_class->finalize = gst_wayland_buffer_pool_finalize; -+ -+ gstbufferpool_class->set_config = gst_wayland_buffer_pool_set_config; -+ gstbufferpool_class->start = gst_wayland_buffer_pool_start; -+ gstbufferpool_class->stop = gst_wayland_buffer_pool_stop; -+ gstbufferpool_class->alloc_buffer = gst_wayland_buffer_pool_alloc; -+ gstbufferpool_class->get_options = gst_wayland_buffer_pool_get_options; -+} -+ -+static void -+gst_wayland_buffer_pool_init (GstWaylandBufferPool * self) -+{ -+ gst_video_info_init (&self->info); -+ g_mutex_init (&self->buffers_map_mutex); -+ self->buffers_map = g_hash_table_new (g_direct_hash, g_direct_equal); -+} -+ -+static void -+gst_wayland_buffer_pool_finalize (GObject * object) -+{ -+ GstWaylandBufferPool *pool = GST_WAYLAND_BUFFER_POOL_CAST (object); -+ if (pool->allocator) -+ gst_object_unref (pool->allocator); -+ pool->allocator = NULL; -+ -+ if (pool->wl_pool) -+ gst_wayland_buffer_pool_stop (GST_BUFFER_POOL (pool)); -+ -+ if (pool->fd != -1) -+ close (pool->fd); -+ -+ g_mutex_clear (&pool->buffers_map_mutex); -+ g_hash_table_unref (pool->buffers_map); -+ -+ g_object_unref (pool->display); -+ -+ G_OBJECT_CLASS (gst_wayland_buffer_pool_parent_class)->finalize (object); -+} -+ -+static void -+buffer_release (void *data, struct wl_buffer *wl_buffer) -+{ -+ GstWaylandBufferPool *self = data; -+ GstBuffer *buffer; -+ GstWlMeta *meta; -+ g_mutex_lock (&self->buffers_map_mutex); -+ buffer = g_hash_table_lookup (self->buffers_map, wl_buffer); -+ -+ GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", buffer); -+ -+ if (buffer) { -+ meta = gst_buffer_get_wl_meta (buffer); -+ if (meta->used_by_compositor) { -+ meta->used_by_compositor = FALSE; -+ /* unlock before unref because stop() may be called from here */ -+ g_mutex_unlock (&self->buffers_map_mutex); -+ gst_buffer_unref (buffer); -+ return; -+ } -+ } -+ g_mutex_unlock (&self->buffers_map_mutex); -+} -+ -+static const struct wl_buffer_listener buffer_listener = { -+ buffer_release -+}; -+ -+void -+gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self, -+ GstBuffer * buffer) -+{ -+ GstWlMeta *meta; -+ -+ meta = gst_buffer_get_wl_meta (buffer); -+ g_return_if_fail (meta != NULL); -+ g_return_if_fail (meta->pool == self); -+ g_return_if_fail (meta->used_by_compositor == FALSE); -+ -+ meta->used_by_compositor = TRUE; -+ gst_buffer_ref (buffer); -+ -+} -+ -+static void -+unref_used_buffers (gpointer key, gpointer value, gpointer data) -+{ -+ GstBuffer *buffer = value; -+ GstWlMeta *meta = gst_buffer_get_wl_meta (buffer); -+ GList **to_unref = data; -+ -+ if (meta->used_by_compositor) { -+ meta->used_by_compositor = FALSE; -+ *to_unref = g_list_prepend (*to_unref, buffer); -+ } -+ -+} -+ -+void -+gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self) -+{ -+ GList *to_unref = NULL; -+ -+ g_mutex_lock (&self->buffers_map_mutex); -+ g_hash_table_foreach (self->buffers_map, unref_used_buffers, &to_unref); -+ g_mutex_unlock (&self->buffers_map_mutex); -+ -+ /* unref without the lock because stop() may be called from here */ -+ if (to_unref) { -+ g_list_free_full (to_unref, (GDestroyNotify) gst_buffer_unref); -+ } -+} -+ -+static gboolean -+gst_wayland_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) -+{ -+ -+ GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); -+ GstWlDisplay *display = self->display; -+ GstAllocationParams params; -+ GstVideoInfo info; -+ GstCaps *caps; -+ guint config_min_buf, config_max_buf; -+ gboolean has_alignment = FALSE; -+ GstVideoAlignment video_align; -+ -+ if (self->allocator) -+ gst_object_unref (self->allocator); -+ self->allocator = NULL; -+ -+ if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &config_min_buf, -+ &config_max_buf)) -+ goto wrong_config; -+ -+ if (caps == NULL) -+ goto no_caps; -+ -+ /* now parse the caps from the config */ -+ if (!gst_video_info_from_caps (&info, caps)) -+ goto wrong_caps; -+ -+ /* parse extra alignment info */ -+ has_alignment = gst_buffer_pool_config_has_option (config, -+ GST_BUFFER_POOL_OPTION_VIDEO_META); -+ if (has_alignment) { -+ /* get and apply the alignment to info */ -+ if (gst_buffer_pool_config_get_video_alignment (config, &video_align)) { -+ gst_video_info_align (&info, &video_align); -+ -+ GST_LOG_OBJECT (self, "padding %u-%ux%u-%u", -+ video_align.padding_top, -+ video_align.padding_left, -+ video_align.padding_right, video_align.padding_bottom); -+ } -+ } -+ -+ if (!gst_buffer_pool_config_get_allocator (config, &self->allocator, ¶ms)) -+ goto wrong_allocator; -+ -+ if (self->allocator) -+ gst_object_ref (self->allocator); -+ -+ GST_LOG_OBJECT (self, "%dx%d, caps %" GST_PTR_FORMAT, -+ info.width, info.height, caps); -+ -+ /* FIXME: Enable metadata checking handling based on the config of pool */ -+ self->caps = gst_caps_ref (caps); -+ self->info = info; -+ self->width = info.width; -+ self->height = info.height; -+ self->size = GST_VIDEO_INFO_SIZE (&info); -+ /* Update size in the config to be inline with buffer pool setup */ -+ gst_buffer_pool_config_set_params (config, caps, self->size, config_min_buf, -+ config_max_buf); -+ -+ self->use_linux_dmabuf = (self->allocator -+ && g_strcmp0 (self->allocator->mem_type, GST_ALLOCATOR_DMABUF) == 0); -+ -+ GST_LOG_OBJECT (self, "pool will use %s", -+ self->use_linux_dmabuf ? "linux_dmabuf" : "wl_shm"); -+ -+ if (self->use_linux_dmabuf && display->device_name) { -+ self->fd = open (display->device_name, O_RDWR, 0); -+ -+ if (self->fd == -1) { -+ GST_ERROR_OBJECT (self, "can't open %s", display->device_name); -+ return FALSE; -+ } -+ } -+ -+ -+ return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config); -+ /* ERRORS */ -+wrong_allocator: -+ { -+ GST_WARNING_OBJECT (pool, "no allocator"); -+ return FALSE; -+ } -+wrong_config: -+ { -+ GST_WARNING_OBJECT (pool, "invalid config"); -+ return FALSE; -+ } -+no_caps: -+ { -+ GST_WARNING_OBJECT (pool, "no caps in config"); -+ return FALSE; -+ } -+wrong_caps: -+ { -+ GST_WARNING_OBJECT (pool, -+ "failed getting geometry from caps %" GST_PTR_FORMAT, caps); -+ return FALSE; -+ } -+ -+} -+ -+static gboolean -+gst_wayland_buffer_pool_start (GstBufferPool * pool) -+{ -+ GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); -+ guint size = 0; -+ int fd; -+ char filename[1024]; -+ static int init = 0; -+ -+ GST_DEBUG_OBJECT (self, "Initializing wayland buffer pool"); -+ -+ /* configure */ -+ size = GST_VIDEO_INFO_SIZE (&self->info) * 15; -+ -+ /* allocate shm pool */ -+ snprintf (filename, 1024, "%s/%s-%d-%s", g_get_user_runtime_dir (), -+ "wayland-shm", init++, "XXXXXX"); -+ -+ fd = mkstemp (filename); -+ if (fd < 0) { -+ GST_ERROR_OBJECT (pool, "opening temp file %s failed: %s", filename, -+ strerror (errno)); -+ -+ return FALSE; -+ } -+ if (ftruncate (fd, size) < 0) { -+ GST_ERROR_OBJECT (pool, "ftruncate failed: %s", strerror (errno)); -+ close (fd); -+ -+ return FALSE; -+ } -+ -+ self->data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); -+ if (self->data == MAP_FAILED) { -+ GST_ERROR_OBJECT (pool, "mmap failed: %s", strerror (errno)); -+ close (fd); -+ -+ return FALSE; -+ } -+ GST_DEBUG_OBJECT (self, "wl_shm_create_pool"); -+ self->wl_pool = wl_shm_create_pool (self->display->shm, fd, size); -+ unlink (filename); -+ close (fd); -+ -+ self->size = size; -+ self->used = 0; -+ -+ -+ return GST_BUFFER_POOL_CLASS (parent_class)->start (pool); -+} -+ -+static gboolean -+gst_wayland_buffer_pool_stop (GstBufferPool * pool) -+{ -+ GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); -+ -+ GST_DEBUG_OBJECT (self, "Stopping wayland buffer pool"); -+ -+ munmap (self->data, self->size); -+ wl_shm_pool_destroy (self->wl_pool); -+ -+ self->wl_pool = NULL; -+ self->size = 0; -+ self->used = 0; -+ -+ /* all buffers are about to be destroyed; -+ * we should no longer do anything with them */ -+ g_mutex_lock (&self->buffers_map_mutex); -+ g_hash_table_remove_all (self->buffers_map); -+ g_mutex_unlock (&self->buffers_map_mutex); -+ -+ -+ return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool); -+} -+ -+/* DMABUF buffer functions */ -+static int -+get_format_bpp (int format) -+{ -+ switch (format) { -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_NV12: -+ return 12; -+ case WL_SHM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XRGB8888: -+ case WL_SHM_FORMAT_ARGB8888: -+ case DRM_FORMAT_ARGB8888: -+ return 32; -+ default: -+ GST_WARNING ("Unknown format: %d", format); -+ return 32; -+ } -+} -+ -+static int -+create_dumb (int drm_fd, uint32_t width, uint32_t height, int format, -+ int *prime_fd, guint * stride) -+{ -+ struct drm_mode_create_dumb create_arg; -+ gint ret; -+ -+ *prime_fd = -1; -+ *stride = 0; -+ -+ -+ memset (&create_arg, 0, sizeof (create_arg)); -+ create_arg.bpp = get_format_bpp (format); -+ create_arg.width = width; -+ create_arg.height = height; -+ -+ ret = drmIoctl (drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); -+ -+ if (ret) { -+ GST_ERROR ("DRM_IOCTL_MODE_CREATE_DUMB failed %s ret =%d on fd %d", -+ strerror (errno), ret, drm_fd); -+ -+ return ret; -+ } -+ -+ *stride = create_arg.pitch; -+ -+ ret = drmPrimeHandleToFD (drm_fd, create_arg.handle, DRM_CLOEXEC, prime_fd); -+ if (ret) { -+ struct drm_mode_destroy_dumb destroy_arg; -+ -+ GST_WARNING ("Can't get fd from handle"); -+ -+ memset (&destroy_arg, 0, sizeof destroy_arg); -+ destroy_arg.handle = create_arg.handle; -+ drmIoctl (drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); -+ } -+ -+ -+ return ret; -+} -+ -+static GstWlMeta * -+gst_buffer_add_wayland_meta (GstBuffer * buffer, GstWaylandBufferPool * self) -+{ -+ GstWlMeta *meta; -+ gint offset; -+ void *data; -+ guint width, height, stride, dumb_stride; -+ gsize size; -+ int format; -+ -+ width = GST_VIDEO_INFO_WIDTH (&self->info); -+ height = GST_VIDEO_INFO_HEIGHT (&self->info); -+ stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0); -+ size = GST_VIDEO_INFO_SIZE (&self->info); -+ -+ if (self->display->dmabuf && self->use_linux_dmabuf) { -+ format = -+ gst_video_format_to_wl_dmabuf_format (GST_VIDEO_INFO_FORMAT -+ (&self->info)); -+ GST_DEBUG_OBJECT (self, -+ "Allocating buffer of size %" G_GSSIZE_FORMAT -+ " (%d x %d, stride %d), format : %s", size, width, height, stride, -+ gst_wl_dmabuf_format_to_string (format)); -+ } else { -+ format = -+ gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&self->info)); -+ GST_DEBUG_OBJECT (self, -+ "Allocating buffer of size %" G_GSSIZE_FORMAT -+ " (%d x %d, stride %d), format : %s", size, width, height, stride, -+ gst_wl_shm_format_to_string (format)); -+ } -+ -+ meta = (GstWlMeta *) gst_buffer_add_meta (buffer, GST_WL_META_INFO, NULL); -+ meta->pool = self; -+ meta->size = size; -+ meta->drm_fd = -1; -+ meta->used_by_compositor = FALSE; -+ -+ gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE, -+ GST_VIDEO_INFO_FORMAT (&self->info), -+ GST_VIDEO_INFO_WIDTH (&self->info), -+ GST_VIDEO_INFO_HEIGHT (&self->info), -+ GST_VIDEO_INFO_N_PLANES (&self->info), self->info.offset, -+ self->info.stride); -+ -+ if (self->display->dmabuf && self->use_linux_dmabuf) { -+ int prime_fd, ret, buf_width, buf_height; -+ -+ buf_width = (stride / (get_format_bpp (format) / 8)); -+ buf_height = (size / buf_width) * 8 / get_format_bpp (format); -+ if ((buf_width * buf_height * get_format_bpp (format)) / 8 < size) -+ GST_ERROR_OBJECT (self, "Not allocating enough memory"); -+ -+ ret = -+ create_dumb (self->fd, width, buf_height, format, &prime_fd, -+ &dumb_stride); -+ if (ret || prime_fd == -1) { -+ -+ return NULL; -+ } -+ GST_LOG_OBJECT (self, "drm buffer prime fd %d", prime_fd); -+ -+ /* The wl_buffer will be created at the first render */ -+ meta->wbuffer = NULL; -+ -+ gst_buffer_append_memory (buffer, -+ gst_dmabuf_allocator_alloc (self->allocator, prime_fd, meta->size)); -+ -+ meta->drm_fd = self->fd; -+ } else { -+ /* try to reserve another memory block from the shm pool */ -+ if (self->used + size > self->size) { -+ -+ return NULL; -+ } -+ offset = self->used; -+ self->used += size; -+ data = ((gchar *) self->data) + offset; -+ -+ meta->wbuffer = wl_shm_pool_create_buffer (self->wl_pool, offset, -+ width, height, stride, format); -+ -+ meta->data = data; -+ -+ /* add the allocated memory on the GstBuffer */ -+ gst_buffer_append_memory (buffer, -+ gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, data, -+ size, 0, size, NULL, NULL)); -+ -+ /* configure listening to wl_buffer.release */ -+ g_mutex_lock (&self->buffers_map_mutex); -+ g_hash_table_insert (self->buffers_map, meta->wbuffer, buffer); -+ g_mutex_unlock (&self->buffers_map_mutex); -+ -+ wl_buffer_add_listener (meta->wbuffer, &buffer_listener, self); -+ } -+ -+ -+ return meta; -+} -+ -+static GstFlowReturn -+gst_wayland_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer, -+ GstBufferPoolAcquireParams * params) -+{ -+ GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); -+ GstWlMeta *meta; -+ -+ /* create buffer and its metadata object */ -+ *buffer = gst_buffer_new (); -+ -+ meta = gst_buffer_add_wayland_meta (*buffer, self); -+ -+ if (meta == NULL) { -+ gst_buffer_unref (*buffer); -+ goto no_buffer; -+ } -+ -+ -+ return GST_FLOW_OK; -+ -+ /* ERROR */ -+no_buffer: -+ { -+ GST_WARNING_OBJECT (pool, "can't create buffer"); -+ -+ return GST_FLOW_ERROR; -+ } -+} -+ -+GstBufferPool * -+gst_wayland_buffer_pool_new (GstWlDisplay * display) -+{ -+ GstWaylandBufferPool *pool; -+ GstStructure *s; -+ GstVideoAlignment align; -+ -+ g_return_val_if_fail (GST_IS_WL_DISPLAY (display), NULL); -+ pool = g_object_new (GST_TYPE_WAYLAND_BUFFER_POOL, NULL); -+ pool->display = g_object_ref (display); -+ -+ pool->fd = -1; -+ -+ s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool)); -+ gst_buffer_pool_config_add_option (s, GST_BUFFER_POOL_OPTION_VIDEO_META); -+ gst_buffer_pool_config_add_option (s, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); -+ /* reset alignment */ -+ gst_video_alignment_reset (&align); -+ gst_buffer_pool_config_set_video_alignment (s, &align); -+ gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pool), s); -+ -+ return GST_BUFFER_POOL_CAST (pool); -+} -diff --git a/ext/wayland/waylandpool.h b/ext/wayland/waylandpool.h -new file mode 100644 -index 0000000..4afd683 ---- /dev/null -+++ b/ext/wayland/waylandpool.h -@@ -0,0 +1,105 @@ -+/* GStreamer Wayland buffer pool -+ * Copyright (C) 2012 Intel Corporation -+ * Copyright (C) 2012 Sreerenj Balachandran -+ * Copyright (C) 2014 Collabora Ltd. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef __GST_WAYLAND_BUFFER_POOL_H__ -+#define __GST_WAYLAND_BUFFER_POOL_H__ -+ -+#include -+#include -+ -+#include "wldisplay.h" -+#include "gstwaylandsink.h" -+ -+G_BEGIN_DECLS -+#define GST_TYPE_WAYLAND_BUFFER_POOL (gst_wayland_buffer_pool_get_type()) -+#define GST_IS_WAYLAND_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WAYLAND_BUFFER_POOL)) -+#define GST_WAYLAND_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WAYLAND_BUFFER_POOL, GstWaylandBufferPool)) -+#define GST_WAYLAND_BUFFER_POOL_CAST(obj) ((GstWaylandBufferPool*)(obj)) -+typedef struct _GstWaylandBufferPool GstWaylandBufferPool; -+typedef struct _GstWaylandBufferPoolClass GstWaylandBufferPoolClass; -+ -+/* buffer meta */ -+typedef struct _GstWlMeta GstWlMeta; -+ -+GType gst_wl_meta_api_get_type (void); -+#define GST_WL_META_API_TYPE (gst_wl_meta_api_get_type()) -+ -+const GstMetaInfo *gst_wl_meta_get_info (void); -+#define GST_WL_META_INFO (gst_wl_meta_get_info()) -+ -+#define gst_buffer_get_wl_meta(b) ((GstWlMeta*)gst_buffer_get_meta((b),GST_WL_META_API_TYPE)) -+ -+struct _GstWlMeta -+{ -+ GstMeta meta; -+ -+ GstWaylandBufferPool *pool; -+ struct wl_buffer *wbuffer; -+ gboolean used_by_compositor; -+ -+ void *data; -+ size_t size; -+ int drm_fd; -+}; -+ -+/* buffer pool */ -+struct _GstWaylandBufferPool -+{ -+ GstBufferPool bufferpool; -+ GstWlDisplay *display; -+ -+ GstAllocator *allocator; -+ -+ /* external configuration */ -+ GstVideoInfo info; -+ -+ /* allocation data */ -+ struct wl_shm_pool *wl_pool; -+ size_t size; -+ size_t used; -+ void *data; -+ -+ gboolean use_linux_dmabuf; -+ gint fd; -+ GstCaps *caps; -+ guint width; -+ guint height; -+ -+ GMutex buffers_map_mutex; -+ GHashTable *buffers_map; -+}; -+ -+struct _GstWaylandBufferPoolClass -+{ -+ GstBufferPoolClass parent_class; -+}; -+ -+GType gst_wayland_buffer_pool_get_type (void); -+ -+GstBufferPool *gst_wayland_buffer_pool_new (GstWlDisplay * display); -+ -+void gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self, -+ GstBuffer * buffer); -+void gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self); -+ -+gboolean gst_buffer_set_wl_buffer (GstBuffer * buf, GstBufferPool * bpool); -+G_END_DECLS -+#endif /*__GST_WAYLAND_BUFFER_POOL_H__*/ -diff --git a/ext/wayland/wldisplay.c b/ext/wayland/wldisplay.c -index f326091..b2e1359 100644 ---- a/ext/wayland/wldisplay.c -+++ b/ext/wayland/wldisplay.c -@@ -50,6 +50,9 @@ gst_wl_display_init (GstWlDisplay * self) - self->wl_fd_poll = gst_poll_new (TRUE); - self->buffers = g_hash_table_new (g_direct_hash, g_direct_equal); - g_mutex_init (&self->buffers_mutex); -+ /* Default DRM */ -+ /* TODO : device name is hardcoded. It should come from protocol */ -+ self->device_name = g_strdup ("/dev/dri/card0"); - } - - static void -diff --git a/ext/wayland/wldisplay.h b/ext/wayland/wldisplay.h -index f2025a6..65c754a 100644 ---- a/ext/wayland/wldisplay.h -+++ b/ext/wayland/wldisplay.h -@@ -29,15 +29,15 @@ - #include "linux-dmabuf-unstable-v1-client-protocol.h" - #include "fullscreen-shell-unstable-v1-client-protocol.h" - --G_BEGIN_DECLS -+#include "gst/allocators/gstdmabuf.h" - -+G_BEGIN_DECLS - #define GST_TYPE_WL_DISPLAY (gst_wl_display_get_type ()) - #define GST_WL_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplay)) - #define GST_IS_WL_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_DISPLAY)) - #define GST_WL_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_DISPLAY, GstWlDisplayClass)) - #define GST_IS_WL_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_DISPLAY)) - #define GST_WL_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplayClass)) -- - typedef struct _GstWlDisplay GstWlDisplay; - typedef struct _GstWlDisplayClass GstWlDisplayClass; - -@@ -67,7 +67,7 @@ struct _GstWlDisplay - gboolean own_display; - GThread *thread; - GstPoll *wl_fd_poll; -- -+ char *device_name; - GMutex buffers_mutex; - GHashTable *buffers; - gboolean shutting_down; -@@ -81,7 +81,7 @@ struct _GstWlDisplayClass - GType gst_wl_display_get_type (void); - - GstWlDisplay *gst_wl_display_new (const gchar * name, GError ** error); --GstWlDisplay *gst_wl_display_new_existing (struct wl_display * display, -+GstWlDisplay *gst_wl_display_new_existing (struct wl_display *display, - gboolean take_ownership, GError ** error); - - /* see wlbuffer.c for explanation */ -@@ -96,5 +96,4 @@ gboolean gst_wl_display_check_format_for_dmabuf (GstWlDisplay * display, - GstVideoFormat format); - - G_END_DECLS -- - #endif /* __GST_WL_DISPLAY_H__ */ --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch new file mode 100644 index 000000000..fc4e12abc --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch @@ -0,0 +1,50 @@ +From 930fb463ebf2fffed6a8e8f8b05594082fb2a3c2 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Feb 2023 11:49:38 -0500 +Subject: [PATCH 02/17] gtkwaylandsink: Remove redefine of + GST_CAPS_FEATURE_MEMORY_DMABUF + +Instead just include the appropriate header file. There is no meson deps +modification as gstallocators_dep is already part of gstwayland_dep. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index e4dd06e..35367ca 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -25,13 +25,13 @@ + #endif + + #include "gstgtkwaylandsink.h" ++#include "gstgtkutils.h" ++#include "gtkgstwaylandwidget.h" + + #include ++#include + #include + +-#include "gstgtkutils.h" +-#include "gtkgstwaylandwidget.h" +- + #ifdef GDK_WINDOWING_WAYLAND + #include + #else +@@ -41,10 +41,6 @@ + #define GST_CAT_DEFAULT gst_debug_gtk_wayland_sink + GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +-#ifndef GST_CAPS_FEATURE_MEMORY_DMABUF +-#define GST_CAPS_FEATURE_MEMORY_DMABUF "memory:DMABuf" +-#endif +- + #define WL_VIDEO_FORMATS \ + "{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, " \ + "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, NV61, " \ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-waylandsink-fix-error-when-mmapping-dmabuf-buffers.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-waylandsink-fix-error-when-mmapping-dmabuf-buffers.patch deleted file mode 100644 index 74aa63d30..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-waylandsink-fix-error-when-mmapping-dmabuf-buffers.patch +++ /dev/null @@ -1,33 +0,0 @@ -From a69cd4d027f3b86fd19a87d8139e08614587885c Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Mon, 26 Nov 2018 17:38:13 +0100 -Subject: [PATCH 02/14] waylandsink: fix error when mmapping dmabuf buffers - -Linux Kernel 4.10 DRM/KMS requires DRM_RDWR rights -in addition to DRM_CLOEXEC (see drmPrimeHandleToFD()) -in order to mmap in RD/WR. - -Upstream-Status: Inappropriate [DEY specific] - -Change-Id: I9c26642a47e6f71b56c0a4ad4714a5b85d814e5f -Signed-off-by: Hugues Fruchet ---- - ext/wayland/waylandpool.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -index 70e40b4..9b20884 100644 ---- a/ext/wayland/waylandpool.c -+++ b/ext/wayland/waylandpool.c -@@ -470,7 +470,7 @@ create_dumb (int drm_fd, uint32_t width, uint32_t height, int format, - - *stride = create_arg.pitch; - -- ret = drmPrimeHandleToFD (drm_fd, create_arg.handle, DRM_CLOEXEC, prime_fd); -+ ret = drmPrimeHandleToFD (drm_fd, create_arg.handle, DRM_CLOEXEC | DRM_RDWR, prime_fd); - if (ret) { - struct drm_mode_destroy_dumb destroy_arg; - --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch new file mode 100644 index 000000000..2ad1085c2 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch @@ -0,0 +1,157 @@ +From 4b6f23a997485355aeca22193c317184fe74a28c Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Feb 2023 12:13:16 -0500 +Subject: [PATCH 03/17] waylandsink: Stop modifying the display GstVideoInfo + +The video_info is supposed to match the display dimentions, but as soon as we +get a padded video buffer, we modify it. This has side effect later on and +maybe cause bad frames. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 22 +++++++++------------- + ext/wayland/gstwaylandsink.c | 18 +++++++----------- + 2 files changed, 16 insertions(+), 24 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 35367ca..dc1e3c3 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -982,7 +982,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GstWlBuffer *wlbuffer; + GstVideoMeta *vmeta; + GstVideoFormat format; +- GstVideoInfo old_vinfo; ++ GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -1023,23 +1023,23 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + +- old_vinfo = priv->video_info; ++ src_vinfo = priv->video_info; + vmeta = gst_buffer_get_video_meta (buffer); + if (vmeta) { + gint i; + + for (i = 0; i < vmeta->n_planes; i++) { +- priv->video_info.offset[i] = vmeta->offset[i]; +- priv->video_info.stride[i] = vmeta->stride[i]; ++ src_vinfo.offset[i] = vmeta->offset[i]; ++ src_vinfo.stride[i] = vmeta->stride[i]; + } +- priv->video_info.size = gst_buffer_get_size (buffer); ++ src_vinfo.size = gst_buffer_get_size (buffer); + } + + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " + "display, creating it", buffer); + +- format = GST_VIDEO_INFO_FORMAT (&priv->video_info); ++ format = GST_VIDEO_INFO_FORMAT (&src_vinfo); + if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) { + guint i, nb_dmabuf = 0; + +@@ -1049,21 +1049,17 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, +- &priv->video_info); ++ &src_vinfo); + } + + if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { + if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, +- &priv->video_info); ++ &src_vinfo); + + /* If nothing worked, copy into our internal pool */ + if (!wbuf) { + GstVideoFrame src, dst; +- GstVideoInfo src_info = priv->video_info; +- +- /* rollback video info changes */ +- priv->video_info = old_vinfo; + + /* we don't know how to create a wl_buffer directly from the provided + * memory, so we have to copy the data to shm memory that we know how +@@ -1115,7 +1111,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &src_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 2f116bf..6dd0807 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -741,7 +741,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GstWlBuffer *wlbuffer; + GstVideoMeta *vmeta; + GstVideoFormat format; +- GstVideoInfo old_vinfo; ++ GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -794,16 +794,16 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + +- old_vinfo = self->video_info; ++ src_vinfo = self->video_info; + vmeta = gst_buffer_get_video_meta (buffer); + if (vmeta) { + gint i; + + for (i = 0; i < vmeta->n_planes; i++) { +- self->video_info.offset[i] = vmeta->offset[i]; +- self->video_info.stride[i] = vmeta->stride[i]; ++ src_vinfo.offset[i] = vmeta->offset[i]; ++ src_vinfo.stride[i] = vmeta->stride[i]; + } +- self->video_info.size = gst_buffer_get_size (buffer); ++ src_vinfo.size = gst_buffer_get_size (buffer); + } + + GST_LOG_OBJECT (self, +@@ -820,7 +820,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, +- &self->video_info); ++ &src_vinfo); + } + + if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) { +@@ -831,10 +831,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* If nothing worked, copy into our internal pool */ + if (!wbuf) { + GstVideoFrame src, dst; +- GstVideoInfo src_info = self->video_info; +- +- /* rollback video info changes */ +- self->video_info = old_vinfo; + + /* we don't know how to create a wl_buffer directly from the provided + * memory, so we have to copy the data to shm memory that we know how +@@ -886,7 +882,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &src_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-fix-wrong-width-when-creating-dmabuf-dum.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-fix-wrong-width-when-creating-dmabuf-dum.patch deleted file mode 100644 index 183efd88b..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-waylandsink-fix-wrong-width-when-creating-dmabuf-dum.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 754625fd6a60fbc529416ba88ff06f542523937a Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Thu, 6 Dec 2018 18:08:15 +0100 -Subject: [PATCH 03/14] waylandsink: fix wrong width when creating dmabuf dumb - buffers - -Unaligned width is given in argument of create_dumb(), fix this. - -Upstream-Status: Inappropriate [DEY specific] - -Change-Id: Ifc57aa24331f58f588aae480baa2099a47d5f31b -Signed-off-by: Hugues Fruchet ---- - ext/wayland/waylandpool.c | 5 ++--- - 1 file changed, 2 insertions(+), 3 deletions(-) - -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -index 9b20884..6e98143 100644 ---- a/ext/wayland/waylandpool.c -+++ b/ext/wayland/waylandpool.c -@@ -537,10 +537,9 @@ gst_buffer_add_wayland_meta (GstBuffer * buffer, GstWaylandBufferPool * self) - buf_height = (size / buf_width) * 8 / get_format_bpp (format); - if ((buf_width * buf_height * get_format_bpp (format)) / 8 < size) - GST_ERROR_OBJECT (self, "Not allocating enough memory"); -- - ret = -- create_dumb (self->fd, width, buf_height, format, &prime_fd, -- &dumb_stride); -+ create_dumb (self->fd, buf_width, buf_height, format, &prime_fd, -+ &dumb_stride); - if (ret || prime_fd == -1) { - - return NULL; --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch new file mode 100644 index 000000000..d908645b4 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch @@ -0,0 +1,40 @@ +From baf873c06399c6ad6062cc1a96cc1b0bfc0edf3c Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Feb 2023 12:34:27 -0500 +Subject: [PATCH 04/17] gtkwaylandsink: Force a redraw on resolution change + +As we don't render into the widget directly, there is no "initial" draw +happening. As a side effect, the internal aspect ratio adapted display +width/height is never initialize leading to assertions when handling navigation +events. + + gst_video_center_rect: assertion 'src->h != 0' failed + +Simply queue a redraw after setting the widget format in order to fix the issue. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index dc1e3c3..5288614 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -875,6 +875,12 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + GST_OBJECT_UNLOCK (self); + return FALSE; + } ++ ++ /* Ensure queue_draw get executed and internal display size get initialized. ++ * This does not happen otherwise as we don't draw in the widget ++ */ ++ gtk_gst_base_widget_queue_draw (GTK_GST_BASE_WIDGET (priv->gtk_widget)); ++ + GST_OBJECT_UNLOCK (self); + + priv->use_dmabuf = use_dmabuf; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-waylandsink-do-not-hardcode-dmabuf-bufferpool-info-s.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-waylandsink-do-not-hardcode-dmabuf-bufferpool-info-s.patch deleted file mode 100644 index 402234625..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-waylandsink-do-not-hardcode-dmabuf-bufferpool-info-s.patch +++ /dev/null @@ -1,46 +0,0 @@ -From f1e46e02aeac410a4bf1faae5a7dac62c0dfbe1d Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Wed, 12 Dec 2018 11:39:33 +0100 -Subject: [PATCH 04/14] waylandsink: do not hardcode dmabuf bufferpool info - size - -Read pool config to get the maximum number of buffers -for info size pool allocation. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/waylandpool.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -index 6e98143..1ba1f85 100644 ---- a/ext/wayland/waylandpool.c -+++ b/ext/wayland/waylandpool.c -@@ -357,11 +357,21 @@ gst_wayland_buffer_pool_start (GstBufferPool * pool) - int fd; - char filename[1024]; - static int init = 0; -+ GstStructure *config; -+ guint config_max_buf; - - GST_DEBUG_OBJECT (self, "Initializing wayland buffer pool"); - -+ /* get max size of pool */ -+ config = gst_buffer_pool_get_config (pool); -+ gst_buffer_pool_config_get_params (config, NULL, NULL, -+ NULL, &config_max_buf); -+ -+ if (config_max_buf == 0) -+ config_max_buf = 32; -+ - /* configure */ -- size = GST_VIDEO_INFO_SIZE (&self->info) * 15; -+ size = GST_VIDEO_INFO_SIZE (&self->info) * config_max_buf; - - /* allocate shm pool */ - snprintf (filename, 1024, "%s/%s-%d-%s", g_get_user_runtime_dir (), --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch new file mode 100644 index 000000000..a7d227c35 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch @@ -0,0 +1,51 @@ +From 9084a960a565ed80d34c41c7b0ab4f4a716a4f54 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Feb 2023 13:11:36 -0500 +Subject: [PATCH] waylandsink: Let the baseclass know when frames are dropped + +This is using the new GST_BASE_SINK_FLOW_DROPPED return value. +With this change, fpsdisplaysink will properly report the +render and dropped rate. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 2 ++ + ext/wayland/gstwaylandsink.c | 1 + + 2 files changed, 3 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 5288614bd7..acc32882ce 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1006,6 +1006,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + if (!priv->wl_window) { + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " dropped (waiting for window)", buffer); ++ ret = GST_BASE_SINK_FLOW_DROPPED; + goto done; + } + +@@ -1013,6 +1014,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + if (priv->redraw_pending) { + GST_LOG_OBJECT (self, "buffer %" GST_PTR_FORMAT " dropped (redraw pending)", + buffer); ++ ret = GST_BASE_SINK_FLOW_DROPPED; + goto done; + } + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 6dd0807820..bbbcb93932 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -773,6 +773,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + if (self->redraw_pending) { + GST_LOG_OBJECT (self, "buffer %" GST_PTR_FORMAT " dropped (redraw pending)", + buffer); ++ ret = GST_BASE_SINK_FLOW_DROPPED; + goto done; + } + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-increase-max-buffers-to-32-to-enable-dma.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-increase-max-buffers-to-32-to-enable-dma.patch deleted file mode 100644 index 209998a34..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-waylandsink-increase-max-buffers-to-32-to-enable-dma.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 837247993dd5399c0faa95950ab62bd1a1f7d6cb Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Thu, 6 Dec 2018 18:08:59 +0100 -Subject: [PATCH 05/14] waylandsink: increase max buffers to 32 to enable - dmabuf with libav decoders - -libav software decoders requires max buffers in pool config to be -at least 32, otherwise this pool is rejected. - -Upstream-Status: Inappropriate [DEY specific] - -Change-Id: If1a54ee8c1a9f320fd91537e8e2082c4a7a78700 ---- - ext/wayland/gstwaylandsink.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 20df1bf..8b807da 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -52,6 +52,9 @@ - #include - #include - -+#define CONFIG_DMABUF_MIN_BUFFERS 2 -+#define CONFIG_DMABUF_MAX_BUFFERS 32 -+ - /* signals */ - enum - { -@@ -574,8 +577,9 @@ gst_wayland_create_dmabuf_pool (GstWaylandSink * sink, GstCaps * caps) - } - - structure = gst_buffer_pool_get_config (pool); -- gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0); -- gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms); -+ gst_buffer_pool_config_set_params (structure, caps, size, -+ CONFIG_DMABUF_MIN_BUFFERS, CONFIG_DMABUF_MAX_BUFFERS); -+ - alloc = gst_dmabuf_allocator_new (); - gst_buffer_pool_config_set_allocator (structure, alloc, NULL); - if (!gst_buffer_pool_set_config (pool, structure)) { --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-Refactor-internal-pool-handling.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-Refactor-internal-pool-handling.patch new file mode 100644 index 000000000..0db5596e7 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-Refactor-internal-pool-handling.patch @@ -0,0 +1,349 @@ +From 79405fb8592f22d62b4faa2f7098b814cc2a0d49 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 16 Feb 2023 13:54:42 -0500 +Subject: [PATCH 06/17] waylandsink: Refactor internal pool handling + +This is to make it easier to support more then one allocators +including falling back from one to another. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 101 +++++++++++++++++++++-------------- + ext/wayland/gstwaylandsink.c | 101 +++++++++++++++++++++-------------- + ext/wayland/gstwaylandsink.h | 1 + + 3 files changed, 124 insertions(+), 79 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 1afad32..8967d97 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -109,6 +109,7 @@ typedef struct _GstGtkWaylandSinkPrivate + + gboolean video_info_changed; + GstVideoInfo video_info; ++ GstCaps *caps; + + gboolean redraw_pending; + GMutex render_lock; +@@ -200,6 +201,7 @@ gst_gtk_wayland_sink_finalize (GObject * object) + gst_gtk_wayland_sink_get_instance_private (self); + + g_clear_object (&priv->gtk_widget); ++ gst_clear_caps (&priv->caps); + + G_OBJECT_CLASS (parent_class)->finalize (object); + } +@@ -801,30 +803,57 @@ gst_gtk_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) + return caps; + } + +-static GstBufferPool * +-gst_gtk_wayland_create_pool (GstGtkWaylandSink * self, GstCaps * caps) ++static gboolean ++gst_gtk_wayland_update_pool (GstGtkWaylandSink * self, GstAllocator * allocator) + { + GstGtkWaylandSinkPrivate *priv = + gst_gtk_wayland_sink_get_instance_private (self); +- GstBufferPool *pool = NULL; +- GstStructure *structure; + gsize size = priv->video_info.size; +- GstAllocator *alloc; ++ GstStructure *config; + +- pool = gst_wl_video_buffer_pool_new (); ++ /* Pools with outstanding buffer cannot be reconfigured, so we must use ++ * a new pool. */ ++ if (priv->pool) { ++ gst_buffer_pool_set_active (priv->pool, FALSE); ++ gst_object_unref (priv->pool); ++ } ++ priv->pool = gst_wl_video_buffer_pool_new (); + +- structure = gst_buffer_pool_get_config (pool); +- gst_buffer_pool_config_set_params (structure, caps, size, 2, 0); ++ config = gst_buffer_pool_get_config (priv->pool); ++ gst_buffer_pool_config_set_params (config, priv->caps, size, 2, 0); ++ gst_buffer_pool_config_set_allocator (config, allocator, NULL); + +- alloc = gst_wl_shm_allocator_get (); +- gst_buffer_pool_config_set_allocator (structure, alloc, NULL); +- if (!gst_buffer_pool_set_config (pool, structure)) { +- g_object_unref (pool); +- pool = NULL; ++ if (!gst_buffer_pool_set_config (priv->pool, config)) ++ return FALSE; ++ ++ return gst_buffer_pool_set_active (priv->pool, TRUE); ++} ++ ++static gboolean ++gst_gtk_wayland_activate_shm_pool (GstGtkWaylandSink * self) ++{ ++ GstGtkWaylandSinkPrivate *priv = ++ gst_gtk_wayland_sink_get_instance_private (self); ++ GstAllocator *alloc = NULL; ++ ++ if (priv->pool && gst_buffer_pool_is_active (priv->pool)) { ++ GstStructure *config = gst_buffer_pool_get_config (priv->pool); ++ gboolean is_shm = FALSE; ++ ++ if (gst_buffer_pool_config_get_allocator (config, &alloc, NULL) && alloc) ++ is_shm = GST_IS_WL_SHM_ALLOCATOR (alloc); ++ ++ gst_structure_free (config); ++ ++ if (is_shm) ++ return TRUE; + } +- g_object_unref (alloc); + +- return pool; ++ alloc = gst_wl_shm_allocator_get (); ++ gst_gtk_wayland_update_pool (self, alloc); ++ gst_object_unref (alloc); ++ ++ return TRUE; + } + + static gboolean +@@ -845,10 +874,11 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + format = GST_VIDEO_INFO_FORMAT (&priv->video_info); + priv->video_info_changed = TRUE; + +- /* create a new pool for the new caps */ +- if (priv->pool) +- gst_object_unref (priv->pool); +- priv->pool = gst_gtk_wayland_create_pool (self, caps); ++ /* free pooled buffer used with previous caps */ ++ if (priv->pool) { ++ gst_buffer_pool_set_active (priv->pool, FALSE); ++ gst_clear_object (&priv->pool); ++ } + + use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), + GST_CAPS_FEATURE_MEMORY_DMABUF); +@@ -884,6 +914,8 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + GST_OBJECT_UNLOCK (self); + + priv->use_dmabuf = use_dmabuf; ++ /* Will be used to create buffer pools */ ++ gst_caps_replace (&priv->caps, caps); + + return TRUE; + +@@ -914,8 +946,14 @@ gst_gtk_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + + gst_query_parse_allocation (query, &caps, &need_pool); + +- if (need_pool) +- pool = gst_gtk_wayland_create_pool (self, caps); ++ if (need_pool) { ++ GstStructure *config; ++ pool = gst_wl_video_buffer_pool_new (); ++ config = gst_buffer_pool_get_config (pool); ++ gst_buffer_pool_config_set_allocator (config, ++ gst_wl_shm_allocator_get (), NULL); ++ gst_buffer_pool_set_config (pool, config); ++ } + + gst_query_add_allocation_pool (query, pool, priv->video_info.size, 2, 0); + if (pool) +@@ -1077,25 +1115,10 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + "buffer %" GST_PTR_FORMAT " cannot have a wl_buffer, " + "copying to wl_shm memory", buffer); + +- /* priv->pool always exists (created in set_caps), but it may not +- * be active if upstream is not using it */ +- if (!gst_buffer_pool_is_active (priv->pool)) { +- GstStructure *config; +- GstCaps *caps; + +- config = gst_buffer_pool_get_config (priv->pool); +- gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL); +- +- /* revert back to default strides and offsets */ +- gst_video_info_from_caps (&priv->video_info, caps); +- gst_buffer_pool_config_set_params (config, caps, priv->video_info.size, +- 2, 0); +- +- /* This is a video pool, it should not fail with basic settings */ +- if (!gst_buffer_pool_set_config (priv->pool, config) || +- !gst_buffer_pool_set_active (priv->pool, TRUE)) +- goto activate_failed; +- } ++ /* ensure the internal pool is configured for SHM */ ++ if (!gst_gtk_wayland_activate_shm_pool (self)) ++ goto activate_failed; + + ret = gst_buffer_pool_acquire_buffer (priv->pool, &to_render, NULL); + if (ret != GST_FLOW_OK) +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 7077ee9..b9b6267 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -315,6 +315,8 @@ gst_wayland_sink_finalize (GObject * object) + if (self->pool) + gst_object_unref (self->pool); + ++ gst_clear_caps (&self->caps); ++ + g_free (self->display_name); + + g_mutex_clear (&self->display_lock); +@@ -578,28 +580,53 @@ gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) + return caps; + } + +-static GstBufferPool * +-gst_wayland_create_pool (GstWaylandSink * self, GstCaps * caps) ++static gboolean ++gst_wayland_update_pool (GstWaylandSink * self, GstAllocator * allocator) + { +- GstBufferPool *pool = NULL; +- GstStructure *structure; + gsize size = self->video_info.size; +- GstAllocator *alloc; ++ GstStructure *config; + +- pool = gst_wl_video_buffer_pool_new (); ++ /* Pools with outstanding buffer cannot be reconfigured, so we must use ++ * a new pool. */ ++ if (self->pool) { ++ gst_buffer_pool_set_active (self->pool, FALSE); ++ gst_object_unref (self->pool); ++ } ++ self->pool = gst_wl_video_buffer_pool_new (); + +- structure = gst_buffer_pool_get_config (pool); +- gst_buffer_pool_config_set_params (structure, caps, size, 2, 0); ++ config = gst_buffer_pool_get_config (self->pool); ++ gst_buffer_pool_config_set_params (config, self->caps, size, 2, 0); ++ gst_buffer_pool_config_set_allocator (config, allocator, NULL); + +- alloc = gst_wl_shm_allocator_get (); +- gst_buffer_pool_config_set_allocator (structure, alloc, NULL); +- if (!gst_buffer_pool_set_config (pool, structure)) { +- g_object_unref (pool); +- pool = NULL; ++ if (!gst_buffer_pool_set_config (self->pool, config)) ++ return FALSE; ++ ++ return gst_buffer_pool_set_active (self->pool, TRUE); ++} ++ ++static gboolean ++gst_wayland_activate_shm_pool (GstWaylandSink * self) ++{ ++ GstAllocator *alloc = NULL; ++ ++ if (self->pool && gst_buffer_pool_is_active (self->pool)) { ++ GstStructure *config = gst_buffer_pool_get_config (self->pool); ++ gboolean is_shm = FALSE; ++ ++ if (gst_buffer_pool_config_get_allocator (config, &alloc, NULL) && alloc) ++ is_shm = GST_IS_WL_SHM_ALLOCATOR (alloc); ++ ++ gst_structure_free (config); ++ ++ if (is_shm) ++ return TRUE; + } +- g_object_unref (alloc); + +- return pool; ++ alloc = gst_wl_shm_allocator_get (); ++ gst_wayland_update_pool (self, alloc); ++ gst_object_unref (alloc); ++ ++ return TRUE; + } + + static gboolean +@@ -618,10 +645,11 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + format = GST_VIDEO_INFO_FORMAT (&self->video_info); + self->video_info_changed = TRUE; + +- /* create a new pool for the new caps */ +- if (self->pool) +- gst_object_unref (self->pool); +- self->pool = gst_wayland_create_pool (self, caps); ++ /* free pooled buffer used with previous caps */ ++ if (self->pool) { ++ gst_buffer_pool_set_active (self->pool, FALSE); ++ gst_clear_object (&self->pool); ++ } + + use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), + GST_CAPS_FEATURE_MEMORY_DMABUF); +@@ -639,6 +667,9 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + + self->use_dmabuf = use_dmabuf; + ++ /* Will be used to create buffer pools */ ++ gst_caps_replace (&self->caps, caps); ++ + return TRUE; + + invalid_format: +@@ -666,8 +697,14 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + + gst_query_parse_allocation (query, &caps, &need_pool); + +- if (need_pool) +- pool = gst_wayland_create_pool (self, caps); ++ if (need_pool) { ++ GstStructure *config; ++ pool = gst_wl_video_buffer_pool_new (); ++ config = gst_buffer_pool_get_config (pool); ++ gst_buffer_pool_config_set_allocator (config, ++ gst_wl_shm_allocator_get (), NULL); ++ gst_buffer_pool_set_config (pool, config); ++ } + + gst_query_add_allocation_pool (query, pool, self->video_info.size, 2, 0); + if (pool) +@@ -844,25 +881,9 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + "buffer %" GST_PTR_FORMAT " cannot have a wl_buffer, " + "copying to wl_shm memory", buffer); + +- /* self->pool always exists (created in set_caps), but it may not +- * be active if upstream is not using it */ +- if (!gst_buffer_pool_is_active (self->pool)) { +- GstStructure *config; +- GstCaps *caps; +- +- config = gst_buffer_pool_get_config (self->pool); +- gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL); +- +- /* revert back to default strides and offsets */ +- gst_video_info_from_caps (&self->video_info, caps); +- gst_buffer_pool_config_set_params (config, caps, self->video_info.size, +- 2, 0); +- +- /* This is a video pool, it should not fail with basic settings */ +- if (!gst_buffer_pool_set_config (self->pool, config) || +- !gst_buffer_pool_set_active (self->pool, TRUE)) +- goto activate_failed; +- } ++ /* ensure the internal pool is configured for SHM */ ++ if (!gst_wayland_activate_shm_pool (self)) ++ goto activate_failed; + + ret = gst_buffer_pool_acquire_buffer (self->pool, &to_render, NULL); + if (ret != GST_FLOW_OK) +diff --git a/ext/wayland/gstwaylandsink.h b/ext/wayland/gstwaylandsink.h +index 3243d8c..1ff50d1 100644 +--- a/ext/wayland/gstwaylandsink.h ++++ b/ext/wayland/gstwaylandsink.h +@@ -56,6 +56,7 @@ struct _GstWaylandSink + gboolean video_info_changed; + GstVideoInfo video_info; + gboolean fullscreen; ++ GstCaps *caps; + + gchar *display_name; + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-always-select-dmabuf-buffer-pool.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-always-select-dmabuf-buffer-pool.patch deleted file mode 100644 index 886ae46c4..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-waylandsink-always-select-dmabuf-buffer-pool.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 36a3da1477d9dd5c23b0c47fd45e61a8c0316600 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Thu, 6 Dec 2018 18:09:20 +0100 -Subject: [PATCH 06/14] waylandsink: always select dmabuf buffer pool - -Force usage of dmabuf buffer pool by default, ie -even if video/x-raw(memory:DMABuf) is not set in caps. -This allows 0-copy path with software downstream elements -(use of dmabuf buffers mmapped memory directly instead -of copying). - -Upstream-Status: Inappropriate [DEY specific] - -Change-Id: Icb6e8ff272c562e829bb3ff91150e7d77b25dfd1 -Signed-off-by: Hugues Fruchet ---- - ext/wayland/gstwaylandsink.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 8b807da..40edff7 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -610,9 +610,17 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) - format = GST_VIDEO_INFO_FORMAT (&sink->video_info); - sink->video_info_changed = TRUE; - -- use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), -- GST_CAPS_FEATURE_MEMORY_DMABUF); -+ /* Force usage of dmabuf buffer pool by default, ie -+ * even if video/x-raw(memory:DMABuf) is not set in caps. -+ * This allows 0-copy path with software downstream elements -+ * (use of dmabuf buffers mmapped memory directly instead -+ * of copying) -+ */ -+ /* use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), -+ GST_CAPS_FEATURE_MEMORY_DMABUF); */ -+ use_dmabuf = TRUE; - -+ /* validate the format base on the memory type */ - if (use_dmabuf) { - if (!gst_wl_display_check_format_for_dmabuf (sink->display, format)) - goto unsupported_format; --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch new file mode 100644 index 000000000..278f6d12b --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch @@ -0,0 +1,31 @@ +From 60b5f9809703c6668e566a8133b000ec2c3c0dfc Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 16 Feb 2023 14:31:10 -0500 +Subject: [PATCH 07/17] gtkwaylandsink: Fix display/wl_window/pool leaks + +These were leaked in the GTK implementation of the sink. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 8967d97..0c74a91 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -200,6 +200,10 @@ gst_gtk_wayland_sink_finalize (GObject * object) + GstGtkWaylandSinkPrivate *priv = + gst_gtk_wayland_sink_get_instance_private (self); + ++ g_clear_object (&priv->display); ++ g_clear_object (&priv->wl_window); ++ g_clear_object (&priv->pool); ++ + g_clear_object (&priv->gtk_widget); + gst_clear_caps (&priv->caps); + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-waylandsink-do-not-destroy-pool-twice.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-waylandsink-do-not-destroy-pool-twice.patch deleted file mode 100644 index 79e365984..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-waylandsink-do-not-destroy-pool-twice.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 15464502cf5851e29fa5da9c6e1547655865de21 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Mon, 10 Dec 2018 17:36:27 +0100 -Subject: [PATCH 07/14] waylandsink: do not destroy pool twice - -Fix segfault in wayland client due to pool -destroy being called with null pool... -This problem was encountered with GStreamer-1.14. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/waylandpool.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -index 1ba1f85..17a94f1 100644 ---- a/ext/wayland/waylandpool.c -+++ b/ext/wayland/waylandpool.c -@@ -417,7 +417,12 @@ gst_wayland_buffer_pool_stop (GstBufferPool * pool) - - GST_DEBUG_OBJECT (self, "Stopping wayland buffer pool"); - -+ /* already stopped... */ -+ if (!self->wl_pool) -+ return FALSE; -+ - munmap (self->data, self->size); -+ - wl_shm_pool_destroy (self->wl_pool); - - self->wl_pool = NULL; --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch new file mode 100644 index 000000000..46957e332 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch @@ -0,0 +1,174 @@ +From 38ee1f8e97429506db0ca9c0c2ac61da4f00ba5d Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 16 Feb 2023 21:12:08 -0500 +Subject: [PATCH 08/17] wllinuxdmabuf: Handle video meta inside the importer + +This allow simplifying the GstVideoInfo handling in the sinks. Instead +of having to update a video info for the import, the sink can simply pass the +video info associated with the caps and rely on the VideoMeta in the GstBuffer +to obtain the appropriate offset and stride. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 22 ++++------------------ + ext/wayland/gstwaylandsink.c | 18 ++---------------- + gst-libs/gst/wayland/gstwllinuxdmabuf.c | 13 +++++++++++-- + 3 files changed, 17 insertions(+), 36 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 0c74a91..c46207c 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1028,9 +1028,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + gst_gtk_wayland_sink_get_instance_private (self); + GstBuffer *to_render; + GstWlBuffer *wlbuffer; +- GstVideoMeta *vmeta; + GstVideoFormat format; +- GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -1073,23 +1071,11 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + +- src_vinfo = priv->video_info; +- vmeta = gst_buffer_get_video_meta (buffer); +- if (vmeta) { +- gint i; +- +- for (i = 0; i < vmeta->n_planes; i++) { +- src_vinfo.offset[i] = vmeta->offset[i]; +- src_vinfo.stride[i] = vmeta->stride[i]; +- } +- src_vinfo.size = gst_buffer_get_size (buffer); +- } +- + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " + "display, creating it", buffer); + +- format = GST_VIDEO_INFO_FORMAT (&src_vinfo); ++ format = GST_VIDEO_INFO_FORMAT (&priv->video_info); + if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) { + guint i, nb_dmabuf = 0; + +@@ -1099,13 +1085,13 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, +- &src_vinfo); ++ &priv->video_info); + } + + if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { + if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, +- &src_vinfo); ++ &priv->video_info); + + /* If nothing worked, copy into our internal pool */ + if (!wbuf) { +@@ -1146,7 +1132,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index b9b6267..aa39447 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -779,9 +779,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GstWaylandSink *self = GST_WAYLAND_SINK (vsink); + GstBuffer *to_render; + GstWlBuffer *wlbuffer; +- GstVideoMeta *vmeta; + GstVideoFormat format; +- GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -835,18 +833,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + +- src_vinfo = self->video_info; +- vmeta = gst_buffer_get_video_meta (buffer); +- if (vmeta) { +- gint i; +- +- for (i = 0; i < vmeta->n_planes; i++) { +- src_vinfo.offset[i] = vmeta->offset[i]; +- src_vinfo.stride[i] = vmeta->stride[i]; +- } +- src_vinfo.size = gst_buffer_get_size (buffer); +- } +- + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " + "display, creating it", buffer); +@@ -861,7 +847,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, +- &src_vinfo); ++ &self->video_info); + } + + if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) { +@@ -907,7 +893,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +index d6ee6ec..4b33d05 100644 +--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c ++++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +@@ -88,6 +88,9 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + GstMemory *mem; + int format; + guint i, width, height; ++ const gsize *offsets = info->offset; ++ const gint *strides = info->stride; ++ GstVideoMeta *vmeta; + guint nplanes, flags = 0; + struct zwp_linux_buffer_params_v1 *params; + gint64 timeout; +@@ -107,6 +110,12 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + height = GST_VIDEO_INFO_HEIGHT (info); + nplanes = GST_VIDEO_INFO_N_PLANES (info); + ++ vmeta = gst_buffer_get_video_meta (buf); ++ if (vmeta) { ++ offsets = vmeta->offset; ++ strides = vmeta->stride; ++ } ++ + GST_DEBUG_OBJECT (display, "Creating wl_buffer from DMABuf of size %" + G_GSSIZE_FORMAT " (%d x %d), format %s", info->size, width, height, + gst_wl_dmabuf_format_to_string (format)); +@@ -119,8 +128,8 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + guint offset, stride, mem_idx, length; + gsize skip; + +- offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); +- stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i); ++ offset = offsets[i]; ++ stride = strides[i]; + if (gst_buffer_find_memory (buf, offset, 1, &mem_idx, &length, &skip)) { + GstMemory *m = gst_buffer_peek_memory (buf, mem_idx); + gint fd = gst_dmabuf_memory_get_fd (m); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-waylandsink-Uprank-to-secondary.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-waylandsink-Uprank-to-secondary.patch deleted file mode 100644 index 992f9eb83..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-waylandsink-Uprank-to-secondary.patch +++ /dev/null @@ -1,28 +0,0 @@ -From cf2dfe7a376d10a3d7815d29ffc1c9efb4d4f19e Mon Sep 17 00:00:00 2001 -From: Christophe Priouzeau -Date: Mon, 21 May 2018 14:26:47 +0200 -Subject: [PATCH 09/14] waylandsink: Uprank to secondary - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Christophe Priouzeau ---- - ext/wayland/gstwaylandsink.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index b97570e..0dc1f0d 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -126,7 +126,7 @@ G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK, - gst_wayland_sink_videooverlay_init) - G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO, - gst_wayland_sink_waylandvideo_init)); --GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_MARGINAL, -+GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_SECONDARY+1, - GST_TYPE_WAYLAND_SINK); - - /* A tiny GstVideoBufferPool subclass that modify the options to remove --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch new file mode 100644 index 000000000..8b8d53475 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch @@ -0,0 +1,93 @@ +From d9b34f66193a5a4ac60df33ec80e0da76d197fe4 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Fri, 17 Feb 2023 09:42:42 -0500 +Subject: [PATCH 09/17] wlvideoformat: Fix sign issue for DRM fourcc + +DRM fourcc ared defined as 32bit unsigned in, but the format helper was passing +an int, while using a unsigned int internally. This is a API/ABI break, but +the API is still unstable. + +Part-of: +Upstream-Status: Backport +--- + gst-libs/gst/wayland/gstwldisplay.c | 2 +- + gst-libs/gst/wayland/gstwllinuxdmabuf.c | 4 ++-- + gst-libs/gst/wayland/gstwlvideoformat.c | 6 +++--- + gst-libs/gst/wayland/gstwlvideoformat.h | 2 +- + 4 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/gst-libs/gst/wayland/gstwldisplay.c b/gst-libs/gst/wayland/gstwldisplay.c +index 1f83cb2..a9da274 100644 +--- a/gst-libs/gst/wayland/gstwldisplay.c ++++ b/gst-libs/gst/wayland/gstwldisplay.c +@@ -226,7 +226,7 @@ gst_wl_display_check_format_for_dmabuf (GstWlDisplay * self, + return FALSE; + + dmabuf_fmt = gst_video_format_to_wl_dmabuf_format (format); +- if (dmabuf_fmt == (guint) - 1) ++ if (!dmabuf_fmt) + return FALSE; + + formats = priv->dmabuf_formats; +diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +index 4b33d05..deb5d32 100644 +--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c ++++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +@@ -175,8 +175,8 @@ out: + GST_ERROR_OBJECT (mem->allocator, "can't create linux-dmabuf buffer"); + } else { + GST_DEBUG_OBJECT (mem->allocator, "created linux_dmabuf wl_buffer (%p):" +- "%dx%d, fmt=%.4s, %d planes", +- data.wbuf, width, height, (char *) &format, nplanes); ++ "%dx%d, fmt=%" GST_FOURCC_FORMAT ", %d planes", ++ data.wbuf, width, height, GST_FOURCC_ARGS (format), nplanes); + } + + g_mutex_unlock (&data.lock); +diff --git a/gst-libs/gst/wayland/gstwlvideoformat.c b/gst-libs/gst/wayland/gstwlvideoformat.c +index 44a9536..49d927a 100644 +--- a/gst-libs/gst/wayland/gstwlvideoformat.c ++++ b/gst-libs/gst/wayland/gstwlvideoformat.c +@@ -48,7 +48,7 @@ gst_wl_videoformat_init_once (void) + typedef struct + { + enum wl_shm_format wl_shm_format; +- guint dma_format; ++ guint32 dma_format; + GstVideoFormat gst_format; + } wl_VideoFormat; + +@@ -96,7 +96,7 @@ gst_video_format_to_wl_shm_format (GstVideoFormat format) + return -1; + } + +-gint ++guint32 + gst_video_format_to_wl_dmabuf_format (GstVideoFormat format) + { + guint i; +@@ -106,7 +106,7 @@ gst_video_format_to_wl_dmabuf_format (GstVideoFormat format) + return wl_formats[i].dma_format; + + GST_WARNING ("wayland dmabuf video format not found"); +- return -1; ++ return 0; + } + + GstVideoFormat +diff --git a/gst-libs/gst/wayland/gstwlvideoformat.h b/gst-libs/gst/wayland/gstwlvideoformat.h +index bc36a08..bbacde3 100644 +--- a/gst-libs/gst/wayland/gstwlvideoformat.h ++++ b/gst-libs/gst/wayland/gstwlvideoformat.h +@@ -36,7 +36,7 @@ GST_WL_API + enum wl_shm_format gst_video_format_to_wl_shm_format (GstVideoFormat format); + + GST_WL_API +-gint gst_video_format_to_wl_dmabuf_format (GstVideoFormat format); ++guint32 gst_video_format_to_wl_dmabuf_format (GstVideoFormat format); + + GST_WL_API + GstVideoFormat gst_wl_shm_format_to_video_format (enum wl_shm_format wl_format); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-waylandsink-set-video-alignment-to-32-bytes.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-waylandsink-set-video-alignment-to-32-bytes.patch deleted file mode 100644 index 845d5d524..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-waylandsink-set-video-alignment-to-32-bytes.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 9eb8fcc53467a575dc3fba3bbeae26ed8752e639 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Mon, 11 Feb 2019 16:00:02 +0100 -Subject: [PATCH 10/14] waylandsink: set video alignment to 32 bytes - -Display subsystem requires at least 32 bytes alignment. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/waylandpool.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/ext/wayland/waylandpool.c b/ext/wayland/waylandpool.c -index 17a94f1..e04a459 100644 ---- a/ext/wayland/waylandpool.c -+++ b/ext/wayland/waylandpool.c -@@ -635,6 +635,7 @@ gst_wayland_buffer_pool_new (GstWlDisplay * display) - GstWaylandBufferPool *pool; - GstStructure *s; - GstVideoAlignment align; -+ guint i; - - g_return_val_if_fail (GST_IS_WL_DISPLAY (display), NULL); - pool = g_object_new (GST_TYPE_WAYLAND_BUFFER_POOL, NULL); -@@ -645,8 +646,12 @@ gst_wayland_buffer_pool_new (GstWlDisplay * display) - s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool)); - gst_buffer_pool_config_add_option (s, GST_BUFFER_POOL_OPTION_VIDEO_META); - gst_buffer_pool_config_add_option (s, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); -- /* reset alignment */ -+ -+ /* display subsystem requires at least 32 bytes alignment */ - gst_video_alignment_reset (&align); -+ for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) -+ align.stride_align[i] = 31; -+ - gst_buffer_pool_config_set_video_alignment (s, &align); - gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pool), s); - --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch new file mode 100644 index 000000000..bc022cb56 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch @@ -0,0 +1,268 @@ +From c5c879049ee10ff0f1a54b90cbb317bca09933a6 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 13:09:09 +0200 +Subject: [PATCH 10/17] wlvideobufferpool: Add DRM Dumb buffer support + +This allow the wayland buffer pool to use a GstDRMDumbAllocator +if it has been configured to do so. + +Part-of: +Upstream-Status: Backport +--- + gst-libs/gst/wayland/gstwlvideobufferpool.c | 200 +++++++++++++++++++- + gst-libs/gst/wayland/gstwlvideobufferpool.h | 10 +- + 2 files changed, 200 insertions(+), 10 deletions(-) + +diff --git a/gst-libs/gst/wayland/gstwlvideobufferpool.c b/gst-libs/gst/wayland/gstwlvideobufferpool.c +index b367d9f..3292056 100644 +--- a/gst-libs/gst/wayland/gstwlvideobufferpool.c ++++ b/gst-libs/gst/wayland/gstwlvideobufferpool.c +@@ -24,8 +24,198 @@ + + #include "gstwlvideobufferpool.h" + +-G_DEFINE_TYPE (GstWlVideoBufferPool, gst_wl_video_buffer_pool, +- GST_TYPE_VIDEO_BUFFER_POOL); ++#include ++#include ++ ++GST_DEBUG_CATEGORY (gst_wl_videobufferpool_debug); ++#define GST_CAT_DEFAULT gst_wl_videobufferpool_debug ++ ++struct _GstWlVideoBufferPool ++{ ++ GstVideoBufferPool parent; ++ GstAllocator *allocator; ++ GstVideoInfo vinfo; ++}; ++ ++#define gst_wl_video_buffer_pool_parent_class parent_class ++G_DEFINE_TYPE_WITH_CODE (GstWlVideoBufferPool, gst_wl_video_buffer_pool, ++ GST_TYPE_VIDEO_BUFFER_POOL, ++ GST_DEBUG_CATEGORY_INIT (gst_wl_videobufferpool_debug, ++ "wl_videobufferpool", 0, "wl_dmabuf library")); ++ ++static void ++gst_wl_update_video_info_from_pitch (GstVideoInfo * vinfo, gint n_planes, ++ guint32 pitch) ++{ ++ gint i; ++ gsize offs = 0; ++ guint32 height = GST_VIDEO_INFO_HEIGHT (vinfo); ++ ++ if (!pitch) ++ return; ++ ++ for (i = 0; i < n_planes; i++) { ++ guint32 plane_pitch; ++ ++ /* Overwrite the video info's stride and offset using the pitch calculcated ++ * by the kms driver. */ ++ plane_pitch = gst_video_format_info_extrapolate_stride (vinfo->finfo, i, ++ pitch); ++ GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = plane_pitch; ++ GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = offs; ++ ++ /* Note that we cannot negotiate special padding betweem each planes, ++ * hence using the display height here. */ ++ offs += plane_pitch ++ * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, height); ++ ++ GST_DEBUG ("Updated plane %i with stride %i and " ++ "offset %" G_GSIZE_FORMAT "(from pitch %u)", i, ++ GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i), ++ GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i), pitch); ++ } ++ ++ /* Update with the size use for display, excluding any padding at the end */ ++ GST_VIDEO_INFO_SIZE (vinfo) = offs; ++} ++ ++static gboolean ++gst_wl_video_buffer_pool_start (GstBufferPool * pool) ++{ ++ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool); ++ GstStructure *config = gst_buffer_pool_get_config (pool); ++ GstCaps *caps; ++ GstAllocator *allocator; ++ gboolean ret = FALSE; ++ GstVideoFormat format; ++ ++ if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) ++ goto wrong_config; ++ ++ if (!gst_buffer_pool_config_get_allocator (config, &allocator, NULL)) ++ goto wrong_config; ++ ++ /* now parse the caps from the config */ ++ if (!gst_video_info_from_caps (&self->vinfo, caps)) ++ goto wrong_caps; ++ ++ format = GST_VIDEO_INFO_FORMAT (&self->vinfo); ++ if (!gst_video_format_to_wl_dmabuf_format (format)) ++ goto unsupported_pixel_format; ++ ++ if (GST_IS_DRM_DUMB_ALLOCATOR (allocator)) { ++ if (!gst_drm_dumb_allocator_has_prime_export (allocator)) ++ goto no_prime_export; ++ ++ self->allocator = gst_object_ref (allocator); ++ } ++ ++ /* all good */ ++ ret = GST_BUFFER_POOL_CLASS (parent_class)->start (pool); ++ ++done: ++ gst_structure_free (config); ++ return ret; ++ ++wrong_config: ++ { ++ GST_WARNING_OBJECT (pool, "invalid config"); ++ goto done; ++ } ++wrong_caps: ++ { ++ GST_WARNING_OBJECT (pool, ++ "failed getting geometry from caps %" GST_PTR_FORMAT, caps); ++ goto done; ++ } ++unsupported_pixel_format: ++ { ++ GST_WARNING_OBJECT (pool, "no support for %s pixel format", ++ gst_video_format_to_string (format)); ++ goto done; ++ } ++no_prime_export: ++ { ++ GST_WARNING_OBJECT (self, ++ "not using DRM Dumb allocator as it can't export DMABuf."); ++ goto done; ++ } ++} ++ ++static gboolean ++gst_wl_video_buffer_pool_stop (GstBufferPool * pool) ++{ ++ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool); ++ gst_clear_object (&self->allocator); ++ gst_video_info_init (&self->vinfo); ++ return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool); ++} ++ ++static GstFlowReturn ++gst_wl_video_buffer_pool_alloc_buffer (GstBufferPool * pool, ++ GstBuffer ** buffer, GstBufferPoolAcquireParams * params) ++{ ++ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool); ++ guint32 pitch; ++ GstBuffer *buf; ++ GstMemory *drm_mem, *dma_mem; ++ gsize dumbbuf_size; ++ GstVideoFormat format = GST_VIDEO_INFO_FORMAT (&self->vinfo); ++ guint32 drm_fourcc = gst_video_format_to_wl_dmabuf_format (format); ++ ++ if (!self->allocator) ++ return GST_BUFFER_POOL_CLASS (parent_class)->alloc_buffer (pool, buffer, ++ params); ++ ++ drm_mem = gst_drm_dumb_allocator_alloc (self->allocator, ++ drm_fourcc, GST_VIDEO_INFO_WIDTH (&self->vinfo), ++ GST_VIDEO_INFO_HEIGHT (&self->vinfo), &pitch); ++ if (!drm_mem) ++ goto alloc_failed; ++ ++ dma_mem = gst_drm_dumb_memory_export_dmabuf (drm_mem); ++ ++ gst_memory_unref (drm_mem); ++ drm_mem = NULL; ++ ++ gst_wl_update_video_info_from_pitch (&self->vinfo, ++ GST_VIDEO_INFO_N_PLANES (&self->vinfo), pitch); ++ ++ gst_memory_get_sizes (dma_mem, NULL, &dumbbuf_size); ++ if (dumbbuf_size < GST_VIDEO_INFO_SIZE (&self->vinfo)) { ++ GST_ERROR_OBJECT (self, ++ "DUMB buffer has a size of %" G_GSIZE_FORMAT ++ " but we require at least %" G_GSIZE_FORMAT " to hold a frame", ++ dumbbuf_size, GST_VIDEO_INFO_SIZE (&self->vinfo)); ++ goto buffer_too_small; ++ } ++ ++ gst_memory_resize (dma_mem, 0, GST_VIDEO_INFO_SIZE (&self->vinfo)); ++ ++ buf = gst_buffer_new (); ++ gst_buffer_append_memory (buf, dma_mem); ++ gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, ++ GST_VIDEO_INFO_FORMAT (&self->vinfo), GST_VIDEO_INFO_WIDTH (&self->vinfo), ++ GST_VIDEO_INFO_HEIGHT (&self->vinfo), ++ GST_VIDEO_INFO_N_PLANES (&self->vinfo), ++ self->vinfo.offset, self->vinfo.stride); ++ ++ *buffer = buf; ++ ++ return GST_FLOW_OK; ++ ++ /* ERRORS */ ++alloc_failed: ++ { ++ GST_ERROR_OBJECT (self, "failed to allocate DRM Dumb buffer."); ++ return GST_FLOW_ERROR; ++ } ++buffer_too_small: ++ { ++ GST_ERROR_OBJECT (self, "dumb buffer too small to store an image."); ++ return GST_FLOW_ERROR; ++ } ++} + + static const gchar ** + gst_wl_video_buffer_pool_get_options (GstBufferPool * pool) +@@ -39,11 +229,15 @@ gst_wl_video_buffer_pool_class_init (GstWlVideoBufferPoolClass * klass) + { + GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass); + pool_class->get_options = gst_wl_video_buffer_pool_get_options; ++ pool_class->start = gst_wl_video_buffer_pool_start; ++ pool_class->stop = gst_wl_video_buffer_pool_stop; ++ pool_class->alloc_buffer = gst_wl_video_buffer_pool_alloc_buffer; + } + + static void +-gst_wl_video_buffer_pool_init (GstWlVideoBufferPool * pool) ++gst_wl_video_buffer_pool_init (GstWlVideoBufferPool * self) + { ++ gst_video_info_init (&self->vinfo); + } + + GstBufferPool * +diff --git a/gst-libs/gst/wayland/gstwlvideobufferpool.h b/gst-libs/gst/wayland/gstwlvideobufferpool.h +index 297afbc..fe04186 100644 +--- a/gst-libs/gst/wayland/gstwlvideobufferpool.h ++++ b/gst-libs/gst/wayland/gstwlvideobufferpool.h +@@ -26,20 +26,16 @@ + + G_BEGIN_DECLS + +-/* A tiny GstVideoBufferPool subclass that modify the options to remove ++/* A GstVideoBufferPool subclass that modify the options to remove + * VideoAlignment. To support VideoAlignment we would need to pass the padded + * width/height + stride and use the viewporter interface to crop, a bit like + * we use to do with XV. It would still be quite limited. It's a bit retro, +- * hopefully there will be a better Wayland interface in the future. */ ++ * hopefully there will be a better Wayland interface in the future. This buffer ++ * pool also support GstDRMDumbAllocator. */ + + #define GST_TYPE_WL_VIDEO_BUFFER_POOL (gst_wl_video_buffer_pool_get_type ()) + G_DECLARE_FINAL_TYPE (GstWlVideoBufferPool, gst_wl_video_buffer_pool, GST, WL_VIDEO_BUFFER_POOL, GstVideoBufferPool); + +-struct _GstWlVideoBufferPool +-{ +- GstVideoBufferPool parent; +-}; +- + GST_WL_API + GstBufferPool * gst_wl_video_buffer_pool_new (void); + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch new file mode 100644 index 000000000..e5dc51fb3 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch @@ -0,0 +1,412 @@ +From d5807fc0840117a8472922d69df76ddebf633325 Mon Sep 17 00:00:00 2001 +From: Colin Kinloch +Date: Thu, 16 Feb 2023 16:21:33 -0500 +Subject: [PATCH 11/17] wayladnsink: Add DRM Dumb allocator support + +If the input is not a DMABuf, attempt to copy into a DRM Dumb +buffer and import it has a DMABuf. This will offload the +compositor from actually doing this copy (needed to handle SHM) +and may allow the software decoded stream to be rendered to +an HW layer, or even reach through some better accelerated +GL import path. + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 121 ++++++++++++++++++++++++++++++++++- + ext/wayland/gstwaylandsink.c | 110 +++++++++++++++++++++++++++++++ + ext/wayland/gstwaylandsink.h | 3 + + 3 files changed, 233 insertions(+), 1 deletion(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index c46207c..438294b 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -87,7 +87,8 @@ enum + PROP_0, + PROP_WIDGET, + PROP_DISPLAY, +- PROP_ROTATE_METHOD ++ PROP_ROTATE_METHOD, ++ PROP_DRM_DEVICE, + }; + + typedef struct _GstGtkWaylandSinkPrivate +@@ -119,6 +120,9 @@ typedef struct _GstGtkWaylandSinkPrivate + GstVideoOrientationMethod current_rotate_method; + + struct wl_callback *callback; ++ ++ gchar *drm_device; ++ gboolean skip_dumb_buffer_copy; + } GstGtkWaylandSinkPrivate; + + #define gst_gtk_wayland_sink_parent_class parent_class +@@ -161,6 +165,17 @@ gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass) + GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + ++ /** ++ * GstGtkWaylandSink:drm-device: ++ * ++ * Since: 1.24 ++ */ ++ g_object_class_install_property (gobject_class, PROP_DRM_DEVICE, ++ g_param_spec_string ("drm-device", "DRM Device", "Path of the " ++ "DRM device to use for dumb buffer allocation", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); ++ + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_gtk_wayland_sink_change_state); + +@@ -207,6 +222,8 @@ gst_gtk_wayland_sink_finalize (GObject * object) + g_clear_object (&priv->gtk_widget); + gst_clear_caps (&priv->caps); + ++ g_free (priv->drm_device); ++ + G_OBJECT_CLASS (parent_class)->finalize (object); + } + +@@ -379,6 +396,11 @@ gst_gtk_wayland_sink_get_property (GObject * object, guint prop_id, + case PROP_ROTATE_METHOD: + g_value_set_enum (value, priv->current_rotate_method); + break; ++ case PROP_DRM_DEVICE: ++ GST_OBJECT_LOCK (self); ++ g_value_set_string (value, priv->drm_device); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -390,12 +412,19 @@ gst_gtk_wayland_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) + { + GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object); ++ GstGtkWaylandSinkPrivate *priv = ++ gst_gtk_wayland_sink_get_instance_private (self); + + switch (prop_id) { + case PROP_ROTATE_METHOD: + gst_gtk_wayland_sink_set_rotate_method (self, g_value_get_enum (value), + FALSE); + break; ++ case PROP_DRM_DEVICE: ++ GST_OBJECT_LOCK (self); ++ priv->drm_device = g_value_dup_string (value); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -710,6 +739,8 @@ gst_gtk_wayland_sink_change_state (GstElement * element, + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: ++ g_clear_object (&priv->pool); ++ /* fallthrough */ + case GST_STATE_CHANGE_NULL_TO_NULL: + gst_gtk_invoke_on_main ((GThreadFunc) + gst_gtk_wayland_sink_stop_on_main, element); +@@ -860,6 +891,41 @@ gst_gtk_wayland_activate_shm_pool (GstGtkWaylandSink * self) + return TRUE; + } + ++static gboolean ++gst_gtk_wayland_activate_drm_dumb_pool (GstGtkWaylandSink * self) ++{ ++ GstGtkWaylandSinkPrivate *priv = ++ gst_gtk_wayland_sink_get_instance_private (self); ++ GstAllocator *alloc; ++ ++ if (!priv->drm_device) ++ return FALSE; ++ ++ if (priv->pool && gst_buffer_pool_is_active (priv->pool)) { ++ GstStructure *config = gst_buffer_pool_get_config (priv->pool); ++ gboolean ret = FALSE; ++ gboolean is_drm_dumb = FALSE; ++ ++ ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL); ++ gst_structure_free (config); ++ ++ if (ret && alloc) ++ is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc); ++ ++ if (is_drm_dumb) ++ return TRUE; ++ } ++ ++ alloc = gst_drm_dumb_allocator_new_with_device_path (priv->drm_device); ++ if (!alloc) ++ return FALSE; ++ ++ gst_gtk_wayland_update_pool (self, alloc); ++ gst_object_unref (alloc); ++ ++ return TRUE; ++} ++ + static gboolean + gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + { +@@ -877,6 +943,7 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + + format = GST_VIDEO_INFO_FORMAT (&priv->video_info); + priv->video_info_changed = TRUE; ++ priv->skip_dumb_buffer_copy = FALSE; + + /* free pooled buffer used with previous caps */ + if (priv->pool) { +@@ -1086,8 +1153,60 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, + &priv->video_info); ++ ++ /* DMABuf did not work, let try and make this a dmabuf, it does not matter ++ * if it was a SHM since the compositor needs to copy that anyway, and ++ * offloading the compositor from a copy helps maintaining a smoother ++ * desktop. ++ */ ++ if (!priv->skip_dumb_buffer_copy) { ++ GstVideoFrame src, dst; ++ ++ if (!gst_gtk_wayland_activate_drm_dumb_pool (self)) { ++ priv->skip_dumb_buffer_copy = TRUE; ++ goto handle_shm; ++ } ++ ++ ret = gst_buffer_pool_acquire_buffer (priv->pool, &to_render, NULL); ++ if (ret != GST_FLOW_OK) ++ goto no_buffer; ++ ++ wlbuffer = gst_buffer_get_wl_buffer (priv->display, to_render); ++ ++ /* attach a wl_buffer if there isn't one yet */ ++ if (G_UNLIKELY (!wlbuffer)) { ++ wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render, ++ priv->display, &priv->video_info); ++ ++ if (G_UNLIKELY (!wbuf)) { ++ GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf"); ++ gst_clear_buffer (&to_render); ++ priv->skip_dumb_buffer_copy = TRUE; ++ goto handle_shm; ++ } ++ ++ wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, priv->display); ++ } ++ ++ if (!gst_video_frame_map (&dst, &priv->video_info, to_render, ++ GST_MAP_WRITE)) ++ goto dst_map_failed; ++ ++ if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { ++ gst_video_frame_unmap (&dst); ++ goto src_map_failed; ++ } ++ ++ gst_video_frame_copy (&dst, &src); ++ ++ gst_video_frame_unmap (&src); ++ gst_video_frame_unmap (&dst); ++ ++ goto render; ++ } + } + ++handle_shm: + if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { + if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index aa39447..bb7fcff 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -61,6 +61,7 @@ enum + PROP_DISPLAY, + PROP_FULLSCREEN, + PROP_ROTATE_METHOD, ++ PROP_DRM_DEVICE, + PROP_LAST + }; + +@@ -177,6 +178,18 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass) + GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + ++ /** ++ * waylandsink:drm-device: ++ * ++ * Since: 1.24 ++ */ ++ g_object_class_install_property (gobject_class, PROP_DRM_DEVICE, ++ g_param_spec_string ("drm-device", "DRM Device", "Path of the " ++ "DRM device to use for dumb buffer allocation", ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); ++ ++ + /** + * waylandsink:render-rectangle: + * +@@ -266,6 +279,11 @@ gst_wayland_sink_get_property (GObject * object, + g_value_set_enum (value, self->current_rotate_method); + GST_OBJECT_UNLOCK (self); + break; ++ case PROP_DRM_DEVICE: ++ GST_OBJECT_LOCK (self); ++ g_value_set_string (value, self->drm_device); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -293,6 +311,11 @@ gst_wayland_sink_set_property (GObject * object, + gst_wayland_sink_set_rotate_method (self, g_value_get_enum (value), + FALSE); + break; ++ case PROP_DRM_DEVICE: ++ GST_OBJECT_LOCK (self); ++ self->drm_device = g_value_dup_string (value); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -318,6 +341,7 @@ gst_wayland_sink_finalize (GObject * object) + gst_clear_caps (&self->caps); + + g_free (self->display_name); ++ g_free (self->drm_device); + + g_mutex_clear (&self->display_lock); + g_mutex_clear (&self->render_lock); +@@ -629,6 +653,39 @@ gst_wayland_activate_shm_pool (GstWaylandSink * self) + return TRUE; + } + ++static gboolean ++gst_wayland_activate_drm_dumb_pool (GstWaylandSink * self) ++{ ++ GstAllocator *alloc; ++ ++ if (!self->drm_device) ++ return FALSE; ++ ++ if (self->pool && gst_buffer_pool_is_active (self->pool)) { ++ GstStructure *config = gst_buffer_pool_get_config (self->pool); ++ gboolean ret = FALSE; ++ gboolean is_drm_dumb = FALSE; ++ ++ ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL); ++ gst_structure_free (config); ++ ++ if (ret && alloc) ++ is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc); ++ ++ if (is_drm_dumb) ++ return TRUE; ++ } ++ ++ alloc = gst_drm_dumb_allocator_new_with_device_path (self->drm_device); ++ if (!alloc) ++ return FALSE; ++ ++ gst_wayland_update_pool (self, alloc); ++ gst_object_unref (alloc); ++ ++ return TRUE; ++} ++ + static gboolean + gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + { +@@ -644,6 +701,7 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + + format = GST_VIDEO_INFO_FORMAT (&self->video_info); + self->video_info_changed = TRUE; ++ self->skip_dumb_buffer_copy = FALSE; + + /* free pooled buffer used with previous caps */ + if (self->pool) { +@@ -848,8 +906,60 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, + &self->video_info); ++ ++ /* DMABuf did not work, let try and make this a dmabuf, it does not matter ++ * if it was a SHM since the compositor needs to copy that anyway, and ++ * offloading the compositor from a copy helps maintaining a smoother ++ * desktop. ++ */ ++ if (!self->skip_dumb_buffer_copy) { ++ GstVideoFrame src, dst; ++ ++ if (!gst_wayland_activate_drm_dumb_pool (self)) { ++ self->skip_dumb_buffer_copy = TRUE; ++ goto handle_shm; ++ } ++ ++ ret = gst_buffer_pool_acquire_buffer (self->pool, &to_render, NULL); ++ if (ret != GST_FLOW_OK) ++ goto no_buffer; ++ ++ wlbuffer = gst_buffer_get_wl_buffer (self->display, to_render); ++ ++ /* attach a wl_buffer if there isn't one yet */ ++ if (G_UNLIKELY (!wlbuffer)) { ++ wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render, ++ self->display, &self->video_info); ++ ++ if (G_UNLIKELY (!wbuf)) { ++ GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf"); ++ gst_clear_buffer (&to_render); ++ self->skip_dumb_buffer_copy = TRUE; ++ goto handle_shm; ++ } ++ ++ wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, self->display); ++ } ++ ++ if (!gst_video_frame_map (&dst, &self->video_info, to_render, ++ GST_MAP_WRITE)) ++ goto dst_map_failed; ++ ++ if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { ++ gst_video_frame_unmap (&dst); ++ goto src_map_failed; ++ } ++ ++ gst_video_frame_copy (&dst, &src); ++ ++ gst_video_frame_unmap (&src); ++ gst_video_frame_unmap (&dst); ++ ++ goto render; ++ } + } + ++handle_shm: + if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) { + if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display, +diff --git a/ext/wayland/gstwaylandsink.h b/ext/wayland/gstwaylandsink.h +index 1ff50d1..ca3400e 100644 +--- a/ext/wayland/gstwaylandsink.h ++++ b/ext/wayland/gstwaylandsink.h +@@ -69,6 +69,9 @@ struct _GstWaylandSink + GstVideoOrientationMethod current_rotate_method; + + struct wl_callback *callback; ++ ++ gchar *drm_device; ++ gboolean skip_dumb_buffer_copy; + }; + + struct _GstWaylandSinkClass +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-waylandsink-fallback-to-shm-if-display-does-not-supp.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-waylandsink-fallback-to-shm-if-display-does-not-supp.patch deleted file mode 100644 index 37e9f9a1c..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0011-waylandsink-fallback-to-shm-if-display-does-not-supp.patch +++ /dev/null @@ -1,41 +0,0 @@ -From e46e02a4745e20b139a5834d235fe2eeb992be14 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Fri, 22 Mar 2019 15:40:03 +0100 -Subject: [PATCH 11/14] waylandsink: fallback to shm if display does not - support dmabuf - -Fallback to shm if display does not support dmabuf. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/gstwaylandsink.c | 10 +++------- - 1 file changed, 3 insertions(+), 7 deletions(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 0dc1f0d..e4952cb 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -618,15 +618,11 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) - */ - /* use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0), - GST_CAPS_FEATURE_MEMORY_DMABUF); */ -- use_dmabuf = TRUE; -+ use_dmabuf = gst_wl_display_check_format_for_dmabuf (sink->display, format); - -- /* validate the format base on the memory type */ -- if (use_dmabuf) { -- if (!gst_wl_display_check_format_for_dmabuf (sink->display, format)) -+ if (!use_dmabuf) -+ if (!gst_wl_display_check_format_for_shm (sink->display, format)) - goto unsupported_format; -- } else if (!gst_wl_display_check_format_for_shm (sink->display, format)) { -- goto unsupported_format; -- } - - /* create a new pool for the new caps */ - if (sink->pool) --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-bad-Update-doc-cache-for-waylandsink-changes.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-bad-Update-doc-cache-for-waylandsink-changes.patch new file mode 100644 index 000000000..4d04fb499 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-bad-Update-doc-cache-for-waylandsink-changes.patch @@ -0,0 +1,58 @@ +From f0e7d7840db7a27d29127acdd42a09ea7450c969 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Fri, 17 Feb 2023 12:10:25 -0500 +Subject: [PATCH 12/17] bad: Update doc cache for waylandsink changes + +A new drm-device property has been added. + +Part-of: +Upstream-Status: Backport +--- + docs/plugins/gst_plugins_cache.json | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json +index 50128fb..4ea6d90 100644 +--- a/docs/plugins/gst_plugins_cache.json ++++ b/docs/plugins/gst_plugins_cache.json +@@ -29462,6 +29462,18 @@ + } + }, + "properties": { ++ "drm-device": { ++ "blurb": "Path of the DRM device to use for dumb buffer allocation", ++ "conditionally-available": false, ++ "construct": false, ++ "construct-only": true, ++ "controllable": false, ++ "default": "NULL", ++ "mutable": "null", ++ "readable": true, ++ "type": "gchararray", ++ "writable": true ++ }, + "rotate-method": { + "blurb": "rotate method", + "conditionally-available": false, +@@ -236521,6 +236533,18 @@ + "type": "gchararray", + "writable": true + }, ++ "drm-device": { ++ "blurb": "Path of the DRM device to use for dumb buffer allocation", ++ "conditionally-available": false, ++ "construct": false, ++ "construct-only": true, ++ "controllable": false, ++ "default": "NULL", ++ "mutable": "null", ++ "readable": true, ++ "type": "gchararray", ++ "writable": true ++ }, + "fullscreen": { + "blurb": "Whether the surface should be made fullscreen ", + "conditionally-available": false, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-waylandsink-XDG-protocol-does-not-work-in-fullscreen.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-waylandsink-XDG-protocol-does-not-work-in-fullscreen.patch deleted file mode 100644 index f44991ca1..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0012-waylandsink-XDG-protocol-does-not-work-in-fullscreen.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 105576bd522f3a751fda51ea35da2147b1adcfe7 Mon Sep 17 00:00:00 2001 -From: Pierre-Yves MORDRET -Date: Fri, 4 Oct 2019 09:47:43 +0200 -Subject: [PATCH 12/14] waylandsink: XDG protocol does not work in fullscreen - -Using new XDG protocol, it turns out to be not working when enabling -fullscreen support. -This patch avoids to over commit surface during configuration and takes -into account render region when provided within wayland callback. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Pierre-Yves MORDRET ---- - ext/wayland/wlwindow.c | 19 +++++++++++++++---- - 1 file changed, 15 insertions(+), 4 deletions(-) - -diff --git a/ext/wayland/wlwindow.c b/ext/wayland/wlwindow.c -index 66df0fc..cc5de26 100644 ---- a/ext/wayland/wlwindow.c -+++ b/ext/wayland/wlwindow.c -@@ -325,10 +325,18 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info, - /* render_rectangle is already set via toplevel_configure in - * xdg_shell fullscreen mode */ - if (!(display->xdg_wm_base && fullscreen)) { -+ gint width, height; -+ - /* set the initial size to be the same as the reported video size */ -- gint width = -- gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); -- gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height); -+ if ( window->render_rectangle.w == 0 || window->render_rectangle.h == 0) { -+ width = -+ gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); -+ height = info->height; -+ } else { -+ width = window->render_rectangle.w; -+ height = window->render_rectangle.h; -+ } -+ gst_wl_window_set_render_rectangle (window, 0, 0, width, height); - } - - return window; -@@ -567,7 +575,10 @@ gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, - gst_wl_window_resize_video_surface (window, TRUE); - } - -- wl_surface_commit (window->area_surface_wrapper); -+ if (window->xdg_surface && window->configured) { -+ wl_surface_damage (window->area_surface_wrapper, 0, 0, w, h); -+ wl_surface_commit (window->area_surface_wrapper); -+ } - - if (window->video_width != 0) - wl_subsurface_set_desync (window->video_subsurface); --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0013-waylandsink-silently-drop-erroneous-frame.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0013-waylandsink-silently-drop-erroneous-frame.patch deleted file mode 100644 index 31a1fc04e..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0013-waylandsink-silently-drop-erroneous-frame.patch +++ /dev/null @@ -1,99 +0,0 @@ -From e14996eb9c0332aeda6390db207380dd0267e7c2 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Wed, 25 Sep 2019 16:01:49 +0200 -Subject: [PATCH 13/14] waylandsink: silently drop erroneous frame - -Do not stop playback when erroneous frame is received at show_frame(): -- test file descriptors validity, to not let throw erroneous -file descriptor to wayland backend (which may crash on fatal error) -- if file descriptors are not valid, do not fall into general -"GST_ELEMENT_ERROR" nor return FLOW_ERROR. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/gstwaylandsink.c | 6 +++--- - ext/wayland/wllinuxdmabuf.c | 14 ++++++++++++++ - ext/wayland/wlshmallocator.c | 10 ++++++++++ - 3 files changed, 27 insertions(+), 3 deletions(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index e4952cb..cf355d3 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -1025,9 +1025,9 @@ activate_failed: - } - src_map_failed: - { -- GST_ELEMENT_ERROR (sink, RESOURCE, READ, -- ("Video memory can not be read from userspace."), (NULL)); -- ret = GST_FLOW_ERROR; -+ /* Silently drop in case of erroneous buffer */ -+ GST_ERROR_OBJECT (sink, "Video memory can not be read from userspace," -+ "dropping buffer %p", buffer); - goto done; - } - dst_map_failed: -diff --git a/ext/wayland/wllinuxdmabuf.c b/ext/wayland/wllinuxdmabuf.c -index 96487d1..36815b5 100644 ---- a/ext/wayland/wllinuxdmabuf.c -+++ b/ext/wayland/wllinuxdmabuf.c -@@ -28,6 +28,8 @@ - #include "wllinuxdmabuf.h" - #include "wlvideoformat.h" - -+#include -+ - GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); - #define GST_CAT_DEFAULT gstwayland_debug - -@@ -110,6 +112,18 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, - if (gst_buffer_find_memory (buf, offset, 1, &mem_idx, &length, &skip)) { - GstMemory *m = gst_buffer_peek_memory (buf, mem_idx); - gint fd = gst_dmabuf_memory_get_fd (m); -+ gint dup_fd = dup (fd); -+ -+ /* Test if file descriptor is valid */ -+ if (dup_fd < 0) { -+ GST_ERROR_OBJECT (display, "zwp_linux_dmabuf: dup failed for fd=%d (err=%d) buffer=%p", -+ fd, dup_fd, buf); -+ zwp_linux_buffer_params_v1_destroy (params); -+ data.wbuf = NULL; -+ goto out; -+ } -+ close(dup_fd); -+ - zwp_linux_buffer_params_v1_add (params, fd, i, m->offset + skip, - stride, 0, 0); - } else { -diff --git a/ext/wayland/wlshmallocator.c b/ext/wayland/wlshmallocator.c -index 0a82a35..8659c2f 100644 ---- a/ext/wayland/wlshmallocator.c -+++ b/ext/wayland/wlshmallocator.c -@@ -212,12 +212,22 @@ gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem, GstWlDisplay * display, - enum wl_shm_format format; - struct wl_shm_pool *wl_pool; - struct wl_buffer *wbuffer; -+ gint fd = gst_fd_memory_get_fd (mem); -+ gint dup_fd = dup(fd); - - if (!gst_wl_shm_validate_video_info (info)) { - GST_DEBUG_OBJECT (display, "Unsupported strides and offsets."); - return NULL; - } - -+ /* Test if file descriptor is valid */ -+ if (dup_fd < 0) { -+ GST_ERROR_OBJECT (display, "wl_shm_memory: dup failed for fd=%d (err=%d) mem=%p", -+ fd, dup_fd, mem); -+ return NULL; -+ } -+ close(dup_fd); -+ - width = GST_VIDEO_INFO_WIDTH (info); - height = GST_VIDEO_INFO_HEIGHT (info); - stride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0); --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch new file mode 100644 index 000000000..06a3b292c --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch @@ -0,0 +1,27 @@ +From 0bc64942a8f0c1b49e2488b52f083c5545b1079c Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 13:11:55 +0200 +Subject: [PATCH 14/17] WAYLANDSINK: use card0a as default drm-device + +Signed-off-by: Christophe Priouzeau +Upstream-Status: Pending +--- + ext/wayland/gstwaylandsink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index bb7fcff..8cf8e36 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -186,7 +186,7 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass) + g_object_class_install_property (gobject_class, PROP_DRM_DEVICE, + g_param_spec_string ("drm-device", "DRM Device", "Path of the " + "DRM device to use for dumb buffer allocation", +- NULL, ++ "/dev/dri/card0", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-waylandsink-add-waylandpool-on-meson-build.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-waylandsink-add-waylandpool-on-meson-build.patch deleted file mode 100644 index 566cbe3a6..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0014-waylandsink-add-waylandpool-on-meson-build.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 678d91a743bb35e202211abc6db495fbf8d75601 Mon Sep 17 00:00:00 2001 -From: Christophe Priouzeau -Date: Tue, 18 Feb 2020 13:33:21 +0100 -Subject: [PATCH 14/14] waylandsink add waylandpool on meson build - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Christophe Priouzeau ---- - ext/wayland/meson.build | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ext/wayland/meson.build b/ext/wayland/meson.build -index a3ffb70..66dd148 100644 ---- a/ext/wayland/meson.build -+++ b/ext/wayland/meson.build -@@ -5,7 +5,8 @@ wl_sources = [ - 'wldisplay.c', - 'wlwindow.c', - 'wlvideoformat.c', -- 'wllinuxdmabuf.c' -+ 'wllinuxdmabuf.c', -+ 'waylandpool.c' - ] - - libdrm_dep = dependency('libdrm', version: '>= 2.4.55', required:get_option('wayland')) --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0015-waylandsink-Uprank-to-secondary.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0015-waylandsink-Uprank-to-secondary.patch new file mode 100644 index 000000000..397b85e2d --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0015-waylandsink-Uprank-to-secondary.patch @@ -0,0 +1,27 @@ +From a4873e027038d26e9fb43a03a016d2975e6b3ba3 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 13:14:01 +0200 +Subject: [PATCH 15/17] waylandsink: Uprank to secondary + +Signed-off-by: Christophe Priouzeau +Upstream-Status: Pending +--- + ext/wayland/gstwaylandsink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 8cf8e36..24a7174 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -114,7 +114,7 @@ static void gst_wayland_sink_expose (GstVideoOverlay * overlay); + G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK, + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_wayland_sink_videooverlay_init)); +-GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_MARGINAL, ++GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_SECONDARY+1, + GST_TYPE_WAYLAND_SINK); + + static void +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-Add-new-gtkwaylandsink-element.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-Add-new-gtkwaylandsink-element.patch deleted file mode 100644 index 1614a6fcd..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-Add-new-gtkwaylandsink-element.patch +++ /dev/null @@ -1,1571 +0,0 @@ -From 33fa3db5ac3c5e3c82df9573dec334ef7bf77ee3 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Fri, 17 Dec 2021 14:02:51 +0200 -Subject: [PATCH 16/16] Add new gtkwaylandsink element - -Upstream-Status: Inappropriate [DEY specific] ---- - ext/gtk/gstgtkutils.c | 71 +++++ - ext/gtk/gstgtkutils.h | 29 ++ - ext/gtk/gstgtkwaylandsink.c | 460 +++++++++++++++++++++++++++++ - ext/gtk/gstgtkwaylandsink.h | 51 ++++ - ext/gtk/gstplugin.c | 43 +++ - ext/gtk/gtkgstbasewidget.c | 542 ++++++++++++++++++++++++++++++++++ - ext/gtk/gtkgstbasewidget.h | 100 +++++++ - ext/gtk/gtkgstwaylandwidget.c | 67 +++++ - ext/gtk/gtkgstwaylandwidget.h | 68 +++++ - ext/gtk/meson.build | 22 ++ - ext/meson.build | 1 + - meson_options.txt | 1 + - 12 files changed, 1455 insertions(+) - create mode 100644 ext/gtk/gstgtkutils.c - create mode 100644 ext/gtk/gstgtkutils.h - create mode 100644 ext/gtk/gstgtkwaylandsink.c - create mode 100644 ext/gtk/gstgtkwaylandsink.h - create mode 100644 ext/gtk/gstplugin.c - create mode 100644 ext/gtk/gtkgstbasewidget.c - create mode 100644 ext/gtk/gtkgstbasewidget.h - create mode 100644 ext/gtk/gtkgstwaylandwidget.c - create mode 100644 ext/gtk/gtkgstwaylandwidget.h - create mode 100644 ext/gtk/meson.build - -diff --git a/ext/gtk/gstgtkutils.c b/ext/gtk/gstgtkutils.c -new file mode 100644 -index 0000000..c730f01 ---- /dev/null -+++ b/ext/gtk/gstgtkutils.c -@@ -0,0 +1,71 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * Copyright (C) 2015 Thibault Saunier -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#include "gstgtkutils.h" -+ -+struct invoke_context -+{ -+ GThreadFunc func; -+ gpointer data; -+ GMutex lock; -+ GCond cond; -+ gboolean fired; -+ -+ gpointer res; -+}; -+ -+static gboolean -+gst_gtk_invoke_func (struct invoke_context *info) -+{ -+ g_mutex_lock (&info->lock); -+ info->res = info->func (info->data); -+ info->fired = TRUE; -+ g_cond_signal (&info->cond); -+ g_mutex_unlock (&info->lock); -+ -+ return G_SOURCE_REMOVE; -+} -+ -+gpointer -+gst_gtk_invoke_on_main (GThreadFunc func, gpointer data) -+{ -+ GMainContext *main_context = g_main_context_default (); -+ struct invoke_context info; -+ -+ g_mutex_init (&info.lock); -+ g_cond_init (&info.cond); -+ info.fired = FALSE; -+ info.func = func; -+ info.data = data; -+ -+ g_main_context_invoke (main_context, (GSourceFunc) gst_gtk_invoke_func, -+ &info); -+ -+ g_mutex_lock (&info.lock); -+ while (!info.fired) -+ g_cond_wait (&info.cond, &info.lock); -+ g_mutex_unlock (&info.lock); -+ -+ g_mutex_clear (&info.lock); -+ g_cond_clear (&info.cond); -+ -+ return info.res; -+} -diff --git a/ext/gtk/gstgtkutils.h b/ext/gtk/gstgtkutils.h -new file mode 100644 -index 0000000..7584ae2 ---- /dev/null -+++ b/ext/gtk/gstgtkutils.h -@@ -0,0 +1,29 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * Copyright (C) 2015 Thibault Saunier -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef __GST_GTK_UTILS_H__ -+#define __GST_GTK_UTILS_H__ -+ -+#include -+ -+gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data); -+ -+#endif /* __GST_GTK_UTILS_H__ */ -diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c -new file mode 100644 -index 0000000..c38ea51 ---- /dev/null -+++ b/ext/gtk/gstgtkwaylandsink.c -@@ -0,0 +1,460 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * Copyright (C) 2021 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstgtkwaylandsink.h" -+#include "gtkgstwaylandwidget.h" -+#include "gstgtkutils.h" -+ -+#include -+#include -+ -+#include -+ -+#ifdef GDK_WINDOWING_WAYLAND -+#include -+#else -+#error "Wayland is not supported in GTK+" -+#endif -+ -+GST_DEBUG_CATEGORY (gst_debug_gtk_wayland_sink); -+#define GST_CAT_DEFAULT gst_debug_gtk_wayland_sink -+ -+#ifndef GST_CAPS_FEATURE_MEMORY_DMABUF -+#define GST_CAPS_FEATURE_MEMORY_DMABUF "memory:DMABuf" -+#endif -+ -+#define WL_VIDEO_FORMATS \ -+ "{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, " \ -+ "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, NV61, " \ -+ "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }" -+ -+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", -+ GST_PAD_SINK, -+ GST_PAD_ALWAYS, -+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (WL_VIDEO_FORMATS) ";" -+ GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF, -+ WL_VIDEO_FORMATS)) -+ ); -+ -+static void -+gst_gtk_wayland_sink_navigation_interface_init (GstNavigationInterface * iface); -+ -+static GstPadProbeReturn sink_pad_probe_cb (GstPad * pad, -+ GstPadProbeInfo * info, gpointer user_data); -+ -+enum -+{ -+ PROP_0, -+ PROP_WIDGET, -+}; -+ -+#define gst_gtk_wayland_sink_parent_class parent_class -+G_DEFINE_TYPE_WITH_CODE (GstGtkWaylandSink, gst_gtk_wayland_sink, GST_TYPE_BIN, -+ G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, -+ gst_gtk_wayland_sink_navigation_interface_init); -+ GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_wayland_sink, "gtkwaylandsink", 0, -+ "Gtk Wayland Video Sink")); -+ -+static void -+gst_gtk_wayland_sink_init (GstGtkWaylandSink * self) -+{ -+ self->waylandsink = gst_element_factory_make ("waylandsink", NULL); -+ if (self->waylandsink) { -+ GstPadTemplate *tmpl; -+ GstPad *pad; -+ -+ gst_bin_add (GST_BIN (self), self->waylandsink); -+ -+ tmpl = gst_static_pad_template_get (&sink_template); -+ pad = gst_element_get_static_pad (self->waylandsink, "sink"); -+ self->ghostpad = gst_ghost_pad_new_from_template ("sink", pad, tmpl); -+ -+ gst_pad_add_probe (self->ghostpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, -+ sink_pad_probe_cb, self, NULL); -+ gst_element_add_pad (GST_ELEMENT (self), self->ghostpad); -+ -+ gst_object_unref (pad); -+ gst_object_unref (tmpl); -+ } -+} -+ -+static void -+gst_gtk_wayland_sink_finalize (GObject * object) -+{ -+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object); -+ -+ g_clear_object (&self->widget); -+ -+ G_OBJECT_CLASS (parent_class)->finalize (object); -+} -+ -+static void -+widget_destroy_cb (GtkWidget * widget, GstGtkWaylandSink * self) -+{ -+ GST_OBJECT_LOCK (self); -+ g_clear_object (&self->widget); -+ GST_OBJECT_UNLOCK (self); -+} -+ -+static void -+window_destroy_cb (GtkWidget * widget, GstGtkWaylandSink * self) -+{ -+ GST_OBJECT_LOCK (self); -+ self->window = NULL; -+ GST_OBJECT_UNLOCK (self); -+ -+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Window was closed"), (NULL)); -+} -+ -+/* We use the "draw" callback to change the size of the sink -+ * because the "configure-event" is only sent to top-level widgets. */ -+static gboolean -+widget_draw_cb (GtkWidget * widget, cairo_t * cr, gpointer user_data) -+{ -+ GstGtkWaylandSink *self = user_data; -+ GtkAllocation allocation; -+ -+ gtk_widget_get_allocation (widget, &allocation); -+ gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (self->waylandsink), -+ allocation.x, allocation.y, allocation.width, allocation.height); -+ -+ return FALSE; -+} -+ -+static GtkWidget * -+gst_gtk_wayland_sink_get_widget (GstGtkWaylandSink * self) -+{ -+ if (self->widget != NULL) -+ return g_object_ref (self->widget); -+ -+ /* Ensure GTK is initialized, this has no side effect if it was already -+ * initialized. Also, we do that lazily, so the application can be first */ -+ if (!gtk_init_check (NULL, NULL)) { -+ GST_INFO_OBJECT (self, "Could not ensure GTK initialization."); -+ return NULL; -+ } -+ -+ self->widget = gtk_gst_wayland_widget_new (); -+ g_signal_connect_object (self->widget, "draw", -+ G_CALLBACK (widget_draw_cb), self, 0); -+ gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (self->widget), -+ GST_ELEMENT (self)); -+ -+ /* Take the floating ref, other wise the destruction of the container will -+ * make this widget disappear possibly before we are done. */ -+ g_object_ref_sink (self->widget); -+ g_signal_connect_object (self->widget, "destroy", -+ G_CALLBACK (widget_destroy_cb), self, 0); -+ -+ return g_object_ref (self->widget); -+} -+ -+static GtkWidget * -+gst_gtk_wayland_sink_acquire_widget (GstGtkWaylandSink * self) -+{ -+ gpointer widget = NULL; -+ -+ GST_OBJECT_LOCK (self); -+ if (self->widget != NULL) -+ widget = g_object_ref (self->widget); -+ GST_OBJECT_UNLOCK (self); -+ -+ if (!widget) -+ widget = -+ gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_wayland_sink_get_widget, -+ self); -+ -+ return widget; -+} -+ -+static void -+gst_gtk_wayland_sink_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object); -+ -+ switch (prop_id) { -+ case PROP_WIDGET: -+ { -+ g_value_take_object (value, gst_gtk_wayland_sink_acquire_widget (self)); -+ break; -+ } -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static gboolean -+gst_gtk_wayland_sink_start_on_main (GstGtkWaylandSink * self) -+{ -+ GtkWidget *toplevel; -+ GstContext *context; -+ GdkDisplay *display; -+ struct wl_display *display_handle; -+ -+ if ((toplevel = gst_gtk_wayland_sink_get_widget (self)) == NULL) { -+ GST_ERROR_OBJECT (self, "Could not ensure GTK initialization."); -+ return FALSE; -+ } -+ g_object_unref (toplevel); -+ -+ /* After this point, self->widget will always be set */ -+ -+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->widget)); -+ if (!gtk_widget_is_toplevel (toplevel)) { -+ /* User did not add widget its own UI, let's popup a new GtkWindow to -+ * make gst-launch-1.0 work. */ -+ self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); -+ gtk_window_set_default_size (GTK_WINDOW (self->window), 640, 480); -+ gtk_window_set_title (GTK_WINDOW (self->window), "Gst GTK Wayland Sink"); -+ gtk_container_add (GTK_CONTAINER (self->window), toplevel); -+ self->window_destroy_id = g_signal_connect (self->window, "destroy", -+ G_CALLBACK (window_destroy_cb), self); -+ } -+ -+ display = gtk_widget_get_display (self->widget); -+ if (!GDK_IS_WAYLAND_DISPLAY (display)) { -+ GST_ERROR_OBJECT (self, "GDK is not using its wayland backend."); -+ return FALSE; -+ } -+ display_handle = gdk_wayland_display_get_wl_display (display); -+ context = gst_wayland_display_handle_context_new (display_handle); -+ gst_element_set_context (self->waylandsink, context); -+ gst_context_unref (context); -+ -+ return TRUE; -+} -+ -+static gboolean -+gst_gtk_wayland_sink_stop_on_main (GstGtkWaylandSink * self) -+{ -+ if (self->window) { -+ if (self->window_destroy_id) -+ g_signal_handler_disconnect (self->window, self->window_destroy_id); -+ self->window_destroy_id = 0; -+ gtk_widget_destroy (self->window); -+ self->window = NULL; -+ } -+ -+ return TRUE; -+} -+ -+static void -+gst_gtk_widget_show_all_and_unref (GtkWidget * widget) -+{ -+ gtk_widget_show_all (widget); -+ g_object_unref (widget); -+} -+ -+static GstStateChangeReturn -+gst_gtk_wayland_sink_change_state (GstElement * element, -+ GstStateChange transition) -+{ -+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (element); -+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; -+ -+ switch (transition) { -+ case GST_STATE_CHANGE_NULL_TO_READY: -+ if (!self->waylandsink) { -+ GST_ERROR_OBJECT (self, "Failed to load waylandsink"); -+ return GST_STATE_CHANGE_FAILURE; -+ } -+ if (!gst_gtk_invoke_on_main ((GThreadFunc) -+ gst_gtk_wayland_sink_start_on_main, element)) -+ return GST_STATE_CHANGE_FAILURE; -+ break; -+ case GST_STATE_CHANGE_READY_TO_PAUSED: -+ { -+ GtkWindow *window = NULL; -+ -+ GST_OBJECT_LOCK (self); -+ if (self->window) -+ window = g_object_ref (GTK_WINDOW (self->window)); -+ GST_OBJECT_UNLOCK (self); -+ -+ if (window) -+ gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_widget_show_all_and_unref, -+ window); -+ -+ break; -+ } -+ default: -+ break; -+ } -+ -+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); -+ if (ret != GST_STATE_CHANGE_SUCCESS) -+ return ret; -+ -+ switch (transition) { -+ case GST_STATE_CHANGE_READY_TO_NULL: -+ case GST_STATE_CHANGE_NULL_TO_NULL: -+ gst_gtk_invoke_on_main ((GThreadFunc) -+ gst_gtk_wayland_sink_stop_on_main, element); -+ break; -+ default: -+ break; -+ } -+ -+ return ret; -+} -+ -+static GstPadProbeReturn -+sink_pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) -+{ -+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (user_data); -+ if (GST_IS_EVENT (info->data) && -+ GST_EVENT_TYPE (info->data) == GST_EVENT_CAPS) { -+ GstVideoInfo v_info; -+ GstCaps *caps; -+ GtkWidget *widget; -+ -+ widget = gst_gtk_wayland_sink_acquire_widget (self); -+ if (widget) { -+ gst_event_parse_caps (GST_EVENT (info->data), &caps); -+ gst_video_info_from_caps (&v_info, caps); -+ gtk_gst_base_widget_set_format (GTK_GST_BASE_WIDGET (widget), &v_info); -+ g_object_unref (widget); -+ } -+ } -+ -+ return GST_PAD_PROBE_OK; -+} -+ -+static void -+gst_gtk_wayland_sink_set_window_handle_on_main (GstGtkWaylandSink * self) -+{ -+ GtkAllocation allocation; -+ GdkWindow *window; -+ struct wl_surface *window_handle; -+ -+ gtk_widget_get_allocation (self->widget, &allocation); -+ window = gtk_widget_get_window (self->widget); -+ window_handle = gdk_wayland_window_get_wl_surface (window); -+ -+ GST_INFO_OBJECT (self, "setting window handle and size (%d x %d)", -+ allocation.width, allocation.height); -+ -+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (self->waylandsink), -+ (guintptr) window_handle); -+ gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (self->waylandsink), -+ allocation.x, allocation.y, allocation.width, allocation.height); -+} -+ -+static void -+gst_gtk_wayland_sink_handle_message (GstBin * bin, GstMessage * message) -+{ -+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (bin); -+ -+ if (gst_is_video_overlay_prepare_window_handle_message (message)) { -+ gst_gtk_invoke_on_main ((GThreadFunc) -+ gst_gtk_wayland_sink_set_window_handle_on_main, self); -+ gst_message_unref (message); -+ return; -+ } -+ -+ GST_BIN_CLASS (parent_class)->handle_message (bin, message); -+} -+ -+static void -+gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass) -+{ -+ GObjectClass *gobject_class = (GObjectClass *) klass; -+ GstElementClass *gstelement_class = (GstElementClass *) klass; -+ GstBinClass *gstbin_class = (GstBinClass *) klass; -+ -+ gobject_class->finalize = gst_gtk_wayland_sink_finalize; -+ gobject_class->get_property = gst_gtk_wayland_sink_get_property; -+ gstelement_class->change_state = gst_gtk_wayland_sink_change_state; -+ gstbin_class->handle_message = gst_gtk_wayland_sink_handle_message; -+ -+ g_object_class_install_property (gobject_class, PROP_WIDGET, -+ g_param_spec_object ("widget", "Gtk Widget", -+ "The GtkWidget to place in the widget hierarchy " -+ "(must only be get from the GTK main thread)", -+ GTK_TYPE_WIDGET, -+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -+ -+ gst_element_class_set_metadata (gstelement_class, "Gtk Wayland Video Sink", -+ "Sink/Video", -+ "A video sink that renders to a GtkWidget using Wayland API", -+ "George Kiagiadakis "); -+ -+ gst_element_class_add_static_pad_template (gstelement_class, &sink_template); -+} -+ -+static void -+gst_gtk_wayland_sink_navigation_send_event (GstNavigation * navigation, -+ GstStructure * structure) -+{ -+ GstGtkWaylandSink *sink = GST_GTK_WAYLAND_SINK (navigation); -+ GstEvent *event; -+ GstPad *pad; -+ gdouble x, y; -+ -+ if (gst_structure_get_double (structure, "pointer_x", &x) && -+ gst_structure_get_double (structure, "pointer_y", &y)) { -+ GtkGstBaseWidget *widget = -+ (GtkGstBaseWidget *) gst_gtk_wayland_sink_acquire_widget (sink); -+ gdouble stream_x, stream_y; -+ -+ if (widget == NULL) { -+ GST_ERROR_OBJECT (sink, "Could not ensure GTK initialization."); -+ return; -+ } -+ -+ gtk_gst_base_widget_display_size_to_stream_size (widget, -+ x, y, &stream_x, &stream_y); -+ gst_structure_set (structure, -+ "pointer_x", G_TYPE_DOUBLE, (gdouble) stream_x, -+ "pointer_y", G_TYPE_DOUBLE, (gdouble) stream_y, NULL); -+ -+ g_object_unref (widget); -+ } -+ -+ event = gst_event_new_navigation (structure); -+ pad = gst_pad_get_peer (sink->ghostpad); -+ -+ GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure); -+ -+ if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) { -+ if (!gst_pad_send_event (pad, gst_event_ref (event))) { -+ /* If upstream didn't handle the event we'll post a message with it -+ * for the application in case it wants to do something with it */ -+ gst_element_post_message (GST_ELEMENT_CAST (sink), -+ gst_navigation_message_new_event (GST_OBJECT_CAST (sink), event)); -+ } -+ gst_event_unref (event); -+ gst_object_unref (pad); -+ } -+} -+ -+static void -+gst_gtk_wayland_sink_navigation_interface_init (GstNavigationInterface * iface) -+{ -+ iface->send_event = gst_gtk_wayland_sink_navigation_send_event; -+} -diff --git a/ext/gtk/gstgtkwaylandsink.h b/ext/gtk/gstgtkwaylandsink.h -new file mode 100644 -index 0000000..33fc96f ---- /dev/null -+++ b/ext/gtk/gstgtkwaylandsink.h -@@ -0,0 +1,51 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2021 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef __GST_GTK_WAYLAND_SINK_H__ -+#define __GST_GTK_WAYLAND_SINK_H__ -+ -+#include -+#include -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_GTK_WAYLAND_SINK (gst_gtk_wayland_sink_get_type()) -+G_DECLARE_FINAL_TYPE (GstGtkWaylandSink, gst_gtk_wayland_sink, GST, GTK_WAYLAND_SINK, GstBin) -+ -+/** -+ * GstGtkWaylandSink: -+ * -+ * Opaque #GstGtkWaylandSink object -+ */ -+struct _GstGtkWaylandSink -+{ -+ /* */ -+ GstBin parent; -+ GstElement *waylandsink; -+ GstPad *ghostpad; -+ GtkWidget *widget; -+ GtkWidget *window; -+ gulong window_destroy_id; -+}; -+ -+G_END_DECLS -+ -+#endif /* __GST_GTK_WAYLAND_SINK_H__ */ -diff --git a/ext/gtk/gstplugin.c b/ext/gtk/gstplugin.c -new file mode 100644 -index 0000000..f154b08 ---- /dev/null -+++ b/ext/gtk/gstplugin.c -@@ -0,0 +1,43 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstgtkwaylandsink.h" -+ -+static gboolean -+plugin_init (GstPlugin * plugin) -+{ -+ gboolean ret = FALSE; -+ -+ ret |= gst_element_register (plugin, "gtkwaylandsink", GST_RANK_NONE, -+ GST_TYPE_GTK_WAYLAND_SINK); -+ -+ return ret; -+} -+ -+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, -+ GST_VERSION_MINOR, -+ gtkwayland, -+ "Gtk+ wayland sink", -+ plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, -+ GST_PACKAGE_ORIGIN) -diff --git a/ext/gtk/gtkgstbasewidget.c b/ext/gtk/gtkgstbasewidget.c -new file mode 100644 -index 0000000..053640b ---- /dev/null -+++ b/ext/gtk/gtkgstbasewidget.c -@@ -0,0 +1,542 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+ -+#include "gtkgstbasewidget.h" -+ -+GST_DEBUG_CATEGORY (gst_debug_gtk_base_widget); -+#define GST_CAT_DEFAULT gst_debug_gtk_base_widget -+ -+#define DEFAULT_FORCE_ASPECT_RATIO TRUE -+#define DEFAULT_PAR_N 0 -+#define DEFAULT_PAR_D 1 -+#define DEFAULT_IGNORE_ALPHA TRUE -+ -+enum -+{ -+ PROP_0, -+ PROP_FORCE_ASPECT_RATIO, -+ PROP_PIXEL_ASPECT_RATIO, -+ PROP_IGNORE_ALPHA, -+}; -+ -+static void -+gtk_gst_base_widget_get_preferred_width (GtkWidget * widget, gint * min, -+ gint * natural) -+{ -+ GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget; -+ gint video_width = gst_widget->display_width; -+ -+ if (!gst_widget->negotiated) -+ video_width = 10; -+ -+ if (min) -+ *min = 1; -+ if (natural) -+ *natural = video_width; -+} -+ -+static void -+gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min, -+ gint * natural) -+{ -+ GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget; -+ gint video_height = gst_widget->display_height; -+ -+ if (!gst_widget->negotiated) -+ video_height = 10; -+ -+ if (min) -+ *min = 1; -+ if (natural) -+ *natural = video_height; -+} -+ -+static void -+gtk_gst_base_widget_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object); -+ -+ switch (prop_id) { -+ case PROP_FORCE_ASPECT_RATIO: -+ gtk_widget->force_aspect_ratio = g_value_get_boolean (value); -+ break; -+ case PROP_PIXEL_ASPECT_RATIO: -+ gtk_widget->par_n = gst_value_get_fraction_numerator (value); -+ gtk_widget->par_d = gst_value_get_fraction_denominator (value); -+ break; -+ case PROP_IGNORE_ALPHA: -+ gtk_widget->ignore_alpha = g_value_get_boolean (value); -+ break; -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+gtk_gst_base_widget_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object); -+ -+ switch (prop_id) { -+ case PROP_FORCE_ASPECT_RATIO: -+ g_value_set_boolean (value, gtk_widget->force_aspect_ratio); -+ break; -+ case PROP_PIXEL_ASPECT_RATIO: -+ gst_value_set_fraction (value, gtk_widget->par_n, gtk_widget->par_d); -+ break; -+ case PROP_IGNORE_ALPHA: -+ g_value_set_boolean (value, gtk_widget->ignore_alpha); -+ break; -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static gboolean -+_calculate_par (GtkGstBaseWidget * widget, GstVideoInfo * info) -+{ -+ gboolean ok; -+ gint width, height; -+ gint par_n, par_d; -+ gint display_par_n, display_par_d; -+ -+ width = GST_VIDEO_INFO_WIDTH (info); -+ height = GST_VIDEO_INFO_HEIGHT (info); -+ -+ par_n = GST_VIDEO_INFO_PAR_N (info); -+ par_d = GST_VIDEO_INFO_PAR_D (info); -+ -+ if (!par_n) -+ par_n = 1; -+ -+ /* get display's PAR */ -+ if (widget->par_n != 0 && widget->par_d != 0) { -+ display_par_n = widget->par_n; -+ display_par_d = widget->par_d; -+ } else { -+ display_par_n = 1; -+ display_par_d = 1; -+ } -+ -+ -+ ok = gst_video_calculate_display_ratio (&widget->display_ratio_num, -+ &widget->display_ratio_den, width, height, par_n, par_d, display_par_n, -+ display_par_d); -+ -+ if (ok) { -+ GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, -+ display_par_d); -+ return TRUE; -+ } -+ -+ return FALSE; -+} -+ -+static void -+_apply_par (GtkGstBaseWidget * widget) -+{ -+ guint display_ratio_num, display_ratio_den; -+ gint width, height; -+ -+ width = GST_VIDEO_INFO_WIDTH (&widget->v_info); -+ height = GST_VIDEO_INFO_HEIGHT (&widget->v_info); -+ -+ display_ratio_num = widget->display_ratio_num; -+ display_ratio_den = widget->display_ratio_den; -+ -+ if (height % display_ratio_den == 0) { -+ GST_DEBUG ("keeping video height"); -+ widget->display_width = (guint) -+ gst_util_uint64_scale_int (height, display_ratio_num, -+ display_ratio_den); -+ widget->display_height = height; -+ } else if (width % display_ratio_num == 0) { -+ GST_DEBUG ("keeping video width"); -+ widget->display_width = width; -+ widget->display_height = (guint) -+ gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num); -+ } else { -+ GST_DEBUG ("approximating while keeping video height"); -+ widget->display_width = (guint) -+ gst_util_uint64_scale_int (height, display_ratio_num, -+ display_ratio_den); -+ widget->display_height = height; -+ } -+ -+ GST_DEBUG ("scaling to %dx%d", widget->display_width, widget->display_height); -+} -+ -+static gboolean -+_queue_draw (GtkGstBaseWidget * widget) -+{ -+ GTK_GST_BASE_WIDGET_LOCK (widget); -+ widget->draw_id = 0; -+ -+ if (widget->pending_resize) { -+ widget->pending_resize = FALSE; -+ -+ widget->v_info = widget->pending_v_info; -+ widget->negotiated = TRUE; -+ -+ _apply_par (widget); -+ -+ gtk_widget_queue_resize (GTK_WIDGET (widget)); -+ } else { -+ gtk_widget_queue_draw (GTK_WIDGET (widget)); -+ } -+ -+ GTK_GST_BASE_WIDGET_UNLOCK (widget); -+ -+ return G_SOURCE_REMOVE; -+} -+ -+static const gchar * -+_gdk_key_to_navigation_string (guint keyval) -+{ -+ /* TODO: expand */ -+ switch (keyval) { -+#define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key) -+ KEY (Up); -+ KEY (Down); -+ KEY (Left); -+ KEY (Right); -+ KEY (Home); -+ KEY (End); -+#undef KEY -+ default: -+ return NULL; -+ } -+} -+ -+static gboolean -+gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event) -+{ -+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); -+ GstElement *element; -+ -+ if ((element = g_weak_ref_get (&base_widget->element))) { -+ if (GST_IS_NAVIGATION (element)) { -+ const gchar *str = _gdk_key_to_navigation_string (event->keyval); -+ const gchar *key_type = -+ event->type == GDK_KEY_PRESS ? "key-press" : "key-release"; -+ -+ if (!str) -+ str = event->string; -+ -+ gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str); -+ } -+ g_object_unref (element); -+ } -+ -+ return FALSE; -+} -+ -+static void -+_fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget, -+ GtkAllocation * allocation, GstVideoRectangle * result) -+{ -+ if (base_widget->force_aspect_ratio) { -+ GstVideoRectangle src, dst; -+ -+ src.x = 0; -+ src.y = 0; -+ src.w = base_widget->display_width; -+ src.h = base_widget->display_height; -+ -+ dst.x = 0; -+ dst.y = 0; -+ dst.w = allocation->width; -+ dst.h = allocation->height; -+ -+ gst_video_sink_center_rect (src, dst, result, TRUE); -+ } else { -+ result->x = 0; -+ result->y = 0; -+ result->w = allocation->width; -+ result->h = allocation->height; -+ } -+} -+ -+void -+gtk_gst_base_widget_display_size_to_stream_size (GtkGstBaseWidget * base_widget, -+ gdouble x, gdouble y, gdouble * stream_x, gdouble * stream_y) -+{ -+ gdouble stream_width, stream_height; -+ GtkAllocation allocation; -+ GstVideoRectangle result; -+ -+ gtk_widget_get_allocation (GTK_WIDGET (base_widget), &allocation); -+ _fit_stream_to_allocated_size (base_widget, &allocation, &result); -+ -+ stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&base_widget->v_info); -+ stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&base_widget->v_info); -+ -+ /* from display coordinates to stream coordinates */ -+ if (result.w > 0) -+ *stream_x = (x - result.x) / result.w * stream_width; -+ else -+ *stream_x = 0.; -+ -+ /* clip to stream size */ -+ if (*stream_x < 0.) -+ *stream_x = 0.; -+ if (*stream_x > GST_VIDEO_INFO_WIDTH (&base_widget->v_info)) -+ *stream_x = GST_VIDEO_INFO_WIDTH (&base_widget->v_info); -+ -+ /* same for y-axis */ -+ if (result.h > 0) -+ *stream_y = (y - result.y) / result.h * stream_height; -+ else -+ *stream_y = 0.; -+ -+ if (*stream_y < 0.) -+ *stream_y = 0.; -+ if (*stream_y > GST_VIDEO_INFO_HEIGHT (&base_widget->v_info)) -+ *stream_y = GST_VIDEO_INFO_HEIGHT (&base_widget->v_info); -+ -+ GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y); -+} -+ -+static gboolean -+gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event) -+{ -+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); -+ GstElement *element; -+ -+ if ((element = g_weak_ref_get (&base_widget->element))) { -+ if (GST_IS_NAVIGATION (element)) { -+ const gchar *key_type = -+ event->type == -+ GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release"; -+ gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type, -+ event->button, event->x, event->y); -+ } -+ g_object_unref (element); -+ } -+ -+ return FALSE; -+} -+ -+static gboolean -+gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event) -+{ -+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); -+ GstElement *element; -+ -+ if ((element = g_weak_ref_get (&base_widget->element))) { -+ if (GST_IS_NAVIGATION (element)) { -+ gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move", -+ 0, event->x, event->y); -+ } -+ g_object_unref (element); -+ } -+ -+ return FALSE; -+} -+ -+static gboolean -+gtk_gst_base_widget_scroll_event (GtkWidget * widget, GdkEventScroll * event) -+{ -+#if GST_CHECK_VERSION(1, 18, 0) -+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget); -+ GstElement *element; -+ -+ if ((element = g_weak_ref_get (&base_widget->element))) { -+ if (GST_IS_NAVIGATION (element)) { -+ gdouble x, y, delta_x, delta_y; -+ -+ gtk_gst_base_widget_display_size_to_stream_size (base_widget, event->x, -+ event->y, &x, &y); -+ -+ if (!gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) { -+ gdouble offset = 20; -+ -+ delta_x = event->delta_x; -+ delta_y = event->delta_y; -+ -+ switch (event->direction) { -+ case GDK_SCROLL_UP: -+ delta_y = offset; -+ break; -+ case GDK_SCROLL_DOWN: -+ delta_y = -offset; -+ break; -+ case GDK_SCROLL_LEFT: -+ delta_x = -offset; -+ break; -+ case GDK_SCROLL_RIGHT: -+ delta_x = offset; -+ break; -+ default: -+ break; -+ } -+ } -+ gst_navigation_send_mouse_scroll_event (GST_NAVIGATION (element), -+ x, y, delta_x, delta_y); -+ } -+ g_object_unref (element); -+ } -+#endif -+ return FALSE; -+} -+ -+ -+void -+gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass) -+{ -+ GObjectClass *gobject_klass = (GObjectClass *) klass; -+ GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass; -+ -+ gobject_klass->set_property = gtk_gst_base_widget_set_property; -+ gobject_klass->get_property = gtk_gst_base_widget_get_property; -+ -+ g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO, -+ g_param_spec_boolean ("force-aspect-ratio", -+ "Force aspect ratio", -+ "When enabled, scaling will respect original aspect ratio", -+ DEFAULT_FORCE_ASPECT_RATIO, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO, -+ gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", -+ "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, -+ G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_klass, PROP_IGNORE_ALPHA, -+ g_param_spec_boolean ("ignore-alpha", "Ignore Alpha", -+ "When enabled, alpha will be ignored and converted to black", -+ DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width; -+ widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height; -+ widget_klass->key_press_event = gtk_gst_base_widget_key_event; -+ widget_klass->key_release_event = gtk_gst_base_widget_key_event; -+ widget_klass->button_press_event = gtk_gst_base_widget_button_event; -+ widget_klass->button_release_event = gtk_gst_base_widget_button_event; -+ widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event; -+ widget_klass->scroll_event = gtk_gst_base_widget_scroll_event; -+ -+ GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0, -+ "Gtk Video Base Widget"); -+} -+ -+void -+gtk_gst_base_widget_init (GtkGstBaseWidget * widget) -+{ -+ int event_mask; -+ -+ widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; -+ widget->par_n = DEFAULT_PAR_N; -+ widget->par_d = DEFAULT_PAR_D; -+ widget->ignore_alpha = DEFAULT_IGNORE_ALPHA; -+ -+ gst_video_info_init (&widget->v_info); -+ gst_video_info_init (&widget->pending_v_info); -+ -+ g_weak_ref_init (&widget->element, NULL); -+ g_mutex_init (&widget->lock); -+ -+ gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE); -+ event_mask = gtk_widget_get_events (GTK_WIDGET (widget)); -+ event_mask |= GDK_KEY_PRESS_MASK -+ | GDK_KEY_RELEASE_MASK -+ | GDK_BUTTON_PRESS_MASK -+ | GDK_BUTTON_RELEASE_MASK -+ | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK; -+ gtk_widget_set_events (GTK_WIDGET (widget), event_mask); -+} -+ -+void -+gtk_gst_base_widget_finalize (GObject * object) -+{ -+ GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object); -+ -+ gst_buffer_replace (&widget->pending_buffer, NULL); -+ gst_buffer_replace (&widget->buffer, NULL); -+ g_mutex_clear (&widget->lock); -+ g_weak_ref_clear (&widget->element); -+ -+ if (widget->draw_id) -+ g_source_remove (widget->draw_id); -+} -+ -+void -+gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget, -+ GstElement * element) -+{ -+ g_weak_ref_set (&widget->element, element); -+} -+ -+gboolean -+gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget, -+ GstVideoInfo * v_info) -+{ -+ GTK_GST_BASE_WIDGET_LOCK (widget); -+ -+ if (gst_video_info_is_equal (&widget->pending_v_info, v_info)) { -+ GTK_GST_BASE_WIDGET_UNLOCK (widget); -+ return TRUE; -+ } -+ -+ if (!_calculate_par (widget, v_info)) { -+ GTK_GST_BASE_WIDGET_UNLOCK (widget); -+ return FALSE; -+ } -+ -+ widget->pending_resize = TRUE; -+ widget->pending_v_info = *v_info; -+ -+ if (!widget->draw_id) { -+ widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT, -+ (GSourceFunc) _queue_draw, widget, NULL); -+ } -+ -+ GTK_GST_BASE_WIDGET_UNLOCK (widget); -+ -+ return TRUE; -+} -+ -+void -+gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer) -+{ -+ /* As we have no type, this is better then no check */ -+ g_return_if_fail (GTK_IS_WIDGET (widget)); -+ -+ GTK_GST_BASE_WIDGET_LOCK (widget); -+ -+ gst_buffer_replace (&widget->pending_buffer, buffer); -+ -+ if (!widget->draw_id) { -+ widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT, -+ (GSourceFunc) _queue_draw, widget, NULL); -+ } -+ -+ GTK_GST_BASE_WIDGET_UNLOCK (widget); -+} -diff --git a/ext/gtk/gtkgstbasewidget.h b/ext/gtk/gtkgstbasewidget.h -new file mode 100644 -index 0000000..c10ed38 ---- /dev/null -+++ b/ext/gtk/gtkgstbasewidget.h -@@ -0,0 +1,100 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef __GTK_GST_BASE_WIDGET_H__ -+#define __GTK_GST_BASE_WIDGET_H__ -+ -+#include -+#include -+#include -+ -+#define GTK_GST_BASE_WIDGET(w) ((GtkGstBaseWidget *)(w)) -+#define GTK_GST_BASE_WIDGET_CLASS(k) ((GtkGstBaseWidgetClass *)(k)) -+#define GTK_GST_BASE_WIDGET_LOCK(w) g_mutex_lock(&((GtkGstBaseWidget*)(w))->lock) -+#define GTK_GST_BASE_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkGstBaseWidget*)(w))->lock) -+ -+G_BEGIN_DECLS -+ -+typedef struct _GtkGstBaseWidget GtkGstBaseWidget; -+typedef struct _GtkGstBaseWidgetClass GtkGstBaseWidgetClass; -+ -+struct _GtkGstBaseWidget -+{ -+ union { -+ GtkDrawingArea drawing_area; -+#if GTK_CHECK_VERSION(3, 15, 0) -+ GtkGLArea gl_area; -+#endif -+ } parent; -+ -+ /* properties */ -+ gboolean force_aspect_ratio; -+ gint par_n, par_d; -+ gboolean ignore_alpha; -+ -+ gint display_width; -+ gint display_height; -+ -+ gboolean negotiated; -+ GstBuffer *pending_buffer; -+ GstBuffer *buffer; -+ GstVideoInfo v_info; -+ -+ /* resize */ -+ gboolean pending_resize; -+ GstVideoInfo pending_v_info; -+ guint display_ratio_num; -+ guint display_ratio_den; -+ -+ /*< private >*/ -+ GMutex lock; -+ GWeakRef element; -+ -+ /* Pending draw idles callback */ -+ guint draw_id; -+}; -+ -+struct _GtkGstBaseWidgetClass -+{ -+ union { -+ GtkDrawingAreaClass drawing_area_class; -+#if GTK_CHECK_VERSION(3, 15, 0) -+ GtkGLAreaClass gl_area_class; -+#endif -+ } parent_class; -+}; -+ -+/* For implementer */ -+void gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass); -+void gtk_gst_base_widget_init (GtkGstBaseWidget * widget); -+ -+void gtk_gst_base_widget_finalize (GObject * object); -+ -+/* API */ -+gboolean gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget, GstVideoInfo * v_info); -+void gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer); -+void gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget, GstElement * element); -+void gtk_gst_base_widget_display_size_to_stream_size (GtkGstBaseWidget * base_widget, -+ gdouble x, gdouble y, -+ gdouble * stream_x, gdouble * stream_y); -+ -+G_END_DECLS -+ -+#endif /* __GTK_GST_BASE_WIDGET_H__ */ -diff --git a/ext/gtk/gtkgstwaylandwidget.c b/ext/gtk/gtkgstwaylandwidget.c -new file mode 100644 -index 0000000..df3d1d5 ---- /dev/null -+++ b/ext/gtk/gtkgstwaylandwidget.c -@@ -0,0 +1,67 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gtkgstwaylandwidget.h" -+#include -+ -+/** -+ * SECTION:gtkgstwidget -+ * @title: GtkGstWaylandWidget -+ * @short_description: a #GtkWidget that renders GStreamer video #GstBuffers -+ * @see_also: #GtkDrawingArea, #GstBuffer -+ * -+ * #GtkGstWaylandWidget is an #GtkWidget that renders GStreamer video buffers. -+ */ -+ -+G_DEFINE_TYPE (GtkGstWaylandWidget, gtk_gst_wayland_widget, -+ GTK_TYPE_DRAWING_AREA); -+ -+static void -+gtk_gst_wayland_widget_finalize (GObject * object) -+{ -+ gtk_gst_base_widget_finalize (object); -+ -+ G_OBJECT_CLASS (gtk_gst_wayland_widget_parent_class)->finalize (object); -+} -+ -+static void -+gtk_gst_wayland_widget_class_init (GtkGstWaylandWidgetClass * klass) -+{ -+ GObjectClass *gobject_klass = (GObjectClass *) klass; -+ -+ gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass)); -+ gobject_klass->finalize = gtk_gst_wayland_widget_finalize; -+} -+ -+static void -+gtk_gst_wayland_widget_init (GtkGstWaylandWidget * widget) -+{ -+ gtk_gst_base_widget_init (GTK_GST_BASE_WIDGET (widget)); -+} -+ -+GtkWidget * -+gtk_gst_wayland_widget_new (void) -+{ -+ return (GtkWidget *) g_object_new (GTK_TYPE_GST_WAYLAND_WIDGET, NULL); -+} -diff --git a/ext/gtk/gtkgstwaylandwidget.h b/ext/gtk/gtkgstwaylandwidget.h -new file mode 100644 -index 0000000..48cfe9e ---- /dev/null -+++ b/ext/gtk/gtkgstwaylandwidget.h -@@ -0,0 +1,68 @@ -+/* -+ * GStreamer -+ * Copyright (C) 2015 Matthew Waters -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this library; if not, write to the -+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -+ * Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef __GTK_GST_WAYLAND_WIDGET_H__ -+#define __GTK_GST_WAYLAND_WIDGET_H__ -+ -+#include -+#include -+ -+#include "gtkgstbasewidget.h" -+ -+G_BEGIN_DECLS -+ -+GType gtk_gst_wayland_widget_get_type (void); -+#define GTK_TYPE_GST_WAYLAND_WIDGET (gtk_gst_wayland_widget_get_type()) -+#define GTK_GST_WAYLAND_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_WAYLAND_WIDGET,GtkGstWaylandWidget)) -+#define GTK_GST_WAYLAND_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_WAYLAND_WIDGET,GtkGstWaylandWidgetClass)) -+#define GTK_IS_GST_WAYLAND_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_WAYLAND_WIDGET)) -+#define GTK_IS_GST_WAYLAND_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_WAYLAND_WIDGET)) -+#define GTK_GST_WAYLAND_WIDGET_CAST(obj) ((GtkGstWaylandWidget*)(obj)) -+ -+typedef struct _GtkGstWaylandWidget GtkGstWaylandWidget; -+typedef struct _GtkGstWaylandWidgetClass GtkGstWaylandWidgetClass; -+ -+/** -+ * GtkGstWaylandWidget: -+ * -+ * Opaque #GtkGstWaylandWidget object -+ */ -+struct _GtkGstWaylandWidget -+{ -+ /* */ -+ GtkGstBaseWidget base; -+}; -+ -+/** -+ * GtkGstWaylandWidgetClass: -+ * -+ * The #GtkGstWaylandWidgetClass struct only contains private data -+ */ -+struct _GtkGstWaylandWidgetClass -+{ -+ /* */ -+ GtkGstBaseWidgetClass base_class; -+}; -+ -+GtkWidget * gtk_gst_wayland_widget_new (void); -+ -+G_END_DECLS -+ -+#endif /* __GTK_GST_WAYLAND_WIDGET_H__ */ -diff --git a/ext/gtk/meson.build b/ext/gtk/meson.build -new file mode 100644 -index 0000000..199188d ---- /dev/null -+++ b/ext/gtk/meson.build -@@ -0,0 +1,22 @@ -+gtkwayland_sources = [ -+ 'gstplugin.c', -+ 'gstgtkutils.c', -+ 'gstgtkwaylandsink.c', -+ 'gtkgstbasewidget.c', -+ 'gtkgstwaylandwidget.c', -+] -+ -+gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3')) -+gtk_wayland_dep = dependency('gtk+-wayland-3.0', required : get_option('gtk3')) -+ -+if gtk_dep.found() and gtk_wayland_dep.found() and use_wayland -+ gstgtkwayland = library('gstgtkwayland', -+ gtkwayland_sources, -+ c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'], -+ include_directories : [configinc], -+ dependencies : [gtk_dep, gstvideo_dep, gstwayland_dep], -+ install : true, -+ install_dir : plugins_install_dir, -+ ) -+ pkgconfig.generate(gstgtkwayland, install_dir : plugins_pkgconfig_install_dir) -+endif -diff --git a/ext/meson.build b/ext/meson.build -index 1e40ace..17195f8 100644 ---- a/ext/meson.build -+++ b/ext/meson.build -@@ -21,6 +21,7 @@ subdir('fluidsynth') - subdir('gme') - subdir('gs') - subdir('gsm') -+subdir('gtk') - subdir('hls') - subdir('iqa') - subdir('isac') -diff --git a/meson_options.txt b/meson_options.txt -index b347dcb..c64f292 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -29,6 +29,7 @@ option('frei0r', type : 'feature', value : 'auto') - option('gaudieffects', type : 'feature', value : 'auto') - option('gdp', type : 'feature', value : 'auto') - option('geometrictransform', type : 'feature', value : 'auto') -+option('gtk3', type : 'feature', value : 'auto') - option('id3tag', type : 'feature', value : 'auto') - option('inter', type : 'feature', value : 'auto') - option('interlace', type : 'feature', value : 'auto') --- -2.34.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-gstwlshmallocator-correct-WL-API-declaration.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-gstwlshmallocator-correct-WL-API-declaration.patch new file mode 100644 index 000000000..ba6f694d9 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0016-gstwlshmallocator-correct-WL-API-declaration.patch @@ -0,0 +1,26 @@ +From a028c20bb3ad4f17b60afacb2a5f2a624432b2d4 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 14:42:17 +0200 +Subject: [PATCH 16/17] gstwlshmallocator: correct WL API declaration + +Signed-off-by: Christophe Priouzeau +Upstream-Status: Pending +--- + gst-libs/gst/wayland/gstwlshmallocator.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/gst-libs/gst/wayland/gstwlshmallocator.h b/gst-libs/gst/wayland/gstwlshmallocator.h +index 0e7e15d..dcbea6c 100644 +--- a/gst-libs/gst/wayland/gstwlshmallocator.h ++++ b/gst-libs/gst/wayland/gstwlshmallocator.h +@@ -30,6 +30,7 @@ + G_BEGIN_DECLS + + #define GST_TYPE_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get_type ()) ++GST_WL_API + G_DECLARE_FINAL_TYPE (GstWlShmAllocator, gst_wl_shm_allocator, GST, WL_SHM_ALLOCATOR, GstFdAllocator); + + #define GST_ALLOCATOR_WL_SHM "wl_shm" +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch new file mode 100644 index 000000000..35f88fea8 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch @@ -0,0 +1,29 @@ +From 31d07ae72c3b78936bd4a385f1f5a72a2171da8b Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 14:46:04 +0200 +Subject: [PATCH 17/17] sts/3801>gtkwaylandsink: Destroy GstWlWindow when + parent GtkWindow is destroyed + +Part-of: +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index e32bbcc..9691255 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -961,6 +961,9 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + if (!gst_wl_display_check_format_for_dmabuf (priv->display, format)) + goto unsupported_format; + } else if (!gst_wl_display_check_format_for_shm (priv->display, format)) { ++ /* Note: we still support dmabuf in this case, but formats must also be ++ * supported on SHM interface to ensure a fallback is possible as we are ++ * not guarantied we'll get dmabuf in the buffers. */ + goto unsupported_format; + } + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-waylandsink-fix-shm-pool-wrongly-selected-with-some-.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-waylandsink-fix-shm-pool-wrongly-selected-with-some-.patch deleted file mode 100644 index cbd84a34d..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0017-waylandsink-fix-shm-pool-wrongly-selected-with-some-.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 2eb1cc3d1af83020cfbce5193b1a273078257668 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Tue, 11 Oct 2022 12:26:15 +0200 -Subject: [PATCH] waylandsink: fix shm pool wrongly selected with some - GStreamer elements - -With some GStreamer elements, and depending on their position in -pipeline, propose_allocation() may be called right before set_caps(), -leading to use sink->use_dmabuf while not being set. -Fix this by checking for dmabuf format also in propose_allocation() -instead of relying on sink->use_dmabuf. - -Example of pipeline which underline the problem: -gst-launch-1.0 filesrc location= ! pngdec ! videoconvert ! imagefreeze ! waylandsink -Problem is not reproduced with this pipeline: -gst-launch-1.0 filesrc location= ! pngdec ! imagefreeze ! videoconvert ! waylandsink - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - ext/wayland/gstwaylandsink.c | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 1f68546..3bbb736 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -688,8 +688,20 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) - GstAllocator *alloc; - GstCaps *pcaps; - guint config_min_buf, config_max_buf; -+ gboolean use_dmabuf; -+ GstVideoFormat format; -+ -+ format = GST_VIDEO_INFO_FORMAT (&sink->video_info); - -- if (!sink->use_dmabuf) -+ /* Force usage of dmabuf buffer pool by default, ie -+ * even if video/x-raw(memory:DMABuf) is not set in caps. -+ * This allows 0-copy path with software downstream elements -+ * (use of dmabuf buffers mmapped memory directly instead -+ * of copying) -+ */ -+ use_dmabuf = gst_wl_display_check_format_for_dmabuf (sink->display, format); -+ -+ if (!use_dmabuf) - return gst_wayland_sink_propose_shm_allocation(bsink, query); - - /* --- -2.25.1 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch new file mode 100644 index 000000000..e45db3f94 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch @@ -0,0 +1,26 @@ +From 11f6e311f37ca4aaf5bafb9bc646686905c5c377 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Wed, 29 Mar 2023 18:28:39 +0200 +Subject: [PATCH] GTKWAYLANDSINK: use card0 as default drm-device + +Upstream-Status: Pending +--- + ext/gtk/gstgtkwaylandsink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 9691255..985ac07 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -173,7 +173,7 @@ gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass) + g_object_class_install_property (gobject_class, PROP_DRM_DEVICE, + g_param_spec_string ("drm-device", "DRM Device", "Path of the " + "DRM device to use for dumb buffer allocation", +- NULL, ++ "/dev/dri/card0", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + + gstelement_class->change_state = +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch new file mode 100644 index 000000000..414ca5db9 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch @@ -0,0 +1,66 @@ +From f7b4ff8313d4e2197bdb91d21cd0a7beb59b7b7c Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 14 Jun 2023 11:24:49 +0200 +Subject: [PATCH 20/21] gtkwaylandsink: do not use drm dumb pool with DMAbuf + buffers + +There is no need to use our DRM dumb pool if buffer to +render is already a DMABuf, just import it and render it. + +This fixes a DMAbuf memory leakage identfied with command: +watch "cat /sys/kernel/debug/dma_buf/bufinfo | grep attached " + +Signed-off-by: Hugues Fruchet +Upstream-Status: Submitted +--- + ext/gtk/gstgtkwaylandsink.c | 6 +++++- + ext/wayland/gstwaylandsink.c | 6 +++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 2debee6..7537200 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1175,12 +1175,16 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, + &priv->video_info); + ++ if (wbuf) ++ GST_LOG_OBJECT (self, ++ "buffer %" GST_PTR_FORMAT " is DMABuf", buffer); ++ + /* DMABuf did not work, let try and make this a dmabuf, it does not matter + * if it was a SHM since the compositor needs to copy that anyway, and + * offloading the compositor from a copy helps maintaining a smoother + * desktop. + */ +- if (!priv->skip_dumb_buffer_copy) { ++ if (!wbuf && !priv->skip_dumb_buffer_copy) { + GstVideoFrame src, dst; + + if (!gst_gtk_wayland_activate_drm_dumb_pool (self)) { +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 24a7174..1662241 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -907,12 +907,16 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, + &self->video_info); + ++ if (wbuf) ++ GST_LOG_OBJECT (self, ++ "buffer %" GST_PTR_FORMAT " is DMABuf", buffer); ++ + /* DMABuf did not work, let try and make this a dmabuf, it does not matter + * if it was a SHM since the compositor needs to copy that anyway, and + * offloading the compositor from a copy helps maintaining a smoother + * desktop. + */ +- if (!self->skip_dumb_buffer_copy) { ++ if (!wbuf && !self->skip_dumb_buffer_copy) { + GstVideoFrame src, dst; + + if (!gst_wayland_activate_drm_dumb_pool (self)) { +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch new file mode 100644 index 000000000..604b9cc39 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch @@ -0,0 +1,651 @@ +From ebb197aacf8cb21a79704851b045d28592848953 Mon Sep 17 00:00:00 2001 +From: Benjamin Gaignard +Date: Mon, 16 Jan 2023 17:34:10 +0100 +Subject: [PATCH 1/5] codecs: Add base class for stateless vp8 encoder + +Add stateless vp8 encoder base class and vp8 frame structure. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gstvp8encoder.c | 351 ++++++++++++++++++++++++++++ + gst-libs/gst/codecs/gstvp8encoder.h | 81 +++++++ + gst-libs/gst/codecs/gstvp8frame.c | 66 ++++++ + gst-libs/gst/codecs/gstvp8frame.h | 70 ++++++ + gst-libs/gst/codecs/meson.build | 7 +- + 5 files changed, 574 insertions(+), 1 deletion(-) + create mode 100644 gst-libs/gst/codecs/gstvp8encoder.c + create mode 100644 gst-libs/gst/codecs/gstvp8encoder.h + create mode 100644 gst-libs/gst/codecs/gstvp8frame.c + create mode 100644 gst-libs/gst/codecs/gstvp8frame.h + +diff --git a/gst-libs/gst/codecs/gstvp8encoder.c b/gst-libs/gst/codecs/gstvp8encoder.c +new file mode 100644 +index 0000000..b105786 +--- /dev/null ++++ b/gst-libs/gst/codecs/gstvp8encoder.c +@@ -0,0 +1,351 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gstvp8encoder.h" ++ ++#include ++#include ++#include ++ ++GST_DEBUG_CATEGORY (gst_vp8_encoder_debug); ++#define GST_CAT_DEFAULT gst_vp8_encoder_debug ++ ++#define VP8ENC_DEFAULT_KEYFRAME_INTERVAL 30 ++ ++#define VP8_MAX_QUALITY 63 ++#define VP8_MIN_QUALITY 0 ++ ++#define VP8_DEFAULT_BITRATE 100000 ++ ++enum ++{ ++ PROP_0, ++ PROP_KEYFRAME_INTERVAL, ++ PROP_MAX_QUALITY, ++ PROP_MIN_QUALITY, ++ PROP_BITRATE, ++}; ++ ++struct _GstVp8EncoderPrivate ++{ ++ gint keyframe_interval; ++ ++ guint32 last_keyframe; ++ ++ guint64 targeted_bitrate; ++ gint max_quality; ++ gint min_quality; ++ gint current_quality; ++ guint64 used_bytes; ++ guint64 nb_frames; ++}; ++ ++#define parent_class gst_vp8_encoder_parent_class ++G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVp8Encoder, gst_vp8_encoder, ++ GST_TYPE_VIDEO_ENCODER, ++ G_ADD_PRIVATE (GstVp8Encoder); ++ GST_DEBUG_CATEGORY_INIT (gst_vp8_encoder_debug, "vp8encoder", 0, ++ "Vp8 Video Encoder")); ++ ++static void ++gst_vp8_encoder_init (GstVp8Encoder * self) ++{ ++ self->priv = gst_vp8_encoder_get_instance_private (self); ++} ++ ++static void ++gst_vp8_encoder_finalize (GObject * object) ++{ ++ G_OBJECT_CLASS (parent_class)->finalize (object); ++} ++ ++static gboolean ++gst_vp8_encoder_start (GstVideoEncoder * encoder) ++{ ++ GstVp8Encoder *self = GST_VP8_ENCODER (encoder); ++ GstVp8EncoderPrivate *priv = self->priv; ++ ++ priv->last_keyframe = 0; ++ priv->current_quality = priv->min_quality; ++ priv->used_bytes = 0; ++ priv->nb_frames = 0; ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_vp8_encoder_stop (GstVideoEncoder * encoder) ++{ ++ return TRUE; ++} ++ ++static gboolean ++gst_vp8_encoder_set_format (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) ++{ ++ return TRUE; ++} ++ ++static GstFlowReturn ++gst_vp8_encoder_set_quality (GstVp8Encoder * self, GstVp8Frame * vp8_frame) ++{ ++ GstVp8EncoderPrivate *priv = self->priv; ++ GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self); ++ GstVideoCodecState *output_state = ++ gst_video_encoder_get_output_state (encoder); ++ gint qp = priv->current_quality; ++ guint64 bitrate = 0; ++ guint fps_n = 30, fps_d = 1; ++ ++ if (output_state == NULL) ++ return qp; ++ ++ if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) { ++ fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info); ++ fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info); ++ } ++ gst_video_codec_state_unref (output_state); ++ ++ bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d); ++ if (bitrate > priv->targeted_bitrate) { ++ qp++; ++ } ++ ++ if (bitrate < priv->targeted_bitrate) { ++ qp--; ++ } ++ ++ if (qp > priv->max_quality) ++ qp = priv->max_quality; ++ if (qp < priv->min_quality) ++ qp = priv->min_quality; ++ ++ vp8_frame->quality = qp; ++ ++ return GST_FLOW_OK; ++} ++ ++static GstFlowReturn ++gst_vp8_encoder_set_frame_type (GstVp8Encoder * self, GstVp8Frame * vp8_frame) ++{ ++ GstVp8EncoderPrivate *priv = self->priv; ++ GstVideoCodecFrame *frame = vp8_frame->frame; ++ ++ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { ++ vp8_frame->type = GstVp8Keyframe; ++ return GST_FLOW_OK; ++ } ++ ++ if ((frame->system_frame_number - priv->last_keyframe) > ++ priv->keyframe_interval || frame->system_frame_number == 0) { ++ /* Generate a keyframe */ ++ GST_DEBUG_OBJECT (self, "Generate a keyframe"); ++ vp8_frame->type = GstVp8Keyframe; ++ return GST_FLOW_OK; ++ } ++ ++ /* Generate a interframe */ ++ GST_DEBUG_OBJECT (self, "Generate a interframe"); ++ vp8_frame->type = GstVp8Inter; ++ return GST_FLOW_OK; ++} ++ ++static void ++gst_vp8_encoder_mark_frame (GstVp8Encoder * self, GstVp8Frame * vp8_frame) ++{ ++ GstVideoCodecFrame *frame = vp8_frame->frame; ++ GstVp8EncoderPrivate *priv = self->priv; ++ ++ switch (vp8_frame->type) { ++ case GstVp8Keyframe: ++ priv->last_keyframe = frame->system_frame_number; ++ break; ++ } ++ ++ priv->current_quality = vp8_frame->quality; ++ priv->used_bytes += gst_buffer_get_size (frame->output_buffer); ++ priv->nb_frames++; ++} ++ ++static GstFlowReturn ++gst_vp8_encoder_handle_frame (GstVideoEncoder * encoder, ++ GstVideoCodecFrame * frame) ++{ ++ GstVp8Encoder *self = GST_VP8_ENCODER (encoder); ++ GstVp8EncoderClass *klass = GST_VP8_ENCODER_GET_CLASS (self); ++ GstFlowReturn ret = GST_FLOW_OK; ++ GstVp8Frame *vp8_frame = gst_vp8_frame_new (frame); ++ ++ ret = gst_vp8_encoder_set_frame_type (self, vp8_frame); ++ if (ret != GST_FLOW_OK) ++ return ret; ++ ++ ret = gst_vp8_encoder_set_quality (self, vp8_frame); ++ if (ret != GST_FLOW_OK) ++ return ret; ++ ++ /* TODO: add encoding parameters management here ++ * for now just send the frame to encode */ ++ if (klass->encode_frame) { ++ ret = klass->encode_frame (self, vp8_frame); ++ if (ret == GST_FLOW_OK) ++ gst_vp8_encoder_mark_frame (self, vp8_frame); ++ } ++ ++ gst_vp8_frame_unref (vp8_frame); ++ ++ return ret; ++} ++ ++static void ++gst_vp8_encoder_get_property (GObject * object, guint property_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstVp8Encoder *self = GST_VP8_ENCODER (object); ++ GstVp8EncoderPrivate *priv = self->priv; ++ ++ switch (property_id) { ++ case PROP_KEYFRAME_INTERVAL: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->keyframe_interval); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MAX_QUALITY: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->max_quality); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MIN_QUALITY: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->min_quality); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_BITRATE: ++ GST_OBJECT_LOCK (self); ++ g_value_set_uint64 (value, priv->targeted_bitrate); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_vp8_encoder_set_property (GObject * object, guint property_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstVp8Encoder *self = GST_VP8_ENCODER (object); ++ GstVp8EncoderPrivate *priv = self->priv; ++ ++ switch (property_id) { ++ case PROP_KEYFRAME_INTERVAL: ++ GST_OBJECT_LOCK (self); ++ priv->keyframe_interval = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MAX_QUALITY: ++ GST_OBJECT_LOCK (self); ++ priv->max_quality = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MIN_QUALITY: ++ GST_OBJECT_LOCK (self); ++ priv->min_quality = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_BITRATE: ++ GST_OBJECT_LOCK (self); ++ priv->targeted_bitrate = g_value_get_uint64 (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_vp8_encoder_class_init (GstVp8EncoderClass * klass) ++{ ++ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = GST_DEBUG_FUNCPTR (gst_vp8_encoder_finalize); ++ object_class->get_property = gst_vp8_encoder_get_property; ++ object_class->set_property = gst_vp8_encoder_set_property; ++ ++ encoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_encoder_start); ++ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_encoder_stop); ++ encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp8_encoder_set_format); ++ encoder_class->handle_frame = ++ GST_DEBUG_FUNCPTR (gst_vp8_encoder_handle_frame); ++ ++ /** ++ * GstVp8Encoder:keyframe-interval: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL, ++ g_param_spec_int ("keyframe-interval", "Keyframe Interval", ++ "Interval between keyframes", ++ 0, G_MAXINT, VP8ENC_DEFAULT_KEYFRAME_INTERVAL, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstVp8Encoder:max-quality: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_MAX_QUALITY, ++ g_param_spec_int ("max-quality", "Max Quality Level", ++ "Set upper quality limit (lower number equates to higher quality but more bits)", ++ VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MAX_QUALITY, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstVp8Encoder:min-quality: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_MIN_QUALITY, ++ g_param_spec_int ("min-quality", "Min Quality Level", ++ "Set lower quality limit (lower number equates to higher quality but more bits)", ++ VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MIN_QUALITY, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstVp8Encoder:bitrate: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_BITRATE, ++ g_param_spec_uint64 ("bitrate", "Targeted bitrate", ++ "Set bitrate target", ++ 0, UINT_MAX, VP8_DEFAULT_BITRATE, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++} +diff --git a/gst-libs/gst/codecs/gstvp8encoder.h b/gst-libs/gst/codecs/gstvp8encoder.h +new file mode 100644 +index 0000000..3999ed3 +--- /dev/null ++++ b/gst-libs/gst/codecs/gstvp8encoder.h +@@ -0,0 +1,81 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GST_VP8_ENCODER_H__ ++#define __GST_VP8_ENCODER_H__ ++ ++#include ++ ++#include ++#include ++ ++#include "gstvp8frame.h" ++ ++G_BEGIN_DECLS ++#define GST_TYPE_VP8_ENCODER (gst_vp8_encoder_get_type()) ++#define GST_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_ENCODER,GstVp8Encoder)) ++#define GST_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass)) ++#define GST_VP8_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass)) ++#define GST_IS_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_ENCODER)) ++#define GST_IS_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_ENCODER)) ++#define GST_VP8_ENCODER_CAST(obj) ((GstVp8Encoder*)obj) ++typedef struct _GstVp8Encoder GstVp8Encoder; ++typedef struct _GstVp8EncoderClass GstVp8EncoderClass; ++typedef struct _GstVp8EncoderPrivate GstVp8EncoderPrivate; ++ ++/** ++ * GstVp8Encoder: ++ * ++ * The opaque #GstVp8Encoder data structure. ++ */ ++struct _GstVp8Encoder ++{ ++ /*< private > */ ++ GstVideoEncoder parent; ++ ++ /*< private > */ ++ GstVp8EncoderPrivate *priv; ++ gpointer padding[GST_PADDING_LARGE]; ++}; ++ ++/** ++ * GstVp8EncoderClass: ++ */ ++struct _GstVp8EncoderClass ++{ ++ GstVideoEncoderClass parent_class; ++ ++ /** ++ * GstVp8EncoderClass::encode_frame: ++ * @encoder: a #GstVp8Encoder ++ * @frame: a #GstVp8Frame ++ * ++ * Provide the frame to be encoded with the encode parameters (to be defined) ++ */ ++ GstFlowReturn (*encode_frame) (GstVp8Encoder * encoder, ++ GstVp8Frame * frame); ++ /*< private > */ ++ gpointer padding[GST_PADDING_LARGE]; ++}; ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstVp8Encoder, gst_object_unref) ++ GST_CODECS_API GType gst_vp8_encoder_get_type (void); ++ ++G_END_DECLS ++#endif /* __GST_VP8_ENCODER_H__ */ +diff --git a/gst-libs/gst/codecs/gstvp8frame.c b/gst-libs/gst/codecs/gstvp8frame.c +new file mode 100644 +index 0000000..6f26bd5 +--- /dev/null ++++ b/gst-libs/gst/codecs/gstvp8frame.c +@@ -0,0 +1,66 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "gstvp8frame.h" ++ ++GST_DEBUG_CATEGORY_EXTERN (gst_vp8_encoder_debug); ++#define GST_CAT_DEFAULT gst_vp8_encoder_debug ++ ++GST_DEFINE_MINI_OBJECT_TYPE (GstVp8Frame, gst_vp8_frame); ++ ++static void ++_gst_vp8_frame_free (GstVp8Frame * frame) ++{ ++ GST_TRACE ("Free frame %p", frame); ++ ++ gst_video_codec_frame_unref (frame->frame); ++ ++ g_free (frame); ++} ++ ++/** ++ * gst_vp8_frame_new: ++ * ++ * Create new #GstVp8Frame ++ * ++ * Returns: a new #GstVp8Frame ++ */ ++GstVp8Frame * ++gst_vp8_frame_new (GstVideoCodecFrame * f) ++{ ++ GstVp8Frame *frame; ++ ++ if (!f) ++ return NULL; ++ ++ frame = g_new0 (GstVp8Frame, 1); ++ ++ gst_mini_object_init (GST_MINI_OBJECT_CAST (frame), 0, ++ GST_TYPE_VP8_FRAME, NULL, NULL, ++ (GstMiniObjectFreeFunction) _gst_vp8_frame_free); ++ ++ frame->frame = gst_video_codec_frame_ref (f); ++ ++ GST_TRACE ("New frame %p", frame); ++ ++ return frame; ++} +diff --git a/gst-libs/gst/codecs/gstvp8frame.h b/gst-libs/gst/codecs/gstvp8frame.h +new file mode 100644 +index 0000000..a3d5c21 +--- /dev/null ++++ b/gst-libs/gst/codecs/gstvp8frame.h +@@ -0,0 +1,70 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GTS_VP8_FRAME_H__ ++#define __GTS_VP8_FRAME_H__ ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_VP8_FRAME (gst_vp8_frame_get_type()) ++#define GST_IS_VP8_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_VP8_FRAME)) ++#define GST_VP8_FRAME(obj) ((GstVp8Frame *)obj) ++#define GST_VP8_FRAME_CAST(obj) (GST_VP8_FRAME(obj)) ++ ++typedef struct _GstVp8Frame GstVp8Frame; ++ ++enum ++{ ++ GstVp8Keyframe, ++ GstVp8Inter, ++}; ++ ++struct _GstVp8Frame ++{ ++ GstMiniObject parent; ++ gint type; ++ gint quality; ++ ++ GstVideoCodecFrame *frame; ++}; ++ ++GST_CODECS_API ++GType gst_vp8_frame_get_type (void); ++ ++GST_CODECS_API ++GstVp8Frame * gst_vp8_frame_new (GstVideoCodecFrame *f); ++ ++static inline GstVp8Frame * ++gst_vp8_frame_ref (GstVp8Frame * frame) ++{ ++ return (GstVp8Frame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame)); ++} ++ ++static inline void ++gst_vp8_frame_unref (GstVp8Frame * frame) ++{ ++ gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame)); ++} ++ ++G_END_DECLS ++ ++#endif /* __GTS_VP8_FRAME_H__ */ +diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build +index f2bec85..458d90d 100644 +--- a/gst-libs/gst/codecs/meson.build ++++ b/gst-libs/gst/codecs/meson.build +@@ -12,6 +12,8 @@ codecs_sources = files( + 'gstav1decoder.c', + 'gstav1picture.c', + 'gstvp9statefulparser.c', ++ 'gstvp8encoder.c', ++ 'gstvp8frame.c', + ) + + codecs_headers = files( +@@ -28,6 +30,8 @@ codecs_headers = files( + 'gstav1decoder.h', + 'gstav1picture.h', + 'gstvp9statefulparser.h', ++ 'gstvp8encoder.h', ++ 'gstvp8frame.h', + ) + + cp_args = [ +@@ -66,6 +70,7 @@ if build_gir + '--c-include=gst/codecs/gstvp9decoder.h', + '--c-include=gst/codecs/gstvp8decoder.h', + '--c-include=gst/codecs/gstmpeg2decoder.h', ++ '--c-include=gst/codecs/gstvp8encoder.h', + ], + 'dependencies' : [gstvideo_dep, gstcodecparsers_dep] + } +@@ -81,4 +86,4 @@ gstcodecs_dep = declare_dependency(link_with : gstcodecs, + include_directories : [libsinc], + sources: gen_sources, + dependencies : [gstvideo_dep, gstcodecparsers_dep]) +-meson.override_dependency(pkg_name, gstcodecs_dep) +\ No newline at end of file ++meson.override_dependency(pkg_name, gstcodecs_dep) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch new file mode 100644 index 000000000..993095447 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch @@ -0,0 +1,131 @@ +From 3addb9d9cc00a6f4bba6d57cee26ce8e47ee1f3d Mon Sep 17 00:00:00 2001 +From: Benjamin Gaignard +Date: Mon, 16 Jan 2023 17:27:35 +0100 +Subject: [PATCH 2/5] v4l2codecs: Add V4L2 VP8 stateless encode uAPI + +Udpate v4l2 header files with the definitions and structures +used for stateless VP8 encode. +Upstream-Status: Pending +--- + sys/v4l2codecs/linux/v4l2-controls.h | 91 ++++++++++++++++++++++++++++ + sys/v4l2codecs/linux/videodev2.h | 2 + + 2 files changed, 93 insertions(+) + +diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h +index 79625ee..8838a5d 100644 +--- a/sys/v4l2codecs/linux/v4l2-controls.h ++++ b/sys/v4l2codecs/linux/v4l2-controls.h +@@ -2771,6 +2771,97 @@ struct v4l2_ctrl_vp9_compressed_hdr { + struct v4l2_vp9_mv_probs mv; + }; + ++#define V4L2_CID_STATELESS_VP8_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 501) ++ ++#define V4L2_VP8_FRAME_FLAG_SHOWFRAME 0x1 ++#define V4L2_VP8_FRAME_FLAG_GOLDEN_REFRESH 0x2 ++#define V4L2_VP8_FRAME_FLAG_ALTREF_REFRESH 0x4 ++#define V4L2_VP8_FRAME_FLAG_SEGMENT_ENABLED 0x8 ++#define V4L2_VP8_FRAME_FLAG_LOOP_FILTER_ADJ_ENABLED 0x10 ++#define V4L2_VP8_FRAME_FLAG_REFRESH_ENTROPY_PROBS 0x20 ++ ++#define V4L2_VP8_FRAME_TYPE_KEYFRAME 0 ++#define V4L2_VP8_FRAME_TYPE_INTER 1 ++ ++#define V4L2_VP8_FRAME_COLOR_SPACE_YUV 0 ++#define V4L2_VP8_FRAME_COLOR_SPACE_RESERVED 1 ++ ++#define V4L2_VP8_FRAME_CLAMPING_REQUIRED 0 ++#define V4L2_VP8_FRAME_CLAMPING_NO 1 ++ ++#define V4L2_VP8_FRAME_FILTER_TYPE_NORMAL 0 ++#define V4L2_VP8_FRAME_FILTER_TYPE_SIMPLE 1 ++ ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_1 0 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_2 1 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_4 2 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_8 3 ++ ++#define V4L2_VP8_FRAME_GOLDEN_KEEP 0 ++#define V4L2_VP8_FRAME_GOLDEN_LASTFRAME 1 ++#define V4L2_VP8_FRAME_GOLDEN_ALTREF 2 ++ ++#define V4L2_VP8_FRAME_ALTREF_KEEP 0 ++#define V4L2_VP8_FRAME_ALTREF_LASTFRAME 1 ++#define V4L2_VP8_FRAME_ALTREF_GOLDEN 2 ++ ++#define V4L2_VP8_FRAME_REF_LAST 0 ++#define V4L2_VP8_FRAME_REF_GOLDEN 1 ++#define V4L2_VP8_FRAME_REF_ALT 2 ++ ++/** ++ * struct v4l2_ctrl_vp8_encode_params - VP8 encode parameters ++ * @flags: combination of V4L2_VP8_FRAME_FLAG_{} flags. ++ * @frame_type: specifies the frame type (key or inter). ++ * Set to one of V4L2_VP8_FRAME_TYPE_{}. ++ * @color_space: defines the YUV color space of the sequence. ++ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_COLOR_SPACE_{}. ++ * @clamping_type: defines pixel value clamping type. ++ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_CLAMPING_{}. ++ * @loop_filter_type: selects the type of loop filter applied. ++ * Set to one of V4L2_VP8_FRAME_FILTER_TYPE_{}. ++ * @loop_filter_level: sets the strength of the applied loop filter. ++ * Set to a value from the rage 0..63. ++ * @sharpness_level: sets the sharpness of the applied loop filter. ++ * Set to a value from the range 0..7. ++ * @log2_nbr_of_dct_partitions: determines the number of separate partitions ++ * containing the DCT coefficients of macroblocks. ++ * Set to one of V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_{}. ++ * @prob_intra: indicates the probability of an intra macroblock. ++ * Set to a value from the range 0..255. ++ * @prob_last: indicates the probability that the last reference frame is used for inter-prediction. ++ * Set to a value from the range 0..255. ++ * @prob_gf: indicates the probability that the golden reference frame is used for inter-prediction. ++ * Set to a value from the range 0..255. ++ * @copy_buffer_to_golden: specifies the golden frame refresh strategy. ++ * Set to one of V4L2_VP8_FRAME_FLAG_GOLDEN_{}. ++ * @copy_buffer_to_alternate: specifies the atlref frame refresh strategy. ++ * Set to one of V4L2_VP8_FRAME_FLAG_ALTREF_{}. ++ * @reference_type: specifies what kind of reference to use for current inter frame. ++ * V4L2_VP8_FRAME_TYPE_KEYFRAME shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_REF_{}. ++ */ ++struct v4l2_ctrl_vp8_encode_params { ++ __u32 flags; ++ __u8 frame_type; ++ __u8 color_space; ++ __u8 clamping_type; ++ __u8 loop_filter_type; ++ __u8 loop_filter_level; ++ __u8 sharpness_level; ++ __u8 log2_nbr_of_dct_partitions; ++ __u8 prob_intra; ++ __u8 prob_last; ++ __u8 prob_gf; ++ __u8 copy_buffer_to_golden; ++ __u8 copy_buffer_to_alternate; ++ __u8 reference_type; ++}; ++ ++#define V4L2_CID_STATELESS_VP8_ENCODE_QP (V4L2_CID_CODEC_STATELESS_BASE + 502) ++ + /* MPEG-compression definitions kept for backwards compatibility */ + #define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC + #define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS +diff --git a/sys/v4l2codecs/linux/videodev2.h b/sys/v4l2codecs/linux/videodev2.h +index 62cc050..687ac85 100644 +--- a/sys/v4l2codecs/linux/videodev2.h ++++ b/sys/v4l2codecs/linux/videodev2.h +@@ -1811,6 +1811,8 @@ enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS = 0x0272, + V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX = 0x0273, + V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS = 0x0274, ++ ++ V4L2_CTRL_TYPE_VP8_ENCODE_PARAMS = 0x0280, + }; + + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0024-v4l2codecs-Add-v4l2-encoder-class.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0024-v4l2codecs-Add-v4l2-encoder-class.patch new file mode 100644 index 000000000..948398b5c --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0024-v4l2codecs-Add-v4l2-encoder-class.patch @@ -0,0 +1,1583 @@ +From 9697eee045811ee5153002529f4e4bf8afa7193a Mon Sep 17 00:00:00 2001 +From: Benjamin Gaignard +Date: Mon, 16 Jan 2023 17:36:14 +0100 +Subject: [PATCH 3/5] v4l2codecs: Add v4l2 encoder class + +Add v4l2 encoder helpers to be used later by v4l2 encoders. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecallocator.c | 105 +- + sys/v4l2codecs/gstv4l2codecallocator.h | 7 +- + sys/v4l2codecs/gstv4l2encoder.c | 1216 ++++++++++++++++++++++++ + sys/v4l2codecs/gstv4l2encoder.h | 139 +++ + sys/v4l2codecs/meson.build | 1 + + 5 files changed, 1463 insertions(+), 5 deletions(-) + create mode 100644 sys/v4l2codecs/gstv4l2encoder.c + create mode 100644 sys/v4l2codecs/gstv4l2encoder.h + +diff --git a/sys/v4l2codecs/gstv4l2codecallocator.c b/sys/v4l2codecs/gstv4l2codecallocator.c +index 40238bd..e0fb371 100644 +--- a/sys/v4l2codecs/gstv4l2codecallocator.c ++++ b/sys/v4l2codecs/gstv4l2codecallocator.c +@@ -49,6 +49,7 @@ struct _GstV4l2CodecAllocator + gboolean flushing; + + GstV4l2Decoder *decoder; ++ GstV4l2Encoder *encoder; + GstPadDirection direction; + }; + +@@ -110,6 +111,44 @@ gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder, + return buf; + } + ++static GstV4l2CodecBuffer * ++gst_v4l2_codec_encoder_buffer_new (GstAllocator * allocator, ++ GstV4l2Encoder * encoder, GstPadDirection direction, gint index) ++{ ++ GstV4l2CodecBuffer *buf; ++ guint i, num_mems; ++ gint fds[GST_VIDEO_MAX_PLANES]; ++ gsize sizes[GST_VIDEO_MAX_PLANES]; ++ gsize offsets[GST_VIDEO_MAX_PLANES]; ++ ++ if (!gst_v4l2_encoder_export_buffer (encoder, direction, index, fds, sizes, ++ offsets, &num_mems)) ++ return NULL; ++ ++ buf = g_new0 (GstV4l2CodecBuffer, 1); ++ buf->index = index; ++ buf->num_mems = num_mems; ++ for (i = 0; i < buf->num_mems; i++) { ++ GstMemory *mem = gst_fd_allocator_alloc (allocator, fds[i], sizes[i], ++ GST_FD_MEMORY_FLAG_KEEP_MAPPED); ++ gst_memory_resize (mem, offsets[i], sizes[i] - offsets[i]); ++ ++ GST_MINI_OBJECT (mem)->dispose = gst_v4l2_codec_allocator_release; ++ gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), ++ gst_v4l2_codec_buffer_quark (), buf, NULL); ++ ++ /* On outstanding memory keeps a reference on the allocator, this is ++ * needed to break the cycle. */ ++ gst_object_unref (mem->allocator); ++ buf->mem[i] = mem; ++ } ++ ++ GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds", ++ buf->index, buf->num_mems); ++ ++ return buf; ++} ++ + static void + gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf) + { +@@ -169,7 +208,7 @@ gst_v4l2_codec_allocator_release (GstMiniObject * mini_object) + } + + static gboolean +-gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self) ++gst_v4l2_codec_decoder_allocator_prepare (GstV4l2CodecAllocator * self) + { + GstV4l2Decoder *decoder = self->decoder; + GstPadDirection direction = self->direction; +@@ -198,6 +237,37 @@ failed: + return FALSE; + } + ++static gboolean ++gst_v4l2_codec_encoder_allocator_prepare (GstV4l2CodecAllocator * self) ++{ ++ GstV4l2Encoder *encoder = self->encoder; ++ GstPadDirection direction = self->direction; ++ gint ret; ++ guint i; ++ ++ ret = gst_v4l2_encoder_request_buffers (encoder, direction, self->pool_size); ++ if (ret < self->pool_size) { ++ if (ret >= 0) ++ GST_ERROR_OBJECT (self, ++ "%i buffer was needed, but only %i could be allocated", ++ self->pool_size, ret); ++ goto failed; ++ } ++ ++ for (i = 0; i < self->pool_size; i++) { ++ GstV4l2CodecBuffer *buf = ++ gst_v4l2_codec_encoder_buffer_new (GST_ALLOCATOR (self), ++ encoder, direction, i); ++ g_queue_push_tail (&self->pool, buf); ++ } ++ ++ return TRUE; ++ ++failed: ++ gst_v4l2_encoder_request_buffers (encoder, direction, 0); ++ return FALSE; ++} ++ + static void + gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self) + { +@@ -218,6 +288,11 @@ gst_v4l2_codec_allocator_dispose (GObject * object) + gst_clear_object (&self->decoder); + } + ++ if (self->encoder) { ++ gst_v4l2_codec_allocator_detach (self); ++ gst_clear_object (&self->encoder); ++ } ++ + G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object); + } + +@@ -253,7 +328,26 @@ gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder, + self->direction = direction; + self->pool_size = num_buffers; + +- if (!gst_v4l2_codec_allocator_prepare (self)) { ++ if (!gst_v4l2_codec_decoder_allocator_prepare (self)) { ++ g_object_unref (self); ++ return NULL; ++ } ++ ++ return self; ++} ++ ++GstV4l2CodecAllocator * ++gst_v4l2_codec_encoder_allocator_new (GstV4l2Encoder * encoder, ++ GstPadDirection direction, guint num_buffers) ++{ ++ GstV4l2CodecAllocator *self = ++ g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL); ++ ++ self->encoder = g_object_ref (encoder); ++ self->direction = direction; ++ self->pool_size = num_buffers; ++ ++ if (!gst_v4l2_codec_encoder_allocator_prepare (self)) { + g_object_unref (self); + return NULL; + } +@@ -348,7 +442,12 @@ gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self) + GST_OBJECT_LOCK (self); + if (!self->detached) { + self->detached = TRUE; +- gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0); ++ if (self->decoder) { ++ gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0); ++ } ++ if (self->encoder) { ++ gst_v4l2_encoder_request_buffers (self->encoder, self->direction, 0); ++ } + } + GST_OBJECT_UNLOCK (self); + } +diff --git a/sys/v4l2codecs/gstv4l2codecallocator.h b/sys/v4l2codecs/gstv4l2codecallocator.h +index 97fe1d2..8a099f0 100644 +--- a/sys/v4l2codecs/gstv4l2codecallocator.h ++++ b/sys/v4l2codecs/gstv4l2codecallocator.h +@@ -26,6 +26,7 @@ + + #include "gstv4l2codecdevice.h" + #include "gstv4l2decoder.h" ++#include "gstv4l2encoder.h" + + #define GST_TYPE_V4L2_CODEC_ALLOCATOR gst_v4l2_codec_allocator_get_type () + G_DECLARE_FINAL_TYPE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator, +@@ -36,9 +37,11 @@ GstV4l2CodecAllocator *gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder, + GstPadDirection direction, + guint num_buffers); + +-GstMemory *gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * allocator); +- ++GstV4l2CodecAllocator *gst_v4l2_codec_encoder_allocator_new (GstV4l2Encoder * encoder, ++ GstPadDirection direction, ++ guint num_buffers); + ++GstMemory *gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * allocator); + + gboolean gst_v4l2_codec_allocator_create_buffer (GstV4l2CodecAllocator * self); + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +new file mode 100644 +index 0000000..5ae2cfa +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -0,0 +1,1216 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "gstv4l2codecallocator.h" ++#include "gstv4l2codecpool.h" ++#include "gstv4l2encoder.h" ++#include "gstv4l2format.h" ++#include "linux/media.h" ++#include "linux/videodev2.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++GST_DEBUG_CATEGORY (v4l2_encoder_debug); ++#define GST_CAT_DEFAULT v4l2_encoder_debug ++ ++enum ++{ ++ PROP_0, ++ PROP_MEDIA_DEVICE, ++ PROP_VIDEO_DEVICE, ++}; ++ ++struct _GstV4l2Request ++{ ++ /* non-thread safe */ ++ gint ref_count; ++ ++ GstV4l2Encoder *encoder; ++ gint fd; ++ guint32 frame_num; ++ GstBuffer *bitstream; ++ GstBuffer *pic_buf; ++ GstPoll *poll; ++ GstPollFD pollfd; ++ ++ /* request state */ ++ gboolean pending; ++ gboolean failed; ++ gboolean hold_pic_buf; ++ gboolean sub_request; ++}; ++ ++struct _GstV4l2Encoder ++{ ++ GstObject parent; ++ ++ gboolean opened; ++ gint media_fd; ++ gint video_fd; ++ GstQueueArray *request_pool; ++ GstQueueArray *pending_requests; ++ guint version; ++ ++ enum v4l2_buf_type src_buf_type; ++ enum v4l2_buf_type sink_buf_type; ++ gboolean mplane; ++ ++ /* properties */ ++ gchar *media_device; ++ gchar *video_device; ++ guint render_delay; ++ ++ /* detected features */ ++ gboolean supports_holding_capture; ++}; ++ ++G_DEFINE_TYPE_WITH_CODE (GstV4l2Encoder, gst_v4l2_encoder, GST_TYPE_OBJECT, ++ GST_DEBUG_CATEGORY_INIT (v4l2_encoder_debug, "v4l2codecs-encoder", 0, ++ "V4L2 stateless encoder helper")); ++ ++static void gst_v4l2_encoder_request_free (GstV4l2Request * request); ++ ++static guint32 ++direction_to_buffer_type (GstV4l2Encoder * self, GstPadDirection direction) ++{ ++ if (direction == GST_PAD_SRC) ++ return self->src_buf_type; ++ else ++ return self->sink_buf_type; ++} ++ ++static void ++gst_v4l2_encoder_finalize (GObject * obj) ++{ ++ GstV4l2Encoder *self = GST_V4L2_ENCODER (obj); ++ ++ gst_v4l2_encoder_close (self); ++ ++ g_free (self->media_device); ++ g_free (self->video_device); ++ gst_queue_array_free (self->request_pool); ++ gst_queue_array_free (self->pending_requests); ++ ++ G_OBJECT_CLASS (gst_v4l2_encoder_parent_class)->finalize (obj); ++} ++ ++static void ++gst_v4l2_encoder_init (GstV4l2Encoder * self) ++{ ++ self->request_pool = gst_queue_array_new (16); ++ self->pending_requests = gst_queue_array_new (16); ++} ++ ++static void ++gst_v4l2_encoder_class_init (GstV4l2EncoderClass * klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->finalize = gst_v4l2_encoder_finalize; ++ gobject_class->get_property = gst_v4l2_encoder_get_property; ++ gobject_class->set_property = gst_v4l2_encoder_set_property; ++ ++ gst_v4l2_encoder_install_properties (gobject_class, 0, NULL); ++} ++ ++GstV4l2Encoder * ++gst_v4l2_encoder_new (GstV4l2CodecDevice * device) ++{ ++ GstV4l2Encoder *encoder; ++ ++ g_return_val_if_fail (device->function == MEDIA_ENT_F_PROC_VIDEO_ENCODER, ++ NULL); ++ ++ encoder = g_object_new (GST_TYPE_V4L2_ENCODER, ++ "media-device", device->media_device_path, ++ "video-device", device->video_device_path, NULL); ++ ++ return gst_object_ref_sink (encoder); ++} ++ ++guint ++gst_v4l2_encoder_get_version (GstV4l2Encoder * self) ++{ ++ return self->version; ++} ++ ++gboolean ++gst_v4l2_encoder_open (GstV4l2Encoder * self) ++{ ++ gint ret; ++ struct v4l2_capability querycap; ++ guint32 capabilities; ++ ++ self->media_fd = open (self->media_device, 0); ++ if (self->media_fd < 0) { ++ GST_ERROR_OBJECT (self, "Failed to open '%s': %s", ++ self->media_device, g_strerror (errno)); ++ return FALSE; ++ } ++ ++ self->video_fd = open (self->video_device, O_NONBLOCK); ++ if (self->video_fd < 0) { ++ GST_ERROR_OBJECT (self, "Failed to open '%s': %s", ++ self->video_device, g_strerror (errno)); ++ return FALSE; ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_QUERYCAP, &querycap); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_QUERYCAP failed: %s", g_strerror (errno)); ++ gst_v4l2_encoder_close (self); ++ return FALSE; ++ } ++ ++ self->version = querycap.version; ++ ++ if (querycap.capabilities & V4L2_CAP_DEVICE_CAPS) ++ capabilities = querycap.device_caps; ++ else ++ capabilities = querycap.capabilities; ++ ++ if (capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { ++ self->sink_buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ self->src_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ self->mplane = TRUE; ++ } else if (capabilities & V4L2_CAP_VIDEO_M2M) { ++ self->sink_buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ self->src_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ self->mplane = FALSE; ++ } else { ++ GST_ERROR_OBJECT (self, "Unsupported memory-2-memory device."); ++ gst_v4l2_encoder_close (self); ++ return FALSE; ++ } ++ ++ self->opened = TRUE; ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_close (GstV4l2Encoder * self) ++{ ++ GstV4l2Request *request; ++ ++ while ((request = gst_queue_array_pop_head (self->pending_requests))) ++ gst_v4l2_encoder_request_unref (request); ++ ++ while ((request = gst_queue_array_pop_head (self->request_pool))) ++ gst_v4l2_encoder_request_free (request); ++ ++ if (self->media_fd) ++ close (self->media_fd); ++ if (self->video_fd) ++ close (self->video_fd); ++ ++ self->media_fd = 0; ++ self->video_fd = 0; ++ self->opened = FALSE; ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_streamon (GstV4l2Encoder * self, GstPadDirection direction) ++{ ++ gint ret; ++ guint32 type = direction_to_buffer_type (self, direction); ++ ++ ret = ioctl (self->video_fd, VIDIOC_STREAMON, &type); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_STREAMON failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_streamoff (GstV4l2Encoder * self, GstPadDirection direction) ++{ ++ guint32 type = direction_to_buffer_type (self, direction); ++ gint ret; ++ ++ if (direction == GST_PAD_SRC) { ++ GstV4l2Request *pending_req; ++ ++ /* STREAMOFF have the effect of cancelling all requests and unqueuing all ++ * buffers, so clear the pending request list */ ++ while ((pending_req = gst_queue_array_pop_head (self->pending_requests))) { ++ pending_req->pending = FALSE; ++ gst_v4l2_encoder_request_unref (pending_req); ++ } ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_STREAMOFF, &type); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_STREAMOFF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_flush (GstV4l2Encoder * self) ++{ ++ /* We ignore streamoff failure as it's not relevant, if we manage to ++ * streamon again, we are good. */ ++ gst_v4l2_encoder_streamoff (self, GST_PAD_SINK); ++ gst_v4l2_encoder_streamoff (self, GST_PAD_SRC); ++ ++ return gst_v4l2_encoder_streamon (self, GST_PAD_SINK) && ++ gst_v4l2_encoder_streamon (self, GST_PAD_SRC); ++} ++ ++GstCaps * ++gst_v4l2_encoder_list_sink_formats (GstV4l2Encoder * self) ++{ ++ gint ret; ++ struct v4l2_format fmt = { ++ .type = self->sink_buf_type, ++ }; ++ GstVideoFormat format; ++ GstCaps *caps; ++ GValue list = G_VALUE_INIT; ++ GValue value = G_VALUE_INIT; ++ gint i; ++ ++ if (!self->opened) ++ return NULL; ++ ++ ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno)); ++ return NULL; ++ } ++ ++ /* We first place a structure with the default pixel format */ ++ if (gst_v4l2_format_to_video_format (fmt.fmt.pix_mp.pixelformat, &format)) ++ caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, ++ gst_video_format_to_string (format), NULL); ++ else ++ caps = gst_caps_new_empty (); ++ ++ /* And then enumerate other possible formats and place that as a second ++ * structure in the caps */ ++ g_value_init (&list, GST_TYPE_LIST); ++ g_value_init (&value, G_TYPE_STRING); ++ ++ for (i = 0; ret >= 0; i++) { ++ struct v4l2_fmtdesc fmtdesc = { i, self->sink_buf_type, }; ++ ++ ret = ioctl (self->video_fd, VIDIOC_ENUM_FMT, &fmtdesc); ++ if (ret < 0) { ++ if (errno != EINVAL) ++ GST_ERROR_OBJECT (self, "VIDIOC_ENUM_FMT failed: %s", ++ g_strerror (errno)); ++ continue; ++ } ++ ++ if (gst_v4l2_format_to_video_format (fmtdesc.pixelformat, &format)) { ++ g_value_set_static_string (&value, gst_video_format_to_string (format)); ++ gst_value_list_append_value (&list, &value); ++ } ++ } ++ g_value_reset (&value); ++ ++ if (gst_value_list_get_size (&list) > 0) { ++ GstStructure *str = gst_structure_new_empty ("video/x-raw"); ++ gst_structure_take_value (str, "format", &list); ++ gst_caps_append_structure (caps, str); ++ } else { ++ g_value_reset (&list); ++ } ++ ++ return caps; ++} ++ ++gboolean ++gst_v4l2_encoder_enum_sink_fmt (GstV4l2Encoder * self, gint i, ++ guint32 * out_fmt) ++{ ++ struct v4l2_fmtdesc fmtdesc = { i, self->sink_buf_type, }; ++ gint ret; ++ ++ g_return_val_if_fail (self->opened, FALSE); ++ ++ ret = ioctl (self->video_fd, VIDIOC_ENUM_FMT, &fmtdesc); ++ if (ret < 0) { ++ if (errno != EINVAL) ++ GST_ERROR_OBJECT (self, "VIDIOC_ENUM_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ GST_DEBUG_OBJECT (self, "Found format %" GST_FOURCC_FORMAT " (%s)", ++ GST_FOURCC_ARGS (fmtdesc.pixelformat), fmtdesc.description); ++ *out_fmt = fmtdesc.pixelformat; ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, ++ GstVideoInfo * out) ++{ ++ gint ret; ++ struct v4l2_format fmt = { ++ .type = self->sink_buf_type, ++ }; ++ guint32 pix_fmt; ++ gint width = in->width; ++ gint height = in->height; ++ ++ ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_format_from_video_format (in->finfo->format, &pix_fmt)) { ++ GST_ERROR_OBJECT (self, "Unsupported pixelformat %s", ++ gst_video_format_to_string (in->finfo->format)); ++ return FALSE; ++ } ++ ++ if (pix_fmt != fmt.fmt.pix_mp.pixelformat ++ || fmt.fmt.pix_mp.width != width || fmt.fmt.pix_mp.height != height) { ++ GST_DEBUG_OBJECT (self, ++ "Trying to use peer format: %" GST_FOURCC_FORMAT " %ix%i", ++ GST_FOURCC_ARGS (pix_fmt), width, height); ++ fmt.fmt.pix_mp.pixelformat = pix_fmt; ++ fmt.fmt.pix_mp.width = width; ++ fmt.fmt.pix_mp.height = height; ++ ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ } ++ ++ if (!gst_v4l2_format_to_video_info (&fmt, out)) { ++ GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT, ++ GST_FOURCC_ARGS (fmt.fmt.pix_mp.pixelformat)); ++ return FALSE; ++ } ++ ++ GST_INFO_OBJECT (self, "Selected sink format %s %ix%i", ++ gst_video_format_to_string (out->finfo->format), out->width, out->height); ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_enum_src_formats (GstV4l2Encoder * self, gint i, ++ guint32 * out_fmt) ++{ ++ struct v4l2_fmtdesc fmtdesc = { i, self->src_buf_type, }; ++ gint ret; ++ ++ ret = ioctl (self->video_fd, VIDIOC_ENUM_FMT, &fmtdesc); ++ if (ret < 0) { ++ if (errno != EINVAL) ++ GST_ERROR_OBJECT (self, "VIDIOC_ENUM_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ GST_DEBUG_OBJECT (self, "Found format %" GST_FOURCC_FORMAT " (%s)", ++ GST_FOURCC_ARGS (fmtdesc.pixelformat), fmtdesc.description); ++ *out_fmt = fmtdesc.pixelformat; ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, ++ guint32 pix_fmt) ++{ ++ struct v4l2_format fmt = (struct v4l2_format) { ++ .type = self->src_buf_type, ++ }; ++ gint ret; ++ gint width = info->width; ++ gint height = info->height; ++ ++ ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (pix_fmt != fmt.fmt.pix_mp.pixelformat ++ || fmt.fmt.pix_mp.width != width || fmt.fmt.pix_mp.height != height) { ++ GST_DEBUG_OBJECT (self, ++ "Trying to use peer format: %" GST_FOURCC_FORMAT " %ix%i", ++ GST_FOURCC_ARGS (pix_fmt), width, height); ++ fmt.fmt.pix_mp.pixelformat = pix_fmt; ++ fmt.fmt.pix_mp.width = width; ++ fmt.fmt.pix_mp.height = height; ++ fmt.fmt.pix_mp.num_planes = 1; ++ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = ++ GST_ROUND_UP_16 (width) * GST_ROUND_UP_16 (height) * 3 / 2; ++ ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ } ++ ++ if (fmt.fmt.pix_mp.pixelformat != pix_fmt) { ++ GST_WARNING_OBJECT (self, "Failed to set sink format to %" ++ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (pix_fmt)); ++ errno = EINVAL; ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gint ++gst_v4l2_encoder_request_buffers (GstV4l2Encoder * self, ++ GstPadDirection direction, guint num_buffers) ++{ ++ gint ret; ++ struct v4l2_requestbuffers reqbufs = { ++ .count = num_buffers, ++ .memory = V4L2_MEMORY_MMAP, ++ .type = direction_to_buffer_type (self, direction), ++ }; ++ ++ GST_DEBUG_OBJECT (self, "Requesting %u buffers", num_buffers); ++ ++ ret = ioctl (self->video_fd, VIDIOC_REQBUFS, &reqbufs); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_REQBUFS failed: %s", g_strerror (errno)); ++ return ret; ++ } ++ ++ if (direction == GST_PAD_SINK) { ++ if (reqbufs.capabilities & V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF) ++ self->supports_holding_capture = TRUE; ++ else ++ self->supports_holding_capture = FALSE; ++ } ++ ++ return reqbufs.count; ++} ++ ++gboolean ++gst_v4l2_encoder_export_buffer (GstV4l2Encoder * self, ++ GstPadDirection direction, gint index, gint * fds, gsize * sizes, ++ gsize * offsets, guint * num_fds) ++{ ++ gint i, ret; ++ struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} }; ++ struct v4l2_buffer v4l2_buf = { ++ .index = 0, ++ .type = direction_to_buffer_type (self, direction), ++ }; ++ ++ if (self->mplane) { ++ v4l2_buf.length = GST_VIDEO_MAX_PLANES; ++ v4l2_buf.m.planes = planes; ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_QUERYBUF, &v4l2_buf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_QUERYBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (self->mplane) { ++ for (i = 0; i < v4l2_buf.length; i++) { ++ struct v4l2_plane *plane = v4l2_buf.m.planes + i; ++ struct v4l2_exportbuffer expbuf = { ++ .type = direction_to_buffer_type (self, direction), ++ .index = index, ++ .plane = i, ++ .flags = O_CLOEXEC | O_RDWR, ++ }; ++ ++ ret = ioctl (self->video_fd, VIDIOC_EXPBUF, &expbuf); ++ if (ret < 0) { ++ gint j; ++ GST_ERROR_OBJECT (self, "VIDIOC_EXPBUF failed: %s", g_strerror (errno)); ++ ++ for (j = i - 1; j >= 0; j--) ++ close (fds[j]); ++ ++ return FALSE; ++ } ++ ++ *num_fds = v4l2_buf.length; ++ fds[i] = expbuf.fd; ++ sizes[i] = plane->length; ++ offsets[i] = plane->data_offset; ++ } ++ } else { ++ struct v4l2_exportbuffer expbuf = { ++ .type = direction_to_buffer_type (self, direction), ++ .index = index, ++ .flags = O_CLOEXEC | O_RDWR, ++ }; ++ ++ ret = ioctl (self->video_fd, VIDIOC_EXPBUF, &expbuf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_EXPBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ *num_fds = 1; ++ fds[0] = expbuf.fd; ++ sizes[0] = v4l2_buf.length; ++ offsets[0] = 0; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_encoder_queue_src_buffer (GstV4l2Encoder * self, ++ GstV4l2Request * request, GstBuffer * buffer) ++{ ++ gint ret; ++ gsize bytesused = gst_buffer_get_size (buffer); ++ struct v4l2_plane plane = { ++ .bytesused = bytesused, ++ }; ++ struct v4l2_buffer buf = { ++ .type = self->src_buf_type, ++ .memory = V4L2_MEMORY_MMAP, ++ .index = gst_v4l2_codec_buffer_get_index (buffer), ++ }; ++ ++ GST_TRACE_OBJECT (self, "Queueing picture buffer %i", buf.index); ++ ++ if (self->mplane) { ++ buf.length = 1; ++ buf.m.planes = &plane; ++ } else { ++ buf.bytesused = bytesused; ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_QBUF, &buf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_QBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_encoder_queue_sink_buffer (GstV4l2Encoder * self, ++ GstV4l2Request * request, GstBuffer * buffer, guint32 frame_num, ++ guint flags) ++{ ++ gint i, ret; ++ struct v4l2_plane planes[GST_VIDEO_MAX_PLANES]; ++ struct v4l2_buffer buf = { ++ .type = self->sink_buf_type, ++ .memory = V4L2_MEMORY_MMAP, ++ .index = gst_v4l2_codec_buffer_get_index (buffer), ++ .timestamp.tv_usec = frame_num, ++ .request_fd = request->fd, ++ .flags = V4L2_BUF_FLAG_REQUEST_FD | flags, ++ }; ++ ++ GST_TRACE_OBJECT (self, "Queuing bitstream buffer %i", buf.index); ++ ++ if (self->mplane) { ++ buf.length = gst_buffer_n_memory (buffer); ++ buf.m.planes = planes; ++ for (i = 0; i < buf.length; i++) { ++ GstMemory *mem = gst_buffer_peek_memory (buffer, i); ++ /* *INDENT-OFF* */ ++ planes[i] = (struct v4l2_plane) { ++ .bytesused = gst_memory_get_sizes (mem, NULL, NULL), ++ }; ++ /* *INDENT-ON* */ ++ } ++ } else { ++ buf.bytesused = gst_buffer_get_size (buffer); ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_QBUF, &buf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_QBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_encoder_dequeue_sink (GstV4l2Encoder * self) ++{ ++ gint ret; ++ struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} }; ++ struct v4l2_buffer buf = { ++ .type = self->sink_buf_type, ++ .memory = V4L2_MEMORY_MMAP, ++ }; ++ ++ if (self->mplane) { ++ buf.length = GST_VIDEO_MAX_PLANES; ++ buf.m.planes = planes; ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_DQBUF, &buf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_DQBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ GST_TRACE_OBJECT (self, "Dequeued picture buffer %i", buf.index); ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_encoder_dequeue_src (GstV4l2Encoder * self, guint32 * out_frame_num, ++ guint32 * bytesused) ++{ ++ gint ret; ++ struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} }; ++ struct v4l2_buffer buf = { ++ .type = self->src_buf_type, ++ .memory = V4L2_MEMORY_MMAP, ++ }; ++ ++ if (self->mplane) { ++ buf.length = GST_VIDEO_MAX_PLANES; ++ buf.m.planes = planes; ++ } ++ ++ ret = ioctl (self->video_fd, VIDIOC_DQBUF, &buf); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_DQBUF failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ *out_frame_num = buf.timestamp.tv_usec; ++ *bytesused = buf.m.planes[0].bytesused; ++ ++ GST_TRACE_OBJECT (self, "Dequeued bitstream buffer %i, %d bytes used", ++ buf.index, buf.m.planes[0].bytesused); ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_set_controls (GstV4l2Encoder * self, GstV4l2Request * request, ++ struct v4l2_ext_control *control, guint count) ++{ ++ gint ret; ++ struct v4l2_ext_controls controls = { ++ .controls = control, ++ .count = count, ++ .request_fd = request ? request->fd : 0, ++ .which = request ? V4L2_CTRL_WHICH_REQUEST_VAL : 0, ++ }; ++ ++ ret = ioctl (self->video_fd, VIDIOC_S_EXT_CTRLS, &controls); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_S_EXT_CTRLS failed: %s", ++ g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_get_controls (GstV4l2Encoder * self, GstV4l2Request * request, ++ struct v4l2_ext_control *control, guint count) ++{ ++ gint ret; ++ struct v4l2_ext_controls controls = { ++ .controls = control, ++ .count = count, ++ .request_fd = request ? request->fd : 0, ++ .which = request ? V4L2_CTRL_WHICH_REQUEST_VAL : 0, ++ }; ++ ++ ret = ioctl (self->video_fd, VIDIOC_G_EXT_CTRLS, &controls); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_G_EXT_CTRLS failed: %s", ++ g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++gst_v4l2_encoder_query_control_size (GstV4l2Encoder * self, ++ unsigned int control_id, unsigned int *control_size) ++{ ++ gint ret; ++ struct v4l2_query_ext_ctrl control = { ++ .id = control_id, ++ }; ++ ++ if (control_size) ++ *control_size = 0; ++ ++ ret = ioctl (self->video_fd, VIDIOC_QUERY_EXT_CTRL, &control); ++ if (ret < 0) ++ /* ++ * It's not an error if a control is not supported by this driver. ++ * Return false but don't print any error. ++ */ ++ return FALSE; ++ ++ if (control_size) ++ *control_size = control.elem_size; ++ return TRUE; ++} ++ ++void ++gst_v4l2_encoder_install_properties (GObjectClass * gobject_class, ++ gint prop_offset, GstV4l2CodecDevice * device) ++{ ++ const gchar *media_device_path = NULL; ++ const gchar *video_device_path = NULL; ++ ++ if (device) { ++ media_device_path = device->media_device_path; ++ video_device_path = device->video_device_path; ++ } ++ ++ g_object_class_install_property (gobject_class, PROP_MEDIA_DEVICE, ++ g_param_spec_string ("media-device", "Media Device Path", ++ "Path to the media device node", media_device_path, ++ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_VIDEO_DEVICE, ++ g_param_spec_string ("video-device", "Video Device Path", ++ "Path to the video device node", video_device_path, ++ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++} ++ ++void ++gst_v4l2_encoder_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2Encoder *self = GST_V4L2_ENCODER (object); ++ ++ switch (prop_id) { ++ case PROP_MEDIA_DEVICE: ++ g_free (self->media_device); ++ self->media_device = g_value_dup_string (value); ++ break; ++ case PROP_VIDEO_DEVICE: ++ g_free (self->video_device); ++ self->video_device = g_value_dup_string (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++void ++gst_v4l2_encoder_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2Encoder *self = GST_V4L2_ENCODER (object); ++ ++ switch (prop_id) { ++ case PROP_MEDIA_DEVICE: ++ g_value_set_string (value, self->media_device); ++ break; ++ case PROP_VIDEO_DEVICE: ++ g_value_set_string (value, self->video_device); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++/** ++ * gst_v4l2_encoder_register: ++ * @plugin: a #GstPlugin ++ * @en_type: A #GType for the codec ++ * @class_init: The #GClassInitFunc for #dec_type ++ * @instance_init: The #GInstanceInitFunc for #dec_type ++ * @element_name_tmpl: A string to use for the first codec found and as a template for the next ones. ++ * @device: (transfer full) A #GstV4l2CodecDevice ++ * @rank: The rank to use for the element ++ * @class_data: (nullable) (transfer full) A #gpointer to pass as class_data, set to @device if null ++ * @element_name (nullable) (out) Sets the pointer to the new element name ++ * ++ * Registers a encoder element as a subtype of @en_type for @plugin. ++ * Will create a different sub_types for each subsequent @encoder of the ++ * same type. ++ */ ++void ++gst_v4l2_encoder_register (GstPlugin * plugin, ++ GType enc_type, GClassInitFunc class_init, gconstpointer class_data, ++ GInstanceInitFunc instance_init, const gchar * element_name_tmpl, ++ GstV4l2CodecDevice * device, guint rank, gchar ** element_name) ++{ ++ GTypeQuery type_query; ++ GTypeInfo type_info = { 0, }; ++ GType subtype; ++ gchar *type_name; ++ ++ g_type_query (enc_type, &type_query); ++ memset (&type_info, 0, sizeof (type_info)); ++ type_info.class_size = type_query.class_size; ++ type_info.instance_size = type_query.instance_size; ++ type_info.class_init = class_init; ++ type_info.class_data = class_data; ++ type_info.instance_init = instance_init; ++ ++ if (class_data == device) ++ GST_MINI_OBJECT_FLAG_SET (device, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); ++ ++ /* The first encoder to be registered should use a constant name, like ++ * v4l2slvp8enc, for any additional encoders, we create unique names. Encoder ++ * names may change between boots, so this should help gain stable names for ++ * the most common use cases. SL stands for state-less, we differentiate ++ * with v4l2vp8enc as this element may not have the same properties */ ++ type_name = g_strdup_printf (element_name_tmpl, ""); ++ ++ if (g_type_from_name (type_name) != 0) { ++ gchar *basename = g_path_get_basename (device->video_device_path); ++ g_free (type_name); ++ type_name = g_strdup_printf (element_name_tmpl, basename); ++ g_free (basename); ++ } ++ ++ subtype = g_type_register_static (enc_type, type_name, &type_info, 0); ++ ++ if (!gst_element_register (plugin, type_name, rank, subtype)) { ++ GST_WARNING ("Failed to register plugin '%s'", type_name); ++ g_free (type_name); ++ type_name = NULL; ++ } ++ ++ if (element_name) ++ *element_name = type_name; ++ else ++ g_free (type_name); ++} ++ ++/* ++ * gst_v4l2_encoder_alloc_request: ++ * @self a #GstV4l2Encoder pointer ++ * @frame_num: Used as a timestamp to identify references ++ * @pic_buf the #GstBuffer holding the raw picture ++ * @bitstream the #GstBuffer that holds the encoded data ++ * ++ * Allocate a Linux media request file descriptor. This request wrapper will ++ * hold a reference to the requested picture buffer to encoded and the ++ * bitstream buffer this request will encode to. This will be used for ++ * transparent management of the V4L2 queues. ++ * ++ * Returns: a new #GstV4l2Request ++ */ ++GstV4l2Request * ++gst_v4l2_encoder_alloc_request (GstV4l2Encoder * self, guint32 frame_num, ++ GstBuffer * pic_buf, GstBuffer * bitstream) ++{ ++ GstV4l2Request *request = gst_queue_array_pop_head (self->request_pool); ++ gint ret; ++ ++ if (!request) { ++ request = g_new0 (GstV4l2Request, 1); ++ ++ ret = ioctl (self->media_fd, MEDIA_IOC_REQUEST_ALLOC, &request->fd); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "MEDIA_IOC_REQUEST_ALLOC failed: %s", ++ g_strerror (errno)); ++ return NULL; ++ } ++ ++ request->poll = gst_poll_new (FALSE); ++ gst_poll_fd_init (&request->pollfd); ++ request->pollfd.fd = request->fd; ++ gst_poll_add_fd (request->poll, &request->pollfd); ++ gst_poll_fd_ctl_pri (request->poll, &request->pollfd, TRUE); ++ } ++ ++ request->encoder = g_object_ref (self); ++ request->bitstream = gst_buffer_ref (bitstream); ++ request->pic_buf = gst_buffer_ref (pic_buf); ++ request->frame_num = frame_num; ++ request->ref_count = 1; ++ ++ return request; ++} ++ ++/* ++ * gst_v4l2_encoder_alloc_sub_request: ++ * @self a #GstV4l2Encoder pointer ++ * @prev_request the #GstV4l2Request this request continue ++ * @bitstream the #GstBuffer that holds the bitstream data ++ * ++ * Allocate a Linux media request file descriptor. Similar to ++ * gst_v4l2_encoder_alloc_request(), but used when a request is the ++ * continuation of the encoding of the same picture. This is notably the case ++ * for subsequent slices or for second field of a frame. ++ * ++ * Returns: a new #GstV4l2Request ++ */ ++GstV4l2Request * ++gst_v4l2_encoder_alloc_sub_request (GstV4l2Encoder * self, ++ GstV4l2Request * prev_request, GstBuffer * bitstream) ++{ ++ GstV4l2Request *request = gst_queue_array_pop_head (self->request_pool); ++ gint ret; ++ ++ if (!request) { ++ request = g_new0 (GstV4l2Request, 1); ++ ++ ret = ioctl (self->media_fd, MEDIA_IOC_REQUEST_ALLOC, &request->fd); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "MEDIA_IOC_REQUEST_ALLOC failed: %s", ++ g_strerror (errno)); ++ return NULL; ++ } ++ ++ request->poll = gst_poll_new (FALSE); ++ gst_poll_fd_init (&request->pollfd); ++ request->pollfd.fd = request->fd; ++ gst_poll_add_fd (request->poll, &request->pollfd); ++ gst_poll_fd_ctl_pri (request->poll, &request->pollfd, TRUE); ++ } ++ ++ request->encoder = g_object_ref (self); ++ request->bitstream = gst_buffer_ref (bitstream); ++ request->pic_buf = gst_buffer_ref (prev_request->pic_buf); ++ request->frame_num = prev_request->frame_num; ++ request->sub_request = TRUE; ++ request->ref_count = 1; ++ ++ return request; ++} ++ ++GstV4l2Request * ++gst_v4l2_encoder_request_ref (GstV4l2Request * request) ++{ ++ request->ref_count++; ++ return request; ++} ++ ++static void ++gst_v4l2_encoder_request_free (GstV4l2Request * request) ++{ ++ GstV4l2Encoder *encoder = request->encoder; ++ ++ request->encoder = NULL; ++ close (request->fd); ++ gst_poll_free (request->poll); ++ g_free (request); ++ ++ if (encoder) ++ g_object_unref (encoder); ++} ++ ++void ++gst_v4l2_encoder_request_unref (GstV4l2Request * request) ++{ ++ GstV4l2Encoder *encoder = request->encoder; ++ gint ret; ++ ++ g_return_if_fail (request->ref_count > 0); ++ ++ if (--request->ref_count > 0) ++ return; ++ ++ g_clear_pointer (&request->bitstream, gst_buffer_unref); ++ g_clear_pointer (&request->pic_buf, gst_buffer_unref); ++ request->frame_num = G_MAXUINT32; ++ request->failed = FALSE; ++ request->hold_pic_buf = FALSE; ++ request->sub_request = FALSE; ++ ++ if (request->pending) { ++ gint idx; ++ ++ GST_DEBUG_OBJECT (encoder, "Freeing pending request %i.", request->fd); ++ ++ idx = gst_queue_array_find (encoder->pending_requests, NULL, request); ++ if (idx >= 0) ++ gst_queue_array_drop_element (encoder->pending_requests, idx); ++ ++ gst_v4l2_encoder_request_free (request); ++ return; ++ } ++ ++ GST_TRACE_OBJECT (encoder, "Recycling request %i.", request->fd); ++ ++ ret = ioctl (request->fd, MEDIA_REQUEST_IOC_REINIT, NULL); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (request->encoder, "MEDIA_REQUEST_IOC_REINIT failed: %s", ++ g_strerror (errno)); ++ gst_v4l2_encoder_request_free (request); ++ return; ++ } ++ ++ gst_queue_array_push_tail (encoder->request_pool, request); ++ g_clear_object (&request->encoder); ++} ++ ++gboolean ++gst_v4l2_encoder_request_queue (GstV4l2Request * request, guint flags) ++{ ++ GstV4l2Encoder *encoder = request->encoder; ++ gint ret; ++ ++ GST_TRACE_OBJECT (encoder, "Queuing request %i.", request->fd); ++ ++ /* this would lead to stalls if we tried to use this feature and it wasn't ++ * supported. */ ++ if ((flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) ++ && !encoder->supports_holding_capture) { ++ GST_ERROR_OBJECT (encoder, ++ "Driver does not support holding capture buffer."); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_queue_sink_buffer (encoder, request, ++ request->pic_buf, request->frame_num, flags)) { ++ GST_ERROR_OBJECT (encoder, "Driver did not accept the picture buffer."); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_queue_src_buffer (encoder, request, request->bitstream)) { ++ GST_ERROR_OBJECT (encoder, "Driver did not accept the bitstream buffer."); ++ return FALSE; ++ } ++ ++ ret = ioctl (request->fd, MEDIA_REQUEST_IOC_QUEUE, NULL); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (encoder, "MEDIA_REQUEST_IOC_QUEUE, failed: %s", ++ g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) ++ request->hold_pic_buf = TRUE; ++ ++ request->pending = TRUE; ++ gst_queue_array_push_tail (encoder->pending_requests, ++ gst_v4l2_encoder_request_ref (request)); ++ ++ if (gst_queue_array_get_length (encoder->pending_requests) > 1) { ++ GstV4l2Request *pending_req; ++ ++ pending_req = gst_queue_array_peek_head (encoder->pending_requests); ++ gst_v4l2_request_set_done (pending_req); ++ } ++ ++ return TRUE; ++} ++ ++gint ++gst_v4l2_encoder_request_set_done (GstV4l2Request * request, ++ guint32 * bytesused) ++{ ++ GstV4l2Encoder *encoder = request->encoder; ++ GstV4l2Request *pending_req = NULL; ++ gint ret; ++ ++ *bytesused = 0; ++ if (!request->pending) ++ return 1; ++ ++ GST_DEBUG_OBJECT (encoder, "Waiting for request %i to complete.", ++ request->fd); ++ ++ ret = gst_poll_wait (request->poll, GST_SECOND); ++ if (ret == 0) { ++ GST_WARNING_OBJECT (encoder, "Request %i took too long.", request->fd); ++ return 0; ++ } ++ ++ if (ret < 0) { ++ GST_WARNING_OBJECT (encoder, "Request %i error: %s (%i)", ++ request->fd, g_strerror (errno), errno); ++ return ret; ++ } ++ ++ while ((pending_req = gst_queue_array_pop_head (encoder->pending_requests))) { ++ gst_v4l2_encoder_dequeue_sink (encoder); ++ ++ if (!pending_req->hold_pic_buf) { ++ guint32 frame_num = G_MAXUINT32; ++ if (!gst_v4l2_encoder_dequeue_src (encoder, &frame_num, bytesused)) { ++ pending_req->failed = TRUE; ++ } else if (frame_num != pending_req->frame_num) { ++ GST_WARNING_OBJECT (encoder, ++ "Requested frame %u, but driver returned frame %u.", ++ pending_req->frame_num, frame_num); ++ pending_req->failed = TRUE; ++ } ++ } ++ ++ pending_req->pending = FALSE; ++ gst_v4l2_encoder_request_unref (pending_req); ++ ++ if (pending_req == request) ++ break; ++ } ++ ++ /* Pending request must be in the pending request list */ ++ g_assert (pending_req == request); ++ ++ return ret; ++} ++ ++gboolean ++gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, ++ guint * qp_min, guint * qp_max) ++{ ++ gint ret; ++ struct v4l2_query_ext_ctrl control = { ++ .id = V4L2_CID_STATELESS_VP8_ENCODE_QP, ++ }; ++ ++ *qp_max = 127; ++ *qp_min = 0; ++ ++ ret = ioctl (self->video_fd, VIDIOC_QUERY_EXT_CTRL, &control); ++ if (ret < 0) { ++ GST_DEBUG_OBJECT (self, "unable to retrieve encoder qp, use default"); ++ return FALSE; ++ } ++ ++ *qp_max = control.maximum; ++ *qp_min = control.minimum; ++ GST_DEBUG_OBJECT (self, "encoder qp min %d qp max %d", *qp_min, *qp_max); ++ ++ return TRUE; ++} +diff --git a/sys/v4l2codecs/gstv4l2encoder.h b/sys/v4l2codecs/gstv4l2encoder.h +new file mode 100644 +index 0000000..7ff01a9 +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2encoder.h +@@ -0,0 +1,139 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GST_V4L2_ENCODER_H__ ++#define __GST_V4L2_ENCODER_H__ ++ ++#include ++#include ++ ++#include "gstv4l2codecdevice.h" ++#include "linux/videodev2.h" ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_V4L2_ENCODER gst_v4l2_encoder_get_type () ++G_DECLARE_FINAL_TYPE (GstV4l2Encoder, gst_v4l2_encoder, GST, V4L2_ENCODER, GstObject); ++ ++typedef struct _GstV4l2Request GstV4l2Request; ++ ++GstV4l2Encoder * gst_v4l2_encoder_new (GstV4l2CodecDevice * device); ++ ++guint gst_v4l2_encoder_get_version (GstV4l2Encoder * self); ++ ++gboolean gst_v4l2_encoder_open (GstV4l2Encoder * decoder); ++ ++gboolean gst_v4l2_encoder_close (GstV4l2Encoder * decoder); ++ ++gboolean gst_v4l2_encoder_streamon (GstV4l2Encoder * self, ++ GstPadDirection direction); ++ ++gboolean gst_v4l2_encoder_streamoff (GstV4l2Encoder * self, ++ GstPadDirection direction); ++ ++gboolean gst_v4l2_encoder_flush (GstV4l2Encoder * self); ++ ++gboolean gst_v4l2_encoder_enum_sink_fmt (GstV4l2Encoder * self, ++ gint i, guint32 * out_fmt); ++ ++GstCaps * gst_v4l2_encoder_list_sink_formats (GstV4l2Encoder * self); ++ ++gboolean gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, ++ GstVideoInfo * in, GstVideoInfo * out); ++ ++gboolean gst_v4l2_encoder_enum_src_formats (GstV4l2Encoder * self, ++ gint i, guint32 * out_fmt); ++ ++gboolean gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, guint32 pix_fmt); ++ ++gint gst_v4l2_encoder_request_buffers (GstV4l2Encoder * self, ++ GstPadDirection direction, ++ guint num_buffers); ++ ++gboolean gst_v4l2_encoder_export_buffer (GstV4l2Encoder * self, ++ GstPadDirection directon, ++ gint index, ++ gint * fds, ++ gsize * sizes, ++ gsize * offsets, ++ guint *num_fds); ++ ++gboolean gst_v4l2_encoder_set_controls (GstV4l2Encoder * self, ++ GstV4l2Request * request, ++ struct v4l2_ext_control *control, ++ guint count); ++ ++gboolean gst_v4l2_encoder_get_controls (GstV4l2Encoder * self, ++ GstV4l2Request * request, ++ struct v4l2_ext_control * control, ++ guint count); ++ ++gboolean gst_v4l2_encoder_query_control_size (GstV4l2Encoder * self, ++ unsigned int control_id, ++ unsigned int *control_size); ++ ++void gst_v4l2_encoder_install_properties (GObjectClass * gobject_class, ++ gint prop_offset, ++ GstV4l2CodecDevice * device); ++ ++void gst_v4l2_encoder_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec); ++ ++void gst_v4l2_encoder_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec); ++ ++void gst_v4l2_encoder_register (GstPlugin * plugin, ++ GType dec_type, ++ GClassInitFunc class_init, ++ gconstpointer class_data, ++ GInstanceInitFunc instance_init, ++ const gchar *element_name_tmpl, ++ GstV4l2CodecDevice * device, ++ guint rank, ++ gchar ** element_name); ++ ++GstV4l2Request *gst_v4l2_encoder_alloc_request (GstV4l2Encoder * self, ++ guint32 frame_num, ++ GstBuffer * pic_buf, ++ GstBuffer * bitstream); ++ ++GstV4l2Request *gst_v4l2_encoder_alloc_ro_request (GstV4l2Encoder * self); ++ ++GstV4l2Request *gst_v4l2_encoder_alloc_sub_request (GstV4l2Encoder * self, ++ GstV4l2Request * prev_request, ++ GstBuffer * bitstream); ++ ++GstV4l2Request * gst_v4l2_encoder_request_ref (GstV4l2Request * request); ++ ++void gst_v4l2_encoder_request_unref (GstV4l2Request * request); ++void gst_v4l2_encoder_ro_request_unref (GstV4l2Request * request); ++ ++ ++gboolean gst_v4l2_encoder_request_queue (GstV4l2Request * request, ++ guint flags); ++ ++gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused); ++ ++gboolean gst_v4l2_encoder_request_failed (GstV4l2Request * request); ++ ++gboolean gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max); ++ ++G_END_DECLS ++ ++#endif /* __GST_V4L2_ENCODER_H__ */ +diff --git a/sys/v4l2codecs/meson.build b/sys/v4l2codecs/meson.build +index 836dc53..53e8923 100644 +--- a/sys/v4l2codecs/meson.build ++++ b/sys/v4l2codecs/meson.build +@@ -11,6 +11,7 @@ v4l2codecs_sources = [ + 'gstv4l2decoder.c', + 'gstv4l2format.c', + 'gstv4l2codecalphadecodebin.c', ++ 'gstv4l2encoder.c', + ] + + libgudev_dep = dependency('gudev-1.0', required: get_option('v4l2codecs')) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch new file mode 100644 index 000000000..6e4dac4ca --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch @@ -0,0 +1,813 @@ +From 6b4f87ff52197510e6d4ca475ecdb5725473e60a Mon Sep 17 00:00:00 2001 +From: Benjamin Gaignard +Date: Mon, 16 Jan 2023 17:38:58 +0100 +Subject: [PATCH 4/5] v4l2codecs: Add V4L2 stateless VP8 encoder + +Add element for V4L2 stateless VP8 encoder. +Using v4l2 request to set driver configuration by using VP8 stateless controls. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 716 ++++++++++++++++++++++++++++ + sys/v4l2codecs/gstv4l2codecvp8enc.h | 54 +++ + sys/v4l2codecs/meson.build | 1 + + 3 files changed, 771 insertions(+) + create mode 100644 sys/v4l2codecs/gstv4l2codecvp8enc.c + create mode 100644 sys/v4l2codecs/gstv4l2codecvp8enc.h + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +new file mode 100644 +index 0000000..2f15caf +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -0,0 +1,716 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "gstv4l2codecallocator.h" ++#include "gstv4l2codecpool.h" ++#include "gstv4l2codecvp8enc.h" ++#include "gstv4l2format.h" ++ ++#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#define V4L2_MIN_KERNEL_VER_MAJOR 5 ++#define V4L2_MIN_KERNEL_VER_MINOR 17 ++#define V4L2_MIN_KERNEL_VERSION KERNEL_VERSION(V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR, 0) ++ ++GST_DEBUG_CATEGORY_STATIC (v4l2_vp8enc_debug); ++#define GST_CAT_DEFAULT v4l2_vp8enc_debug ++ ++enum ++{ ++ PROP_0, ++ PROP_LAST = PROP_0 ++}; ++ ++static GstStaticPadTemplate sink_template = ++GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME, ++ GST_PAD_SINK, GST_PAD_ALWAYS, ++ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_V4L2_DEFAULT_VIDEO_FORMATS))); ++ ++static GstStaticPadTemplate src_template = ++GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, ++ GST_PAD_SRC, GST_PAD_ALWAYS, ++ GST_STATIC_CAPS ("video/x-vp8")); ++ ++struct _vp8_frame_hdr ++{ ++ guint8 tag[3]; ++ guint8 start_code[3]; ++ guint16 width; ++ guint16 height; ++}; ++ ++#define VP8_FRAME_HDR_INTRA_FLAG 0x0 ++#define VP8_FRAME_HDR_INTER_FLAG 0x1 ++#define VP8_FRAME_HDR_VERSION1_FLAG (1 << 1) ++#define VP8_FRAME_HDR_SHOW_FRAME (1 << 4) ++#define VP8_FRAME_HDR_WIDTH_MASK 0x3fff ++#define VP8_FRAME_HDR_HEIGHT_MASK 0x3fff ++ ++struct _GstV4l2CodecVp8Enc ++{ ++ GstVp8Encoder parent; ++ GstV4l2Encoder *encoder; ++ GstVideoCodecState *output_state; ++ GstVideoInfo vinfo; ++ gint width; ++ gint height; ++ guint qp_max, qp_min; ++ ++ GstV4l2CodecAllocator *sink_allocator; ++ GstV4l2CodecAllocator *src_allocator; ++ GstV4l2CodecPool *sink_pool; ++ GstV4l2CodecPool *src_pool; ++ ++ struct v4l2_ctrl_vp8_encode_params encode_params; ++}; ++ ++G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecVp8Enc, gst_v4l2_codec_vp8_enc, ++ GST_TYPE_VP8_ENCODER); ++ ++#define parent_class gst_v4l2_codec_vp8_enc_parent_class ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_open (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ guint version; ++ ++ if (!gst_v4l2_encoder_open (self->encoder)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, ++ ("Failed to open VP8 encoder"), ++ ("gst_v4l2_encoder_open() failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ version = gst_v4l2_encoder_get_version (self->encoder); ++ if (version < V4L2_MIN_KERNEL_VERSION) ++ GST_WARNING_OBJECT (self, ++ "V4L2 API v%u.%u too old, at least v%u.%u required", ++ (version >> 16) & 0xff, (version >> 8) & 0xff, ++ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR); ++ ++ GST_DEBUG_OBJECT (self, "open vp8 encoder"); ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_api_check (GstV4l2Encoder * encoder) ++{ ++ guint i, ret_size; ++ /* *INDENT-OFF* */ ++ #define SET_ID(cid) .id = (cid), .name = #cid ++ struct ++ { ++ const gchar *name; ++ unsigned int id; ++ unsigned int size; ++ gboolean optional; ++ } controls[] = { ++ { ++ SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_PARAMS), ++ .size = sizeof(struct v4l2_ctrl_vp8_encode_params), ++ }, { ++ SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_QP), ++ .size = sizeof(__u32), ++ }, ++ }; ++ #undef SET_ID ++ /* *INDENT-ON* */ ++ ++ /* ++ * Compatibility check: make sure the pointer controls are ++ * the right size. ++ */ ++ for (i = 0; i < G_N_ELEMENTS (controls); i++) { ++ gboolean control_found; ++ ++ control_found = gst_v4l2_encoder_query_control_size (encoder, ++ controls[i].id, &ret_size); ++ ++ if (!controls[i].optional && !control_found) { ++ GST_WARNING ("Driver is missing %s support.", controls[i].name); ++ return FALSE; ++ } ++ ++ if (control_found && ret_size != controls[i].size) { ++ GST_WARNING ("%s control size mismatch: got %d bytes but %d expected.", ++ controls[i].name, ret_size, controls[i].size); ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_close (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ gst_v4l2_encoder_close (self->encoder); ++ return TRUE; ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_reset_allocation (GstV4l2CodecVp8Enc * self) ++{ ++ if (self->sink_allocator) { ++ gst_v4l2_codec_allocator_detach (self->sink_allocator); ++ g_clear_object (&self->sink_allocator); ++ g_clear_object (&self->sink_pool); ++ } ++ ++ if (self->src_allocator) { ++ gst_v4l2_codec_allocator_detach (self->src_allocator); ++ g_clear_object (&self->src_allocator); ++ g_clear_object (&self->src_pool); ++ } ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_start (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "start"); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->start (encoder); ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_stop (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "stop"); ++ ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK); ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC); ++ ++ gst_v4l2_codec_vp8_enc_reset_allocation (self); ++ ++ if (self->output_state) ++ gst_video_codec_state_unref (self->output_state); ++ self->output_state = NULL; ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (encoder); ++} ++ ++static GstCaps * ++gst_v4l2_codec_vp8_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ GstCaps *caps, *result; ++ ++ caps = gst_v4l2_encoder_list_sink_formats (self->encoder); ++ GST_DEBUG_OBJECT (self, "Supported input formats: %" GST_PTR_FORMAT, caps); ++ ++ result = gst_video_encoder_proxy_getcaps (encoder, caps, filter); ++ ++ if (caps) ++ gst_caps_unref (caps); ++ ++ GST_DEBUG_OBJECT (self, "Returning sink caps: %" GST_PTR_FORMAT, result); ++ ++ return result; ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "buffers allocation"); ++ ++ g_clear_object (&self->sink_pool); ++ g_clear_object (&self->src_pool); ++ g_clear_object (&self->src_allocator); ++ ++ self->sink_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder, ++ GST_PAD_SINK, 4); ++ if (!self->sink_allocator) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Not enough memory to allocate sink buffers."), (NULL)); ++ return FALSE; ++ } ++ ++ self->sink_pool = ++ gst_v4l2_codec_pool_new (self->sink_allocator, &self->vinfo); ++ ++ self->src_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder, ++ GST_PAD_SRC, 4); ++ if (!self->src_allocator) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Not enough memory to allocate source buffers."), (NULL)); ++ g_clear_object (&self->sink_allocator); ++ return FALSE; ++ } ++ ++ self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ GstCaps *caps; ++ ++ GST_DEBUG_OBJECT (self, "Set format"); ++ ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK); ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC); ++ ++ gst_v4l2_codec_vp8_enc_reset_allocation (self); ++ ++ if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, ++ &self->vinfo)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ++ ("Failed to configure VP8 encoder"), ++ ("gst_v4l2_encoder_select_sink_format() failed: %s", ++ g_strerror (errno))); ++ gst_v4l2_encoder_close (self->encoder); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &self->vinfo, ++ V4L2_PIX_FMT_VP8_FRAME)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), ++ ("No support for %ux%u format VP8", state->info.width, ++ state->info.height)); ++ return FALSE; ++ } ++ ++ self->width = state->info.width; ++ self->height = state->info.height; ++ gst_v4l2_codec_vp8_enc_buffers_allocation (encoder); ++ ++ if (self->output_state) ++ gst_video_codec_state_unref (self->output_state); ++ ++ caps = gst_caps_new_empty_simple ("video/x-vp8"); ++ ++ self->output_state = ++ gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), ++ caps, state); ++ ++ if (GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) { ++ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ++ ("Could not enable the encoder driver."), ++ ("VIDIOC_STREAMON(SINK) failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SRC)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ++ ("Could not enable the encoder driver."), ++ ("VIDIOC_STREAMON(SRC) failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ gst_v4l2_codec_vp8_enc_get_qp_range (self->encoder, &self->qp_min, ++ &self->qp_max); ++ ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_set_flushing (GstV4l2CodecVp8Enc * self, ++ gboolean flushing) ++{ ++ if (self->sink_allocator) ++ gst_v4l2_codec_allocator_set_flushing (self->sink_allocator, flushing); ++ if (self->src_allocator) ++ gst_v4l2_codec_allocator_set_flushing (self->src_allocator, flushing); ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_flush (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "Flushing encoder state."); ++ ++ gst_v4l2_encoder_flush (self->encoder); ++ gst_v4l2_codec_vp8_enc_set_flushing (self, FALSE); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (encoder); ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ switch (GST_EVENT_TYPE (event)) { ++ case GST_EVENT_FLUSH_START: ++ GST_DEBUG_OBJECT (self, "flush start"); ++ gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE); ++ break; ++ default: ++ break; ++ } ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event); ++} ++ ++static GstStateChangeReturn ++gst_v4l2_codec_vp8_enc_change_state (GstElement * element, ++ GstStateChange transition) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (element); ++ ++ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) ++ gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE); ++ ++ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); ++} ++ ++ ++static void ++gst_v4l2_codec_vp8_enc_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object); ++ GObject *dec = G_OBJECT (self->encoder); ++ ++ switch (prop_id) { ++ default: ++ gst_v4l2_encoder_set_property (dec, prop_id - PROP_LAST, value, pspec); ++ break; ++ } ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object); ++ GObject *dec = G_OBJECT (self->encoder); ++ ++ switch (prop_id) { ++ default: ++ gst_v4l2_encoder_get_property (dec, prop_id - PROP_LAST, value, pspec); ++ break; ++ } ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self, ++ GstVideoCodecFrame * frame) ++{ ++ GstVideoFrame src_frame; ++ GstVideoFrame dest_frame; ++ GstVideoInfo dest_vinfo; ++ GstBuffer *buffer; ++ GstFlowReturn flow_ret; ++ ++ gst_video_info_set_format (&dest_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), ++ self->width, self->height); ++ ++ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), ++ &buffer, NULL); ++ if (flow_ret != GST_FLOW_OK) { ++ if (flow_ret == GST_FLOW_FLUSHING) ++ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing."); ++ else ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("No more picture buffer available."), (NULL)); ++ return FALSE; ++ } ++ ++ if (!buffer) ++ goto fail; ++ ++ if (!gst_video_frame_map (&src_frame, &self->vinfo, ++ frame->input_buffer, GST_MAP_READ)) ++ goto fail; ++ ++ if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) { ++ gst_video_frame_unmap (&dest_frame); ++ goto fail; ++ } ++ ++ if (!gst_video_frame_copy (&dest_frame, &src_frame)) { ++ gst_video_frame_unmap (&src_frame); ++ gst_video_frame_unmap (&dest_frame); ++ goto fail; ++ } ++ ++ gst_video_frame_unmap (&src_frame); ++ gst_video_frame_unmap (&dest_frame); ++ gst_buffer_replace (&frame->input_buffer, buffer); ++ gst_buffer_unref (buffer); ++ ++ return TRUE; ++ ++fail: ++ GST_ERROR_OBJECT (self, "Failed copy input buffer."); ++ return FALSE; ++} ++ ++static gboolean ++gst_v4l2_codec_vp8_enc_ensure_output_bitstream (GstV4l2CodecVp8Enc * self, ++ GstVideoCodecFrame * frame) ++{ ++ GstFlowReturn flow_ret; ++ ++ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool), ++ &frame->output_buffer, NULL); ++ if (flow_ret != GST_FLOW_OK) { ++ if (flow_ret == GST_FLOW_FLUSHING) ++ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing."); ++ else ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("No more encoded buffer available."), (NULL)); ++ return FALSE; ++ } ++ ++ if (!frame->output_buffer) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_fill_encode_params (GstVp8Encoder * encoder, ++ GstVp8Frame * vp8_frame) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ switch (vp8_frame->type) { ++ case GstVp8Keyframe: ++ self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_KEYFRAME; ++ self->encode_params.loop_filter_level = 26; ++ break; ++ case GstVp8Inter: ++ default: ++ self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_INTER; ++ self->encode_params.loop_filter_level = 12; ++ break; ++ } ++ ++ self->encode_params.flags = V4L2_VP8_FRAME_FLAG_SHOWFRAME; ++} ++ ++static guint ++gst_v4l2_codec_vp8_enc_check_qp_range (GstV4l2CodecVp8Enc * self, ++ GstVp8Frame * vp8_frame) ++{ ++ if (vp8_frame->quality > self->qp_max) ++ return self->qp_max; ++ if (vp8_frame->quality < self->qp_min) ++ return self->qp_min; ++ ++ return vp8_frame->quality; ++} ++ ++static GstFlowReturn ++gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, ++ GstVp8Frame * vp8_frame) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ GstVideoEncoder *venc = GST_VIDEO_ENCODER (encoder); ++ GstV4l2Request *request = NULL; ++ GstFlowReturn ret = GST_FLOW_ERROR; ++ GstVideoCodecFrame *frame = vp8_frame->frame; ++ GstBuffer *resized_buffer; ++ guint32 bytesused; ++ ++ /* *INDENT-OFF* */ ++ struct v4l2_ext_control control[] = { ++ { ++ .id = V4L2_CID_STATELESS_VP8_ENCODE_PARAMS, ++ .ptr = &self->encode_params, ++ .size = sizeof (self->encode_params), ++ }, { ++ .id = V4L2_CID_STATELESS_VP8_ENCODE_QP, ++ .value = gst_v4l2_codec_vp8_enc_check_qp_range (self, vp8_frame), ++ .size = sizeof (guint), ++ }, ++ }; ++ /* *INDENT-ON* */ ++ ++ GST_DEBUG_OBJECT (self, "encode vp8 frame with quality = %d", ++ vp8_frame->quality); ++ ++ if (!gst_v4l2_codec_vp8_enc_ensure_output_bitstream (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate output buffer."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_codec_vp8_enc_copy_input_buffer (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate/copy input buffer."), (NULL)); ++ goto done; ++ } ++ ++ request = gst_v4l2_encoder_alloc_request (self->encoder, ++ frame->system_frame_number, frame->input_buffer, frame->output_buffer); ++ ++ if (!request) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate a media request object."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_codec_vp8_enc_fill_encode_params (encoder, vp8_frame); ++ ++ if (!gst_v4l2_encoder_set_controls (self->encoder, request, control, ++ G_N_ELEMENTS (control))) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the control parameters."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_encoder_request_queue (request, 0)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the encode request."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_encoder_request_set_done (request, &bytesused)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not ack the request."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_encoder_request_unref (request); ++ ++ resized_buffer = gst_buffer_copy_region (frame->output_buffer, ++ GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_DEEP, 0, bytesused); ++ gst_buffer_replace (&frame->output_buffer, resized_buffer); ++ gst_buffer_unref (resized_buffer); ++ ++ return gst_video_encoder_finish_frame (venc, frame); ++ ++done: ++ if (request) ++ gst_v4l2_encoder_request_unref (request); ++ ++ return ret; ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_init (GstV4l2CodecVp8Enc * self) ++{ ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_subinit (GstV4l2CodecVp8Enc * self, ++ GstV4l2CodecVp8EncClass * klass) ++{ ++ self->encoder = gst_v4l2_encoder_new (klass->device); ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_dispose (GObject * object) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object); ++ ++ g_clear_object (&self->encoder); ++ ++ G_OBJECT_CLASS (parent_class)->dispose (object); ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_class_init (GstV4l2CodecVp8EncClass * klass) ++{ ++} ++ ++static void ++gst_v4l2_codec_vp8_enc_subclass_init (GstV4l2CodecVp8EncClass * klass, ++ GstV4l2CodecDevice * device) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GstElementClass *element_class = GST_ELEMENT_CLASS (klass); ++ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass); ++ GstVp8EncoderClass *vp8encoder_class = GST_VP8_ENCODER_CLASS (klass); ++ ++ gobject_class->set_property = gst_v4l2_codec_vp8_enc_set_property; ++ gobject_class->get_property = gst_v4l2_codec_vp8_enc_get_property; ++ gobject_class->dispose = gst_v4l2_codec_vp8_enc_dispose; ++ ++ gst_element_class_set_static_metadata (element_class, ++ "V4L2 Stateless VP8 Video Encoder", ++ "Codec/Encoder/Video/Hardware", ++ "A V4L2 based VP8 video encoder", ++ "Benjamin Gaignard "); ++ ++ gst_element_class_add_static_pad_template (element_class, &sink_template); ++ gst_element_class_add_static_pad_template (element_class, &src_template); ++ ++ element_class->change_state = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_change_state); ++ ++ encoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_open); ++ encoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_close); ++ encoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_start); ++ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_stop); ++ encoder_class->set_format = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_set_format); ++ encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_flush); ++ encoder_class->sink_event = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_sink_event); ++ encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_getcaps); ++ vp8encoder_class->encode_frame = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_encode_frame); ++ ++ klass->device = device; ++ gst_v4l2_encoder_install_properties (gobject_class, PROP_LAST, device); ++} ++ ++void ++gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin, GstV4l2Encoder * encoder, ++ GstV4l2CodecDevice * device, guint rank) ++{ ++ gchar *element_name; ++ guint version; ++ ++ GST_DEBUG_CATEGORY_INIT (v4l2_vp8enc_debug, "v4l2codecs-vp8enc", 0, ++ "V4L2 stateless VP8 encoder"); ++ ++ version = gst_v4l2_encoder_get_version (encoder); ++ if (version < V4L2_MIN_KERNEL_VERSION) ++ GST_WARNING ("V4L2 API v%u.%u too old, at least v%u.%u required", ++ (version >> 16) & 0xff, (version >> 8) & 0xff, ++ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR); ++ ++ if (!gst_v4l2_codec_vp8_enc_api_check (encoder)) { ++ GST_WARNING ("Not registering VP8 encoder as it failed ABI check."); ++ return; ++ } ++ ++ gst_v4l2_encoder_register (plugin, GST_TYPE_V4L2_CODEC_VP8_ENC, ++ (GClassInitFunc) gst_v4l2_codec_vp8_enc_subclass_init, ++ gst_mini_object_ref (GST_MINI_OBJECT (device)), ++ (GInstanceInitFunc) gst_v4l2_codec_vp8_enc_subinit, ++ "v4l2sl%svp8enc", device, rank, &element_name); ++} +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.h b/sys/v4l2codecs/gstv4l2codecvp8enc.h +new file mode 100644 +index 0000000..115b147 +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.h +@@ -0,0 +1,54 @@ ++/* GStreamer ++ * Copyright (C) 2022 Benjamin Gaignard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GST_V4L2_CODEC_VP8_ENC_H__ ++#define __GST_V4L2_CODEC_VP8_ENC_H__ ++ ++#define GST_USE_UNSTABLE_API ++#include ++ ++#include "gstv4l2encoder.h" ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_V4L2_CODEC_VP8_ENC (gst_v4l2_codec_vp8_enc_get_type()) ++#define GST_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8Enc)) ++#define GST_V4L2_CODEC_VP8_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8EncClass)) ++#define GST_V4L2_CODEC_VP8_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_VP8_ENC, GstV4l2CodecVp8EncClass)) ++#define GST_IS_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_VP8_ENC)) ++#define GST_IS_V4L2_CODEC_VP8_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_VP8_ENC)) ++ ++typedef struct _GstV4l2CodecVp8Enc GstV4l2CodecVp8Enc; ++typedef struct _GstV4l2CodecVp8EncClass GstV4l2CodecVp8EncClass; ++ ++struct _GstV4l2CodecVp8EncClass ++{ ++ GstVp8EncoderClass parent_class; ++ GstV4l2CodecDevice *device; ++}; ++ ++GType gst_v4l2_codec_vp8_enc_get_type (void); ++void gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin, ++ GstV4l2Encoder * encoder, ++ GstV4l2CodecDevice * device, ++ guint rank); ++ ++G_END_DECLS ++ ++#endif /* __GST_V4L2_CODEC_VP8_ENC_H__ */ +diff --git a/sys/v4l2codecs/meson.build b/sys/v4l2codecs/meson.build +index 53e8923..638c578 100644 +--- a/sys/v4l2codecs/meson.build ++++ b/sys/v4l2codecs/meson.build +@@ -12,6 +12,7 @@ v4l2codecs_sources = [ + 'gstv4l2format.c', + 'gstv4l2codecalphadecodebin.c', + 'gstv4l2encoder.c', ++ 'gstv4l2codecvp8enc.c', + ] + + libgudev_dep = dependency('gudev-1.0', required: get_option('v4l2codecs')) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch new file mode 100644 index 000000000..df74af9d7 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch @@ -0,0 +1,77 @@ +From 9f38752fa28cb43b1069fe6fa9ce7e1db5958962 Mon Sep 17 00:00:00 2001 +From: Benjamin Gaignard +Date: Mon, 16 Jan 2023 17:40:30 +0100 +Subject: [PATCH 5/5] v4l2codecs: Register V4L2 stateless Vp8 encoder + +If a v4l2 stateless VP8 encoder is detected register the matching +element. +Upstream-Status: Pending +--- + sys/v4l2codecs/plugin.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/sys/v4l2codecs/plugin.c b/sys/v4l2codecs/plugin.c +index ac83e1b..7867474 100644 +--- a/sys/v4l2codecs/plugin.c ++++ b/sys/v4l2codecs/plugin.c +@@ -27,8 +27,10 @@ + #include "gstv4l2codech265dec.h" + #include "gstv4l2codecmpeg2dec.h" + #include "gstv4l2codecvp8dec.h" ++#include "gstv4l2codecvp8enc.h" + #include "gstv4l2codecvp9dec.h" + #include "gstv4l2decoder.h" ++#include "gstv4l2encoder.h" + #include "linux/v4l2-controls.h" + #include "linux/media.h" + +@@ -91,6 +93,36 @@ register_video_decoder (GstPlugin * plugin, GstV4l2CodecDevice * device) + g_object_unref (decoder); + } + ++static void ++register_video_encoder (GstPlugin * plugin, GstV4l2CodecDevice * device) ++{ ++ GstV4l2Encoder *encoder = gst_v4l2_encoder_new (device); ++ gint i; ++ guint32 fmt; ++ ++ if (!gst_v4l2_encoder_open (encoder)) { ++ g_object_unref (encoder); ++ return; ++ } ++ ++ for (i = 0; gst_v4l2_encoder_enum_src_formats (encoder, i, &fmt); i++) { ++ switch (fmt) { ++ case V4L2_PIX_FMT_VP8_FRAME: ++ GST_INFO_OBJECT (encoder, "Registering %s as VP8 Encoder", ++ device->name); ++ gst_v4l2_codec_vp8_enc_register (plugin, encoder, device, ++ GST_RANK_PRIMARY + 1); ++ break; ++ default: ++ GST_FIXME_OBJECT (encoder, "%" GST_FOURCC_FORMAT " is not supported.", ++ GST_FOURCC_ARGS (fmt)); ++ break; ++ } ++ } ++ ++ g_object_unref (encoder); ++} ++ + static gboolean + plugin_init (GstPlugin * plugin) + { +@@ -112,6 +144,9 @@ plugin_init (GstPlugin * plugin) + + if (device->function == MEDIA_ENT_F_PROC_VIDEO_DECODER) + register_video_decoder (plugin, device); ++ ++ if (device->function == MEDIA_ENT_F_PROC_VIDEO_ENCODER) ++ register_video_encoder (plugin, device); + } + + gst_v4l2_codec_device_list_free (devices); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-waylandsink-HACK-disable-frame-dropping-while-redraw.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch similarity index 83% rename from meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-waylandsink-HACK-disable-frame-dropping-while-redraw.patch rename to meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch index ac225acad..6913258d0 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0008-waylandsink-HACK-disable-frame-dropping-while-redraw.patch +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch @@ -18,20 +18,19 @@ Even if not observed yet, there is a risk of visible decoding artefacts due to the fact that downstream elements could rewrite in a frame being currently rendered by display subsystem... -Upstream-Status: Inappropriate [DEY specific] - Signed-off-by: Hugues Fruchet +Upstream-Status: Submitted --- ext/wayland/gstwaylandsink.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index 40edff7..b97570e 100644 +index a5fb0cd..d98c53f 100644 --- a/ext/wayland/gstwaylandsink.c +++ b/ext/wayland/gstwaylandsink.c -@@ -774,10 +774,29 @@ render_last_buffer (GstWaylandSink * sink, gboolean redraw) - wlbuffer = gst_buffer_get_wl_buffer (sink->display, sink->last_buffer); - surface = gst_wl_window_get_wl_surface (sink->window); +@@ -806,10 +806,29 @@ render_last_buffer (GstWaylandSink * self, gboolean redraw) + wlbuffer = gst_buffer_get_wl_buffer (self->display, self->last_buffer); + surface = gst_wl_window_get_wl_surface (self->window); + /* + * HACK: disable frame dropping while redraw is pending @@ -51,14 +50,14 @@ index 40edff7..b97570e 100644 + * being currently rendered by display subsystem... + */ +#if 0 - sink->redraw_pending = TRUE; + self->redraw_pending = TRUE; callback = wl_surface_frame (surface); - sink->callback = callback; - wl_callback_add_listener (callback, &frame_callback_listener, sink); + self->callback = callback; + wl_callback_add_listener (callback, &frame_callback_listener, self); +#endif - if (G_UNLIKELY (sink->video_info_changed && !redraw)) { - info = &sink->video_info; + if (G_UNLIKELY (self->video_info_changed && !redraw)) { + info = &self->video_info; -- 2.25.1 diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0028-v4l2codecs-add-key-frame-signaling.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0028-v4l2codecs-add-key-frame-signaling.patch new file mode 100644 index 000000000..35de61bbb --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0028-v4l2codecs-add-key-frame-signaling.patch @@ -0,0 +1,107 @@ +From cf7bb1dd6061de058c33dbc9cd268441af784309 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 8 Aug 2023 16:13:35 +0200 +Subject: [PATCH] v4l2codecs: add key frame signaling + +Propagate V4L2 keyframe flags signaling to upstream elements +by setting GstBuffer delta-unit flag & GstVideoCodecFrame sync point flag. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 11 ++++++++++- + sys/v4l2codecs/gstv4l2encoder.c | 7 ++++--- + sys/v4l2codecs/gstv4l2encoder.h | 2 +- + 3 files changed, 15 insertions(+), 5 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 2f15caf..71c7dd2 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -542,6 +542,7 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, + GstVideoCodecFrame *frame = vp8_frame->frame; + GstBuffer *resized_buffer; + guint32 bytesused; ++ guint32 flags; + + /* *INDENT-OFF* */ + struct v4l2_ext_control control[] = { +@@ -596,7 +597,7 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, + goto done; + } + +- if (!gst_v4l2_encoder_request_set_done (request, &bytesused)) { ++ if (!gst_v4l2_encoder_request_set_done (request, &bytesused, &flags)) { + GST_ELEMENT_ERROR (self, RESOURCE, WRITE, + ("Driver did not ack the request."), (NULL)); + goto done; +@@ -609,6 +610,14 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, + gst_buffer_replace (&frame->output_buffer, resized_buffer); + gst_buffer_unref (resized_buffer); + ++ if (flags & V4L2_BUF_FLAG_KEYFRAME) { ++ GST_BUFFER_FLAG_UNSET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT); ++ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); ++ } else { ++ GST_BUFFER_FLAG_SET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT); ++ GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame); ++ } ++ + return gst_video_encoder_finish_frame (venc, frame); + + done: +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 5ae2cfa..e2ef423 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -696,7 +696,7 @@ gst_v4l2_encoder_dequeue_sink (GstV4l2Encoder * self) + + static gboolean + gst_v4l2_encoder_dequeue_src (GstV4l2Encoder * self, guint32 * out_frame_num, +- guint32 * bytesused) ++ guint32 * bytesused, guint32 * flags) + { + gint ret; + struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} }; +@@ -718,6 +718,7 @@ gst_v4l2_encoder_dequeue_src (GstV4l2Encoder * self, guint32 * out_frame_num, + + *out_frame_num = buf.timestamp.tv_usec; + *bytesused = buf.m.planes[0].bytesused; ++ *flags = buf.flags; + + GST_TRACE_OBJECT (self, "Dequeued bitstream buffer %i, %d bytes used", + buf.index, buf.m.planes[0].bytesused); +@@ -1137,7 +1138,7 @@ gst_v4l2_encoder_request_queue (GstV4l2Request * request, guint flags) + + gint + gst_v4l2_encoder_request_set_done (GstV4l2Request * request, +- guint32 * bytesused) ++ guint32 * bytesused, guint32 * flags) + { + GstV4l2Encoder *encoder = request->encoder; + GstV4l2Request *pending_req = NULL; +@@ -1167,7 +1168,7 @@ gst_v4l2_encoder_request_set_done (GstV4l2Request * request, + + if (!pending_req->hold_pic_buf) { + guint32 frame_num = G_MAXUINT32; +- if (!gst_v4l2_encoder_dequeue_src (encoder, &frame_num, bytesused)) { ++ if (!gst_v4l2_encoder_dequeue_src (encoder, &frame_num, bytesused, flags)) { + pending_req->failed = TRUE; + } else if (frame_num != pending_req->frame_num) { + GST_WARNING_OBJECT (encoder, +diff --git a/sys/v4l2codecs/gstv4l2encoder.h b/sys/v4l2codecs/gstv4l2encoder.h +index 7ff01a9..9acb0e3 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.h ++++ b/sys/v4l2codecs/gstv4l2encoder.h +@@ -128,7 +128,7 @@ void gst_v4l2_encoder_ro_request_unref (GstV4l2Request * request); + gboolean gst_v4l2_encoder_request_queue (GstV4l2Request * request, + guint flags); + +-gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused); ++gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused, guint32 * flags); + + gboolean gst_v4l2_encoder_request_failed (GstV4l2Request * request); + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch new file mode 100644 index 000000000..37d499c6d --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch @@ -0,0 +1,63 @@ +From 3ffaca250df9e72917ed3306d538474f44d7b1e2 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Thu, 17 Aug 2023 11:10:54 +0200 +Subject: [PATCH 2/2] gtkwaylandsink: HACK: disable frame dropping while redraw + is pending + +This workaround is needed to reach 30fps video playback. + +Redraw callback is received late, leading to many frames +being dropped and so low framerate on display driver stage. +The fact that redraw callback is received late is not yet understood +but display subsystem is able to sustain a higher framerate +than the one reached with redraw callback mechanism. +This workaround consists to ignore redraw callback allowing to +get back to the expected framerate performances. + +Even if not observed yet, there is a risk of visible decoding artefacts +due to the fact that downstream elements could rewrite in a frame +being currently rendered by display subsystem... + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + ext/gtk/gstgtkwaylandsink.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 26e4aa0..88cbe5d 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1095,10 +1095,29 @@ render_last_buffer (GstGtkWaylandSink * self, gboolean redraw) + wlbuffer = gst_buffer_get_wl_buffer (priv->display, priv->last_buffer); + surface = gst_wl_window_get_wl_surface (priv->wl_window); + ++ /* ++ * HACK: disable frame dropping while redraw is pending ++ * ++ * This workaround is needed to reach 30fps video playback. ++ * ++ * Redraw callback is received late, leading to many frames ++ * being dropped and so low framerate on display driver stage. ++ * The fact that redraw callback is received late is not yet understood ++ * but display subsystem is able to sustain a higher framerate ++ * than the one reached with redraw callback mechanism. ++ * This workaround consists to ignore redraw callback allowing to ++ * get back to the expected framerate performances. ++ * ++ * Why not observed yet, there is a risk of visible decoding artefacts ++ * due to the fact that downstream elements could rewrite in a frame ++ * being currently rendered by display subsystem... ++ */ ++#if 0 + priv->redraw_pending = TRUE; + callback = wl_surface_frame (surface); + priv->callback = callback; + wl_callback_add_listener (callback, &frame_callback_listener, self); ++#endif + + if (G_UNLIKELY (priv->video_info_changed && !redraw)) { + info = &priv->video_info; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0030-gtkwaylandsink-cancel-pending-redraw-callback-on-pau.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0030-gtkwaylandsink-cancel-pending-redraw-callback-on-pau.patch new file mode 100644 index 000000000..a490f8958 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0030-gtkwaylandsink-cancel-pending-redraw-callback-on-pau.patch @@ -0,0 +1,63 @@ +From ec109f9a3b50f21227e15f45b1558b0f5c6f73ef Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 16 Aug 2023 17:14:55 +0200 +Subject: [PATCH 1/2] gtkwaylandsink: cancel pending redraw callback on + pause/resume + +Pipeline freeze have been observed when doing intensive pause/resume +on video playback. + +Analysis shows that gtkwaylandsink element is stalled on state change +from PLAYING to PAUSED or PAUSED to PLAYING. + +There is a first problem when ASYNC state change is returned by parent +class; in this case we prematurely return from state_change, fix that by +checking GST_STATE_CHANGE_FAILURE instead. + +The other problem is about eventual pending wayland listener callback +(see redraw_callback mechanism) while changing state which cause +further freeze of element. Fix this by destroying listener callback +and resetting redraw_callback flag when going from PLAYING to PAUSED and +PAUSED to PLAYING. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + ext/gtk/gstgtkwaylandsink.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 0d6ea69..26e4aa0 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -736,7 +736,7 @@ gst_gtk_wayland_sink_change_state (GstElement * element, + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +- if (ret != GST_STATE_CHANGE_SUCCESS) ++ if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { +@@ -762,6 +762,18 @@ gst_gtk_wayland_sink_change_state (GstElement * element, + priv->redraw_pending = FALSE; + g_mutex_unlock (&priv->render_lock); + break; ++ case GST_STATE_CHANGE_PAUSED_TO_PLAYING: ++ case GST_STATE_CHANGE_PLAYING_TO_PAUSED: ++ /* Destroy pending redraw callback otherwise ++ * element may freeze */ ++ g_mutex_lock (&priv->render_lock); ++ if (priv->callback) { ++ wl_callback_destroy (priv->callback); ++ priv->callback = NULL; ++ } ++ priv->redraw_pending = FALSE; ++ g_mutex_unlock (&priv->render_lock); ++ break; + default: + break; + } +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0031-v4l2codecs-fix-support-of-unaligned-videos.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0031-v4l2codecs-fix-support-of-unaligned-videos.patch new file mode 100644 index 000000000..b09d6c31d --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0031-v4l2codecs-fix-support-of-unaligned-videos.patch @@ -0,0 +1,35 @@ +From ee75c40eff3ed516535e8b150e5a17206e73d81a Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 30 Aug 2023 10:48:22 +0200 +Subject: [PATCH 1/3] v4l2codecs: fix support of unaligned videos + +Current implementation fails on frame input mapping when +driver returns an aligned width & height. +Fix this by ignoring aligned width & height returned by S_FMT, +it is useless till buffer alignment is extrapolated later on +from bytesperline and sizeimage S_FMT values. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index e2ef423..eabb74a 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -415,6 +415,10 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + } + } + ++ ++ fmt.fmt.pix_mp.width = width; ++ fmt.fmt.pix_mp.height = height; ++ + if (!gst_v4l2_format_to_video_info (&fmt, out)) { + GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fmt.fmt.pix_mp.pixelformat)); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0033-waylandsink-Fix-rendering-of-unaligned-content.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0033-waylandsink-Fix-rendering-of-unaligned-content.patch new file mode 100644 index 000000000..e0407f586 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0033-waylandsink-Fix-rendering-of-unaligned-content.patch @@ -0,0 +1,53 @@ +From f6bf17e24601a6e5ba91d147a2532b9ce03f984c Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Mon, 11 Sep 2023 14:12:12 +0200 +Subject: [PATCH 2/5] waylandsink: Fix rendering of unaligned content + +Non 16 pixels aligned YUV content is displayed with a stride +effect with waylandsink/gtkwaylandsink. +Fix this by forcing 16 pixels alignment of width when creating +zwp linux dmabuf wayland plane. +This should not have any effect because of well aligned stride/offset +but it does... so align width as a temporary workaround till problem +is fully understood. + +To reproduce issue: + +export WAYLAND_DEBUG=1 +gst-launch-1.0 videotestsrc ! video/x-raw, format=NV12, width=306, height=480 ! waylandsink drm-device=/dev/dri/card0 2>&1 | grep zwp +[3956691.525] -> zwp_linux_buffer_params_v1@21.add(fd 19, 0, 0, 384, 0, 0) +[3956691.555] -> zwp_linux_buffer_params_v1@21.add(fd 20, 1, 184320, 384, 0, 0) +[3956691.580] -> zwp_linux_buffer_params_v1@21.create(306, 480, 842094158, 0) +-------------------------------------------------------^^^ +=> bad rendering with stride effect + +With this fix: +[ 48713.010] -> zwp_linux_buffer_params_v1@21.add(fd 19, 0, 0, 384, 0, 0) +[ 48713.039] -> zwp_linux_buffer_params_v1@21.add(fd 20, 1, 184320, 384, 0, 0) +[ 48713.065] -> zwp_linux_buffer_params_v1@21.create(320, 480, 842094158, 0) +-------------------------------------------------------^^^ +=> rendering is correct + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + gst-libs/gst/wayland/gstwllinuxdmabuf.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +index deb5d32..2fbe57e 100644 +--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c ++++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +@@ -154,6 +154,9 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + } + } + ++ //FIXME, otherwise stride effect with non 16-pixels aligned videos ++ width = GST_ROUND_UP_16(width); ++ + /* Request buffer creation */ + zwp_linux_buffer_params_v1_add_listener (params, ¶ms_listener, &data); + zwp_linux_buffer_params_v1_create (params, width, height, format, flags); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0035-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0035-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch new file mode 100644 index 000000000..edce48616 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0035-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch @@ -0,0 +1,25 @@ +From 86e5e19f3f9af7cf5c7d6f875e799aaf0eb40b92 Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Thu, 2 Mar 2023 09:18:49 +0100 +Subject: [PATCH 1/4] v4l2codecs: add NV12M format to gstv4l2format + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index 64c47e1..bc20d68 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -33,6 +33,7 @@ struct FormatEntry + + static struct FormatEntry format_map[] = { + {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, ++ {V4L2_PIX_FMT_NV12M, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422}, + {V4L2_PIX_FMT_SUNXI_TILED_NV12, 1, GST_VIDEO_FORMAT_NV12_32L32, 8, 422}, + {V4L2_PIX_FMT_NV12_4L4, 1, GST_VIDEO_FORMAT_NV12_4L4, 8, 420}, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0036-v4l2codecs-fix-gst_v4l2_encoder_set_src_fmt-call.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0036-v4l2codecs-fix-gst_v4l2_encoder_set_src_fmt-call.patch new file mode 100644 index 000000000..9e6d56176 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0036-v4l2codecs-fix-gst_v4l2_encoder_set_src_fmt-call.patch @@ -0,0 +1,52 @@ +From a68f43ffe7be63cb2dd5839b493547249211ad0a Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Mon, 18 Sep 2023 13:28:32 +0200 +Subject: [PATCH 2/4] v4l2codecs: fix gst_v4l2_encoder_set_src_fmt() call + +Set source format regarding state->vinfo and call it +right before setting sink format. +Fixes issues when encoding from YUY2 and I420 formats. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 71c7dd2..2d476b3 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -285,6 +285,14 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + + gst_v4l2_codec_vp8_enc_reset_allocation (self); + ++ if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, ++ V4L2_PIX_FMT_VP8_FRAME)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), ++ ("No support for %ux%u format VP8", state->info.width, ++ state->info.height)); ++ return FALSE; ++ } ++ + if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, + &self->vinfo)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, +@@ -295,14 +303,6 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + } + +- if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &self->vinfo, +- V4L2_PIX_FMT_VP8_FRAME)) { +- GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), +- ("No support for %ux%u format VP8", state->info.width, +- state->info.height)); +- return FALSE; +- } +- + self->width = state->info.width; + self->height = state->info.height; + gst_v4l2_codec_vp8_enc_buffers_allocation (encoder); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0037-v4l2codecs-add-support-of-encoding-from-RGB.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0037-v4l2codecs-add-support-of-encoding-from-RGB.patch new file mode 100644 index 000000000..330281416 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0037-v4l2codecs-add-support-of-encoding-from-RGB.patch @@ -0,0 +1,51 @@ +From af0ac2c50aa14d1a0cb8aad8aa3820ee70acd7a5 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 10 Oct 2023 15:48:35 +0200 +Subject: [PATCH 3/4] v4l2codecs: add support of encoding from RGB + +Add support of encoding from RGB in 16 bits and 32 bits formats: +- RGB16 +- BGRx +- RGBx +- xBGR +- xRGB +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 7 +++++++ + sys/v4l2codecs/gstv4l2format.h | 2 +- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index bc20d68..604876e 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -40,6 +40,13 @@ static struct FormatEntry format_map[] = { + {V4L2_PIX_FMT_MM21, 2, GST_VIDEO_FORMAT_NV12_16L32S, 8, 420}, + {V4L2_PIX_FMT_YUV420M, 3, GST_VIDEO_FORMAT_I420, 8, 420}, + {V4L2_PIX_FMT_P010, 1, GST_VIDEO_FORMAT_P010_10LE, 16, 420}, ++ {V4L2_PIX_FMT_RGB565, 1, GST_VIDEO_FORMAT_RGB16, 16, 0}, ++ {V4L2_PIX_FMT_XBGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0}, ++ {V4L2_PIX_FMT_BGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0}, ++ {V4L2_PIX_FMT_RGBX32, 1, GST_VIDEO_FORMAT_RGBx, 32, 0}, ++ {V4L2_PIX_FMT_BGRX32, 1, GST_VIDEO_FORMAT_xBGR, 32, 0}, ++ {V4L2_PIX_FMT_XRGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0}, ++ {V4L2_PIX_FMT_RGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0}, + {0,} + }; + +diff --git a/sys/v4l2codecs/gstv4l2format.h b/sys/v4l2codecs/gstv4l2format.h +index b579248..dd1e1ea 100644 +--- a/sys/v4l2codecs/gstv4l2format.h ++++ b/sys/v4l2codecs/gstv4l2format.h +@@ -24,7 +24,7 @@ + #include + #include "linux/videodev2.h" + +-#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE}" ++#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE, RGB16, BGRx, RGBx, xBGR, xRGB}" + + gboolean gst_v4l2_format_to_video_info (struct v4l2_format * fmt, + GstVideoInfo * out_info); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0038-v4l2codecs-add-colorimetry-support-at-sink-pad.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0038-v4l2codecs-add-colorimetry-support-at-sink-pad.patch new file mode 100644 index 000000000..f97e43407 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0038-v4l2codecs-add-colorimetry-support-at-sink-pad.patch @@ -0,0 +1,193 @@ +From 7edfb2c56b58498589000824021d9a4a11f97439 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 18 Oct 2023 17:45:46 +0200 +Subject: [PATCH 4/4] v4l2codecs: add colorimetry support at sink pad + +Forward sink pad colorimetry informations to V4L2. +This is needed to precisely configure the matrix +used by encoder for RGB to YUV conversion. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 152 ++++++++++++++++++++++++++++++++ + 1 file changed, 152 insertions(+) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index eabb74a..b9e529f 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -387,6 +387,11 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + guint32 pix_fmt; + gint width = in->width; + gint height = in->height; ++ GstVideoInfo info = *in; ++ enum v4l2_colorspace colorspace = 0; ++ enum v4l2_quantization range = 0; ++ enum v4l2_ycbcr_encoding matrix = 0; ++ enum v4l2_xfer_func transfer = 0; + + ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); + if (ret < 0) { +@@ -400,6 +405,149 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + return FALSE; + } + ++ /* We first pick the main colorspace from the primaries */ ++ switch (info.colorimetry.primaries) { ++ case GST_VIDEO_COLOR_PRIMARIES_BT709: ++ /* There is two colorspaces using these primaries, use the range to ++ * differentiate */ ++ if (info.colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) ++ colorspace = V4L2_COLORSPACE_REC709; ++ else ++ colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ case GST_VIDEO_COLOR_PRIMARIES_BT2020: ++ colorspace = V4L2_COLORSPACE_BT2020; ++ break; ++ case GST_VIDEO_COLOR_PRIMARIES_BT470M: ++ colorspace = V4L2_COLORSPACE_470_SYSTEM_M; ++ break; ++ case GST_VIDEO_COLOR_PRIMARIES_BT470BG: ++ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; ++ break; ++ case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M: ++ colorspace = V4L2_COLORSPACE_SMPTE170M; ++ break; ++ case GST_VIDEO_COLOR_PRIMARIES_SMPTE240M: ++ colorspace = V4L2_COLORSPACE_SMPTE240M; ++ break; ++ ++ case GST_VIDEO_COLOR_PRIMARIES_FILM: ++ case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN: ++ /* We don't know, we will guess */ ++ break; ++ ++ default: ++ GST_WARNING_OBJECT (self, ++ "Unknown colorimetry primaries %d", info.colorimetry.primaries); ++ break; ++ } ++ ++ switch (info.colorimetry.range) { ++ case GST_VIDEO_COLOR_RANGE_0_255: ++ range = V4L2_QUANTIZATION_FULL_RANGE; ++ break; ++ case GST_VIDEO_COLOR_RANGE_16_235: ++ range = V4L2_QUANTIZATION_LIM_RANGE; ++ break; ++ case GST_VIDEO_COLOR_RANGE_UNKNOWN: ++ /* We let the driver pick a default one */ ++ break; ++ default: ++ GST_WARNING_OBJECT (self, ++ "Unknown colorimetry range %d", info.colorimetry.range); ++ break; ++ } ++ ++ switch (info.colorimetry.matrix) { ++ case GST_VIDEO_COLOR_MATRIX_RGB: ++ /* Unspecified, leave to default */ ++ break; ++ /* FCC is about the same as BT601 with less digit */ ++ case GST_VIDEO_COLOR_MATRIX_FCC: ++ case GST_VIDEO_COLOR_MATRIX_BT601: ++ matrix = V4L2_YCBCR_ENC_601; ++ break; ++ case GST_VIDEO_COLOR_MATRIX_BT709: ++ matrix = V4L2_YCBCR_ENC_709; ++ break; ++ case GST_VIDEO_COLOR_MATRIX_SMPTE240M: ++ matrix = V4L2_YCBCR_ENC_SMPTE240M; ++ break; ++ case GST_VIDEO_COLOR_MATRIX_BT2020: ++ matrix = V4L2_YCBCR_ENC_BT2020; ++ break; ++ case GST_VIDEO_COLOR_MATRIX_UNKNOWN: ++ /* We let the driver pick a default one */ ++ break; ++ default: ++ GST_WARNING_OBJECT (self, ++ "Unknown colorimetry matrix %d", info.colorimetry.matrix); ++ break; ++ } ++ ++ switch (info.colorimetry.transfer) { ++ case GST_VIDEO_TRANSFER_GAMMA18: ++ case GST_VIDEO_TRANSFER_GAMMA20: ++ case GST_VIDEO_TRANSFER_GAMMA22: ++ case GST_VIDEO_TRANSFER_GAMMA28: ++ GST_WARNING_OBJECT (self, ++ "GAMMA 18, 20, 22, 28 transfer functions not supported"); ++ /* fallthrough */ ++ case GST_VIDEO_TRANSFER_GAMMA10: ++ transfer = V4L2_XFER_FUNC_NONE; ++ break; ++ case GST_VIDEO_TRANSFER_SMPTE2084: ++ transfer = V4L2_XFER_FUNC_SMPTE2084; ++ break; ++ case GST_VIDEO_TRANSFER_BT601: ++ case GST_VIDEO_TRANSFER_BT2020_12: ++ case GST_VIDEO_TRANSFER_BT2020_10: ++ case GST_VIDEO_TRANSFER_BT709: ++ transfer = V4L2_XFER_FUNC_709; ++ break; ++ case GST_VIDEO_TRANSFER_SMPTE240M: ++ transfer = V4L2_XFER_FUNC_SMPTE240M; ++ break; ++ case GST_VIDEO_TRANSFER_SRGB: ++ transfer = V4L2_XFER_FUNC_SRGB; ++ break; ++ case GST_VIDEO_TRANSFER_LOG100: ++ case GST_VIDEO_TRANSFER_LOG316: ++ GST_WARNING_OBJECT (self, ++ "LOG 100, 316 transfer functions not supported"); ++ /* FIXME No known sensible default, maybe AdobeRGB ? */ ++ break; ++ case GST_VIDEO_TRANSFER_UNKNOWN: ++ /* We let the driver pick a default one */ ++ break; ++ default: ++ GST_WARNING_OBJECT (self, ++ "Unknown colorimetry transfer %d", info.colorimetry.transfer); ++ break; ++ } ++ ++ if (colorspace == 0) { ++ /* Try to guess colorspace according to pixelformat and size */ ++ if (GST_VIDEO_INFO_IS_YUV (&info)) { ++ if (range == V4L2_QUANTIZATION_FULL_RANGE ++ && matrix == V4L2_YCBCR_ENC_601 && transfer == 0) { ++ /* Full range BT.601 YCbCr encoding with unknown primaries and transfer ++ * function most likely is JPEG */ ++ colorspace = V4L2_COLORSPACE_JPEG; ++ transfer = V4L2_XFER_FUNC_SRGB; ++ } else { ++ /* SD streams likely use SMPTE170M and HD streams REC709 */ ++ if (width <= 720 && GST_VIDEO_INFO_HEIGHT (&info) <= 576) ++ colorspace = V4L2_COLORSPACE_SMPTE170M; ++ else ++ colorspace = V4L2_COLORSPACE_REC709; ++ } ++ } else if (GST_VIDEO_INFO_IS_RGB (&info)) { ++ colorspace = V4L2_COLORSPACE_SRGB; ++ transfer = V4L2_XFER_FUNC_NONE; ++ } ++ } ++ + if (pix_fmt != fmt.fmt.pix_mp.pixelformat + || fmt.fmt.pix_mp.width != width || fmt.fmt.pix_mp.height != height) { + GST_DEBUG_OBJECT (self, +@@ -408,6 +556,10 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + fmt.fmt.pix_mp.pixelformat = pix_fmt; + fmt.fmt.pix_mp.width = width; + fmt.fmt.pix_mp.height = height; ++ fmt.fmt.pix_mp.colorspace = colorspace; ++ fmt.fmt.pix_mp.quantization = range; ++ fmt.fmt.pix_mp.ycbcr_enc = matrix; ++ fmt.fmt.pix_mp.xfer_func = transfer; + ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt); + if (ret < 0) { + GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0040-Revert-v4l2codecs-fix-support-of-unaligned-videos.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0040-Revert-v4l2codecs-fix-support-of-unaligned-videos.patch new file mode 100644 index 000000000..4bfc67371 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0040-Revert-v4l2codecs-fix-support-of-unaligned-videos.patch @@ -0,0 +1,29 @@ +From e68bd1ef1e283d37c584c6bedfcdc8e82159bcbf Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 21 Nov 2023 10:51:47 +0100 +Subject: [PATCH 40/68] Revert "v4l2codecs: fix support of unaligned videos" + +This reverts commit cfd4935254b0d438ea60d350c0c3521b435aee84. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index b9e529f..26d79c1 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -567,10 +567,6 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + } + } + +- +- fmt.fmt.pix_mp.width = width; +- fmt.fmt.pix_mp.height = height; +- + if (!gst_v4l2_format_to_video_info (&fmt, out)) { + GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fmt.fmt.pix_mp.pixelformat)); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0041-h264parser-Define-level-enum-values.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0041-h264parser-Define-level-enum-values.patch new file mode 100644 index 000000000..065770152 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0041-h264parser-Define-level-enum-values.patch @@ -0,0 +1,210 @@ +From 97575d37f6f584211553f5637b8b517762acb408 Mon Sep 17 00:00:00 2001 +From: Seungha Yang +Date: Thu, 11 May 2023 04:53:24 +0900 +Subject: [PATCH 41/68] h264parser: Define level enum values + +... and stop duplicating it + +Part-of: +Upstream-Status: Backport +--- + gst-libs/gst/codecparsers/gsth264parser.h | 31 +++++++++++++++++++++++ + gst-libs/gst/codecs/gsth264decoder.c | 30 +++------------------- + gst/codectimestamper/gsth264timestamper.c | 30 +++------------------- + gst/videoparsers/gsth264parse.c | 24 ------------------ + 4 files changed, 37 insertions(+), 78 deletions(-) + +diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h +index eb99b6a..f18a1f0 100644 +--- a/gst-libs/gst/codecparsers/gsth264parser.h ++++ b/gst-libs/gst/codecparsers/gsth264parser.h +@@ -352,6 +352,37 @@ typedef enum + GST_H264_CT_TYPE_UNKNOWN = 2, + } GstCtType; + ++/** ++ * GstH264Level: ++ * ++ * H.264 level ++ * ++ * Since: 1.24 ++ */ ++typedef enum ++{ ++ GST_H264_LEVEL_L1 = 10, ++ GST_H264_LEVEL_L1B = 9, ++ GST_H264_LEVEL_L1_1 = 11, ++ GST_H264_LEVEL_L1_2 = 12, ++ GST_H264_LEVEL_L1_3 = 13, ++ GST_H264_LEVEL_L2_0 = 20, ++ GST_H264_LEVEL_L2_1 = 21, ++ GST_H264_LEVEL_L2_2 = 22, ++ GST_H264_LEVEL_L3 = 30, ++ GST_H264_LEVEL_L3_1 = 31, ++ GST_H264_LEVEL_L3_2 = 32, ++ GST_H264_LEVEL_L4 = 40, ++ GST_H264_LEVEL_L4_1 = 41, ++ GST_H264_LEVEL_L4_2 = 42, ++ GST_H264_LEVEL_L5 = 50, ++ GST_H264_LEVEL_L5_1 = 51, ++ GST_H264_LEVEL_L5_2 = 52, ++ GST_H264_LEVEL_L6 = 60, ++ GST_H264_LEVEL_L6_1 = 61, ++ GST_H264_LEVEL_L6_2 = 62, ++} GstH264Level; ++ + typedef struct _GstH264NalParser GstH264NalParser; + + typedef struct _GstH264NalUnit GstH264NalUnit; +diff --git a/gst-libs/gst/codecs/gsth264decoder.c b/gst-libs/gst/codecs/gsth264decoder.c +index fc7d3a1..576e77c 100644 +--- a/gst-libs/gst/codecs/gsth264decoder.c ++++ b/gst-libs/gst/codecs/gsth264decoder.c +@@ -2220,33 +2220,9 @@ gst_h264_decoder_get_max_num_reorder_frames (GstH264Decoder * self, + return max_dpb_size; + } + +-typedef enum +-{ +- GST_H264_LEVEL_L1 = 10, +- GST_H264_LEVEL_L1B = 9, +- GST_H264_LEVEL_L1_1 = 11, +- GST_H264_LEVEL_L1_2 = 12, +- GST_H264_LEVEL_L1_3 = 13, +- GST_H264_LEVEL_L2_0 = 20, +- GST_H264_LEVEL_L2_1 = 21, +- GST_H264_LEVEL_L2_2 = 22, +- GST_H264_LEVEL_L3 = 30, +- GST_H264_LEVEL_L3_1 = 31, +- GST_H264_LEVEL_L3_2 = 32, +- GST_H264_LEVEL_L4 = 40, +- GST_H264_LEVEL_L4_1 = 41, +- GST_H264_LEVEL_L4_2 = 42, +- GST_H264_LEVEL_L5 = 50, +- GST_H264_LEVEL_L5_1 = 51, +- GST_H264_LEVEL_L5_2 = 52, +- GST_H264_LEVEL_L6 = 60, +- GST_H264_LEVEL_L6_1 = 61, +- GST_H264_LEVEL_L6_2 = 62, +-} GstH264DecoderLevel; +- + typedef struct + { +- GstH264DecoderLevel level; ++ GstH264Level level; + + guint32 max_mbps; + guint32 max_fs; +@@ -2278,7 +2254,7 @@ static const LevelLimits level_limits_map[] = { + }; + + static gint +-h264_level_to_max_dpb_mbs (GstH264DecoderLevel level) ++h264_level_to_max_dpb_mbs (GstH264Level level) + { + gint i; + for (i = 0; i < G_N_ELEMENTS (level_limits_map); i++) { +@@ -2397,7 +2373,7 @@ gst_h264_decoder_process_sps (GstH264Decoder * self, GstH264SPS * sps) + level = 9; + } + +- max_dpb_mbs = h264_level_to_max_dpb_mbs ((GstH264DecoderLevel) level); ++ max_dpb_mbs = h264_level_to_max_dpb_mbs ((GstH264Level) level); + if (!max_dpb_mbs) + return GST_FLOW_ERROR; + +diff --git a/gst/codectimestamper/gsth264timestamper.c b/gst/codectimestamper/gsth264timestamper.c +index d4f136d..8e73dc2 100644 +--- a/gst/codectimestamper/gsth264timestamper.c ++++ b/gst/codectimestamper/gsth264timestamper.c +@@ -194,33 +194,9 @@ gst_h264_timestamper_set_caps (GstCodecTimestamper * timestamper, + return TRUE; + } + +-typedef enum +-{ +- GST_H264_LEVEL_L1 = 10, +- GST_H264_LEVEL_L1B = 9, +- GST_H264_LEVEL_L1_1 = 11, +- GST_H264_LEVEL_L1_2 = 12, +- GST_H264_LEVEL_L1_3 = 13, +- GST_H264_LEVEL_L2_0 = 20, +- GST_H264_LEVEL_L2_1 = 21, +- GST_H264_LEVEL_L2_2 = 22, +- GST_H264_LEVEL_L3 = 30, +- GST_H264_LEVEL_L3_1 = 31, +- GST_H264_LEVEL_L3_2 = 32, +- GST_H264_LEVEL_L4 = 40, +- GST_H264_LEVEL_L4_1 = 41, +- GST_H264_LEVEL_L4_2 = 42, +- GST_H264_LEVEL_L5 = 50, +- GST_H264_LEVEL_L5_1 = 51, +- GST_H264_LEVEL_L5_2 = 52, +- GST_H264_LEVEL_L6 = 60, +- GST_H264_LEVEL_L6_1 = 61, +- GST_H264_LEVEL_L6_2 = 62, +-} GstH264DecoderLevel; +- + typedef struct + { +- GstH264DecoderLevel level; ++ GstH264Level level; + + guint32 max_mbps; + guint32 max_fs; +@@ -252,7 +228,7 @@ static const LevelLimits level_limits_map[] = { + }; + + static guint +-h264_level_to_max_dpb_mbs (GstH264DecoderLevel level) ++h264_level_to_max_dpb_mbs (GstH264Level level) + { + gint i; + for (i = 0; i < G_N_ELEMENTS (level_limits_map); i++) { +@@ -283,7 +259,7 @@ gst_h264_timestamper_process_sps (GstH264Timestamper * self, GstH264SPS * sps) + level = 9; + } + +- max_dpb_mbs = h264_level_to_max_dpb_mbs ((GstH264DecoderLevel) level); ++ max_dpb_mbs = h264_level_to_max_dpb_mbs ((GstH264Level) level); + if (sps->vui_parameters_present_flag + && sps->vui_parameters.bitstream_restriction_flag) { + max_dpb_frames = MAX (1, sps->vui_parameters.max_dec_frame_buffering); +diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c +index 8bb47f9..f8b22df 100644 +--- a/gst/videoparsers/gsth264parse.c ++++ b/gst/videoparsers/gsth264parse.c +@@ -2040,30 +2040,6 @@ get_level_string (GstH264SPS * sps) + } + } + +-typedef enum +-{ +- GST_H264_LEVEL_L1 = 10, +- GST_H264_LEVEL_L1B = 9, +- GST_H264_LEVEL_L1_1 = 11, +- GST_H264_LEVEL_L1_2 = 12, +- GST_H264_LEVEL_L1_3 = 13, +- GST_H264_LEVEL_L2_0 = 20, +- GST_H264_LEVEL_L2_1 = 21, +- GST_H264_LEVEL_L2_2 = 22, +- GST_H264_LEVEL_L3 = 30, +- GST_H264_LEVEL_L3_1 = 31, +- GST_H264_LEVEL_L3_2 = 32, +- GST_H264_LEVEL_L4 = 40, +- GST_H264_LEVEL_L4_1 = 41, +- GST_H264_LEVEL_L4_2 = 42, +- GST_H264_LEVEL_L5 = 50, +- GST_H264_LEVEL_L5_1 = 51, +- GST_H264_LEVEL_L5_2 = 52, +- GST_H264_LEVEL_L6 = 60, +- GST_H264_LEVEL_L6_1 = 61, +- GST_H264_LEVEL_L6_2 = 62, +-} GstH264Level; +- + typedef struct + { + GstH264Level level; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0042-codecparsers-keep-naming-consistency-in-GST_H264_LEV.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0042-codecparsers-keep-naming-consistency-in-GST_H264_LEV.patch new file mode 100644 index 000000000..fdfd08dd1 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0042-codecparsers-keep-naming-consistency-in-GST_H264_LEV.patch @@ -0,0 +1,71 @@ +From 98729dcc40b5e1fa407559ee74c8e98a38d9ecac Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= +Date: Wed, 31 May 2023 14:51:34 +0200 +Subject: [PATCH 42/68] codecparsers: keep naming consistency in GST_H264_LEVEL + +GST_H264_LEVEL_2 should be used instead of GST_H264_LEVEL_2_0 + +Part-of: +Upstream-Status: Backport +--- + gst-libs/gst/codecparsers/gsth264parser.h | 2 +- + gst-libs/gst/codecs/gsth264decoder.c | 2 +- + gst/codectimestamper/gsth264timestamper.c | 2 +- + gst/videoparsers/gsth264parse.c | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h +index f18a1f0..e9e79e6 100644 +--- a/gst-libs/gst/codecparsers/gsth264parser.h ++++ b/gst-libs/gst/codecparsers/gsth264parser.h +@@ -366,7 +366,7 @@ typedef enum + GST_H264_LEVEL_L1_1 = 11, + GST_H264_LEVEL_L1_2 = 12, + GST_H264_LEVEL_L1_3 = 13, +- GST_H264_LEVEL_L2_0 = 20, ++ GST_H264_LEVEL_L2 = 20, + GST_H264_LEVEL_L2_1 = 21, + GST_H264_LEVEL_L2_2 = 22, + GST_H264_LEVEL_L3 = 30, +diff --git a/gst-libs/gst/codecs/gsth264decoder.c b/gst-libs/gst/codecs/gsth264decoder.c +index 576e77c..a62751b 100644 +--- a/gst-libs/gst/codecs/gsth264decoder.c ++++ b/gst-libs/gst/codecs/gsth264decoder.c +@@ -2236,7 +2236,7 @@ static const LevelLimits level_limits_map[] = { + {GST_H264_LEVEL_L1_1, 3000, 396, 900, 192}, + {GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384}, + {GST_H264_LEVEL_L1_3, 11800, 396, 2376, 768}, +- {GST_H264_LEVEL_L2_0, 11880, 396, 2376, 2000}, ++ {GST_H264_LEVEL_L2, 11880, 396, 2376, 2000}, + {GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000}, + {GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000}, + {GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000}, +diff --git a/gst/codectimestamper/gsth264timestamper.c b/gst/codectimestamper/gsth264timestamper.c +index 8e73dc2..4200187 100644 +--- a/gst/codectimestamper/gsth264timestamper.c ++++ b/gst/codectimestamper/gsth264timestamper.c +@@ -210,7 +210,7 @@ static const LevelLimits level_limits_map[] = { + {GST_H264_LEVEL_L1_1, 3000, 396, 900, 192}, + {GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384}, + {GST_H264_LEVEL_L1_3, 11800, 396, 2376, 768}, +- {GST_H264_LEVEL_L2_0, 11880, 396, 2376, 2000}, ++ {GST_H264_LEVEL_L2, 11880, 396, 2376, 2000}, + {GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000}, + {GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000}, + {GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000}, +diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c +index f8b22df..0ce77a5 100644 +--- a/gst/videoparsers/gsth264parse.c ++++ b/gst/videoparsers/gsth264parse.c +@@ -2052,7 +2052,7 @@ static const GstH264LevelLimit level_limits_map[] = { + {GST_H264_LEVEL_L1_1, 768000}, + {GST_H264_LEVEL_L1_2, 1536000}, + {GST_H264_LEVEL_L1_3, 3041280}, +- {GST_H264_LEVEL_L2_0, 3041280}, ++ {GST_H264_LEVEL_L2, 3041280}, + {GST_H264_LEVEL_L2_1, 5068800}, + {GST_H264_LEVEL_L2_2, 5184000}, + {GST_H264_LEVEL_L3, 10368000}, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0043-v4l2codecs-encoder-No-need-to-set-num_planes-of-plan.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0043-v4l2codecs-encoder-No-need-to-set-num_planes-of-plan.patch new file mode 100644 index 000000000..13d1d4ebc --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0043-v4l2codecs-encoder-No-need-to-set-num_planes-of-plan.patch @@ -0,0 +1,34 @@ +From 9d989a3eb0f1d38e73013f3e866f30e5b5606ddd Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 2 Nov 2023 10:38:51 -0400 +Subject: [PATCH 43/68] v4l2codecs: encoder: No need to set num_planes of plane + size + +This is going to be filled by the driver. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 26d79c1..0938bb1 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -622,12 +622,11 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + GST_DEBUG_OBJECT (self, + "Trying to use peer format: %" GST_FOURCC_FORMAT " %ix%i", + GST_FOURCC_ARGS (pix_fmt), width, height); ++ + fmt.fmt.pix_mp.pixelformat = pix_fmt; + fmt.fmt.pix_mp.width = width; + fmt.fmt.pix_mp.height = height; +- fmt.fmt.pix_mp.num_planes = 1; +- fmt.fmt.pix_mp.plane_fmt[0].sizeimage = +- GST_ROUND_UP_16 (width) * GST_ROUND_UP_16 (height) * 3 / 2; ++ + ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt); + if (ret < 0) { + GST_ERROR_OBJECT (self, "VIDIOC_S_FMT failed: %s", g_strerror (errno)); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0044-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0044-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch new file mode 100644 index 000000000..35bc57c36 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0044-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch @@ -0,0 +1,26 @@ +From 99b66f0a6014d9f9beab8a4d17d39388738d3ce3 Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Thu, 2 Mar 2023 09:18:49 +0100 +Subject: [PATCH 44/68] v4l2codecs: add NV12M format to gstv4l2format + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index 604876e..bbfdee2 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -33,7 +33,7 @@ struct FormatEntry + + static struct FormatEntry format_map[] = { + {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, +- {V4L2_PIX_FMT_NV12M, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, ++ {V4L2_PIX_FMT_NV12M, 2, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422}, + {V4L2_PIX_FMT_SUNXI_TILED_NV12, 1, GST_VIDEO_FORMAT_NV12_32L32, 8, 422}, + {V4L2_PIX_FMT_NV12_4L4, 1, GST_VIDEO_FORMAT_NV12_4L4, 8, 420}, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0045-v4l2codecs-Add-V4L2-H264-stateless-encode-uAPI.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0045-v4l2codecs-Add-V4L2-H264-stateless-encode-uAPI.patch new file mode 100644 index 000000000..877f50bcd --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0045-v4l2codecs-Add-V4L2-H264-stateless-encode-uAPI.patch @@ -0,0 +1,124 @@ +From 205b58f87337b9cfba33b63224ec968f0ac7d6a2 Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Tue, 21 Feb 2023 01:57:50 +0100 +Subject: [PATCH 45/68] v4l2codecs: Add V4L2 H264 stateless encode uAPI + +Upstream-Status: Pending +--- + sys/v4l2codecs/linux/v4l2-controls.h | 85 ++++++++++++++++++++++++++++ + sys/v4l2codecs/linux/videodev2.h | 3 + + 2 files changed, 88 insertions(+) + +diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h +index 8838a5d..9c83596 100644 +--- a/sys/v4l2codecs/linux/v4l2-controls.h ++++ b/sys/v4l2codecs/linux/v4l2-controls.h +@@ -1615,6 +1615,91 @@ struct v4l2_ctrl_h264_decode_params { + __u32 flags; + }; + ++#define V4L2_H264_NAL_CODED_SLICE_NON_IDR_PIC 1 ++#define V4L2_H264_NAL_CODED_SLICE_IDR_PIC 5 ++ ++#define V4L2_CID_STATELESS_H264_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 8) ++ ++/** ++ * struct v4l2_ctrl_h264_encode_params ++ * @slice_type: selects slice type. Set to one of V4L2_H264_SLICE_TYPE_{} ++ * @pic_parameter_set_id: identifies the picture parameter set that is referred to ++ * in the slice header. The value shall be in the range of 0 to 255, inclusive. ++ * @frame_num: an identifier for pictures. ++ * @idr_pic_id: identifies an IDR picture. ++ * @cabac_init_idc: index for determining the initialization table used in the ++ * initialization process for context variables. The value of cabac_init_idc ++ * shall be in the range of 0 to 2, inclusive. ++ * @disable_deblocking_filter_idc: specifies whether the operation of the ++ * deblocking filter shall be disabled across some block edges of the slice and ++ * specifies for which edges the filtering is disabled. ++ * @slice_alpha_c0_offset_div2: offset used in accessing the alpha and tC0 ++ * deblocking filter tables for filtering operations controlled by the macroblocks ++ * within the slice. ++ * @slice_beta_offset_div2: offset used in accessing the beta deblocking filter ++ * table for filtering operations controlled by the macroblocks within the slice. ++ * @slice_size_mb_rows: number of macroblock rows in a slice. ++ * @pic_init_qp_minus26: initial value minus 26 of luma qp for each slice. ++ * @chroma_qp_index_offset: offset that shall be added to qp luma for addressing the ++ * table of qp chroma values for the Cb chroma component. ++ * @flags: combination of V4L2_H264_SLICE_FLAG_{} flags. ++ */ ++struct v4l2_ctrl_h264_encode_params { ++ /* Slice parameters */ ++ ++ __u8 slice_type; ++ __u8 pic_parameter_set_id; ++ __u16 frame_num; ++ __u16 idr_pic_id; ++ __u8 cabac_init_idc; ++ __u8 disable_deblocking_filter_idc; ++ __s8 slice_alpha_c0_offset_div2; ++ __s8 slice_beta_offset_div2; ++ ++ __s32 slice_size_mb_rows; ++ ++ /* PPS parameters */ ++ ++ __s8 pic_init_qp_minus26; ++ __s8 chroma_qp_index_offset; ++ ++ __u32 flags; /* V4L2_H264_ENCODE_FLAG_ */ ++ ++ /* Nal parameters */ ++ __u8 nal_reference_idc; // 2 bit ++ __u8 nalu_type; // 5 bit ++ ++ /* Reference */ ++ ++ __u64 reference_ts; ++}; ++ ++#define V4L2_H264_ENCODE_FLAG_ENTROPY_CABAC 0x01 ++#define V4L2_H264_ENCODE_FLAG_TRANSFORM_8X8_MODE 0x02 ++#define V4L2_H264_ENCODE_FLAG_CONSTRAINED_INTRA_PRED 0x04 ++#define V4L2_H264_ENCODE_FLAG_MARK_LONGTERM 0x08 ++ ++#define V4L2_CID_STATELESS_H264_ENCODE_RC (V4L2_CID_CODEC_STATELESS_BASE + 9) ++ ++/** ++ * struct v4l2_ctrl_h264_encode_rc ++ * ++ * @qp: quantization parameter for the currently encoded slice ++ * ++ * TODO: other fields likely not needed ++ */ ++struct v4l2_ctrl_h264_encode_rc { ++ __u32 qp; ++ __u32 qp_min; ++ __u32 qp_max; ++ __s32 mad_qp_delta; ++ __u32 mad_threshold; ++ ++ __u32 cp_distance_mbs; ++ __u32 cp_target[10]; ++ __s32 cp_target_error[6]; ++ __s32 cp_qp_delta[7]; ++}; + + /* Stateless FWHT control, used by the vicodec driver */ + +diff --git a/sys/v4l2codecs/linux/videodev2.h b/sys/v4l2codecs/linux/videodev2.h +index 687ac85..efaa797 100644 +--- a/sys/v4l2codecs/linux/videodev2.h ++++ b/sys/v4l2codecs/linux/videodev2.h +@@ -1794,6 +1794,9 @@ enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_H264_SLICE_PARAMS = 0x0203, + V4L2_CTRL_TYPE_H264_DECODE_PARAMS = 0x0204, + V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205, ++ V4L2_CTRL_TYPE_H264_ENCODE_PARAMS = 0x0206, ++ V4L2_CTRL_TYPE_H264_ENCODE_RC = 0x0207, ++ V4L2_CTRL_TYPE_H264_ENCODE_FEEDBACK = 0x0208, + + V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220, + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0046-codecs-Add-base-class-for-stateless-h264-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0046-codecs-Add-base-class-for-stateless-h264-encoder.patch new file mode 100644 index 000000000..4748faa16 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0046-codecs-Add-base-class-for-stateless-h264-encoder.patch @@ -0,0 +1,645 @@ +From dfe8a89861ce2ea180c67b916afa6b91fc6fca9c Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Mon, 20 Feb 2023 23:39:33 +0100 +Subject: [PATCH 46/68] codecs: Add base class for stateless h264 encoder + +Add stateless h264 encoder base class and h264 frame structure. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 352 +++++++++++++++++++++++++++ + gst-libs/gst/codecs/gsth264encoder.h | 81 ++++++ + gst-libs/gst/codecs/gsth264frame.c | 66 +++++ + gst-libs/gst/codecs/gsth264frame.h | 70 ++++++ + gst-libs/gst/codecs/meson.build | 5 + + 5 files changed, 574 insertions(+) + create mode 100644 gst-libs/gst/codecs/gsth264encoder.c + create mode 100644 gst-libs/gst/codecs/gsth264encoder.h + create mode 100644 gst-libs/gst/codecs/gsth264frame.c + create mode 100644 gst-libs/gst/codecs/gsth264frame.h + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +new file mode 100644 +index 0000000..b024939 +--- /dev/null ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -0,0 +1,352 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gsth264encoder.h" ++ ++#include ++#include ++#include ++#include ++ ++GST_DEBUG_CATEGORY (gst_h264_encoder_debug); ++#define GST_CAT_DEFAULT gst_h264_encoder_debug ++ ++#define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 ++ ++#define H264_MAX_QUALITY 63 ++#define H264_MIN_QUALITY 0 ++ ++#define H264_DEFAULT_BITRATE 100000 ++ ++enum ++{ ++ PROP_0, ++ PROP_KEYFRAME_INTERVAL, ++ PROP_MAX_QUALITY, ++ PROP_MIN_QUALITY, ++ PROP_BITRATE, ++}; ++ ++struct _GstH264EncoderPrivate ++{ ++ gint keyframe_interval; ++ ++ guint32 last_keyframe; ++ ++ guint64 targeted_bitrate; ++ gint max_quality; ++ gint min_quality; ++ gint current_quality; ++ guint64 used_bytes; ++ guint64 nb_frames; ++}; ++ ++#define parent_class gst_h264_encoder_parent_class ++G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstH264Encoder, gst_h264_encoder, ++ GST_TYPE_VIDEO_ENCODER, ++ G_ADD_PRIVATE (GstH264Encoder); ++ GST_DEBUG_CATEGORY_INIT (gst_h264_encoder_debug, "h264encoder", 0, ++ "H264 Video Encoder")); ++ ++static void ++gst_h264_encoder_init (GstH264Encoder * self) ++{ ++ self->priv = gst_h264_encoder_get_instance_private (self); ++} ++ ++static void ++gst_h264_encoder_finalize (GObject * object) ++{ ++ G_OBJECT_CLASS (parent_class)->finalize (object); ++} ++ ++static gboolean ++gst_h264_encoder_start (GstVideoEncoder * encoder) ++{ ++ GstH264Encoder *self = GST_H264_ENCODER (encoder); ++ GstH264EncoderPrivate *priv = self->priv; ++ ++ priv->last_keyframe = 0; ++ priv->current_quality = priv->min_quality; ++ priv->used_bytes = 0; ++ priv->nb_frames = 0; ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_h264_encoder_stop (GstVideoEncoder * encoder) ++{ ++ return TRUE; ++} ++ ++static gboolean ++gst_h264_encoder_set_format (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) ++{ ++ return TRUE; ++} ++ ++static GstFlowReturn ++gst_h264_encoder_set_quality (GstH264Encoder * self, GstH264Frame * h264_frame) ++{ ++ GstH264EncoderPrivate *priv = self->priv; ++ GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self); ++ GstVideoCodecState *output_state = ++ gst_video_encoder_get_output_state (encoder); ++ gint qp = priv->current_quality; ++ guint64 bitrate = 0; ++ guint fps_n = 30, fps_d = 1; ++ ++ if (output_state == NULL) ++ return qp; ++ ++ if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) { ++ fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info); ++ fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info); ++ } ++ gst_video_codec_state_unref (output_state); ++ ++ bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d); ++ if (bitrate > priv->targeted_bitrate) { ++ qp++; ++ } ++ ++ if (bitrate < priv->targeted_bitrate) { ++ qp--; ++ } ++ ++ if (qp > priv->max_quality) ++ qp = priv->max_quality; ++ if (qp < priv->min_quality) ++ qp = priv->min_quality; ++ ++ h264_frame->quality = qp; ++ ++ return GST_FLOW_OK; ++} ++ ++static GstFlowReturn ++gst_h264_encoder_set_frame_type (GstH264Encoder * self, GstH264Frame * h264_frame) ++{ ++ GstH264EncoderPrivate *priv = self->priv; ++ GstVideoCodecFrame *frame = h264_frame->frame; ++ ++ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { ++ h264_frame->type = GstH264Keyframe; ++ return GST_FLOW_OK; ++ } ++ ++ if ((frame->system_frame_number - priv->last_keyframe) > ++ priv->keyframe_interval || frame->system_frame_number == 0) { ++ /* Generate a keyframe */ ++ GST_DEBUG_OBJECT (self, "Generate a keyframe"); ++ h264_frame->type = GstH264Keyframe; ++ return GST_FLOW_OK; ++ } ++ ++ /* Generate a interframe */ ++ GST_DEBUG_OBJECT (self, "Generate a interframe"); ++ h264_frame->type = GstH264Inter; ++ return GST_FLOW_OK; ++} ++ ++static void ++gst_h264_encoder_mark_frame (GstH264Encoder * self, GstH264Frame * h264_frame) ++{ ++ GstVideoCodecFrame *frame = h264_frame->frame; ++ GstH264EncoderPrivate *priv = self->priv; ++ ++ switch (h264_frame->type) { ++ case GstH264Keyframe: ++ priv->last_keyframe = frame->system_frame_number; ++ break; ++ } ++ ++ priv->current_quality = h264_frame->quality; ++ priv->used_bytes += gst_buffer_get_size (frame->output_buffer); ++ priv->nb_frames++; ++} ++ ++static GstFlowReturn ++gst_h264_encoder_handle_frame (GstVideoEncoder * encoder, ++ GstVideoCodecFrame * frame) ++{ ++ GstH264Encoder *self = GST_H264_ENCODER (encoder); ++ GstH264EncoderClass *klass = GST_H264_ENCODER_GET_CLASS (self); ++ GstFlowReturn ret = GST_FLOW_OK; ++ GstH264Frame *h264_frame = gst_h264_frame_new (frame); ++ ++ ret = gst_h264_encoder_set_frame_type (self, h264_frame); ++ if (ret != GST_FLOW_OK) ++ return ret; ++ ++ ret = gst_h264_encoder_set_quality (self, h264_frame); ++ if (ret != GST_FLOW_OK) ++ return ret; ++ ++ /* TODO: add encoding parameters management here ++ * for now just send the frame to encode */ ++ if (klass->encode_frame) { ++ ret = klass->encode_frame (self, h264_frame); ++ if (ret == GST_FLOW_OK) ++ gst_h264_encoder_mark_frame (self, h264_frame); ++ } ++ ++ gst_h264_frame_unref (h264_frame); ++ ++ return ret; ++} ++ ++static void ++gst_h264_encoder_get_property (GObject * object, guint property_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstH264Encoder *self = GST_H264_ENCODER (object); ++ GstH264EncoderPrivate *priv = self->priv; ++ ++ switch (property_id) { ++ case PROP_KEYFRAME_INTERVAL: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->keyframe_interval); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MAX_QUALITY: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->max_quality); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MIN_QUALITY: ++ GST_OBJECT_LOCK (self); ++ g_value_set_int (value, priv->min_quality); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_BITRATE: ++ GST_OBJECT_LOCK (self); ++ g_value_set_uint64 (value, priv->targeted_bitrate); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_h264_encoder_set_property (GObject * object, guint property_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstH264Encoder *self = GST_H264_ENCODER (object); ++ GstH264EncoderPrivate *priv = self->priv; ++ ++ switch (property_id) { ++ case PROP_KEYFRAME_INTERVAL: ++ GST_OBJECT_LOCK (self); ++ priv->keyframe_interval = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MAX_QUALITY: ++ GST_OBJECT_LOCK (self); ++ priv->max_quality = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_MIN_QUALITY: ++ GST_OBJECT_LOCK (self); ++ priv->min_quality = g_value_get_int (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_BITRATE: ++ GST_OBJECT_LOCK (self); ++ priv->targeted_bitrate = g_value_get_uint64 (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_h264_encoder_class_init (GstH264EncoderClass * klass) ++{ ++ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = GST_DEBUG_FUNCPTR (gst_h264_encoder_finalize); ++ object_class->get_property = gst_h264_encoder_get_property; ++ object_class->set_property = gst_h264_encoder_set_property; ++ ++ encoder_class->start = GST_DEBUG_FUNCPTR (gst_h264_encoder_start); ++ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_h264_encoder_stop); ++ encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_h264_encoder_set_format); ++ encoder_class->handle_frame = ++ GST_DEBUG_FUNCPTR (gst_h264_encoder_handle_frame); ++ ++ /** ++ * GstH264Encoder:keyframe-interval: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL, ++ g_param_spec_int ("keyframe-interval", "Keyframe Interval", ++ "Interval between keyframes", ++ 0, G_MAXINT, H264ENC_DEFAULT_KEYFRAME_INTERVAL, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:max-quality: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_MAX_QUALITY, ++ g_param_spec_int ("max-quality", "Max Quality Level", ++ "Set upper quality limit (lower number equates to higher quality but more bits)", ++ H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MAX_QUALITY, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:min-quality: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_MIN_QUALITY, ++ g_param_spec_int ("min-quality", "Min Quality Level", ++ "Set lower quality limit (lower number equates to higher quality but more bits)", ++ H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MIN_QUALITY, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:bitrate: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_BITRATE, ++ g_param_spec_uint64 ("bitrate", "Targeted bitrate", ++ "Set bitrate target", ++ 0, UINT_MAX, H264_DEFAULT_BITRATE, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++} +diff --git a/gst-libs/gst/codecs/gsth264encoder.h b/gst-libs/gst/codecs/gsth264encoder.h +new file mode 100644 +index 0000000..49f9fd3 +--- /dev/null ++++ b/gst-libs/gst/codecs/gsth264encoder.h +@@ -0,0 +1,81 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GST_H264_ENCODER_H__ ++#define __GST_H264_ENCODER_H__ ++ ++#include ++ ++#include ++#include ++ ++#include "gsth264frame.h" ++ ++G_BEGIN_DECLS ++#define GST_TYPE_H264_ENCODER (gst_h264_encoder_get_type()) ++#define GST_H264_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_H264_ENCODER,GstH264Encoder)) ++#define GST_H264_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_H264_ENCODER,GstH264EncoderClass)) ++#define GST_H264_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_H264_ENCODER,GstH264EncoderClass)) ++#define GST_IS_H264_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_H264_ENCODER)) ++#define GST_IS_H264_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_H264_ENCODER)) ++#define GST_H264_ENCODER_CAST(obj) ((GstH264Encoder*)obj) ++typedef struct _GstH264Encoder GstH264Encoder; ++typedef struct _GstH264EncoderClass GstH264EncoderClass; ++typedef struct _GstH264EncoderPrivate GstH264EncoderPrivate; ++ ++/** ++ * GstH264Encoder: ++ * ++ * The opaque #GstH264Encoder data structure. ++ */ ++struct _GstH264Encoder ++{ ++ /*< private > */ ++ GstVideoEncoder parent; ++ ++ /*< private > */ ++ GstH264EncoderPrivate *priv; ++ gpointer padding[GST_PADDING_LARGE]; ++}; ++ ++/** ++ * GstH264EncoderClass: ++ */ ++struct _GstH264EncoderClass ++{ ++ GstVideoEncoderClass parent_class; ++ ++ /** ++ * GstH264EncoderClass::encode_frame: ++ * @encoder: a #GstH264Encoder ++ * @frame: a #GstH264Frame ++ * ++ * Provide the frame to be encoded with the encode parameters (to be defined) ++ */ ++ GstFlowReturn (*encode_frame) (GstH264Encoder * encoder, ++ GstH264Frame * frame); ++ /*< private > */ ++ gpointer padding[GST_PADDING_LARGE]; ++}; ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstH264Encoder, gst_object_unref) ++ GST_CODECS_API GType gst_h264_encoder_get_type (void); ++ ++G_END_DECLS ++#endif /* __GST_H264_ENCODER_H__ */ +diff --git a/gst-libs/gst/codecs/gsth264frame.c b/gst-libs/gst/codecs/gsth264frame.c +new file mode 100644 +index 0000000..81420d7 +--- /dev/null ++++ b/gst-libs/gst/codecs/gsth264frame.c +@@ -0,0 +1,66 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "gsth264frame.h" ++ ++GST_DEBUG_CATEGORY_EXTERN (gst_h264_encoder_debug); ++#define GST_CAT_DEFAULT gst_h264_encoder_debug ++ ++GST_DEFINE_MINI_OBJECT_TYPE (GstH264Frame, gst_h264_frame); ++ ++static void ++_gst_h264_frame_free (GstH264Frame * frame) ++{ ++ GST_TRACE ("Free frame %p", frame); ++ ++ gst_video_codec_frame_unref (frame->frame); ++ ++ g_free (frame); ++} ++ ++/** ++ * gst_h264_frame_new: ++ * ++ * Create new #GstH264Frame ++ * ++ * Returns: a new #GstH264Frame ++ */ ++GstH264Frame * ++gst_h264_frame_new (GstVideoCodecFrame * f) ++{ ++ GstH264Frame *frame; ++ ++ if (!f) ++ return NULL; ++ ++ frame = g_new0 (GstH264Frame, 1); ++ ++ gst_mini_object_init (GST_MINI_OBJECT_CAST (frame), 0, ++ GST_TYPE_H264_FRAME, NULL, NULL, ++ (GstMiniObjectFreeFunction) _gst_h264_frame_free); ++ ++ frame->frame = gst_video_codec_frame_ref (f); ++ ++ GST_TRACE ("New frame %p", frame); ++ ++ return frame; ++} +diff --git a/gst-libs/gst/codecs/gsth264frame.h b/gst-libs/gst/codecs/gsth264frame.h +new file mode 100644 +index 0000000..3c90d53 +--- /dev/null ++++ b/gst-libs/gst/codecs/gsth264frame.h +@@ -0,0 +1,70 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GTS_H264_FRAME_H__ ++#define __GTS_H264_FRAME_H__ ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_H264_FRAME (gst_h264_frame_get_type()) ++#define GST_IS_H264_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_H264_FRAME)) ++#define GST_H264_FRAME(obj) ((GstH264Frame *)obj) ++#define GST_H264_FRAME_CAST(obj) (GST_H264_FRAME(obj)) ++ ++typedef struct _GstH264Frame GstH264Frame; ++ ++enum ++{ ++ GstH264Keyframe, ++ GstH264Inter, ++}; ++ ++struct _GstH264Frame ++{ ++ GstMiniObject parent; ++ gint type; ++ gint quality; ++ ++ GstVideoCodecFrame *frame; ++}; ++ ++GST_CODECS_API ++GType gst_h264_frame_get_type (void); ++ ++GST_CODECS_API ++GstH264Frame * gst_h264_frame_new (GstVideoCodecFrame *f); ++ ++static inline GstH264Frame * ++gst_h264_frame_ref (GstH264Frame * frame) ++{ ++ return (GstH264Frame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame)); ++} ++ ++static inline void ++gst_h264_frame_unref (GstH264Frame * frame) ++{ ++ gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame)); ++} ++ ++G_END_DECLS ++ ++#endif /* __GTS_H264_FRAME_H__ */ +diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build +index 458d90d..3ea3bdc 100644 +--- a/gst-libs/gst/codecs/meson.build ++++ b/gst-libs/gst/codecs/meson.build +@@ -14,6 +14,8 @@ codecs_sources = files( + 'gstvp9statefulparser.c', + 'gstvp8encoder.c', + 'gstvp8frame.c', ++ 'gsth264encoder.c', ++ 'gsth264frame.c', + ) + + codecs_headers = files( +@@ -32,6 +34,8 @@ codecs_headers = files( + 'gstvp9statefulparser.h', + 'gstvp8encoder.h', + 'gstvp8frame.h', ++ 'gsth264encoder.h', ++ 'gsth264frame.h', + ) + + cp_args = [ +@@ -71,6 +75,7 @@ if build_gir + '--c-include=gst/codecs/gstvp8decoder.h', + '--c-include=gst/codecs/gstmpeg2decoder.h', + '--c-include=gst/codecs/gstvp8encoder.h', ++ '--c-include=gst/codecs/gsth264encoder.h', + ], + 'dependencies' : [gstvideo_dep, gstcodecparsers_dep] + } +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0047-v4l2codecs-encoder-Add-h264-code-to-v4l2-encoder-cla.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0047-v4l2codecs-encoder-Add-h264-code-to-v4l2-encoder-cla.patch new file mode 100644 index 000000000..ca2498946 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0047-v4l2codecs-encoder-Add-h264-code-to-v4l2-encoder-cla.patch @@ -0,0 +1,61 @@ +From e9b9898be9c2cbf007b5e6580d4f90c93163502b Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Tue, 21 Feb 2023 02:10:52 +0100 +Subject: [PATCH 47/68] v4l2codecs: encoder: Add h264 code to v4l2 encoder + class + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 25 +++++++++++++++++++++++++ + sys/v4l2codecs/gstv4l2encoder.h | 2 ++ + 2 files changed, 27 insertions(+) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 0938bb1..cded8bf 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -1366,3 +1366,28 @@ gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, + + return TRUE; + } ++ ++gboolean ++gst_v4l2_codec_h264_enc_get_qp_range (GstV4l2Encoder * self, ++ guint * qp_min, guint * qp_max) ++{ ++ gint ret; ++ struct v4l2_query_ext_ctrl control = { ++ .id = V4L2_CID_STATELESS_H264_ENCODE_RC, ++ }; ++ ++ *qp_max = 127; ++ *qp_min = 0; ++ ++ ret = ioctl (self->video_fd, VIDIOC_QUERY_EXT_CTRL, &control); ++ if (ret < 0) { ++ GST_DEBUG_OBJECT (self, "unable to retrieve encoder qp, use default"); ++ return FALSE; ++ } ++ ++ *qp_max = control.maximum; ++ *qp_min = control.minimum; ++ GST_DEBUG_OBJECT (self, "encoder qp min %d qp max %d", *qp_min, *qp_max); ++ ++ return TRUE; ++} +diff --git a/sys/v4l2codecs/gstv4l2encoder.h b/sys/v4l2codecs/gstv4l2encoder.h +index 9acb0e3..e7f44ba 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.h ++++ b/sys/v4l2codecs/gstv4l2encoder.h +@@ -134,6 +134,8 @@ gboolean gst_v4l2_encoder_request_failed (GstV4l2Request * request); + + gboolean gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max); + ++gboolean gst_v4l2_codec_h264_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max); ++ + G_END_DECLS + + #endif /* __GST_V4L2_ENCODER_H__ */ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0048-v4l2codecs-Add-V4L2-stateless-H264-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0048-v4l2codecs-Add-V4L2-stateless-H264-encoder.patch new file mode 100644 index 000000000..cefbe8ed2 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0048-v4l2codecs-Add-V4L2-stateless-H264-encoder.patch @@ -0,0 +1,1210 @@ +From f3734b12e252d8cc10d1fd5ec7bbf1d46868c378 Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Mon, 20 Feb 2023 23:30:28 +0100 +Subject: [PATCH 48/68] v4l2codecs: Add V4L2 stateless H264 encoder + +Using v4l2 request to set driver configuration by using H264 stateless controls. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 1114 ++++++++++++++++++++++++++ + sys/v4l2codecs/gstv4l2codech264enc.h | 54 ++ + sys/v4l2codecs/meson.build | 1 + + 3 files changed, 1169 insertions(+) + create mode 100644 sys/v4l2codecs/gstv4l2codech264enc.c + create mode 100644 sys/v4l2codecs/gstv4l2codech264enc.h + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +new file mode 100644 +index 0000000..9f1b994 +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -0,0 +1,1114 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "gstv4l2codecallocator.h" ++#include "gstv4l2codech264enc.h" ++#include "gstv4l2codecpool.h" ++#include "gstv4l2format.h" ++#include ++ ++#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#define V4L2_MIN_KERNEL_VER_MAJOR 5 ++#define V4L2_MIN_KERNEL_VER_MINOR 17 ++#define V4L2_MIN_KERNEL_VERSION KERNEL_VERSION(V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR, 0) ++ ++GST_DEBUG_CATEGORY_STATIC (v4l2_h264enc_debug); ++#define GST_CAT_DEFAULT v4l2_h264enc_debug ++ ++enum ++{ ++ PROP_0, ++ PROP_LAST = PROP_0 ++}; ++ ++#define GST_PAD_SINK_FORMATS "{NV12, YUY2, I420}" ++ ++static GstStaticPadTemplate sink_template = ++ GST_STATIC_PAD_TEMPLATE(GST_VIDEO_ENCODER_SINK_NAME, ++ GST_PAD_SINK, GST_PAD_ALWAYS, ++ GST_STATIC_CAPS(GST_VIDEO_CAPS_MAKE(GST_PAD_SINK_FORMATS) ++ ", colorimetry= {bt601,bt709,bt601-full-range,bt709-full-range}" ++ ) ++ ); ++ ++static GstStaticPadTemplate src_template = ++GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, ++ GST_PAD_SRC, GST_PAD_ALWAYS, ++ GST_STATIC_CAPS ("video/x-h264,alignment=nal,stream-format=byte-stream")); ++ ++#define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 ++ ++#define H264_MAX_QUALITY 63 ++#define H264_MIN_QUALITY 0 ++ ++#define H264_DEFAULT_BITRATE 100000 ++ ++/* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */ ++#define SX_BITRATE 6 ++/* Scale factor for CPB size (HRD cpb_size_scale: min = 4) */ ++#define SX_CPB_SIZE 4 ++/* Maximum sizes for common headers (in bits) */ ++#define MAX_SPS_HDR_SIZE 16473 ++#define MAX_VUI_PARAMS_SIZE 210 ++#define MAX_HRD_PARAMS_SIZE 4103 ++#define MAX_PPS_HDR_SIZE 101 ++#define MAX_SLICE_HDR_SIZE 397 + 2572 + 6670 + 2402 ++ ++#define MAX_GOP_SIZE 1024 ++ ++#define SPS_SIZE 4 + GST_ROUND_UP_8 (MAX_SPS_HDR_SIZE + MAX_VUI_PARAMS_SIZE + \ ++ 2 * MAX_HRD_PARAMS_SIZE) / 8 ++#define PPS_SIZE 4 + GST_ROUND_UP_8 (MAX_PPS_HDR_SIZE) / 8 ++ ++struct _GstV4l2CodecH264Enc ++{ ++ GstH264Encoder parent; ++ GstV4l2Encoder *encoder; ++ GstVideoCodecState *output_state; ++ GstVideoInfo vinfo; ++ gint width; ++ gint height; ++ gint width_mbs; ++ gint height_mbs; ++ guint qp_max, qp_min; ++ guint64 targeted_bitrate; ++ ++ GstV4l2CodecAllocator *sink_allocator; ++ GstV4l2CodecAllocator *src_allocator; ++ GstV4l2CodecPool *sink_pool; ++ GstV4l2CodecPool *src_pool; ++ ++ gboolean first_frame; ++ guint64 reference_timestamp; ++ ++ struct v4l2_ctrl_h264_encode_params encode_params; ++ struct v4l2_ctrl_h264_encode_rc encode_rc; ++ GstH264SPS sps; ++ GstH264PPS pps; ++}; ++ ++G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecH264Enc, gst_v4l2_codec_h264_enc, ++ GST_TYPE_H264_ENCODER); ++ ++#define parent_class gst_v4l2_codec_h264_enc_parent_class ++ ++static gboolean ++gst_v4l2_codec_h264_enc_open (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ guint version; ++ ++ if (!gst_v4l2_encoder_open (self->encoder)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, ++ ("Failed to open H264 encoder"), ++ ("gst_v4l2_encoder_open() failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ version = gst_v4l2_encoder_get_version (self->encoder); ++ if (version < V4L2_MIN_KERNEL_VERSION) ++ GST_WARNING_OBJECT (self, ++ "V4L2 API v%u.%u too old, at least v%u.%u required", ++ (version >> 16) & 0xff, (version >> 8) & 0xff, ++ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR); ++ ++ GST_DEBUG_OBJECT (self, "open h264 encoder"); ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_api_check (GstV4l2Encoder * encoder) ++{ ++ guint i, ret_size; ++ /* *INDENT-OFF* */ ++ #define SET_ID(cid) .id = (cid), .name = #cid ++ struct ++ { ++ const gchar *name; ++ unsigned int id; ++ unsigned int size; ++ gboolean optional; ++ } controls[] = { ++ { ++ SET_ID (V4L2_CID_STATELESS_H264_ENCODE_PARAMS), ++ .size = sizeof(struct v4l2_ctrl_h264_encode_params), ++ }, { ++ SET_ID (V4L2_CID_STATELESS_H264_ENCODE_RC), ++ .size = sizeof(struct v4l2_ctrl_h264_encode_rc), ++ // }, { ++ // SET_ID (V4L2_CID_STATELESS_H264_SPS), ++ // .size = sizeof(struct v4l2_ctrl_h264_sps), ++ // }, { ++ // SET_ID (V4L2_CID_STATELESS_H264_PPS), ++ // .size = sizeof(struct v4l2_ctrl_h264_pps), ++ }, ++ }; ++ #undef SET_ID ++ /* *INDENT-ON* */ ++ ++ /* ++ * Compatibility check: make sure the pointer controls are ++ * the right size. ++ */ ++ for (i = 0; i < G_N_ELEMENTS (controls); i++) { ++ gboolean control_found; ++ ++ control_found = gst_v4l2_encoder_query_control_size (encoder, ++ controls[i].id, &ret_size); ++ ++ if (!controls[i].optional && !control_found) { ++ GST_WARNING ("Driver is missing %s support.", controls[i].name); ++ return FALSE; ++ } ++ ++ if (control_found && ret_size != controls[i].size) { ++ GST_WARNING ("%s control size mismatch: got %d bytes but %d expected.", ++ controls[i].name, ret_size, controls[i].size); ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_close (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ gst_v4l2_encoder_close (self->encoder); ++ return TRUE; ++} ++ ++static void ++gst_v4l2_codec_h264_enc_reset_allocation (GstV4l2CodecH264Enc * self) ++{ ++ if (self->sink_allocator) { ++ gst_v4l2_codec_allocator_detach (self->sink_allocator); ++ g_clear_object (&self->sink_allocator); ++ g_clear_object (&self->sink_pool); ++ } ++ ++ if (self->src_allocator) { ++ gst_v4l2_codec_allocator_detach (self->src_allocator); ++ g_clear_object (&self->src_allocator); ++ g_clear_object (&self->src_pool); ++ } ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_start (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "start"); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->start (encoder); ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_stop (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "stop"); ++ ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK); ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC); ++ ++ gst_v4l2_codec_h264_enc_reset_allocation (self); ++ ++ if (self->output_state) ++ gst_video_codec_state_unref (self->output_state); ++ self->output_state = NULL; ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (encoder); ++} ++ ++static GstCaps * ++gst_v4l2_codec_h264_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ GstCaps *caps, *result; ++ ++ caps = gst_v4l2_encoder_list_sink_formats (self->encoder); ++ GST_DEBUG_OBJECT (self, "Supported input formats: %" GST_PTR_FORMAT, caps); ++ ++ result = gst_video_encoder_proxy_getcaps (encoder, caps, filter); ++ ++ if (caps) ++ gst_caps_unref (caps); ++ ++ GST_DEBUG_OBJECT (self, "Returning sink caps: %" GST_PTR_FORMAT, result); ++ ++ return result; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "buffers allocation"); ++ ++ g_clear_object (&self->sink_pool); ++ g_clear_object (&self->src_pool); ++ g_clear_object (&self->src_allocator); ++ ++ self->sink_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder, ++ GST_PAD_SINK, 4); ++ if (!self->sink_allocator) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Not enough memory to allocate sink buffers."), (NULL)); ++ return FALSE; ++ } ++ ++ self->sink_pool = ++ gst_v4l2_codec_pool_new (self->sink_allocator, &self->vinfo); ++ ++ self->src_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder, ++ GST_PAD_SRC, 4); ++ if (!self->src_allocator) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Not enough memory to allocate source buffers."), (NULL)); ++ g_clear_object (&self->sink_allocator); ++ return FALSE; ++ } ++ ++ self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); ++ ++ return TRUE; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, ++ GstBuffer *codec_data, guint *data_size) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ GstMapInfo info; ++ guint8 *data; ++ guint size; ++ ++ gst_buffer_map (codec_data, &info, GST_MAP_WRITE); ++ data = (guint8 *) info.data; ++ memset (data, 0, info.size); ++ ++ size = SPS_SIZE; ++ ++ if (gst_h264_bit_writer_sps (&self->sps, TRUE, data, ++ &size) != GST_H264_BIT_WRITER_OK) { ++ gst_buffer_unmap (codec_data, &info); ++ return FALSE; ++ } ++ ++ data += size; ++ *data_size = size; ++ ++ size = PPS_SIZE; ++ if (gst_h264_bit_writer_pps (&self->pps, TRUE, data, ++ &size) != GST_H264_BIT_WRITER_OK) { ++ gst_buffer_unmap (codec_data, &info); ++ return FALSE; ++ } ++ ++ *data_size += size; ++ ++ gst_buffer_unmap (codec_data, &info); ++ ++ return TRUE; ++} ++ ++static guint8 ++get_sps_aspect_ratio_idc(guint par_n, guint par_d) ++{ ++ if (par_n == 1 && par_d == 1) ++ return 1; ++ else if (par_n == 12 && par_d == 11) ++ return 2; ++ else if (par_n == 10 && par_d == 11) ++ return 3; ++ else if (par_n == 16 && par_d == 11) ++ return 4; ++ else if (par_n == 40 && par_d == 33) ++ return 5; ++ else if (par_n == 24 && par_d == 11) ++ return 6; ++ else if (par_n == 20 && par_d == 11) ++ return 7; ++ else if (par_n == 32 && par_d == 11) ++ return 8; ++ else if (par_n == 80 && par_d == 33) ++ return 9; ++ else if (par_n == 18 && par_d == 11) ++ return 10; ++ else if (par_n == 15 && par_d == 11) ++ return 11; ++ else if (par_n == 64 && par_d == 33) ++ return 12; ++ else if (par_n == 160 && par_d == 99) ++ return 13; ++ else if (par_n == 4 && par_d == 3) ++ return 14; ++ else if (par_n == 3 && par_d == 2) ++ return 15; ++ else if (par_n == 2 && par_d == 1) ++ return 16; ++ else ++ return 0; // Extended_SAR for custom ratios ++} ++ ++/* Begin of code taken from VA plugin */ ++typedef struct ++{ ++ const char *level; ++ int idc; ++ int MaxMBPS; ++ int MaxFS; ++ int MaxDpbMbs; ++ int MaxBR; ++ int MaxCPB; ++ int MinCr; ++} GstVaH264LevelLimits; ++/* *INDENT-OFF* */ ++ ++static const GstVaH264LevelLimits _va_h264_level_limits[] = { ++ /* level idc MaxMBPS MaxFS MaxDpbMbs MaxBR MaxCPB MinCr */ ++ {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2}, ++ {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2}, ++ {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2}, ++ {"1.2", GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384, 1000, 2}, ++ {"1.3", GST_H264_LEVEL_L1_3, 11880, 396, 2376, 768, 2000, 2}, ++ {"2", GST_H264_LEVEL_L2, 11880, 396, 2376, 2000, 2000, 2}, ++ {"2.1", GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000, 4000, 2}, ++ {"2.2", GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000, 4000, 2}, ++ {"3", GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000, 10000, 2}, ++ {"3.1", GST_H264_LEVEL_L3_1, 108000, 3600, 18000, 14000, 14000, 4}, ++ {"3.2", GST_H264_LEVEL_L3_2, 216000, 5120, 20480, 20000, 20000, 4}, ++ {"4", GST_H264_LEVEL_L4, 245760, 8192, 32768, 20000, 25000, 4}, ++ {"4.1", GST_H264_LEVEL_L4_1, 245760, 8192, 32768, 50000, 62500, 2}, ++ {"4.2", GST_H264_LEVEL_L4_2, 522240, 8704, 34816, 50000, 62500, 2}, ++ {"5", GST_H264_LEVEL_L5, 589824, 22080, 110400, 135000, 135000, 2}, ++ {"5.1", GST_H264_LEVEL_L5_1, 983040, 36864, 184320, 240000, 240000, 2}, ++ {"5.2", GST_H264_LEVEL_L5_2, 2073600, 36864, 184320, 240000, 240000, 2}, ++ {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2}, ++ {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2}, ++ {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2}, ++}; ++/* *INDENT-ON* */ ++/* Enf of code taken from VA Plugin */ ++ ++// Recursive function to find the greatest common divisor ++static gint ++gcd(int a, int b) ++{ ++ if (b == 0) ++ return a; ++ return gcd(b, a % b); ++} ++ ++static gint ++find_time_scale_multiplier(int fps_d) ++{ ++ guint base_value = 1000; ++ return (fps_d * base_value) / gcd(fps_d, base_value); ++} ++ ++static void ++gst_v4l2_codec_h264_enc_init_sps_pps(GstV4l2CodecH264Enc *self, ++ GstVideoCodecState * state) ++{ ++ memset(&self->sps, 0, sizeof(self->sps)); ++ memset(&self->sps, 0, sizeof(self->pps)); ++ ++ /* SPS */ ++ self->sps.profile_idc = 66; ++ self->sps.constraint_set0_flag = 1; ++ self->sps.constraint_set1_flag = 0; ++ ++ ++ // sps.seq_parameter_set_id = 0; // TODO: Set it properly ++ self->sps.chroma_format_idc = 1; /* YUV 4:2:0 */ ++ ++ self->sps.pic_width_in_mbs_minus1 = self->width_mbs - 1; ++ self->sps.pic_height_in_map_units_minus1 = self->height_mbs - 1; ++ ++ self->sps.num_ref_frames = 1; // FIXME: rename according to spec, max_num_ref_frames ++ self->sps.num_ref_frames_in_pic_order_cnt_cycle = 2; ++ ++ // XXX: fixed by hardware ++ self->sps.pic_order_cnt_type = 2; ++ ++ // XXX: fixed by hardware FOSHO ++ self->sps.log2_max_frame_num_minus4 = 12; ++ self->sps.log2_max_pic_order_cnt_lsb_minus4 = 0; ++ ++ // XXX: fixed by hardware (at least constant in MPP) ++ self->sps.direct_8x8_inference_flag = 1; ++ self->sps.frame_mbs_only_flag = 1; ++ ++ // Calculate lowest acceptable level ++ GstStructure *structure; ++ gint fps_n, fps_d; ++ structure = gst_caps_get_structure(state->caps, 0); ++ gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d); ++ gint n_levels = sizeof(_va_h264_level_limits) / sizeof(_va_h264_level_limits[0]); ++ GValue bitrate = G_VALUE_INIT; ++ g_object_get_property(G_OBJECT(&self->parent), "bitrate", &bitrate); ++ self->targeted_bitrate = g_value_get_uint64(&bitrate); ++ int MaxBR = self->targeted_bitrate; ++ int FS = self->width * self->height / 256; ++ int MBPS = FS * fps_n / fps_d; // Assuming each macroblock is 16x16 ++ int DpbMbs = FS; ++ int CPB = MaxBR; ++ int CR = fps_n / fps_d; ++ gint lowest_level = 0; ++ for (; lowest_level < n_levels; ++lowest_level) ++ { ++ const GstVaH264LevelLimits *level = &_va_h264_level_limits[lowest_level]; ++ if (MBPS <= level->MaxMBPS && FS <= level->MaxFS && DpbMbs <= level->MaxDpbMbs && ++ MaxBR <= level->MaxBR && CPB <= level->MaxCPB && CR >= level->MinCr) ++ { ++ break; ++ } ++ } ++ self->sps.level_idc = _va_h264_level_limits[lowest_level].idc; ++ if (self->sps.level_idc == GST_H264_LEVEL_L1B) ++ { ++ self->sps.constraint_set3_flag = 1; ++ } ++ GST_DEBUG_OBJECT(self, "width:%d height:%d targeted_bitrate:%ld", ++ self->height, self->height, self->targeted_bitrate); ++ GST_DEBUG_OBJECT(self, "MBPS=%d,FS=%d,DpbMbs=%d,MaxBR=%d,CPB=%d,CR=%d", ++ MBPS, FS, DpbMbs, MaxBR, CPB, CR); ++ GST_DEBUG_OBJECT(self, "level_idc: %d", self->sps.level_idc); ++ ++ /* Crop unaligned videos */ ++ if (self->width & 15 || self->height & 15) ++ { ++ static const guint SubWidthC[] = {1, 2, 2, 1}; ++ static const guint SubHeightC[] = {1, 2, 1, 1}; ++ const guint CropUnitX = SubWidthC[self->sps.chroma_format_idc]; ++ const guint CropUnitY = SubHeightC[self->sps.chroma_format_idc] * ++ (2 - self->sps.frame_mbs_only_flag); ++ ++ self->sps.frame_cropping_flag = 1; ++ self->sps.frame_crop_left_offset = 0; ++ self->sps.frame_crop_right_offset = (16 * self->width_mbs - ++ self->width) / ++ CropUnitX; ++ self->sps.frame_crop_top_offset = 0; ++ self->sps.frame_crop_bottom_offset = (16 * self->height_mbs - ++ self->height) / ++ CropUnitY; ++ } ++ ++ ++ // set colorimetry ++ const gchar *colorimetry; ++ colorimetry = gst_structure_get_string(structure, "colorimetry"); ++ ++ if (colorimetry) ++ { ++ self->sps.vui_parameters_present_flag = 1; ++ self->sps.vui_parameters.colour_description_present_flag = 1; ++ if ((strcmp(colorimetry, "bt709") == 0) || ++ (strcmp(colorimetry, "bt709-full-range") == 0)) ++ { ++ self->sps.vui_parameters.colour_primaries = 1; ++ self->sps.vui_parameters.transfer_characteristics = 1; ++ self->sps.vui_parameters.matrix_coefficients = 1; ++ if (strcmp(colorimetry, "bt709-full-range") == 0) ++ { ++ self->sps.vui_parameters.video_full_range_flag = 1; ++ } ++ } ++ else if ((strcmp(colorimetry, "bt601") == 0) || ++ (strcmp(colorimetry, "bt601-full-range") == 0)) ++ { ++ self->sps.vui_parameters.colour_primaries = 6; ++ self->sps.vui_parameters.transfer_characteristics = 6; ++ self->sps.vui_parameters.matrix_coefficients = 6; ++ if (strcmp(colorimetry, "bt601-full-range") == 0) ++ { ++ self->sps.vui_parameters.video_full_range_flag = 1; ++ } ++ } ++ } ++ ++ // set aspect ratio ++ gint par_n, par_d; ++ if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &par_n, &par_d)) ++ { ++ self->sps.vui_parameters.aspect_ratio_info_present_flag = 1; ++ self->sps.vui_parameters.aspect_ratio_idc = get_sps_aspect_ratio_idc(par_n, par_d); ++ if (self->sps.vui_parameters.aspect_ratio_idc == 255) ++ { ++ self->sps.vui_parameters.sar_width = par_n; ++ self->sps.vui_parameters.sar_height = par_d; ++ } ++ } ++ ++ // set Frame rate ++ if (gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d)) ++ { ++ self->sps.vui_parameters.timing_info_present_flag = 1; ++ self->sps.vui_parameters.fixed_frame_rate_flag = 1; // Only supports fixed frame rate for now ++ gint multiplier = find_time_scale_multiplier(fps_d); ++ gint time_scale = fps_n * multiplier; ++ self->sps.vui_parameters.time_scale = time_scale; ++ self->sps.vui_parameters.num_units_in_tick = time_scale / (2 * fps_n / fps_d ); ++ } ++ ++ /* PPS */ ++ self->pps.id = 0; ++ self->pps.sequence = &self->sps; ++ ++ self->pps.deblocking_filter_control_present_flag = 1; // TODO: Set it properly ++ ++ /* XXX: fixed by hardware */ ++ self->pps.weighted_bipred_idc = 0; ++ ++ /* Rate Control */ ++ ++ // h264_rate_control_step(encoder); ++ ++ self->pps.chroma_qp_index_offset = 4; ++ // pps.pic_init_qp_minus26 = encoder.rc.qp - 26; ++ self->pps.pic_init_qp_minus26 = -13; ++ self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; ++ self->pps.deblocking_filter_control_present_flag = 1; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ GstCaps *caps; ++ ++ GST_DEBUG_OBJECT (self, "Set format"); ++ ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK); ++ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC); ++ ++ gst_v4l2_codec_h264_enc_reset_allocation (self); ++ ++ if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, ++ V4L2_PIX_FMT_H264_SLICE)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), ++ ("No support for %ux%u format H264", state->info.width, ++ state->info.height)); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, ++ &self->vinfo)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ++ ("Failed to configure H264 encoder"), ++ ("gst_v4l2_encoder_select_sink_format() failed: %s", ++ g_strerror (errno))); ++ gst_v4l2_encoder_close (self->encoder); ++ return FALSE; ++ } ++ ++ self->width = state->info.width; ++ self->height = state->info.height; ++ gst_v4l2_codec_h264_enc_buffers_allocation (encoder); ++ self->width_mbs = (self->width + 15) / 16; ++ self->height_mbs = (self->height + 15) / 16; ++ self->first_frame = TRUE; ++ ++ if (self->output_state) ++ gst_video_codec_state_unref (self->output_state); ++ ++ caps = gst_caps_new_simple ("video/x-h264", ++ "stream-format", G_TYPE_STRING, "byte-stream", ++ "alignment", G_TYPE_STRING, "nal", NULL); ++ ++ self->output_state = ++ gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), ++ caps, state); ++ ++ if (GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) { ++ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ++ ("Could not enable the encoder driver."), ++ ("VIDIOC_STREAMON(SINK) failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SRC)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ++ ("Could not enable the encoder driver."), ++ ("VIDIOC_STREAMON(SRC) failed: %s", g_strerror (errno))); ++ return FALSE; ++ } ++ ++ gst_v4l2_codec_h264_enc_get_qp_range (self->encoder, &self->qp_min, ++ &self->qp_max); ++ ++ gst_v4l2_codec_h264_enc_init_sps_pps(self, state); ++ ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void ++gst_v4l2_codec_h264_enc_set_flushing (GstV4l2CodecH264Enc * self, ++ gboolean flushing) ++{ ++ if (self->sink_allocator) ++ gst_v4l2_codec_allocator_set_flushing (self->sink_allocator, flushing); ++ if (self->src_allocator) ++ gst_v4l2_codec_allocator_set_flushing (self->src_allocator, flushing); ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_flush (GstVideoEncoder * encoder) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ GST_DEBUG_OBJECT (self, "Flushing encoder state."); ++ ++ gst_v4l2_encoder_flush (self->encoder); ++ gst_v4l2_codec_h264_enc_set_flushing (self, FALSE); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (encoder); ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ switch (GST_EVENT_TYPE (event)) { ++ case GST_EVENT_FLUSH_START: ++ GST_DEBUG_OBJECT (self, "flush start"); ++ gst_v4l2_codec_h264_enc_set_flushing (self, TRUE); ++ break; ++ default: ++ break; ++ } ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event); ++} ++ ++static GstStateChangeReturn ++gst_v4l2_codec_h264_enc_change_state (GstElement * element, ++ GstStateChange transition) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (element); ++ ++ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) ++ gst_v4l2_codec_h264_enc_set_flushing (self, TRUE); ++ ++ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); ++} ++ ++static void ++gst_v4l2_codec_h264_enc_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (object); ++ GObject *enc = G_OBJECT (self->encoder); ++ ++ switch (prop_id) { ++ default: ++ gst_v4l2_encoder_set_property (enc, prop_id - PROP_LAST, value, pspec); ++ break; ++ } ++} ++ ++static void ++gst_v4l2_codec_h264_enc_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (object); ++ GObject *enc = G_OBJECT (self->encoder); ++ ++ switch (prop_id) { ++ default: ++ gst_v4l2_encoder_get_property (enc, prop_id - PROP_LAST, value, pspec); ++ break; ++ } ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_copy_input_buffer (GstV4l2CodecH264Enc * self, ++ GstVideoCodecFrame * frame) ++{ ++ GstVideoFrame src_frame; ++ GstVideoFrame dest_frame; ++ GstVideoInfo src_vinfo; ++ GstBuffer *buffer; ++ GstFlowReturn flow_ret; ++ ++ gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), ++ self->width, self->height); ++ ++ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), ++ &buffer, NULL); ++ if (flow_ret != GST_FLOW_OK) { ++ if (flow_ret == GST_FLOW_FLUSHING) ++ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing."); ++ else ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("No more picture buffer available."), (NULL)); ++ return FALSE; ++ } ++ ++ if (!buffer) ++ goto fail; ++ ++ if (!gst_video_frame_map (&src_frame, &src_vinfo, ++ frame->input_buffer, GST_MAP_READ)) ++ goto fail; ++ ++ if (!gst_video_frame_map (&dest_frame, &self->vinfo, buffer, GST_MAP_WRITE)) { ++ gst_video_frame_unmap (&dest_frame); ++ goto fail; ++ } ++ ++ if (!gst_video_frame_copy (&dest_frame, &src_frame)) { ++ gst_video_frame_unmap (&src_frame); ++ gst_video_frame_unmap (&dest_frame); ++ goto fail; ++ } ++ ++ gst_video_frame_unmap (&src_frame); ++ gst_video_frame_unmap (&dest_frame); ++ gst_buffer_replace (&frame->input_buffer, buffer); ++ gst_buffer_unref (buffer); ++ ++ return TRUE; ++ ++fail: ++ GST_ERROR_OBJECT (self, "Failed copy input buffer."); ++ return FALSE; ++} ++ ++static gboolean ++gst_v4l2_codec_h264_enc_ensure_output_bitstream (GstV4l2CodecH264Enc * self, ++ GstVideoCodecFrame * frame) ++{ ++ GstFlowReturn flow_ret; ++ ++ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool), ++ &frame->output_buffer, NULL); ++ if (flow_ret != GST_FLOW_OK) { ++ if (flow_ret == GST_FLOW_FLUSHING) ++ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing."); ++ else ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("No more encoded buffer available."), (NULL)); ++ return FALSE; ++ } ++ ++ if (!frame->output_buffer) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static void ++gst_v4l2_codec_h264_enc_fill_encode_params (GstH264Encoder * encoder, ++ GstH264Frame * h264_frame) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ /* FIXME ++ * Completely rewrite encode_parms each run, this is error prone otherwise, ++ * keep state outside of the structure ++ */ ++ self->encode_params.flags = 0; ++ ++ switch (h264_frame->type) { ++ case GstH264Keyframe: ++ self->encode_params.slice_type = V4L2_H264_SLICE_TYPE_I; ++ self->encode_params.nalu_type = V4L2_H264_NAL_CODED_SLICE_IDR_PIC; ++ self->encode_params.idr_pic_id++; ++ self->encode_params.frame_num = 0; ++ self->encode_params.nalu_type = 5; ++ self->encode_params.nal_reference_idc = 1; ++ break; ++ case GstH264Inter: ++ default: ++ self->encode_params.slice_type = V4L2_H264_SLICE_TYPE_P; ++ self->encode_params.nalu_type = V4L2_H264_NAL_CODED_SLICE_NON_IDR_PIC; ++ self->encode_params.reference_ts = self->reference_timestamp; ++ self->encode_params.frame_num++; ++ self->encode_params.frame_num %= (1 << (self->sps.log2_max_frame_num_minus4 + 4)); ++ self->encode_params.nalu_type = 1; ++ self->encode_params.nal_reference_idc = 2; ++ break; ++ } ++ ++ self->encode_params.pic_parameter_set_id = 0; ++ self->encode_params.cabac_init_idc = 0; ++ ++ self->encode_params.pic_init_qp_minus26 = self->pps.pic_init_qp_minus26; ++ self->encode_params.chroma_qp_index_offset = self->pps.chroma_qp_index_offset; ++ self->encode_params.disable_deblocking_filter_idc = 0; ++} ++ ++static void ++gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, ++ GstH264Frame * h264_frame) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ /* Rate Control */ ++ ++ //h264_rate_control_step(encoder); ++ ++ self->encode_rc.qp = self->pps.pic_init_qp_minus26 + 26;//encoder->rc.qp; ++ self->encode_rc.qp_min = self->qp_min; ++ self->encode_rc.qp_max = self->qp_max; ++} ++ ++static guint ++gst_v4l2_codec_h264_enc_check_qp_range (GstV4l2CodecH264Enc * self, ++ GstH264Frame * h264_frame) ++{ ++ if (h264_frame->quality > self->qp_max) ++ return self->qp_max; ++ if (h264_frame->quality < self->qp_min) ++ return self->qp_min; ++ ++ return h264_frame->quality; ++} ++ ++static GstFlowReturn ++gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, ++ GstH264Frame * h264_frame) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ GstVideoEncoder *venc = GST_VIDEO_ENCODER (encoder); ++ GstV4l2Request *request = NULL; ++ GstFlowReturn ret = GST_FLOW_ERROR; ++ GstVideoCodecFrame *frame = h264_frame->frame; ++ GstBuffer *codec_data = NULL; ++ GstBuffer *resized_buffer; ++ guint32 bytesused; ++ guint data_size; ++ guint32 flags; ++ ++ if (self->first_frame) { ++ codec_data = gst_buffer_new_and_alloc (38 + SPS_SIZE + PPS_SIZE); ++ if (!gst_v4l2_codec_h264_enc_set_codec_data (venc, codec_data, &data_size)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to create sps/pps buffer."), (NULL)); ++ gst_buffer_unref (codec_data); ++ goto done; ++ } ++ gst_buffer_resize (codec_data, 0, data_size); ++ } ++ ++ /* *INDENT-OFF* */ ++ struct v4l2_ext_control control[] = { ++ { ++ .id = V4L2_CID_STATELESS_H264_ENCODE_PARAMS, ++ .ptr = &self->encode_params, ++ .size = sizeof (self->encode_params), ++ }, { ++ .id = V4L2_CID_STATELESS_H264_ENCODE_RC, ++ .ptr = &self->encode_rc, ++ .size = sizeof (self->encode_rc), ++ }, ++ }; ++ /* *INDENT-ON* */ ++ ++ GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", ++ h264_frame->quality); ++ ++ if (!gst_v4l2_codec_h264_enc_ensure_output_bitstream (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate output buffer."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_codec_h264_enc_copy_input_buffer (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate/copy input buffer."), (NULL)); ++ goto done; ++ } ++ ++ request = gst_v4l2_encoder_alloc_request (self->encoder, ++ frame->system_frame_number, frame->input_buffer, frame->output_buffer); ++ ++ if (!request) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate a media request object."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_codec_h264_enc_fill_encode_params (encoder, h264_frame); ++ ++ gst_v4l2_codec_h264_enc_fill_encode_rc (encoder, h264_frame); ++ ++ if (!gst_v4l2_encoder_set_controls (self->encoder, request, control, ++ G_N_ELEMENTS (control))) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the control parameters."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_encoder_request_queue (request, 0)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the encode request."), (NULL)); ++ goto done; ++ } ++ ++ if (!gst_v4l2_encoder_request_set_done(request, &bytesused, &flags)) ++ { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not ack the request."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_encoder_request_unref (request); ++ ++ resized_buffer = gst_buffer_copy_region (frame->output_buffer, ++ GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_DEEP, 0, bytesused); ++ if (self->first_frame) { ++ gst_buffer_append (codec_data, resized_buffer); ++ gst_buffer_replace (&frame->output_buffer, codec_data); ++ gst_buffer_unref (codec_data); ++ self->first_frame = FALSE; ++ } else { ++ gst_buffer_replace (&frame->output_buffer, resized_buffer); ++ gst_buffer_unref (resized_buffer); ++ } ++ ++ /* save last reference frame */ ++ self->reference_timestamp = (guint64) frame->system_frame_number * 1000; ++ ++ return gst_video_encoder_finish_frame (venc, frame); ++ ++done: ++ if (request) ++ gst_v4l2_encoder_request_unref (request); ++ ++ return ret; ++} ++ ++static void ++gst_v4l2_codec_h264_enc_init (GstV4l2CodecH264Enc * self) ++{ ++} ++ ++static void ++gst_v4l2_codec_h264_enc_subinit (GstV4l2CodecH264Enc * self, ++ GstV4l2CodecH264EncClass * klass) ++{ ++ self->encoder = gst_v4l2_encoder_new (klass->device); ++} ++ ++static void ++gst_v4l2_codec_h264_enc_dispose (GObject * object) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (object); ++ ++ g_clear_object (&self->encoder); ++ ++ G_OBJECT_CLASS (parent_class)->dispose (object); ++} ++ ++static void ++gst_v4l2_codec_h264_enc_class_init (GstV4l2CodecH264EncClass * klass) ++{ ++} ++ ++static void ++gst_v4l2_codec_h264_enc_subclass_init (GstV4l2CodecH264EncClass * klass, ++ GstV4l2CodecDevice * device) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GstElementClass *element_class = GST_ELEMENT_CLASS (klass); ++ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass); ++ GstH264EncoderClass *h264encoder_class = GST_H264_ENCODER_CLASS (klass); ++ ++ gobject_class->set_property = gst_v4l2_codec_h264_enc_set_property; ++ gobject_class->get_property = gst_v4l2_codec_h264_enc_get_property; ++ gobject_class->dispose = gst_v4l2_codec_h264_enc_dispose; ++ ++ gst_element_class_set_static_metadata (element_class, ++ "V4L2 Stateless H264 Video Encoder", ++ "Codec/Encoder/Video/Hardware", ++ "A V4L2 based H264 video encoder", ++ "Benjamin Gaignard "); ++ ++ gst_element_class_add_static_pad_template (element_class, &sink_template); ++ gst_element_class_add_static_pad_template (element_class, &src_template); ++ ++ element_class->change_state = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_change_state); ++ ++ encoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_open); ++ encoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_close); ++ encoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_start); ++ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_stop); ++ encoder_class->set_format = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_set_format); ++ encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_flush); ++ encoder_class->sink_event = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_sink_event); ++ encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_getcaps); ++ h264encoder_class->encode_frame = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_encode_frame); ++ ++ klass->device = device; ++ gst_v4l2_encoder_install_properties (gobject_class, PROP_LAST, device); ++} ++ ++void ++gst_v4l2_codec_h264_enc_register (GstPlugin * plugin, GstV4l2Encoder * encoder, ++ GstV4l2CodecDevice * device, guint rank) ++{ ++ gchar *element_name; ++ guint version; ++ ++ GST_DEBUG_CATEGORY_INIT (v4l2_h264enc_debug, "v4l2codecs-h264enc", 0, ++ "V4L2 stateless H264 encoder"); ++ ++ version = gst_v4l2_encoder_get_version (encoder); ++ if (version < V4L2_MIN_KERNEL_VERSION) ++ GST_WARNING ("V4L2 API v%u.%u too old, at least v%u.%u required", ++ (version >> 16) & 0xff, (version >> 8) & 0xff, ++ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR); ++ ++ if (!gst_v4l2_codec_h264_enc_api_check (encoder)) { ++ GST_WARNING ("Not registering H264 encoder as it failed ABI check."); ++ return; ++ } ++ ++ gst_v4l2_encoder_register (plugin, GST_TYPE_V4L2_CODEC_H264_ENC, ++ (GClassInitFunc) gst_v4l2_codec_h264_enc_subclass_init, ++ gst_mini_object_ref (GST_MINI_OBJECT (device)), ++ (GInstanceInitFunc) gst_v4l2_codec_h264_enc_subinit, ++ "v4l2sl%sh264enc", device, rank, &element_name); ++} +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.h b/sys/v4l2codecs/gstv4l2codech264enc.h +new file mode 100644 +index 0000000..2b9b694 +--- /dev/null ++++ b/sys/v4l2codecs/gstv4l2codech264enc.h +@@ -0,0 +1,54 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef __GST_V4L2_CODEC_H264_ENC_H__ ++#define __GST_V4L2_CODEC_H264_ENC_H__ ++ ++#define GST_USE_UNSTABLE_API ++#include ++ ++#include "gstv4l2encoder.h" ++ ++G_BEGIN_DECLS ++ ++#define GST_TYPE_V4L2_CODEC_H264_ENC (gst_v4l2_codec_h264_enc_get_type()) ++#define GST_V4L2_CODEC_H264_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_H264_ENC,GstV4l2CodecH264Enc)) ++#define GST_V4L2_CODEC_H264_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_H264_ENC,GstV4l2CodecH264EncClass)) ++#define GST_V4L2_CODEC_H264_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_H264_ENC, GstV4l2CodecH264EncClass)) ++#define GST_IS_V4L2_CODEC_H264_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_H264_ENC)) ++#define GST_IS_V4L2_CODEC_H264_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_H264_ENC)) ++ ++typedef struct _GstV4l2CodecH264Enc GstV4l2CodecH264Enc; ++typedef struct _GstV4l2CodecH264EncClass GstV4l2CodecH264EncClass; ++ ++struct _GstV4l2CodecH264EncClass ++{ ++ GstH264EncoderClass parent_class; ++ GstV4l2CodecDevice *device; ++}; ++ ++GType gst_v4l2_codec_h264_enc_get_type (void); ++void gst_v4l2_codec_h264_enc_register (GstPlugin * plugin, ++ GstV4l2Encoder * encoder, ++ GstV4l2CodecDevice * device, ++ guint rank); ++ ++G_END_DECLS ++ ++#endif /* __GST_V4L2_CODEC_H264_ENC_H__ */ +diff --git a/sys/v4l2codecs/meson.build b/sys/v4l2codecs/meson.build +index 638c578..44c0a72 100644 +--- a/sys/v4l2codecs/meson.build ++++ b/sys/v4l2codecs/meson.build +@@ -13,6 +13,7 @@ v4l2codecs_sources = [ + 'gstv4l2codecalphadecodebin.c', + 'gstv4l2encoder.c', + 'gstv4l2codecvp8enc.c', ++ 'gstv4l2codech264enc.c', + ] + + libgudev_dep = dependency('gudev-1.0', required: get_option('v4l2codecs')) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0049-v4l2codecs-Register-V4L2-stateless-H264-encoder.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0049-v4l2codecs-Register-V4L2-stateless-H264-encoder.patch new file mode 100644 index 000000000..aba2864df --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0049-v4l2codecs-Register-V4L2-stateless-H264-encoder.patch @@ -0,0 +1,40 @@ +From fd4de5d5f6b5b16777cdd89b3b29861926c5d915 Mon Sep 17 00:00:00 2001 +From: Michael Grzeschik +Date: Mon, 20 Feb 2023 23:45:33 +0100 +Subject: [PATCH 49/68] v4l2codecs: Register V4L2 stateless H264 encoder + +If a v4l2 stateless VP8 encoder is detected register the matching +element. +Upstream-Status: Pending +--- + sys/v4l2codecs/plugin.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/sys/v4l2codecs/plugin.c b/sys/v4l2codecs/plugin.c +index 7867474..bd052dc 100644 +--- a/sys/v4l2codecs/plugin.c ++++ b/sys/v4l2codecs/plugin.c +@@ -24,6 +24,7 @@ + + #include "gstv4l2codecdevice.h" + #include "gstv4l2codech264dec.h" ++#include "gstv4l2codech264enc.h" + #include "gstv4l2codech265dec.h" + #include "gstv4l2codecmpeg2dec.h" + #include "gstv4l2codecvp8dec.h" +@@ -113,6 +114,12 @@ register_video_encoder (GstPlugin * plugin, GstV4l2CodecDevice * device) + gst_v4l2_codec_vp8_enc_register (plugin, encoder, device, + GST_RANK_PRIMARY + 1); + break; ++ case V4L2_PIX_FMT_H264_SLICE: ++ GST_INFO_OBJECT (encoder, "Registering %s as H264 Encoder", ++ device->name); ++ gst_v4l2_codec_h264_enc_register (plugin, encoder, device, ++ GST_RANK_PRIMARY + 1); ++ break; + default: + GST_FIXME_OBJECT (encoder, "%" GST_FOURCC_FORMAT " is not supported.", + GST_FOURCC_ARGS (fmt)); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0050-v4l2codecs-encoders-Implement-propose-allocation.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0050-v4l2codecs-encoders-Implement-propose-allocation.patch new file mode 100644 index 000000000..b45dbed3c --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0050-v4l2codecs-encoders-Implement-propose-allocation.patch @@ -0,0 +1,83 @@ +From d89ede36298dd4985548c64ef177454b31e3c576 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 1 Nov 2023 10:16:27 -0400 +Subject: [PATCH 50/68] v4l2codecs: encoders: Implement propose allocation + +For now, do not offer any pool since our buffer pool implementation is +not complete. But at least advertise GstVideoMeta support in order to +avoid upstream element having to normalize the strides. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 15 +++++++++++++++ + sys/v4l2codecs/gstv4l2codecvp8enc.c | 15 +++++++++++++++ + 2 files changed, 30 insertions(+) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 9f1b994..070c51a 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -265,6 +265,19 @@ gst_v4l2_codec_h264_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) + return result; + } + ++static gboolean ++gst_v4l2_codec_h264_enc_propose_allocation (GstVideoEncoder * encoder, ++ GstQuery * query) ++{ ++ GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ ++ gst_query_add_allocation_pool (query, NULL, self->vinfo.size, 2, 0); ++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, ++ query); ++} ++ + static gboolean + gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) + { +@@ -1078,6 +1091,8 @@ gst_v4l2_codec_h264_enc_subclass_init (GstV4l2CodecH264EncClass * klass, + encoder_class->sink_event = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_sink_event); + encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_getcaps); ++ encoder_class->propose_allocation = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_propose_allocation); + h264encoder_class->encode_frame = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_h264_enc_encode_frame); + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 2d476b3..24e8540 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -235,6 +235,19 @@ gst_v4l2_codec_vp8_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) + return result; + } + ++static gboolean ++gst_v4l2_codec_vp8_enc_propose_allocation (GstVideoEncoder * encoder, ++ GstQuery * query) ++{ ++ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); ++ ++ gst_query_add_allocation_pool (query, NULL, self->vinfo.size, 2, 0); ++ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); ++ ++ return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, ++ query); ++} ++ + static gboolean + gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) + { +@@ -689,6 +702,8 @@ gst_v4l2_codec_vp8_enc_subclass_init (GstV4l2CodecVp8EncClass * klass, + encoder_class->sink_event = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_sink_event); + encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_getcaps); ++ encoder_class->propose_allocation = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_propose_allocation); + vp8encoder_class->encode_frame = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_encode_frame); + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0051-v4l2codecs-format-Allow-matching-planar-format-varia.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0051-v4l2codecs-format-Allow-matching-planar-format-varia.patch new file mode 100644 index 000000000..187ee9e61 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0051-v4l2codecs-format-Allow-matching-planar-format-varia.patch @@ -0,0 +1,71 @@ +From 7ea1579a4df24d7a2d4bbb1ded764ca47000e299 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 1 Nov 2023 11:49:45 -0400 +Subject: [PATCH 51/68] v4l2codecs: format: Allow matching planar format + variants + +This is to allow matching formats that simply have different numbrer of +planes like NV12 and NV12M. This avoids the spurious S_FMT when this is +the only difference reported by the driver. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 2 +- + sys/v4l2codecs/gstv4l2format.c | 20 ++++++++++++++++++++ + sys/v4l2codecs/gstv4l2format.h | 2 ++ + 3 files changed, 23 insertions(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index cded8bf..0886d15 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -617,7 +617,7 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + return FALSE; + } + +- if (pix_fmt != fmt.fmt.pix_mp.pixelformat ++ if (!gst_v4l2_format_equivalent (pix_fmt, fmt.fmt.pix_mp.pixelformat) + || fmt.fmt.pix_mp.width != width || fmt.fmt.pix_mp.height != height) { + GST_DEBUG_OBJECT (self, + "Trying to use peer format: %" GST_FOURCC_FORMAT " %ix%i", +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index bbfdee2..e029d7d 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -209,3 +209,23 @@ gst_v4l2_format_from_video_format (GstVideoFormat format, guint32 * out_pix_fmt) + *out_pix_fmt = entry->v4l2_pix_fmt; + return TRUE; + } ++ ++gboolean ++gst_v4l2_format_equivalent (guint32 pix_fmt_a, guint32 pix_fmt_b) ++{ ++ struct FormatEntry *entry_a, *entry_b; ++ ++ if (pix_fmt_a == pix_fmt_b) ++ return TRUE; ++ ++ entry_a = lookup_v4l2_fmt (pix_fmt_a); ++ entry_b = lookup_v4l2_fmt (pix_fmt_b); ++ ++ if (!entry_a || !entry_b) ++ return FALSE; ++ ++ if (entry_a->gst_fmt == entry_b->gst_fmt) ++ return TRUE; ++ ++ return FALSE; ++} +diff --git a/sys/v4l2codecs/gstv4l2format.h b/sys/v4l2codecs/gstv4l2format.h +index dd1e1ea..1f634ee 100644 +--- a/sys/v4l2codecs/gstv4l2format.h ++++ b/sys/v4l2codecs/gstv4l2format.h +@@ -35,4 +35,6 @@ gboolean gst_v4l2_format_to_video_format (guint32 pix_fmt, + gboolean gst_v4l2_format_from_video_format (GstVideoFormat format, + guint32 * out_pix_fmt); + ++gboolean gst_v4l2_format_equivalent (guint32 pix_fmt_a, guint32 pix_fmt_b); ++ + #endif /* __GST_V4L2_FORMAT_H__ */ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0052-v4l2codecs-encoder-Save-the-sink-format.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0052-v4l2codecs-encoder-Save-the-sink-format.patch new file mode 100644 index 000000000..b840d9a72 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0052-v4l2codecs-encoder-Save-the-sink-format.patch @@ -0,0 +1,38 @@ +From 2bf76dc97e65611bcbc5bb82a264ae6a88df8903 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 2 Nov 2023 10:39:34 -0400 +Subject: [PATCH 52/68] v4l2codecs: encoder: Save the sink format + +Save the V4L2 sink format that has been selected. The information +enclosed will be used to validate buffer importation. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2encoder.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 0886d15..a9e0939 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -77,8 +77,9 @@ struct _GstV4l2Encoder + GstQueueArray *pending_requests; + guint version; + +- enum v4l2_buf_type src_buf_type; + enum v4l2_buf_type sink_buf_type; ++ struct v4l2_format sink_fmt; ++ enum v4l2_buf_type src_buf_type; + gboolean mplane; + + /* properties */ +@@ -575,6 +576,7 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in, + + GST_INFO_OBJECT (self, "Selected sink format %s %ix%i", + gst_video_format_to_string (out->finfo->format), out->width, out->height); ++ self->sink_fmt = fmt; + + return TRUE; + } +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0053-v4l2codecs-format-Prefer-mplane-variants.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0053-v4l2codecs-format-Prefer-mplane-variants.patch new file mode 100644 index 000000000..28315dfd2 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0053-v4l2codecs-format-Prefer-mplane-variants.patch @@ -0,0 +1,29 @@ +From 98d79a4ea5acc38550b1cead1dc0e876c710273f Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 2 Nov 2023 10:41:18 -0400 +Subject: [PATCH 53/68] v4l2codecs: format: Prefer mplane variants + +If we have the choice, prefer the mplane variant of the pixel formats +since it allows importing the single plane variant. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index e029d7d..7af79f9 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -32,8 +32,8 @@ struct FormatEntry + }; + + static struct FormatEntry format_map[] = { +- {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_NV12M, 2, GST_VIDEO_FORMAT_NV12, 8, 420}, ++ {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422}, + {V4L2_PIX_FMT_SUNXI_TILED_NV12, 1, GST_VIDEO_FORMAT_NV12_32L32, 8, 422}, + {V4L2_PIX_FMT_NV12_4L4, 1, GST_VIDEO_FORMAT_NV12_4L4, 8, 420}, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0054-v4l2codecs-encoder-Implement-zero-copy-support.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0054-v4l2codecs-encoder-Implement-zero-copy-support.patch new file mode 100644 index 000000000..94c0411eb --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0054-v4l2codecs-encoder-Implement-zero-copy-support.patch @@ -0,0 +1,517 @@ +From c7ebabfc115055d62b47910cb136ea2abe6b5428 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Tue, 31 Oct 2023 16:24:33 -0400 +Subject: [PATCH 54/68] v4l2codecs: encoder: Implement zero-copy support + +In this implementation we switch encoders to always run in memory type +V4L2_MEMORY_DMABUF. To alllow fallback, we allocated 4 frame using +V4L2_MEMORY_MMAP at setup time and detach the dmabuf from the driver. If +upstream provides dmabuf, we try importating that, and if that fails, we +make a copy and continue. + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecallocator.c | 14 +- + sys/v4l2codecs/gstv4l2codech264enc.c | 35 +++-- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 21 ++- + sys/v4l2codecs/gstv4l2encoder.c | 198 ++++++++++++++++++++++--- + sys/v4l2codecs/gstv4l2encoder.h | 8 +- + 5 files changed, 228 insertions(+), 48 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codecallocator.c b/sys/v4l2codecs/gstv4l2codecallocator.c +index e0fb371..fd77347 100644 +--- a/sys/v4l2codecs/gstv4l2codecallocator.c ++++ b/sys/v4l2codecs/gstv4l2codecallocator.c +@@ -245,7 +245,8 @@ gst_v4l2_codec_encoder_allocator_prepare (GstV4l2CodecAllocator * self) + gint ret; + guint i; + +- ret = gst_v4l2_encoder_request_buffers (encoder, direction, self->pool_size); ++ ret = gst_v4l2_encoder_request_buffers (encoder, direction, self->pool_size, ++ V4L2_MEMORY_MMAP); + if (ret < self->pool_size) { + if (ret >= 0) + GST_ERROR_OBJECT (self, +@@ -261,10 +262,16 @@ gst_v4l2_codec_encoder_allocator_prepare (GstV4l2CodecAllocator * self) + g_queue_push_tail (&self->pool, buf); + } + ++ if (direction == GST_PAD_SINK) { ++ gst_v4l2_codec_allocator_detach (self); ++ gst_v4l2_encoder_request_buffers (encoder, direction, ++ VIDEO_MAX_FRAME, V4L2_MEMORY_DMABUF); ++ } ++ + return TRUE; + + failed: +- gst_v4l2_encoder_request_buffers (encoder, direction, 0); ++ gst_v4l2_encoder_request_buffers (encoder, direction, 0, V4L2_MEMORY_MMAP); + return FALSE; + } + +@@ -446,7 +453,8 @@ gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self) + gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0); + } + if (self->encoder) { +- gst_v4l2_encoder_request_buffers (self->encoder, self->direction, 0); ++ gst_v4l2_encoder_request_buffers (self->encoder, self->direction, 0, ++ V4L2_MEMORY_MMAP); + } + } + GST_OBJECT_UNLOCK (self); +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 070c51a..e7a6e07 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -270,8 +270,16 @@ gst_v4l2_codec_h264_enc_propose_allocation (GstVideoEncoder * encoder, + GstQuery * query) + { + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); ++ GstV4l2CodecPool *pool = NULL; ++ gboolean need_pool; + +- gst_query_add_allocation_pool (query, NULL, self->vinfo.size, 2, 0); ++ gst_query_parse_allocation (query, NULL, &need_pool); ++ ++ if (need_pool) ++ pool = gst_v4l2_codec_pool_new (self->sink_allocator, &self->vinfo); ++ ++ gst_query_add_allocation_pool (query, GST_BUFFER_POOL (pool), ++ self->vinfo.size, 2, 0); + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, +@@ -964,12 +972,6 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + goto done; + } + +- if (!gst_v4l2_codec_h264_enc_copy_input_buffer (self, frame)) { +- GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, +- ("Failed to allocate/copy input buffer."), (NULL)); +- goto done; +- } +- + request = gst_v4l2_encoder_alloc_request (self->encoder, + frame->system_frame_number, frame->input_buffer, frame->output_buffer); + +@@ -991,13 +993,22 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + } + + if (!gst_v4l2_encoder_request_queue (request, 0)) { +- GST_ELEMENT_ERROR (self, RESOURCE, WRITE, +- ("Driver did not accept the encode request."), (NULL)); +- goto done; ++ if (!gst_v4l2_codec_h264_enc_copy_input_buffer (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate/copy input buffer."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_encoder_request_replace_pic_buf (request, frame->input_buffer); ++ ++ if (!gst_v4l2_encoder_request_queue (request, 0)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the encode request."), (NULL)); ++ goto done; ++ } + } + +- if (!gst_v4l2_encoder_request_set_done(request, &bytesused, &flags)) +- { ++ if (!gst_v4l2_encoder_request_set_done(request, &bytesused, &flags)) { + GST_ELEMENT_ERROR (self, RESOURCE, WRITE, + ("Driver did not ack the request."), (NULL)); + goto done; +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 24e8540..5990c73 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -580,11 +580,6 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, + goto done; + } + +- if (!gst_v4l2_codec_vp8_enc_copy_input_buffer (self, frame)) { +- GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, +- ("Failed to allocate/copy input buffer."), (NULL)); +- goto done; +- } + + request = gst_v4l2_encoder_alloc_request (self->encoder, + frame->system_frame_number, frame->input_buffer, frame->output_buffer); +@@ -605,9 +600,19 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder, + } + + if (!gst_v4l2_encoder_request_queue (request, 0)) { +- GST_ELEMENT_ERROR (self, RESOURCE, WRITE, +- ("Driver did not accept the encode request."), (NULL)); +- goto done; ++ if (!gst_v4l2_codec_vp8_enc_copy_input_buffer (self, frame)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, ++ ("Failed to allocate/copy input buffer."), (NULL)); ++ goto done; ++ } ++ ++ gst_v4l2_encoder_request_replace_pic_buf (request, frame->input_buffer); ++ ++ if (!gst_v4l2_encoder_request_queue (request, 0)) { ++ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ++ ("Driver did not accept the encode request."), (NULL)); ++ goto done; ++ } + } + + if (!gst_v4l2_encoder_request_set_done (request, &bytesused, &flags)) { +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index a9e0939..85f0457 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -66,6 +66,12 @@ struct _GstV4l2Request + gboolean sub_request; + }; + ++struct BufState ++{ ++ gboolean queued; ++ gint dmabuf_fd; ++}; ++ + struct _GstV4l2Encoder + { + GstObject parent; +@@ -82,6 +88,9 @@ struct _GstV4l2Encoder + enum v4l2_buf_type src_buf_type; + gboolean mplane; + ++ /* Rember which FD was used with which index */ ++ struct BufState buffer_state[VIDEO_MAX_FRAME]; ++ + /* properties */ + gchar *media_device; + gchar *video_device; +@@ -106,6 +115,67 @@ direction_to_buffer_type (GstV4l2Encoder * self, GstPadDirection direction) + return self->sink_buf_type; + } + ++static void ++buffer_state_init (GstV4l2Encoder * self) ++{ ++ gint i; ++ ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) ++ self->buffer_state[i].dmabuf_fd = -1; ++} ++ ++static void ++buffer_state_streamoff (GstV4l2Encoder * self) ++{ ++ gint i; ++ ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) ++ self->buffer_state[i].queued = FALSE; ++} ++ ++static gint ++buffer_state_find_buffer (GstV4l2Encoder * self, gint dmabuf_fd) ++{ ++ gint outstanding_index = -1; ++ gint i; ++ ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) { ++ if (self->buffer_state[i].queued) ++ continue; ++ ++ if (outstanding_index == -1) ++ outstanding_index = i; ++ ++ if (self->buffer_state[i].dmabuf_fd == dmabuf_fd) ++ break; ++ } ++ ++ if (i == VIDEO_MAX_FRAME) { ++ if (outstanding_index == -1) { ++ return -1; ++ } ++ ++ i = outstanding_index; ++ } ++ ++ return i; ++} ++ ++static void ++buffer_state_queue (GstV4l2Encoder * self, gint index, gint dmabuf_fd) ++{ ++ g_assert (!self->buffer_state[index].queued); ++ ++ self->buffer_state[index].queued = TRUE; ++ self->buffer_state[index].dmabuf_fd = dmabuf_fd; ++} ++ ++static void ++buffer_state_dequeue (GstV4l2Encoder * self, gint index) ++{ ++ self->buffer_state[index].queued = FALSE; ++} ++ + static void + gst_v4l2_encoder_finalize (GObject * obj) + { +@@ -126,6 +196,8 @@ gst_v4l2_encoder_init (GstV4l2Encoder * self) + { + self->request_pool = gst_queue_array_new (16); + self->pending_requests = gst_queue_array_new (16); ++ ++ buffer_state_init (self); + } + + static void +@@ -268,6 +340,8 @@ gst_v4l2_encoder_streamoff (GstV4l2Encoder * self, GstPadDirection direction) + pending_req->pending = FALSE; + gst_v4l2_encoder_request_unref (pending_req); + } ++ } else { ++ buffer_state_streamoff (self); + } + + ret = ioctl (self->video_fd, VIDIOC_STREAMOFF, &type); +@@ -619,8 +693,8 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + return FALSE; + } + +- if (!gst_v4l2_format_equivalent (pix_fmt, fmt.fmt.pix_mp.pixelformat) +- || fmt.fmt.pix_mp.width != width || fmt.fmt.pix_mp.height != height) { ++ if (pix_fmt != fmt.fmt.pix_mp.pixelformat || fmt.fmt.pix_mp.width != width ++ || fmt.fmt.pix_mp.height != height) { + GST_DEBUG_OBJECT (self, + "Trying to use peer format: %" GST_FOURCC_FORMAT " %ix%i", + GST_FOURCC_ARGS (pix_fmt), width, height); +@@ -628,6 +702,7 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + fmt.fmt.pix_mp.pixelformat = pix_fmt; + fmt.fmt.pix_mp.width = width; + fmt.fmt.pix_mp.height = height; ++ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 4 * 1024 * 1024;//FIXME + + ret = ioctl (self->video_fd, VIDIOC_S_FMT, &fmt); + if (ret < 0) { +@@ -648,12 +723,12 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + + gint + gst_v4l2_encoder_request_buffers (GstV4l2Encoder * self, +- GstPadDirection direction, guint num_buffers) ++ GstPadDirection direction, guint num_buffers, guint mem_type) + { + gint ret; + struct v4l2_requestbuffers reqbufs = { + .count = num_buffers, +- .memory = V4L2_MEMORY_MMAP, ++ .memory = mem_type, + .type = direction_to_buffer_type (self, direction), + }; + +@@ -779,38 +854,104 @@ gst_v4l2_encoder_queue_src_buffer (GstV4l2Encoder * self, + return TRUE; + } + ++static gboolean ++gst_v4l2_encoder_import_buffer (GstV4l2Encoder * self, GstBuffer * buffer, ++ struct v4l2_buffer *v4l2_buffer) ++{ ++ GstVideoMeta *vmeta = NULL; ++ gint i; ++ ++ if (!self->mplane) { ++ GST_INFO_OBJECT (self, ++ "zero copy is only support on MPLANE drivers implementation."); ++ return FALSE; ++ } ++ ++ vmeta = gst_buffer_get_video_meta (buffer); ++ if (!vmeta) { ++ GST_INFO_OBJECT (self, "a GstVideoMeta is required for zero copy."); ++ return FALSE; ++ } ++ ++ if (self->sink_fmt.fmt.pix_mp.num_planes != vmeta->n_planes) { ++ GST_INFO_OBJECT (self, ++ "planar format into single plane is not support in zero copy."); ++ return FALSE; ++ } ++ ++ for (i = 0; i < vmeta->n_planes; i++) { ++ struct v4l2_plane *planes = v4l2_buffer->m.planes; ++ struct v4l2_plane_pix_format *plane_fmt = ++ self->sink_fmt.fmt.pix_mp.plane_fmt; ++ gsize size, offset, maxsize; ++ guint length, mem_idx; ++ gsize mem_skip; ++ GstMemory *mem; ++ ++ if (vmeta->stride[i] != plane_fmt[i].bytesperline) { ++ GST_INFO_OBJECT (self, ++ "Stride miss-match at plane %i: got %i but expect %u", ++ i, vmeta->stride[i], plane_fmt[i].bytesperline); ++ return FALSE; ++ } ++ ++ if (!gst_buffer_find_memory (buffer, vmeta->offset[i], 1, &mem_idx, ++ &length, &mem_skip)) { ++ GST_INFO_OBJECT (self, "no memory found for plane %i at offset %" ++ G_GSIZE_FORMAT ".", i, vmeta->offset[i]); ++ return FALSE; ++ } ++ ++ mem = gst_buffer_peek_memory (buffer, mem_idx); ++ if (!gst_is_dmabuf_memory (mem)) { ++ GST_INFO_OBJECT (self, "cannot import non-dmabuf memory"); ++ return FALSE; ++ } ++ ++ size = gst_memory_get_sizes (mem, &offset, &maxsize); ++ ++ /* *INDENT-OFF* */ ++ planes[i] = (struct v4l2_plane) { ++ .bytesused = offset + size, ++ .length = offset + maxsize, ++ .data_offset = offset + mem_skip, ++ .m.fd = gst_dmabuf_memory_get_fd (mem), ++ }; ++ /* *INDENT-ON* */ ++ } ++ ++ v4l2_buffer->length = vmeta->n_planes; ++ v4l2_buffer->index = buffer_state_find_buffer (self, ++ v4l2_buffer->m.planes[0].m.fd); ++ ++ if (v4l2_buffer->index < 0) { ++ GST_INFO_OBJECT (self, "no more free buffer to queue the picture."); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + gst_v4l2_encoder_queue_sink_buffer (GstV4l2Encoder * self, + GstV4l2Request * request, GstBuffer * buffer, guint32 frame_num, + guint flags) + { +- gint i, ret; ++ gint ret; + struct v4l2_plane planes[GST_VIDEO_MAX_PLANES]; + struct v4l2_buffer buf = { + .type = self->sink_buf_type, +- .memory = V4L2_MEMORY_MMAP, +- .index = gst_v4l2_codec_buffer_get_index (buffer), ++ .memory = V4L2_MEMORY_DMABUF, + .timestamp.tv_usec = frame_num, + .request_fd = request->fd, + .flags = V4L2_BUF_FLAG_REQUEST_FD | flags, ++ .m.planes = planes, + }; + + GST_TRACE_OBJECT (self, "Queuing bitstream buffer %i", buf.index); + +- if (self->mplane) { +- buf.length = gst_buffer_n_memory (buffer); +- buf.m.planes = planes; +- for (i = 0; i < buf.length; i++) { +- GstMemory *mem = gst_buffer_peek_memory (buffer, i); +- /* *INDENT-OFF* */ +- planes[i] = (struct v4l2_plane) { +- .bytesused = gst_memory_get_sizes (mem, NULL, NULL), +- }; +- /* *INDENT-ON* */ +- } +- } else { +- buf.bytesused = gst_buffer_get_size (buffer); +- } ++ if (!gst_v4l2_encoder_import_buffer (self, buffer, &buf)) ++ return FALSE; + + ret = ioctl (self->video_fd, VIDIOC_QBUF, &buf); + if (ret < 0) { +@@ -818,6 +959,9 @@ gst_v4l2_encoder_queue_sink_buffer (GstV4l2Encoder * self, + return FALSE; + } + ++ buffer_state_queue (self, buf.index, ++ gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (buffer, 0))); ++ + return TRUE; + } + +@@ -828,7 +972,7 @@ gst_v4l2_encoder_dequeue_sink (GstV4l2Encoder * self) + struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} }; + struct v4l2_buffer buf = { + .type = self->sink_buf_type, +- .memory = V4L2_MEMORY_MMAP, ++ .memory = V4L2_MEMORY_DMABUF, + }; + + if (self->mplane) { +@@ -843,6 +987,7 @@ gst_v4l2_encoder_dequeue_sink (GstV4l2Encoder * self) + } + + GST_TRACE_OBJECT (self, "Dequeued picture buffer %i", buf.index); ++ buffer_state_dequeue (self, buf.index); + + return TRUE; + } +@@ -1256,7 +1401,7 @@ gst_v4l2_encoder_request_queue (GstV4l2Request * request, guint flags) + + if (!gst_v4l2_encoder_queue_sink_buffer (encoder, request, + request->pic_buf, request->frame_num, flags)) { +- GST_ERROR_OBJECT (encoder, "Driver did not accept the picture buffer."); ++ GST_INFO_OBJECT (encoder, "Driver did not accept the picture buffer."); + return FALSE; + } + +@@ -1344,6 +1489,13 @@ gst_v4l2_encoder_request_set_done (GstV4l2Request * request, + return ret; + } + ++void ++gst_v4l2_encoder_request_replace_pic_buf (GstV4l2Request * request, ++ GstBuffer * pic_buf) ++{ ++ gst_buffer_replace (&request->pic_buf, pic_buf); ++} ++ + gboolean + gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, + guint * qp_min, guint * qp_max) +diff --git a/sys/v4l2codecs/gstv4l2encoder.h b/sys/v4l2codecs/gstv4l2encoder.h +index e7f44ba..09016e1 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.h ++++ b/sys/v4l2codecs/gstv4l2encoder.h +@@ -64,7 +64,8 @@ gboolean gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoI + + gint gst_v4l2_encoder_request_buffers (GstV4l2Encoder * self, + GstPadDirection direction, +- guint num_buffers); ++ guint num_buffers, ++ guint mem_type); + + gboolean gst_v4l2_encoder_export_buffer (GstV4l2Encoder * self, + GstPadDirection directon, +@@ -130,12 +131,15 @@ gboolean gst_v4l2_encoder_request_queue (GstV4l2Request * request, + + gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused, guint32 * flags); + +-gboolean gst_v4l2_encoder_request_failed (GstV4l2Request * request); ++void gst_v4l2_encoder_request_replace_pic_buf (GstV4l2Request * request, ++ GstBuffer * pic_buf); + + gboolean gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max); + + gboolean gst_v4l2_codec_h264_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max); + ++gboolean gst_v4l2_encoder_uses_zero_copy (GstV4l2Encoder * self); ++ + G_END_DECLS + + #endif /* __GST_V4L2_ENCODER_H__ */ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0055-codecs-h264enc-Fix-indentation.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0055-codecs-h264enc-Fix-indentation.patch new file mode 100644 index 000000000..4f401119b --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0055-codecs-h264enc-Fix-indentation.patch @@ -0,0 +1,27 @@ +From 3029b0bd479ee17c15aca93b4c6e2fdad44c7824 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 11:30:58 -0500 +Subject: [PATCH 55/68] codecs: h264enc: Fix indentation + +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +index b024939..5769446 100644 +--- a/gst-libs/gst/codecs/gsth264encoder.c ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -147,7 +147,8 @@ gst_h264_encoder_set_quality (GstH264Encoder * self, GstH264Frame * h264_frame) + } + + static GstFlowReturn +-gst_h264_encoder_set_frame_type (GstH264Encoder * self, GstH264Frame * h264_frame) ++gst_h264_encoder_set_frame_type (GstH264Encoder * self, ++ GstH264Frame * h264_frame) + { + GstH264EncoderPrivate *priv = self->priv; + GstVideoCodecFrame *frame = h264_frame->frame; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0056-v4l2codecs-h264enc-Fix-indentation.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0056-v4l2codecs-h264enc-Fix-indentation.patch new file mode 100644 index 000000000..7332f1169 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0056-v4l2codecs-h264enc-Fix-indentation.patch @@ -0,0 +1,362 @@ +From e3f07a8e64dcd406b6665a7bce125efdf603efb9 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 11:31:14 -0500 +Subject: [PATCH 56/68] v4l2codecs: h264enc: Fix indentation + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 157 ++++++++++++--------------- + 1 file changed, 69 insertions(+), 88 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index e7a6e07..6bf249a 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -45,12 +45,11 @@ enum + #define GST_PAD_SINK_FORMATS "{NV12, YUY2, I420}" + + static GstStaticPadTemplate sink_template = +- GST_STATIC_PAD_TEMPLATE(GST_VIDEO_ENCODER_SINK_NAME, +- GST_PAD_SINK, GST_PAD_ALWAYS, +- GST_STATIC_CAPS(GST_VIDEO_CAPS_MAKE(GST_PAD_SINK_FORMATS) +- ", colorimetry= {bt601,bt709,bt601-full-range,bt709-full-range}" +- ) +- ); ++GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME, ++ GST_PAD_SINK, GST_PAD_ALWAYS, ++ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_PAD_SINK_FORMATS) ++ ", colorimetry= {bt601,bt709,bt601-full-range,bt709-full-range}") ++ ); + + static GstStaticPadTemplate src_template = + GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, +@@ -157,13 +156,7 @@ gst_v4l2_codec_h264_enc_api_check (GstV4l2Encoder * encoder) + }, { + SET_ID (V4L2_CID_STATELESS_H264_ENCODE_RC), + .size = sizeof(struct v4l2_ctrl_h264_encode_rc), +- // }, { +- // SET_ID (V4L2_CID_STATELESS_H264_SPS), +- // .size = sizeof(struct v4l2_ctrl_h264_sps), +- // }, { +- // SET_ID (V4L2_CID_STATELESS_H264_PPS), +- // .size = sizeof(struct v4l2_ctrl_h264_pps), +- }, ++ } + }; + #undef SET_ID + /* *INDENT-ON* */ +@@ -324,7 +317,7 @@ gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) + + static gboolean + gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, +- GstBuffer *codec_data, guint *data_size) ++ GstBuffer * codec_data, guint * data_size) + { + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); + +@@ -362,7 +355,7 @@ gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, + } + + static guint8 +-get_sps_aspect_ratio_idc(guint par_n, guint par_d) ++get_sps_aspect_ratio_idc (guint par_n, guint par_d) + { + if (par_n == 1 && par_d == 1) + return 1; +@@ -397,7 +390,7 @@ get_sps_aspect_ratio_idc(guint par_n, guint par_d) + else if (par_n == 2 && par_d == 1) + return 16; + else +- return 0; // Extended_SAR for custom ratios ++ return 0; // Extended_SAR for custom ratios + } + + /* Begin of code taken from VA plugin */ +@@ -412,9 +405,9 @@ typedef struct + int MaxCPB; + int MinCr; + } GstVaH264LevelLimits; +-/* *INDENT-OFF* */ + + static const GstVaH264LevelLimits _va_h264_level_limits[] = { ++ /* *INDENT-OFF* */ + /* level idc MaxMBPS MaxFS MaxDpbMbs MaxBR MaxCPB MinCr */ + {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2}, + {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2}, +@@ -436,32 +429,33 @@ static const GstVaH264LevelLimits _va_h264_level_limits[] = { + {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2}, + {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2}, + {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2}, ++ /* *INDENT-ON* */ + }; +-/* *INDENT-ON* */ ++ + /* Enf of code taken from VA Plugin */ + + // Recursive function to find the greatest common divisor + static gint +-gcd(int a, int b) ++gcd (int a, int b) + { + if (b == 0) + return a; +- return gcd(b, a % b); ++ return gcd (b, a % b); + } + + static gint +-find_time_scale_multiplier(int fps_d) ++find_time_scale_multiplier (int fps_d) + { + guint base_value = 1000; +- return (fps_d * base_value) / gcd(fps_d, base_value); ++ return (fps_d * base_value) / gcd (fps_d, base_value); + } + + static void +-gst_v4l2_codec_h264_enc_init_sps_pps(GstV4l2CodecH264Enc *self, ++gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + GstVideoCodecState * state) + { +- memset(&self->sps, 0, sizeof(self->sps)); +- memset(&self->sps, 0, sizeof(self->pps)); ++ memset (&self->sps, 0, sizeof (self->sps)); ++ memset (&self->sps, 0, sizeof (self->pps)); + + /* SPS */ + self->sps.profile_idc = 66; +@@ -470,7 +464,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps(GstV4l2CodecH264Enc *self, + + + // sps.seq_parameter_set_id = 0; // TODO: Set it properly +- self->sps.chroma_format_idc = 1; /* YUV 4:2:0 */ ++ self->sps.chroma_format_idc = 1; /* YUV 4:2:0 */ + + self->sps.pic_width_in_mbs_minus1 = self->width_mbs - 1; + self->sps.pic_height_in_map_units_minus1 = self->height_mbs - 1; +@@ -492,114 +486,100 @@ gst_v4l2_codec_h264_enc_init_sps_pps(GstV4l2CodecH264Enc *self, + // Calculate lowest acceptable level + GstStructure *structure; + gint fps_n, fps_d; +- structure = gst_caps_get_structure(state->caps, 0); +- gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d); +- gint n_levels = sizeof(_va_h264_level_limits) / sizeof(_va_h264_level_limits[0]); ++ structure = gst_caps_get_structure (state->caps, 0); ++ gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d); ++ gint n_levels = ++ sizeof (_va_h264_level_limits) / sizeof (_va_h264_level_limits[0]); + GValue bitrate = G_VALUE_INIT; +- g_object_get_property(G_OBJECT(&self->parent), "bitrate", &bitrate); +- self->targeted_bitrate = g_value_get_uint64(&bitrate); ++ g_object_get_property (G_OBJECT (&self->parent), "bitrate", &bitrate); ++ self->targeted_bitrate = g_value_get_uint64 (&bitrate); + int MaxBR = self->targeted_bitrate; + int FS = self->width * self->height / 256; +- int MBPS = FS * fps_n / fps_d; // Assuming each macroblock is 16x16 ++ int MBPS = FS * fps_n / fps_d; // Assuming each macroblock is 16x16 + int DpbMbs = FS; + int CPB = MaxBR; + int CR = fps_n / fps_d; + gint lowest_level = 0; +- for (; lowest_level < n_levels; ++lowest_level) +- { ++ for (; lowest_level < n_levels; ++lowest_level) { + const GstVaH264LevelLimits *level = &_va_h264_level_limits[lowest_level]; +- if (MBPS <= level->MaxMBPS && FS <= level->MaxFS && DpbMbs <= level->MaxDpbMbs && +- MaxBR <= level->MaxBR && CPB <= level->MaxCPB && CR >= level->MinCr) +- { ++ if (MBPS <= level->MaxMBPS && FS <= level->MaxFS ++ && DpbMbs <= level->MaxDpbMbs && MaxBR <= level->MaxBR ++ && CPB <= level->MaxCPB && CR >= level->MinCr) { + break; + } + } + self->sps.level_idc = _va_h264_level_limits[lowest_level].idc; +- if (self->sps.level_idc == GST_H264_LEVEL_L1B) +- { ++ if (self->sps.level_idc == GST_H264_LEVEL_L1B) { + self->sps.constraint_set3_flag = 1; + } +- GST_DEBUG_OBJECT(self, "width:%d height:%d targeted_bitrate:%ld", +- self->height, self->height, self->targeted_bitrate); +- GST_DEBUG_OBJECT(self, "MBPS=%d,FS=%d,DpbMbs=%d,MaxBR=%d,CPB=%d,CR=%d", +- MBPS, FS, DpbMbs, MaxBR, CPB, CR); +- GST_DEBUG_OBJECT(self, "level_idc: %d", self->sps.level_idc); ++ GST_DEBUG_OBJECT (self, "width:%d height:%d targeted_bitrate:%ld", ++ self->height, self->height, self->targeted_bitrate); ++ GST_DEBUG_OBJECT (self, "MBPS=%d,FS=%d,DpbMbs=%d,MaxBR=%d,CPB=%d,CR=%d", ++ MBPS, FS, DpbMbs, MaxBR, CPB, CR); ++ GST_DEBUG_OBJECT (self, "level_idc: %d", self->sps.level_idc); + + /* Crop unaligned videos */ +- if (self->width & 15 || self->height & 15) +- { +- static const guint SubWidthC[] = {1, 2, 2, 1}; +- static const guint SubHeightC[] = {1, 2, 1, 1}; ++ if (self->width & 15 || self->height & 15) { ++ static const guint SubWidthC[] = { 1, 2, 2, 1 }; ++ static const guint SubHeightC[] = { 1, 2, 1, 1 }; + const guint CropUnitX = SubWidthC[self->sps.chroma_format_idc]; + const guint CropUnitY = SubHeightC[self->sps.chroma_format_idc] * +- (2 - self->sps.frame_mbs_only_flag); ++ (2 - self->sps.frame_mbs_only_flag); + + self->sps.frame_cropping_flag = 1; + self->sps.frame_crop_left_offset = 0; + self->sps.frame_crop_right_offset = (16 * self->width_mbs - +- self->width) / +- CropUnitX; ++ self->width) / CropUnitX; + self->sps.frame_crop_top_offset = 0; + self->sps.frame_crop_bottom_offset = (16 * self->height_mbs - +- self->height) / +- CropUnitY; ++ self->height) / CropUnitY; + } +- +- + // set colorimetry + const gchar *colorimetry; +- colorimetry = gst_structure_get_string(structure, "colorimetry"); ++ colorimetry = gst_structure_get_string (structure, "colorimetry"); + +- if (colorimetry) +- { ++ if (colorimetry) { + self->sps.vui_parameters_present_flag = 1; + self->sps.vui_parameters.colour_description_present_flag = 1; +- if ((strcmp(colorimetry, "bt709") == 0) || +- (strcmp(colorimetry, "bt709-full-range") == 0)) +- { ++ if ((strcmp (colorimetry, "bt709") == 0) || ++ (strcmp (colorimetry, "bt709-full-range") == 0)) { + self->sps.vui_parameters.colour_primaries = 1; + self->sps.vui_parameters.transfer_characteristics = 1; + self->sps.vui_parameters.matrix_coefficients = 1; +- if (strcmp(colorimetry, "bt709-full-range") == 0) +- { ++ if (strcmp (colorimetry, "bt709-full-range") == 0) { + self->sps.vui_parameters.video_full_range_flag = 1; + } +- } +- else if ((strcmp(colorimetry, "bt601") == 0) || +- (strcmp(colorimetry, "bt601-full-range") == 0)) +- { ++ } else if ((strcmp (colorimetry, "bt601") == 0) || ++ (strcmp (colorimetry, "bt601-full-range") == 0)) { + self->sps.vui_parameters.colour_primaries = 6; + self->sps.vui_parameters.transfer_characteristics = 6; + self->sps.vui_parameters.matrix_coefficients = 6; +- if (strcmp(colorimetry, "bt601-full-range") == 0) +- { ++ if (strcmp (colorimetry, "bt601-full-range") == 0) { + self->sps.vui_parameters.video_full_range_flag = 1; + } + } + } +- + // set aspect ratio + gint par_n, par_d; +- if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &par_n, &par_d)) +- { ++ if (gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, ++ &par_d)) { + self->sps.vui_parameters.aspect_ratio_info_present_flag = 1; +- self->sps.vui_parameters.aspect_ratio_idc = get_sps_aspect_ratio_idc(par_n, par_d); +- if (self->sps.vui_parameters.aspect_ratio_idc == 255) +- { ++ self->sps.vui_parameters.aspect_ratio_idc = ++ get_sps_aspect_ratio_idc (par_n, par_d); ++ if (self->sps.vui_parameters.aspect_ratio_idc == 255) { + self->sps.vui_parameters.sar_width = par_n; + self->sps.vui_parameters.sar_height = par_d; + } + } +- + // set Frame rate +- if (gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d)) +- { ++ if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { + self->sps.vui_parameters.timing_info_present_flag = 1; + self->sps.vui_parameters.fixed_frame_rate_flag = 1; // Only supports fixed frame rate for now +- gint multiplier = find_time_scale_multiplier(fps_d); ++ gint multiplier = find_time_scale_multiplier (fps_d); + gint time_scale = fps_n * multiplier; + self->sps.vui_parameters.time_scale = time_scale; +- self->sps.vui_parameters.num_units_in_tick = time_scale / (2 * fps_n / fps_d ); ++ self->sps.vui_parameters.num_units_in_tick = ++ time_scale / (2 * fps_n / fps_d); + } + + /* PPS */ +@@ -665,8 +645,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + gst_video_codec_state_unref (self->output_state); + + caps = gst_caps_new_simple ("video/x-h264", +- "stream-format", G_TYPE_STRING, "byte-stream", +- "alignment", G_TYPE_STRING, "nal", NULL); ++ "stream-format", G_TYPE_STRING, "byte-stream", ++ "alignment", G_TYPE_STRING, "nal", NULL); + + self->output_state = + gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), +@@ -690,7 +670,7 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + gst_v4l2_codec_h264_enc_get_qp_range (self->encoder, &self->qp_min, + &self->qp_max); + +- gst_v4l2_codec_h264_enc_init_sps_pps(self, state); ++ gst_v4l2_codec_h264_enc_init_sps_pps (self, state); + + return TRUE; + } +@@ -882,7 +862,8 @@ gst_v4l2_codec_h264_enc_fill_encode_params (GstH264Encoder * encoder, + self->encode_params.nalu_type = V4L2_H264_NAL_CODED_SLICE_NON_IDR_PIC; + self->encode_params.reference_ts = self->reference_timestamp; + self->encode_params.frame_num++; +- self->encode_params.frame_num %= (1 << (self->sps.log2_max_frame_num_minus4 + 4)); ++ self->encode_params.frame_num %= ++ (1 << (self->sps.log2_max_frame_num_minus4 + 4)); + self->encode_params.nalu_type = 1; + self->encode_params.nal_reference_idc = 2; + break; +@@ -906,7 +887,7 @@ gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, + + //h264_rate_control_step(encoder); + +- self->encode_rc.qp = self->pps.pic_init_qp_minus26 + 26;//encoder->rc.qp; ++ self->encode_rc.qp = self->pps.pic_init_qp_minus26 + 26; //encoder->rc.qp; + self->encode_rc.qp_min = self->qp_min; + self->encode_rc.qp_max = self->qp_max; + } +@@ -949,8 +930,8 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + gst_buffer_resize (codec_data, 0, data_size); + } + +- /* *INDENT-OFF* */ + struct v4l2_ext_control control[] = { ++ /* *INDENT-OFF* */ + { + .id = V4L2_CID_STATELESS_H264_ENCODE_PARAMS, + .ptr = &self->encode_params, +@@ -960,8 +941,8 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + .ptr = &self->encode_rc, + .size = sizeof (self->encode_rc), + }, ++ /* *INDENT-ON* */ + }; +- /* *INDENT-ON* */ + + GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", + h264_frame->quality); +@@ -1008,7 +989,7 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + } + } + +- if (!gst_v4l2_encoder_request_set_done(request, &bytesused, &flags)) { ++ if (!gst_v4l2_encoder_request_set_done (request, &bytesused, &flags)) { + GST_ELEMENT_ERROR (self, RESOURCE, WRITE, + ("Driver did not ack the request."), (NULL)); + goto done; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0057-v4l2codecs-h264enc-Cleanup-caps-colorimetry-framerat.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0057-v4l2codecs-h264enc-Cleanup-caps-colorimetry-framerat.patch new file mode 100644 index 000000000..eae7e0558 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0057-v4l2codecs-h264enc-Cleanup-caps-colorimetry-framerat.patch @@ -0,0 +1,351 @@ +From 4c2bad1d72ed1d87df1112b9d14dfdfb300ca4f2 Mon Sep 17 00:00:00 2001 +From: Denis Shimizu +Date: Tue, 14 Nov 2023 06:11:11 -0300 +Subject: [PATCH 57/68] v4l2codecs: h264enc: Cleanup caps, colorimetry, + framerate and style + +This is an overall fix of how colorimetry, framerate and caps are +handled, with additionall correction regarding GStreamer coding +standard. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 203 +++++++++++---------------- + 1 file changed, 84 insertions(+), 119 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 6bf249a..505d4e9 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -42,19 +42,15 @@ enum + PROP_LAST = PROP_0 + }; + +-#define GST_PAD_SINK_FORMATS "{NV12, YUY2, I420}" +- + static GstStaticPadTemplate sink_template = + GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME, + GST_PAD_SINK, GST_PAD_ALWAYS, +- GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_PAD_SINK_FORMATS) +- ", colorimetry= {bt601,bt709,bt601-full-range,bt709-full-range}") +- ); ++ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_V4L2_DEFAULT_VIDEO_FORMATS))); + + static GstStaticPadTemplate src_template = + GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, + GST_PAD_SRC, GST_PAD_ALWAYS, +- GST_STATIC_CAPS ("video/x-h264,alignment=nal,stream-format=byte-stream")); ++ GST_STATIC_CAPS ("video/x-h264,alignment=au,stream-format=byte-stream")); + + #define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 + +@@ -65,7 +61,7 @@ GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, + + /* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */ + #define SX_BITRATE 6 +-/* Scale factor for CPB size (HRD cpb_size_scale: min = 4) */ ++/* Scale factor for cpb_size (HRD cpb_size_scale: min = 4) */ + #define SX_CPB_SIZE 4 + /* Maximum sizes for common headers (in bits) */ + #define MAX_SPS_HDR_SIZE 16473 +@@ -88,8 +84,8 @@ struct _GstV4l2CodecH264Enc + GstVideoInfo vinfo; + gint width; + gint height; +- gint width_mbs; +- gint height_mbs; ++ gint width_in_macroblocks; ++ gint height_in_macroblocks; + guint qp_max, qp_min; + guint64 targeted_bitrate; + +@@ -390,7 +386,7 @@ get_sps_aspect_ratio_idc (guint par_n, guint par_d) + else if (par_n == 2 && par_d == 1) + return 16; + else +- return 0; // Extended_SAR for custom ratios ++ return 255; // Extended_SAR for custom ratios + } + + /* Begin of code taken from VA plugin */ +@@ -398,17 +394,18 @@ typedef struct + { + const char *level; + int idc; +- int MaxMBPS; +- int MaxFS; +- int MaxDpbMbs; +- int MaxBR; +- int MaxCPB; +- int MinCr; ++ int max_macroblocks_per_second; ++ int max_frame_size_in_macroblocks; ++ int max_dpb_macroblocks; ++ int maximum_bitrate; ++ int max_cpb_size; ++ int minimum_compression_ratio; + } GstVaH264LevelLimits; + +-static const GstVaH264LevelLimits _va_h264_level_limits[] = { ++static const GstVaH264LevelLimits _h264_level_limits[] = { + /* *INDENT-OFF* */ +- /* level idc MaxMBPS MaxFS MaxDpbMbs MaxBR MaxCPB MinCr */ ++ /*level, idc, max_macroblocks_per_second, max_frame_size_in_macroblocks, ++ max_dpb_macroblocks, max_bitrate, max_cpb_size, minimum_compression_ratio */ + {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2}, + {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2}, + {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2}, +@@ -434,26 +431,14 @@ static const GstVaH264LevelLimits _va_h264_level_limits[] = { + + /* Enf of code taken from VA Plugin */ + +-// Recursive function to find the greatest common divisor +-static gint +-gcd (int a, int b) +-{ +- if (b == 0) +- return a; +- return gcd (b, a % b); +-} +- +-static gint +-find_time_scale_multiplier (int fps_d) +-{ +- guint base_value = 1000; +- return (fps_d * base_value) / gcd (fps_d, base_value); +-} +- + static void + gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + GstVideoCodecState * state) + { ++ gint maximum_bitrate, frame_size_in_macroblocks, ++ macroblocks_per_second, dpb_macroblocks, cpb_size, compression_ratio, ++ minimum_level_index; ++ + memset (&self->sps, 0, sizeof (self->sps)); + memset (&self->sps, 0, sizeof (self->pps)); + +@@ -462,12 +447,10 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->sps.constraint_set0_flag = 1; + self->sps.constraint_set1_flag = 0; + +- +- // sps.seq_parameter_set_id = 0; // TODO: Set it properly + self->sps.chroma_format_idc = 1; /* YUV 4:2:0 */ + +- self->sps.pic_width_in_mbs_minus1 = self->width_mbs - 1; +- self->sps.pic_height_in_map_units_minus1 = self->height_mbs - 1; ++ self->sps.pic_width_in_mbs_minus1 = self->width_in_macroblocks - 1; ++ self->sps.pic_height_in_map_units_minus1 = self->height_in_macroblocks - 1; + + self->sps.num_ref_frames = 1; // FIXME: rename according to spec, max_num_ref_frames + self->sps.num_ref_frames_in_pic_order_cnt_cycle = 2; +@@ -484,103 +467,92 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->sps.frame_mbs_only_flag = 1; + + // Calculate lowest acceptable level +- GstStructure *structure; +- gint fps_n, fps_d; +- structure = gst_caps_get_structure (state->caps, 0); +- gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d); +- gint n_levels = +- sizeof (_va_h264_level_limits) / sizeof (_va_h264_level_limits[0]); ++ gint n_levels = G_N_ELEMENTS (_h264_level_limits); + GValue bitrate = G_VALUE_INIT; +- g_object_get_property (G_OBJECT (&self->parent), "bitrate", &bitrate); ++ g_object_get_property (G_OBJECT (self), "bitrate", &bitrate); + self->targeted_bitrate = g_value_get_uint64 (&bitrate); +- int MaxBR = self->targeted_bitrate; +- int FS = self->width * self->height / 256; +- int MBPS = FS * fps_n / fps_d; // Assuming each macroblock is 16x16 +- int DpbMbs = FS; +- int CPB = MaxBR; +- int CR = fps_n / fps_d; +- gint lowest_level = 0; +- for (; lowest_level < n_levels; ++lowest_level) { +- const GstVaH264LevelLimits *level = &_va_h264_level_limits[lowest_level]; +- if (MBPS <= level->MaxMBPS && FS <= level->MaxFS +- && DpbMbs <= level->MaxDpbMbs && MaxBR <= level->MaxBR +- && CPB <= level->MaxCPB && CR >= level->MinCr) { ++ maximum_bitrate = self->targeted_bitrate; ++ frame_size_in_macroblocks = self->width * self->height / 256; ++ macroblocks_per_second = frame_size_in_macroblocks * state->info.fps_n / state->info.fps_d; // Assuming each macroblock is 16x16 ++ dpb_macroblocks = frame_size_in_macroblocks; ++ cpb_size = maximum_bitrate; ++ compression_ratio = state->info.fps_n / state->info.fps_d; ++ minimum_level_index = 0; ++ for (; minimum_level_index < n_levels; ++minimum_level_index) { ++ const GstVaH264LevelLimits *level = ++ &_h264_level_limits[minimum_level_index]; ++ if (macroblocks_per_second <= level->max_macroblocks_per_second ++ && frame_size_in_macroblocks <= level->max_frame_size_in_macroblocks ++ && dpb_macroblocks <= level->max_dpb_macroblocks ++ && maximum_bitrate <= level->maximum_bitrate ++ && cpb_size <= level->max_cpb_size ++ && compression_ratio >= level->minimum_compression_ratio) { + break; + } + } +- self->sps.level_idc = _va_h264_level_limits[lowest_level].idc; ++ self->sps.level_idc = _h264_level_limits[minimum_level_index].idc; + if (self->sps.level_idc == GST_H264_LEVEL_L1B) { + self->sps.constraint_set3_flag = 1; + } + GST_DEBUG_OBJECT (self, "width:%d height:%d targeted_bitrate:%ld", + self->height, self->height, self->targeted_bitrate); +- GST_DEBUG_OBJECT (self, "MBPS=%d,FS=%d,DpbMbs=%d,MaxBR=%d,CPB=%d,CR=%d", +- MBPS, FS, DpbMbs, MaxBR, CPB, CR); ++ GST_DEBUG_OBJECT (self, ++ "macroblocks_per_second=%d,frame_size_in_macroblocks=%d,dpb_macroblocks=%d,maximum_bitrate=%d,cpb_size=%d,compression_ratio=%d", ++ macroblocks_per_second, frame_size_in_macroblocks, dpb_macroblocks, ++ maximum_bitrate, cpb_size, compression_ratio); + GST_DEBUG_OBJECT (self, "level_idc: %d", self->sps.level_idc); + + /* Crop unaligned videos */ + if (self->width & 15 || self->height & 15) { +- static const guint SubWidthC[] = { 1, 2, 2, 1 }; +- static const guint SubHeightC[] = { 1, 2, 1, 1 }; +- const guint CropUnitX = SubWidthC[self->sps.chroma_format_idc]; +- const guint CropUnitY = SubHeightC[self->sps.chroma_format_idc] * +- (2 - self->sps.frame_mbs_only_flag); ++ static const guint chroma_subsampling_width[] = { 1, 2, 2, 1 }; ++ static const guint chroma_subsampling_height[] = { 1, 2, 1, 1 }; ++ const guint crop_unit_x = ++ chroma_subsampling_width[self->sps.chroma_format_idc]; ++ const guint crop_unit_y = ++ chroma_subsampling_height[self->sps.chroma_format_idc] * (2 - ++ self->sps.frame_mbs_only_flag); + + self->sps.frame_cropping_flag = 1; + self->sps.frame_crop_left_offset = 0; +- self->sps.frame_crop_right_offset = (16 * self->width_mbs - +- self->width) / CropUnitX; ++ self->sps.frame_crop_right_offset = (16 * self->width_in_macroblocks - ++ self->width) / crop_unit_x; + self->sps.frame_crop_top_offset = 0; +- self->sps.frame_crop_bottom_offset = (16 * self->height_mbs - +- self->height) / CropUnitY; ++ self->sps.frame_crop_bottom_offset = (16 * self->height_in_macroblocks - ++ self->height) / crop_unit_y; + } + // set colorimetry +- const gchar *colorimetry; +- colorimetry = gst_structure_get_string (structure, "colorimetry"); +- +- if (colorimetry) { +- self->sps.vui_parameters_present_flag = 1; ++ self->sps.vui_parameters_present_flag = 1; ++ if (state->info.colorimetry.range != GST_VIDEO_COLOR_RANGE_UNKNOWN && ++ state->info.colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN && ++ state->info.colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN && ++ state->info.colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN) { ++ ++ self->sps.vui_parameters.video_signal_type_present_flag = 1; ++ self->sps.vui_parameters.video_format = 5; + self->sps.vui_parameters.colour_description_present_flag = 1; +- if ((strcmp (colorimetry, "bt709") == 0) || +- (strcmp (colorimetry, "bt709-full-range") == 0)) { +- self->sps.vui_parameters.colour_primaries = 1; +- self->sps.vui_parameters.transfer_characteristics = 1; +- self->sps.vui_parameters.matrix_coefficients = 1; +- if (strcmp (colorimetry, "bt709-full-range") == 0) { +- self->sps.vui_parameters.video_full_range_flag = 1; +- } +- } else if ((strcmp (colorimetry, "bt601") == 0) || +- (strcmp (colorimetry, "bt601-full-range") == 0)) { +- self->sps.vui_parameters.colour_primaries = 6; +- self->sps.vui_parameters.transfer_characteristics = 6; +- self->sps.vui_parameters.matrix_coefficients = 6; +- if (strcmp (colorimetry, "bt601-full-range") == 0) { +- self->sps.vui_parameters.video_full_range_flag = 1; +- } ++ self->sps.vui_parameters.colour_primaries = ++ gst_video_color_primaries_to_iso (state->info.colorimetry.primaries); ++ self->sps.vui_parameters.transfer_characteristics = ++ gst_video_transfer_function_to_iso (state->info.colorimetry.transfer); ++ self->sps.vui_parameters.matrix_coefficients = ++ gst_video_color_matrix_to_iso (state->info.colorimetry.matrix); ++ if (state->info.colorimetry.range == GST_VIDEO_COLOR_RANGE_0_255) { ++ self->sps.vui_parameters.video_full_range_flag = 1; + } + } + // set aspect ratio +- gint par_n, par_d; +- if (gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, +- &par_d)) { +- self->sps.vui_parameters.aspect_ratio_info_present_flag = 1; +- self->sps.vui_parameters.aspect_ratio_idc = +- get_sps_aspect_ratio_idc (par_n, par_d); +- if (self->sps.vui_parameters.aspect_ratio_idc == 255) { +- self->sps.vui_parameters.sar_width = par_n; +- self->sps.vui_parameters.sar_height = par_d; +- } ++ self->sps.vui_parameters.aspect_ratio_info_present_flag = 1; ++ self->sps.vui_parameters.aspect_ratio_idc = ++ get_sps_aspect_ratio_idc (state->info.par_n, state->info.par_d); ++ if (self->sps.vui_parameters.aspect_ratio_idc == 255) { ++ self->sps.vui_parameters.sar_width = state->info.par_n; ++ self->sps.vui_parameters.sar_height = state->info.par_d; + } + // set Frame rate +- if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { +- self->sps.vui_parameters.timing_info_present_flag = 1; +- self->sps.vui_parameters.fixed_frame_rate_flag = 1; // Only supports fixed frame rate for now +- gint multiplier = find_time_scale_multiplier (fps_d); +- gint time_scale = fps_n * multiplier; +- self->sps.vui_parameters.time_scale = time_scale; +- self->sps.vui_parameters.num_units_in_tick = +- time_scale / (2 * fps_n / fps_d); +- } ++ self->sps.vui_parameters.timing_info_present_flag = 1; ++ self->sps.vui_parameters.fixed_frame_rate_flag = 1; // Only supports fixed frame rate for now ++ self->sps.vui_parameters.num_units_in_tick = state->info.fps_d; ++ self->sps.vui_parameters.time_scale = state->info.fps_n * 2; + + /* PPS */ + self->pps.id = 0; +@@ -592,11 +564,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.weighted_bipred_idc = 0; + + /* Rate Control */ +- +- // h264_rate_control_step(encoder); +- + self->pps.chroma_qp_index_offset = 4; +- // pps.pic_init_qp_minus26 = encoder.rc.qp - 26; + self->pps.pic_init_qp_minus26 = -13; + self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; + self->pps.deblocking_filter_control_present_flag = 1; +@@ -637,8 +605,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + self->width = state->info.width; + self->height = state->info.height; + gst_v4l2_codec_h264_enc_buffers_allocation (encoder); +- self->width_mbs = (self->width + 15) / 16; +- self->height_mbs = (self->height + 15) / 16; ++ self->width_in_macroblocks = (self->width + 15) / 16; ++ self->height_in_macroblocks = (self->height + 15) / 16; + self->first_frame = TRUE; + + if (self->output_state) +@@ -646,7 +614,7 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + + caps = gst_caps_new_simple ("video/x-h264", + "stream-format", G_TYPE_STRING, "byte-stream", +- "alignment", G_TYPE_STRING, "nal", NULL); ++ "alignment", G_TYPE_STRING, "au", NULL); + + self->output_state = + gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), +@@ -884,9 +852,6 @@ gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); + + /* Rate Control */ +- +- //h264_rate_control_step(encoder); +- + self->encode_rc.qp = self->pps.pic_init_qp_minus26 + 26; //encoder->rc.qp; + self->encode_rc.qp_min = self->qp_min; + self->encode_rc.qp_max = self->qp_max; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0058-v4l2codecs-h264enc-add-CABAC-enable-disable-control.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0058-v4l2codecs-h264enc-add-CABAC-enable-disable-control.patch new file mode 100644 index 000000000..8dfb472bd --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0058-v4l2codecs-h264enc-add-CABAC-enable-disable-control.patch @@ -0,0 +1,154 @@ +From 928f0ed7c027046999f09834fbf2baa139c10d1c Mon Sep 17 00:00:00 2001 +From: Denis Shimizu +Date: Wed, 8 Nov 2023 06:58:33 -0300 +Subject: [PATCH 58/68] v4l2codecs: h264enc: add CABAC enable/disable control + +Add option to enable/disable CABAC. It will change the encoder +parameters and PPS related flag. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 47 ++++++++++++++++++++++++++++ + sys/v4l2codecs/gstv4l2codech264enc.c | 14 ++++++++- + 2 files changed, 60 insertions(+), 1 deletion(-) + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +index 5769446..4c35956 100644 +--- a/gst-libs/gst/codecs/gsth264encoder.c ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -38,6 +38,8 @@ GST_DEBUG_CATEGORY (gst_h264_encoder_debug); + + #define H264_DEFAULT_BITRATE 100000 + ++#define H264ENC_DEFAULT_CABAC_IDC 0 ++ + enum + { + PROP_0, +@@ -45,6 +47,8 @@ enum + PROP_MAX_QUALITY, + PROP_MIN_QUALITY, + PROP_BITRATE, ++ PROP_CABAC, ++ PROP_CABAC_INIT_IDC, + }; + + struct _GstH264EncoderPrivate +@@ -59,6 +63,8 @@ struct _GstH264EncoderPrivate + gint current_quality; + guint64 used_bytes; + guint64 nb_frames; ++ gboolean cabac; ++ guint cabac_init_idc; + }; + + #define parent_class gst_h264_encoder_parent_class +@@ -247,6 +253,16 @@ gst_h264_encoder_get_property (GObject * object, guint property_id, + g_value_set_uint64 (value, priv->targeted_bitrate); + GST_OBJECT_UNLOCK (self); + break; ++ case PROP_CABAC: ++ GST_OBJECT_LOCK (self); ++ g_value_set_boolean (value, priv->cabac); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_CABAC_INIT_IDC: ++ GST_OBJECT_LOCK (self); ++ g_value_set_uint (value, priv->cabac_init_idc); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; +@@ -281,6 +297,16 @@ gst_h264_encoder_set_property (GObject * object, guint property_id, + priv->targeted_bitrate = g_value_get_uint64 (value); + GST_OBJECT_UNLOCK (self); + break; ++ case PROP_CABAC: ++ GST_OBJECT_LOCK (self); ++ priv->cabac = g_value_get_boolean (value); ++ GST_OBJECT_UNLOCK (self); ++ break; ++ case PROP_CABAC_INIT_IDC: ++ GST_OBJECT_LOCK (self); ++ priv->cabac_init_idc = g_value_get_uint (value); ++ GST_OBJECT_UNLOCK (self); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; +@@ -350,4 +376,25 @@ gst_h264_encoder_class_init (GstH264EncoderClass * klass) + "Set bitrate target", + 0, UINT_MAX, H264_DEFAULT_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:cabac: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_CABAC, ++ g_param_spec_boolean ("cabac", "CABAC", "Enable use of CABAC over CAVLC", ++ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ /** ++ * GstH264Encoder:cabac-init-idc: ++ * ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_CABAC_INIT_IDC, ++ g_param_spec_uint ("cabac-init-idc", "Initial CABAC table ID", ++ "Set initial CABAC table idc value", 0, 2, H264ENC_DEFAULT_CABAC_IDC, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + } +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 505d4e9..4591af3 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -88,6 +88,8 @@ struct _GstV4l2CodecH264Enc + gint height_in_macroblocks; + guint qp_max, qp_min; + guint64 targeted_bitrate; ++ gboolean cabac; ++ guint cabac_init_idc; + + GstV4l2CodecAllocator *sink_allocator; + GstV4l2CodecAllocator *src_allocator; +@@ -568,6 +570,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.pic_init_qp_minus26 = -13; + self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; + self->pps.deblocking_filter_control_present_flag = 1; ++ self->pps.entropy_coding_mode_flag = self->cabac; + } + + static gboolean +@@ -638,6 +641,9 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + gst_v4l2_codec_h264_enc_get_qp_range (self->encoder, &self->qp_min, + &self->qp_max); + ++ g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", ++ &self->cabac_init_idc, NULL); ++ + gst_v4l2_codec_h264_enc_init_sps_pps (self, state); + + return TRUE; +@@ -837,8 +843,14 @@ gst_v4l2_codec_h264_enc_fill_encode_params (GstH264Encoder * encoder, + break; + } + ++ if (self->cabac) { ++ self->encode_params.flags |= V4L2_H264_ENCODE_FLAG_ENTROPY_CABAC; ++ } else { ++ self->encode_params.flags &= ~V4L2_H264_ENCODE_FLAG_ENTROPY_CABAC; ++ } ++ + self->encode_params.pic_parameter_set_id = 0; +- self->encode_params.cabac_init_idc = 0; ++ self->encode_params.cabac_init_idc = self->cabac_init_idc; + + self->encode_params.pic_init_qp_minus26 = self->pps.pic_init_qp_minus26; + self->encode_params.chroma_qp_index_offset = self->pps.chroma_qp_index_offset; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0059-v4l2codecs-h264enc-Don-t-call-virtual-negotiate-dire.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0059-v4l2codecs-h264enc-Don-t-call-virtual-negotiate-dire.patch new file mode 100644 index 000000000..7491f60a5 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0059-v4l2codecs-h264enc-Don-t-call-virtual-negotiate-dire.patch @@ -0,0 +1,29 @@ +From 43ec43c0d889ecabcb06fbc6b334e884ad44a48f Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 13:19:16 -0500 +Subject: [PATCH 59/68] v4l2codecs: h264enc: Don't call virtual negotiate() + directly + +This is not meant to be used like this, call +gst_video_encoder_negotiate() instead. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 4591af3..e7ae63a 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -623,7 +623,7 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), + caps, state); + +- if (GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) { ++ if (gst_video_encoder_negotiate (encoder)) { + if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) { + GST_ELEMENT_ERROR (self, RESOURCE, FAILED, + ("Could not enable the encoder driver."), +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0060-v4l2codecs-vp8enc-Don-t-call-virtual-negotiate-direc.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0060-v4l2codecs-vp8enc-Don-t-call-virtual-negotiate-direc.patch new file mode 100644 index 000000000..6414d96f6 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0060-v4l2codecs-vp8enc-Don-t-call-virtual-negotiate-direc.patch @@ -0,0 +1,29 @@ +From 5c450c21d66fe2ad1be120ff427c3d2eb1b6e283 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 15:30:22 -0500 +Subject: [PATCH 60/68] v4l2codecs: vp8enc: Don't call virtual negotiate() + directly + +This is not meant to be used like this, call +gst_video_encoder_negotiate() instead. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 5990c73..af84a44 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -329,7 +329,7 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), + caps, state); + +- if (GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) { ++ if (gst_video_encoder_negotiate (encoder)) { + if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) { + GST_ELEMENT_ERROR (self, RESOURCE, FAILED, + ("Could not enable the encoder driver."), +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0061-v4l2codecs-h264enc-Add-profile-negotiation.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0061-v4l2codecs-h264enc-Add-profile-negotiation.patch new file mode 100644 index 000000000..8466f0b73 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0061-v4l2codecs-h264enc-Add-profile-negotiation.patch @@ -0,0 +1,150 @@ +From 9984bd8f4646ce48b4d9b3645ffc67ffcdc75161 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 15:31:05 -0500 +Subject: [PATCH 61/68] v4l2codecs: h264enc: Add profile negotiation + +We currently only support main, constrained-baseline and baseline. Some +work on the SPS is needed to generate a valid header for high. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 80 ++++++++++++++++++++++++---- + 1 file changed, 69 insertions(+), 11 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index e7ae63a..b1617c9 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -47,10 +47,17 @@ GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME, + GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_V4L2_DEFAULT_VIDEO_FORMATS))); + +-static GstStaticPadTemplate src_template = +-GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME, +- GST_PAD_SRC, GST_PAD_ALWAYS, +- GST_STATIC_CAPS ("video/x-h264,alignment=au,stream-format=byte-stream")); ++static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", ++ GST_PAD_SRC, ++ GST_PAD_ALWAYS, ++ GST_STATIC_CAPS ("video/x-h264, " ++ "framerate = (fraction) [0/1, MAX], " ++ "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], " ++ "stream-format = (string) byte-stream, " ++ "alignment = (string) au, " ++ "profile = (string) { main, constrained-baseline, baseline}") ++ ); ++ + + #define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 + +@@ -90,6 +97,7 @@ struct _GstV4l2CodecH264Enc + guint64 targeted_bitrate; + gboolean cabac; + guint cabac_init_idc; ++ gchar *profile_name; + + GstV4l2CodecAllocator *sink_allocator; + GstV4l2CodecAllocator *src_allocator; +@@ -234,6 +242,8 @@ gst_v4l2_codec_h264_enc_stop (GstVideoEncoder * encoder) + gst_video_codec_state_unref (self->output_state); + self->output_state = NULL; + ++ g_clear_pointer (&self->profile_name, g_free); ++ + return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (encoder); + } + +@@ -445,9 +455,19 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + memset (&self->sps, 0, sizeof (self->pps)); + + /* SPS */ +- self->sps.profile_idc = 66; +- self->sps.constraint_set0_flag = 1; +- self->sps.constraint_set1_flag = 0; ++ if (g_str_equal (self->profile_name, "baseline")) { ++ self->sps.profile_idc = GST_H264_PROFILE_BASELINE; ++ self->sps.constraint_set0_flag = 1; ++ self->sps.constraint_set1_flag = 0; ++ } else if (g_str_equal (self->profile_name, "constrained-baseline")) { ++ self->sps.profile_idc = GST_H264_PROFILE_BASELINE; ++ self->sps.constraint_set0_flag = 1; ++ self->sps.constraint_set1_flag = 1; ++ } else if (g_str_equal (self->profile_name, "main")) { ++ self->sps.profile_idc = GST_H264_PROFILE_MAIN; ++ } else if (g_str_equal (self->profile_name, "high")) { ++ self->sps.profile_idc = GST_H264_PROFILE_HIGH; ++ } + + self->sps.chroma_format_idc = 1; /* YUV 4:2:0 */ + +@@ -573,6 +593,43 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.entropy_coding_mode_flag = self->cabac; + } + ++static gchar * ++gst_v4l2_codec_h264_enc_decide_profile (GstV4l2CodecH264Enc * self) ++{ ++ GstCaps *allowed_caps = NULL; ++ const gchar *profile_name; ++ GstStructure *structure; ++ gchar *ret; ++ ++ g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", ++ &self->cabac_init_idc, NULL); ++ ++ /* First, check whether the downstream requires a specified profile. */ ++ allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (self)); ++ if (!allowed_caps) ++ allowed_caps = gst_pad_query_caps (GST_VIDEO_ENCODER_SRC_PAD (self), NULL); ++ ++ allowed_caps = gst_caps_make_writable (allowed_caps); ++ allowed_caps = gst_caps_fixate (allowed_caps); ++ structure = gst_caps_get_structure (allowed_caps, 0); ++ profile_name = gst_structure_get_string (structure, "profile"); ++ ++ if (self->cabac) { ++ if (!g_strstr_len (profile_name, -1, "main") ++ && !g_strstr_len (profile_name, -1, "high")) { ++ GST_WARNING_OBJECT (self, ++ "CABAC is not support by user selected profile '%s'" ++ ", disabling this features", profile_name); ++ self->cabac = FALSE; ++ } ++ } ++ ++ ret = g_strdup (profile_name); ++ gst_caps_unref (allowed_caps); ++ ++ return ret; ++} ++ + static gboolean + gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state) +@@ -615,9 +672,13 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + if (self->output_state) + gst_video_codec_state_unref (self->output_state); + ++ g_free (self->profile_name); ++ self->profile_name = gst_v4l2_codec_h264_enc_decide_profile (self); ++ + caps = gst_caps_new_simple ("video/x-h264", + "stream-format", G_TYPE_STRING, "byte-stream", +- "alignment", G_TYPE_STRING, "au", NULL); ++ "alignment", G_TYPE_STRING, "au", ++ "profile", G_TYPE_STRING, self->profile_name, NULL); + + self->output_state = + gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), +@@ -641,9 +702,6 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + gst_v4l2_codec_h264_enc_get_qp_range (self->encoder, &self->qp_min, + &self->qp_max); + +- g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", +- &self->cabac_init_idc, NULL); +- + gst_v4l2_codec_h264_enc_init_sps_pps (self, state); + + return TRUE; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0062-codecs-h264encoder-Fix-H.264-QP-max-value.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0062-codecs-h264encoder-Fix-H.264-QP-max-value.patch new file mode 100644 index 000000000..591e4c95b --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0062-codecs-h264encoder-Fix-H.264-QP-max-value.patch @@ -0,0 +1,27 @@ +From e3872d514987c3a7eda8e97d2daf6293db06a145 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 19:13:55 -0500 +Subject: [PATCH 62/68] codecs: h264encoder: Fix H.264 QP max value + +should be 51 as per spec. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +index 4c35956..032b57a 100644 +--- a/gst-libs/gst/codecs/gsth264encoder.c ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -33,7 +33,7 @@ GST_DEBUG_CATEGORY (gst_h264_encoder_debug); + + #define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 + +-#define H264_MAX_QUALITY 63 ++#define H264_MAX_QUALITY 51 + #define H264_MIN_QUALITY 0 + + #define H264_DEFAULT_BITRATE 100000 +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0063-v4l2codecs-h264dec-Remove-dead-and-broken-code.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0063-v4l2codecs-h264dec-Remove-dead-and-broken-code.patch new file mode 100644 index 000000000..4a4441a9f --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0063-v4l2codecs-h264dec-Remove-dead-and-broken-code.patch @@ -0,0 +1,142 @@ +From 88f6aeb90e1f3138eaa4f1ffd44d34b51dd43192 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Nov 2023 19:18:18 -0500 +Subject: [PATCH 63/68] v4l2codecs: h264dec: Remove dead and broken code + +Its not possible with the current uAPI to ask the driver for the QP +range. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 40 +++++----------------------- + sys/v4l2codecs/gstv4l2encoder.c | 25 ----------------- + 2 files changed, 7 insertions(+), 58 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index b1617c9..967fd4d 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -58,18 +58,6 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + "profile = (string) { main, constrained-baseline, baseline}") + ); + +- +-#define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 +- +-#define H264_MAX_QUALITY 63 +-#define H264_MIN_QUALITY 0 +- +-#define H264_DEFAULT_BITRATE 100000 +- +-/* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */ +-#define SX_BITRATE 6 +-/* Scale factor for cpb_size (HRD cpb_size_scale: min = 4) */ +-#define SX_CPB_SIZE 4 + /* Maximum sizes for common headers (in bits) */ + #define MAX_SPS_HDR_SIZE 16473 + #define MAX_VUI_PARAMS_SIZE 210 +@@ -77,8 +65,6 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + #define MAX_PPS_HDR_SIZE 101 + #define MAX_SLICE_HDR_SIZE 397 + 2572 + 6670 + 2402 + +-#define MAX_GOP_SIZE 1024 +- + #define SPS_SIZE 4 + GST_ROUND_UP_8 (MAX_SPS_HDR_SIZE + MAX_VUI_PARAMS_SIZE + \ + 2 * MAX_HRD_PARAMS_SIZE) / 8 + #define PPS_SIZE 4 + GST_ROUND_UP_8 (MAX_PPS_HDR_SIZE) / 8 +@@ -93,7 +79,6 @@ struct _GstV4l2CodecH264Enc + gint height; + gint width_in_macroblocks; + gint height_in_macroblocks; +- guint qp_max, qp_min; + guint64 targeted_bitrate; + gboolean cabac; + guint cabac_init_idc; +@@ -699,9 +684,6 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + } + +- gst_v4l2_codec_h264_enc_get_qp_range (self->encoder, &self->qp_min, +- &self->qp_max); +- + gst_v4l2_codec_h264_enc_init_sps_pps (self, state); + + return TRUE; +@@ -911,6 +893,7 @@ gst_v4l2_codec_h264_enc_fill_encode_params (GstH264Encoder * encoder, + self->encode_params.cabac_init_idc = self->cabac_init_idc; + + self->encode_params.pic_init_qp_minus26 = self->pps.pic_init_qp_minus26; ++ + self->encode_params.chroma_qp_index_offset = self->pps.chroma_qp_index_offset; + self->encode_params.disable_deblocking_filter_idc = 0; + } +@@ -922,21 +905,9 @@ gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); + + /* Rate Control */ +- self->encode_rc.qp = self->pps.pic_init_qp_minus26 + 26; //encoder->rc.qp; +- self->encode_rc.qp_min = self->qp_min; +- self->encode_rc.qp_max = self->qp_max; +-} +- +-static guint +-gst_v4l2_codec_h264_enc_check_qp_range (GstV4l2CodecH264Enc * self, +- GstH264Frame * h264_frame) +-{ +- if (h264_frame->quality > self->qp_max) +- return self->qp_max; +- if (h264_frame->quality < self->qp_min) +- return self->qp_min; +- +- return h264_frame->quality; ++ self->encode_rc.qp = h264_frame->quality; ++ self->encode_rc.qp_min = 0; ++ self->encode_rc.qp_max = 51; + } + + static GstFlowReturn +@@ -988,6 +959,9 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + goto done; + } + ++ GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", ++ h264_frame->quality); ++ + request = gst_v4l2_encoder_alloc_request (self->encoder, + frame->system_frame_number, frame->input_buffer, frame->output_buffer); + +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 85f0457..27f03e9 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -1520,28 +1520,3 @@ gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, + + return TRUE; + } +- +-gboolean +-gst_v4l2_codec_h264_enc_get_qp_range (GstV4l2Encoder * self, +- guint * qp_min, guint * qp_max) +-{ +- gint ret; +- struct v4l2_query_ext_ctrl control = { +- .id = V4L2_CID_STATELESS_H264_ENCODE_RC, +- }; +- +- *qp_max = 127; +- *qp_min = 0; +- +- ret = ioctl (self->video_fd, VIDIOC_QUERY_EXT_CTRL, &control); +- if (ret < 0) { +- GST_DEBUG_OBJECT (self, "unable to retrieve encoder qp, use default"); +- return FALSE; +- } +- +- *qp_max = control.maximum; +- *qp_min = control.minimum; +- GST_DEBUG_OBJECT (self, "encoder qp min %d qp max %d", *qp_min, *qp_max); +- +- return TRUE; +-} +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0064-codecs-Introduce-a-PID-based-rate-controller.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0064-codecs-Introduce-a-PID-based-rate-controller.patch new file mode 100644 index 000000000..51dd666cb --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0064-codecs-Introduce-a-PID-based-rate-controller.patch @@ -0,0 +1,355 @@ +From ab51f9d3d91b8210a4e8d5796eeaba21ccbda6d8 Mon Sep 17 00:00:00 2001 +From: Denis Shimizu +Date: Mon, 27 Nov 2023 11:25:54 -0500 +Subject: [PATCH 64/68] codecs: Introduce a PID based rate controller + +This is a simplistic rate controller using a PID and some boundaries. +This has only been tested for H.264 for now and may need some more work +to support various quantizer scale. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gstratecontroller.c | 241 ++++++++++++++++++++++++ + gst-libs/gst/codecs/gstratecontroller.h | 70 +++++++ + gst-libs/gst/codecs/meson.build | 1 + + 3 files changed, 312 insertions(+) + create mode 100644 gst-libs/gst/codecs/gstratecontroller.c + create mode 100644 gst-libs/gst/codecs/gstratecontroller.h + +diff --git a/gst-libs/gst/codecs/gstratecontroller.c b/gst-libs/gst/codecs/gstratecontroller.c +new file mode 100644 +index 0000000..42f7d0c +--- /dev/null ++++ b/gst-libs/gst/codecs/gstratecontroller.c +@@ -0,0 +1,241 @@ ++/* GStreamer ++ * Copyright (C) 2023 Denis Yuji Shimizu ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include "gstratecontroller.h" ++ ++#include ++ ++#define KP_CONST 0.0001 ++#define KI_CONST 0.0000000 ++#define KD_CONST 0.0000000 ++ ++typedef struct ++{ ++ gdouble prev_error; ++ gdouble integral; ++} PIDController; ++ ++struct _GstRateController ++{ ++ GstObject parent; ++ ++ PIDController pid; ++ gdouble qp; ++ gsize total_macroblocks; ++ GstClockTime frame_duration; ++ ++ /* configuration */ ++ gint max_qp; ++ gint min_qp; ++ gint init_qp; ++ gint qp_step; ++ GstRateControlMode mode; ++ gint bitrate; ++}; ++ ++GType ++gst_rate_control_mode_get_type (void) ++{ ++ const static GEnumValue rate_control_modes[] = { ++ {GST_RC_CONSTANT_QP, "cqp", "Constant QP"}, ++ {GST_RC_CONSTANT_BITRATE, "cbr", "Constant Bitrate"}, ++ {0, NULL, NULL}, ++ }; ++ static GType rc_mode_type = 0; ++ ++ if (g_once_init_enter (&rc_mode_type)) { ++ GType _type = g_enum_register_static ("GstRateControlMode", ++ rate_control_modes); ++ g_once_init_leave (&rc_mode_type, _type); ++ } ++ ++ return rc_mode_type; ++} ++ ++G_DEFINE_TYPE (GstRateController, gst_rc, G_TYPE_OBJECT); ++ ++static void ++gst_rc_init (GstRateController * self) ++{ ++} ++ ++static void ++gst_rc_class_init (GstRateControllerClass * klass) ++{ ++} ++ ++GstRateController * ++gst_rc_new (void) ++{ ++ return g_object_new (GST_RATE_CONTROLLER_TYPE, NULL); ++} ++ ++static gdouble ++pid_control (GstRateController * self, gdouble measured_value, ++ gdouble target_bitrate) ++{ ++ PIDController *pid = &self->pid; ++ gdouble error = target_bitrate - measured_value; ++ gdouble derivative = error - pid->prev_error; ++ pid->integral += error; ++ pid->prev_error = error; ++ ++ /* Output is inverted, since QP and bitrate are inversely related (higher QP ++ * means lower bitrate) */ ++ gdouble output = ++ 0.0 - (KP_CONST * error + ++ KI_CONST * pid->integral + KD_CONST * derivative); ++ ++ return output; ++} ++ ++void ++gst_rc_record (GstRateController * self, GstRcFrameType frame_type, ++ gsize coded_size, GstClockTime duration) ++{ ++ /* If there is no duration, use frame rate */ ++ if (!GST_CLOCK_TIME_IS_VALID (duration) || duration == 0) ++ duration = self->frame_duration; ++ ++ /* We simply ignore keyframe for now */ ++ if (frame_type != GST_RC_KEY_FRAME) { ++ GstClockTime bps = ++ gst_util_uint64_scale (coded_size * 8, GST_SECOND, duration); ++ ++ double target_bps_per_macroblock = ++ (gdouble) self->bitrate / self->total_macroblocks; ++ double bps_per_macroblock = ++ gst_guint64_to_gdouble (bps) / self->total_macroblocks; ++ ++ gdouble pid_output = ++ pid_control (self, bps_per_macroblock, target_bps_per_macroblock); ++ gdouble qp_increase = CLAMP (pid_output, ++ 0.0 - (gdouble) self->qp_step, (gdouble) self->qp_step); ++ self->qp += qp_increase; ++ } ++} ++ ++gint ++gst_rc_get_max_qp (GstRateController * self) ++{ ++ return self->max_qp; ++} ++ ++gint ++gst_rc_get_min_qp (GstRateController * self) ++{ ++ return self->min_qp; ++} ++ ++gint ++gst_rc_get_qp_step (GstRateController * self) ++{ ++ return self->qp_step; ++} ++ ++gint ++gst_rc_get_init_qp (GstRateController * self) ++{ ++ return self->init_qp; ++} ++ ++GstRateControlMode ++gst_rc_get_mode (GstRateController * self) ++{ ++ return self->mode; ++} ++ ++gint ++gst_rc_get_bitrate (GstRateController * self) ++{ ++ return self->bitrate; ++} ++ ++void ++gst_rc_set_format (GstRateController * self, const GstVideoInfo * vinfo) ++{ ++ gint width = GST_VIDEO_INFO_WIDTH (vinfo); ++ gint height = GST_VIDEO_INFO_HEIGHT (vinfo); ++ gint fps_n = GST_VIDEO_INFO_FPS_N (vinfo); ++ gint fps_d = GST_VIDEO_INFO_FPS_D (vinfo); ++ ++ gint width_in_macroblocks = (width + 15) / 16; ++ gint height_in_macroblocks = (height + 15) / 16; ++ ++ self->total_macroblocks = width_in_macroblocks * height_in_macroblocks; ++ self->pid.prev_error = 0; ++ self->pid.integral = 0; ++ self->qp = self->init_qp; ++ ++ if (!fps_n || !fps_d) { ++ fps_n = 30; ++ fps_d = 1; ++ } ++ ++ self->frame_duration = gst_util_uint64_scale (fps_d, GST_SECOND, fps_n); ++} ++ ++void ++gst_rc_set_max_qp (GstRateController * self, gint max_qp) ++{ ++ self->max_qp = max_qp; ++} ++ ++void ++gst_rc_set_min_qp (GstRateController * self, gint min_qp) ++{ ++ self->min_qp = min_qp; ++} ++ ++void ++gst_rc_set_qp_step (GstRateController * self, gint qp_step) ++{ ++ self->qp_step = qp_step; ++} ++ ++void ++gst_rc_set_init_qp (GstRateController * self, gint init_qp) ++{ ++ self->init_qp = self->qp = init_qp; ++} ++ ++void ++gst_rc_set_mode (GstRateController * self, GstRateControlMode mode) ++{ ++ self->mode = mode; ++} ++ ++void ++gst_rc_set_bitrate (GstRateController * self, gint bitrate) ++{ ++ self->bitrate = bitrate; ++} ++ ++gint ++gst_rc_get_qp (GstRateController * self) ++{ ++ switch (self->mode) { ++ case GST_RC_CONSTANT_BITRATE: ++ return CLAMP (round (self->qp), self->min_qp, self->max_qp); ++ case GST_RC_CONSTANT_QP: ++ return self->init_qp; ++ } ++ ++ g_assert_not_reached (); ++} +diff --git a/gst-libs/gst/codecs/gstratecontroller.h b/gst-libs/gst/codecs/gstratecontroller.h +new file mode 100644 +index 0000000..f71cb8e +--- /dev/null ++++ b/gst-libs/gst/codecs/gstratecontroller.h +@@ -0,0 +1,70 @@ ++/* GStreamer ++ * Copyright (C) 2023 Michael Grzeschik ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++typedef enum ++{ ++ GST_RC_CONSTANT_QP, ++ GST_RC_CONSTANT_BITRATE, ++} GstRateControlMode; ++ ++typedef enum ++{ ++ GST_RC_KEY_FRAME, ++ GST_RC_INTER_FRAME, ++} GstRcFrameType; ++ ++GType gst_rate_control_mode_get_type (void); ++ ++#define GST_RATE_CONTROLLER_TYPE (gst_rc_get_type()) ++G_DECLARE_FINAL_TYPE(GstRateController, gst_rc, ++ GST, RC, GObject); ++ ++GstRateController* gst_rc_new (void); ++ ++void gst_rc_record (GstRateController * self, ++ GstRcFrameType frame_type, ++ gsize coded_size, ++ GstClockTime duration); ++ ++gint gst_rc_get_max_qp (GstRateController *self); ++gint gst_rc_get_min_qp (GstRateController *self); ++gint gst_rc_get_qp_step (GstRateController *self); ++gint gst_rc_get_init_qp (GstRateController *self); ++GstRateControlMode gst_rc_get_mode (GstRateController *self); ++gint gst_rc_get_bitrate (GstRateController *self); ++ ++void gst_rc_set_format (GstRateController *self, ++ const GstVideoInfo * vinfo); ++void gst_rc_set_max_qp (GstRateController *self, gint max_qp); ++void gst_rc_set_min_qp (GstRateController *self, gint min_qp); ++void gst_rc_set_qp_step (GstRateController *self, gint qp_step); ++void gst_rc_set_init_qp (GstRateController *self, gint init_qp); ++void gst_rc_set_mode (GstRateController *self, GstRateControlMode mode); ++void gst_rc_set_bitrate (GstRateController *self, gint bitrate); ++ ++gint gst_rc_get_qp(GstRateController *self); ++ ++G_END_DECLS +diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build +index 3ea3bdc..c04600e 100644 +--- a/gst-libs/gst/codecs/meson.build ++++ b/gst-libs/gst/codecs/meson.build +@@ -16,6 +16,7 @@ codecs_sources = files( + 'gstvp8frame.c', + 'gsth264encoder.c', + 'gsth264frame.c', ++ 'gstratecontroller.c', + ) + + codecs_headers = files( +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0065-codecs-h264encoder-Use-the-PID-base-rate-controller.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0065-codecs-h264encoder-Use-the-PID-base-rate-controller.patch new file mode 100644 index 000000000..e10e79b5b --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0065-codecs-h264encoder-Use-the-PID-base-rate-controller.patch @@ -0,0 +1,578 @@ +From 8455dc4d016d32559a5da33e6b03d860e33c4800 Mon Sep 17 00:00:00 2001 +From: Denis Shimizu +Date: Wed, 15 Nov 2023 16:59:14 -0300 +Subject: [PATCH 65/68] codecs: h264encoder: Use the PID base rate controller + +This port the h264encoder to use the generic rate controller and adapt +the v4l2codecs accordingly. +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 256 +++++++++++++-------------- + gst-libs/gst/codecs/gsth264frame.h | 2 +- + sys/v4l2codecs/gstv4l2codech264enc.c | 27 +-- + 3 files changed, 143 insertions(+), 142 deletions(-) + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +index 032b57a..f1b0200 100644 +--- a/gst-libs/gst/codecs/gsth264encoder.c ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -1,5 +1,6 @@ + /* GStreamer + * Copyright (C) 2023 Michael Grzeschik ++ * Copyright (C) 2023 Denis Shimizu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public +@@ -22,47 +23,44 @@ + #endif + + #include "gsth264encoder.h" +- ++#include "gstratecontroller.h" + #include +-#include + #include +-#include + + GST_DEBUG_CATEGORY (gst_h264_encoder_debug); + #define GST_CAT_DEFAULT gst_h264_encoder_debug + +-#define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 +- +-#define H264_MAX_QUALITY 51 +-#define H264_MIN_QUALITY 0 ++#define H264_MAX_QP 51 ++#define H264_MIN_QP 0 + +-#define H264_DEFAULT_BITRATE 100000 +- +-#define H264ENC_DEFAULT_CABAC_IDC 0 ++#define DEFAULT_KEYFRAME_INTERVAL 30 ++#define DEFAULT_MAX_QP 51 ++#define DEFAULT_MIN_QP 10 ++#define DEFAULT_QP_STEP 4 ++#define DEFAULT_QUANTIZER 18 ++#define DEFAULT_BITRATE G_MAXUINT + + enum + { + PROP_0, + PROP_KEYFRAME_INTERVAL, +- PROP_MAX_QUALITY, +- PROP_MIN_QUALITY, ++ PROP_MAX_QP, ++ PROP_MIN_QP, ++ PROP_QP_STEP, ++ PROP_QUANTIZER, + PROP_BITRATE, + PROP_CABAC, + PROP_CABAC_INIT_IDC, ++ PROP_RATE_CONTROL, + }; + + struct _GstH264EncoderPrivate + { +- gint keyframe_interval; +- + guint32 last_keyframe; ++ GstRateController *rate_controller; + +- guint64 targeted_bitrate; +- gint max_quality; +- gint min_quality; +- gint current_quality; +- guint64 used_bytes; +- guint64 nb_frames; ++ /* properties */ ++ gint keyframe_interval; + gboolean cabac; + guint cabac_init_idc; + }; +@@ -78,25 +76,22 @@ static void + gst_h264_encoder_init (GstH264Encoder * self) + { + self->priv = gst_h264_encoder_get_instance_private (self); ++ self->priv->rate_controller = gst_rc_new (); + } + + static void + gst_h264_encoder_finalize (GObject * object) + { ++ GstH264Encoder *self = GST_H264_ENCODER (object); ++ ++ gst_object_unref (self->priv->rate_controller); ++ + G_OBJECT_CLASS (parent_class)->finalize (object); + } + + static gboolean + gst_h264_encoder_start (GstVideoEncoder * encoder) + { +- GstH264Encoder *self = GST_H264_ENCODER (encoder); +- GstH264EncoderPrivate *priv = self->priv; +- +- priv->last_keyframe = 0; +- priv->current_quality = priv->min_quality; +- priv->used_bytes = 0; +- priv->nb_frames = 0; +- + return TRUE; + } + +@@ -110,46 +105,12 @@ static gboolean + gst_h264_encoder_set_format (GstVideoEncoder * encoder, + GstVideoCodecState * state) + { +- return TRUE; +-} +- +-static GstFlowReturn +-gst_h264_encoder_set_quality (GstH264Encoder * self, GstH264Frame * h264_frame) +-{ ++ GstH264Encoder *self = GST_H264_ENCODER (encoder); + GstH264EncoderPrivate *priv = self->priv; +- GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self); +- GstVideoCodecState *output_state = +- gst_video_encoder_get_output_state (encoder); +- gint qp = priv->current_quality; +- guint64 bitrate = 0; +- guint fps_n = 30, fps_d = 1; +- +- if (output_state == NULL) +- return qp; +- +- if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) { +- fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info); +- fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info); +- } +- gst_video_codec_state_unref (output_state); +- +- bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d); +- if (bitrate > priv->targeted_bitrate) { +- qp++; +- } +- +- if (bitrate < priv->targeted_bitrate) { +- qp--; +- } + +- if (qp > priv->max_quality) +- qp = priv->max_quality; +- if (qp < priv->min_quality) +- qp = priv->min_quality; ++ gst_rc_set_format (priv->rate_controller, &state->info); + +- h264_frame->quality = qp; +- +- return GST_FLOW_OK; ++ return TRUE; + } + + static GstFlowReturn +@@ -183,16 +144,19 @@ gst_h264_encoder_mark_frame (GstH264Encoder * self, GstH264Frame * h264_frame) + { + GstVideoCodecFrame *frame = h264_frame->frame; + GstH264EncoderPrivate *priv = self->priv; ++ GstRcFrameType rc_frame_type = GST_RC_INTER_FRAME; + + switch (h264_frame->type) { + case GstH264Keyframe: + priv->last_keyframe = frame->system_frame_number; ++ rc_frame_type = GST_RC_KEY_FRAME; ++ break; ++ default: + break; + } + +- priv->current_quality = h264_frame->quality; +- priv->used_bytes += gst_buffer_get_size (frame->output_buffer); +- priv->nb_frames++; ++ gst_rc_record (priv->rate_controller, rc_frame_type, ++ gst_buffer_get_size (frame->output_buffer), frame->duration); + } + + static GstFlowReturn +@@ -200,6 +164,7 @@ gst_h264_encoder_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame) + { + GstH264Encoder *self = GST_H264_ENCODER (encoder); ++ GstH264EncoderPrivate *priv = self->priv; + GstH264EncoderClass *klass = GST_H264_ENCODER_GET_CLASS (self); + GstFlowReturn ret = GST_FLOW_OK; + GstH264Frame *h264_frame = gst_h264_frame_new (frame); +@@ -208,12 +173,9 @@ gst_h264_encoder_handle_frame (GstVideoEncoder * encoder, + if (ret != GST_FLOW_OK) + return ret; + +- ret = gst_h264_encoder_set_quality (self, h264_frame); +- if (ret != GST_FLOW_OK) +- return ret; ++ h264_frame->qp = gst_rc_get_qp (priv->rate_controller); + +- /* TODO: add encoding parameters management here +- * for now just send the frame to encode */ ++ /* Send the frame to encode */ + if (klass->encode_frame) { + ret = klass->encode_frame (self, h264_frame); + if (ret == GST_FLOW_OK) +@@ -232,41 +194,42 @@ gst_h264_encoder_get_property (GObject * object, guint property_id, + GstH264Encoder *self = GST_H264_ENCODER (object); + GstH264EncoderPrivate *priv = self->priv; + ++ GST_OBJECT_LOCK (self); ++ + switch (property_id) { + case PROP_KEYFRAME_INTERVAL: +- GST_OBJECT_LOCK (self); + g_value_set_int (value, priv->keyframe_interval); +- GST_OBJECT_UNLOCK (self); + break; +- case PROP_MAX_QUALITY: +- GST_OBJECT_LOCK (self); +- g_value_set_int (value, priv->max_quality); +- GST_OBJECT_UNLOCK (self); ++ case PROP_MAX_QP: ++ g_value_set_int (value, gst_rc_get_max_qp (priv->rate_controller)); ++ break; ++ case PROP_MIN_QP: ++ g_value_set_int (value, gst_rc_get_min_qp (priv->rate_controller)); + break; +- case PROP_MIN_QUALITY: +- GST_OBJECT_LOCK (self); +- g_value_set_int (value, priv->min_quality); +- GST_OBJECT_UNLOCK (self); ++ case PROP_QP_STEP: ++ g_value_set_int (value, gst_rc_get_qp_step (priv->rate_controller)); ++ break; ++ case PROP_QUANTIZER: ++ g_value_set_int (value, gst_rc_get_init_qp (priv->rate_controller)); + break; + case PROP_BITRATE: +- GST_OBJECT_LOCK (self); +- g_value_set_uint64 (value, priv->targeted_bitrate); +- GST_OBJECT_UNLOCK (self); ++ g_value_set_uint (value, gst_rc_get_bitrate (priv->rate_controller)); ++ break; ++ case PROP_RATE_CONTROL: ++ g_value_set_enum (value, gst_rc_get_mode (priv->rate_controller)); + break; + case PROP_CABAC: +- GST_OBJECT_LOCK (self); + g_value_set_boolean (value, priv->cabac); +- GST_OBJECT_UNLOCK (self); + break; + case PROP_CABAC_INIT_IDC: +- GST_OBJECT_LOCK (self); + g_value_set_uint (value, priv->cabac_init_idc); +- GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } ++ ++ GST_OBJECT_UNLOCK (self); + } + + static void +@@ -276,41 +239,42 @@ gst_h264_encoder_set_property (GObject * object, guint property_id, + GstH264Encoder *self = GST_H264_ENCODER (object); + GstH264EncoderPrivate *priv = self->priv; + ++ GST_OBJECT_LOCK (self); ++ + switch (property_id) { + case PROP_KEYFRAME_INTERVAL: +- GST_OBJECT_LOCK (self); + priv->keyframe_interval = g_value_get_int (value); +- GST_OBJECT_UNLOCK (self); + break; +- case PROP_MAX_QUALITY: +- GST_OBJECT_LOCK (self); +- priv->max_quality = g_value_get_int (value); +- GST_OBJECT_UNLOCK (self); ++ case PROP_MAX_QP: ++ gst_rc_set_max_qp (priv->rate_controller, g_value_get_int (value)); ++ break; ++ case PROP_MIN_QP: ++ gst_rc_set_min_qp (priv->rate_controller, g_value_get_int (value)); ++ break; ++ case PROP_QP_STEP: ++ gst_rc_set_qp_step (priv->rate_controller, g_value_get_int (value)); + break; +- case PROP_MIN_QUALITY: +- GST_OBJECT_LOCK (self); +- priv->min_quality = g_value_get_int (value); +- GST_OBJECT_UNLOCK (self); ++ case PROP_QUANTIZER: ++ gst_rc_set_init_qp (priv->rate_controller, g_value_get_int (value)); + break; + case PROP_BITRATE: +- GST_OBJECT_LOCK (self); +- priv->targeted_bitrate = g_value_get_uint64 (value); +- GST_OBJECT_UNLOCK (self); ++ gst_rc_set_bitrate (priv->rate_controller, g_value_get_uint (value)); ++ break; ++ case PROP_RATE_CONTROL: ++ gst_rc_set_mode (priv->rate_controller, g_value_get_enum (value)); + break; + case PROP_CABAC: +- GST_OBJECT_LOCK (self); + priv->cabac = g_value_get_boolean (value); +- GST_OBJECT_UNLOCK (self); + break; + case PROP_CABAC_INIT_IDC: +- GST_OBJECT_LOCK (self); + priv->cabac_init_idc = g_value_get_uint (value); +- GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } ++ ++ GST_OBJECT_UNLOCK (self); + } + + static void +@@ -332,69 +296,99 @@ gst_h264_encoder_class_init (GstH264EncoderClass * klass) + /** + * GstH264Encoder:keyframe-interval: + * +- * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL, + g_param_spec_int ("keyframe-interval", "Keyframe Interval", +- "Interval between keyframes", +- 0, G_MAXINT, H264ENC_DEFAULT_KEYFRAME_INTERVAL, ++ "Maximum distance in frames between IDR.", ++ 0, G_MAXINT, DEFAULT_KEYFRAME_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** +- * GstH264Encoder:max-quality: +- * ++ * GstH264Encoder:qp-max: + * + * Since: 1.2x + */ +- g_object_class_install_property (object_class, PROP_MAX_QUALITY, +- g_param_spec_int ("max-quality", "Max Quality Level", +- "Set upper quality limit (lower number equates to higher quality but more bits)", +- H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MAX_QUALITY, ++ g_object_class_install_property (object_class, PROP_MAX_QP, ++ g_param_spec_int ("qp-max", "Max Quantizer Level", ++ "Set upper qp limit (lower number equates to higher quality but more bits)", ++ H264_MIN_QP, H264_MAX_QP, DEFAULT_MAX_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** +- * GstH264Encoder:min-quality: ++ * GstH264Encoder:qp-min: + * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_MIN_QP, ++ g_param_spec_int ("qp-min", "Min Quantizer Level", ++ "Set lower qp limit (lower number equates to higher quality but more bits)", ++ H264_MIN_QP, H264_MAX_QP, DEFAULT_MIN_QP, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:qp-step: ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_QP_STEP, ++ g_param_spec_int ("qp-step", "Max QP increase/decrease step", ++ "Set maximum value which qp value can be increase/decrease by the bitrate controller (Valid only with rate-control=cbr)", ++ H264_MIN_QP, H264_MAX_QP, DEFAULT_QP_STEP, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:quantizer: + * + * Since: 1.2x + */ +- g_object_class_install_property (object_class, PROP_MIN_QUALITY, +- g_param_spec_int ("min-quality", "Min Quality Level", +- "Set lower quality limit (lower number equates to higher quality but more bits)", +- H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MIN_QUALITY, ++ g_object_class_install_property (object_class, PROP_QUANTIZER, ++ g_param_spec_int ("quantizer", "Quantizer Level", ++ "Set the qp value (lower number equates to higher quality but more bits, initial value for rate-control=cbr)", ++ H264_MIN_QP, H264_MAX_QP, DEFAULT_QUANTIZER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** + * GstH264Encoder:bitrate: + * +- * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_BITRATE, +- g_param_spec_uint64 ("bitrate", "Targeted bitrate", +- "Set bitrate target", +- 0, UINT_MAX, H264_DEFAULT_BITRATE, ++ g_param_spec_uint ("bitrate", "Targeted bitrate", ++ "Set the targeted bitrate (in bit/s)", ++ 0, UINT_MAX, DEFAULT_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** + * GstH264Encoder:cabac: +- * ++ * Note: Supported only on main profile + * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_CABAC, +- g_param_spec_boolean ("cabac", "CABAC", "Enable use of CABAC over CAVLC", +- FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ g_param_spec_boolean ("cabac", "CABAC", ++ "Enable Cabac", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstH264Encoder:cabac-init-idc: +- * ++ * Note: Supported only on main profile + * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_CABAC_INIT_IDC, +- g_param_spec_uint ("cabac-init-idc", "Initial CABAC table ID", +- "Set initial CABAC table idc value", 0, 2, H264ENC_DEFAULT_CABAC_IDC, ++ g_param_spec_uint ("cabac-init-idc", "PROP_CABAC_INIT_IDC", ++ "Set Cabac init idc value", ++ 0, 2, 0, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GstH264Encoder:rate-control: ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_RATE_CONTROL, ++ g_param_spec_enum ("rate-control", "Rate Control Mode", ++ "Select rate control mode", gst_rate_control_mode_get_type (), ++ GST_RC_CONSTANT_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + } +diff --git a/gst-libs/gst/codecs/gsth264frame.h b/gst-libs/gst/codecs/gsth264frame.h +index 3c90d53..3a5a658 100644 +--- a/gst-libs/gst/codecs/gsth264frame.h ++++ b/gst-libs/gst/codecs/gsth264frame.h +@@ -42,7 +42,7 @@ struct _GstH264Frame + { + GstMiniObject parent; + gint type; +- gint quality; ++ gint qp; + + GstVideoCodecFrame *frame; + }; +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 967fd4d..7d52fba 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -1,5 +1,6 @@ + /* GStreamer + * Copyright (C) 2023 Michael Grzeschik ++ * Copyright (C) 2023 Denis Shimizu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public +@@ -20,7 +21,6 @@ + #ifdef HAVE_CONFIG_H + #include + #endif +- + #include "gstv4l2codecallocator.h" + #include "gstv4l2codech264enc.h" + #include "gstv4l2codecpool.h" +@@ -79,6 +79,7 @@ struct _GstV4l2CodecH264Enc + gint height; + gint width_in_macroblocks; + gint height_in_macroblocks; ++ gint qp_init, qp_max, qp_min; + guint64 targeted_bitrate; + gboolean cabac; + guint cabac_init_idc; +@@ -477,7 +478,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + gint n_levels = G_N_ELEMENTS (_h264_level_limits); + GValue bitrate = G_VALUE_INIT; + g_object_get_property (G_OBJECT (self), "bitrate", &bitrate); +- self->targeted_bitrate = g_value_get_uint64 (&bitrate); ++ self->targeted_bitrate = g_value_get_uint (&bitrate); + maximum_bitrate = self->targeted_bitrate; + frame_size_in_macroblocks = self->width * self->height / 256; + macroblocks_per_second = frame_size_in_macroblocks * state->info.fps_n / state->info.fps_d; // Assuming each macroblock is 16x16 +@@ -571,8 +572,11 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.weighted_bipred_idc = 0; + + /* Rate Control */ ++ GValue qp_init = G_VALUE_INIT; ++ g_object_get_property (G_OBJECT (self), "quantizer", &qp_init); ++ self->qp_init = g_value_get_int (&qp_init); + self->pps.chroma_qp_index_offset = 4; +- self->pps.pic_init_qp_minus26 = -13; ++ self->pps.pic_init_qp_minus26 = self->qp_init - 26; + self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; + self->pps.deblocking_filter_control_present_flag = 1; + self->pps.entropy_coding_mode_flag = self->cabac; +@@ -649,6 +653,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + + self->width = state->info.width; + self->height = state->info.height; ++ GST_VIDEO_ENCODER_CLASS (parent_class)->set_format (encoder, state); ++ + gst_v4l2_codec_h264_enc_buffers_allocation (encoder); + self->width_in_macroblocks = (self->width + 15) / 16; + self->height_in_macroblocks = (self->height + 15) / 16; +@@ -684,6 +690,9 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + } + ++ g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", ++ &self->cabac_init_idc, NULL); ++ + gst_v4l2_codec_h264_enc_init_sps_pps (self, state); + + return TRUE; +@@ -905,9 +914,9 @@ gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); + + /* Rate Control */ +- self->encode_rc.qp = h264_frame->quality; +- self->encode_rc.qp_min = 0; +- self->encode_rc.qp_max = 51; ++ self->encode_rc.qp = h264_frame->qp; ++ self->encode_rc.qp_min = self->qp_min; ++ self->encode_rc.qp_max = self->qp_max; + } + + static GstFlowReturn +@@ -950,8 +959,7 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + /* *INDENT-ON* */ + }; + +- GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", +- h264_frame->quality); ++ GST_DEBUG_OBJECT (self, "encode h264 frame with qp = %d", h264_frame->qp); + + if (!gst_v4l2_codec_h264_enc_ensure_output_bitstream (self, frame)) { + GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, +@@ -959,8 +967,7 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, + goto done; + } + +- GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", +- h264_frame->quality); ++ GST_DEBUG_OBJECT (self, "encode h264 frame with qp = %d", h264_frame->qp); + + request = gst_v4l2_encoder_alloc_request (self->encoder, + frame->system_frame_number, frame->input_buffer, frame->output_buffer); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0066-v4l2codecs-h264enc-Rewrite-profile-level-negotiation.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0066-v4l2codecs-h264enc-Rewrite-profile-level-negotiation.patch new file mode 100644 index 000000000..06f4cc194 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0066-v4l2codecs-h264enc-Rewrite-profile-level-negotiation.patch @@ -0,0 +1,286 @@ +From 5d051f38c95bb6f2f72588d55696a14939bc5fb5 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Thu, 30 Nov 2023 10:49:57 -0500 +Subject: [PATCH 66/68] v4l2codecs: h264enc: Rewrite profile/level negotiation + +This aligns the SPS/PPS with caps and properly negotiate the level. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 213 ++++++++++++++++----------- + 1 file changed, 124 insertions(+), 89 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 7d52fba..911ed34 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -80,10 +80,11 @@ struct _GstV4l2CodecH264Enc + gint width_in_macroblocks; + gint height_in_macroblocks; + gint qp_init, qp_max, qp_min; +- guint64 targeted_bitrate; + gboolean cabac; + guint cabac_init_idc; ++ + gchar *profile_name; ++ guint level_idc; + + GstV4l2CodecAllocator *sink_allocator; + GstV4l2CodecAllocator *src_allocator; +@@ -387,56 +388,10 @@ get_sps_aspect_ratio_idc (guint par_n, guint par_d) + return 255; // Extended_SAR for custom ratios + } + +-/* Begin of code taken from VA plugin */ +-typedef struct +-{ +- const char *level; +- int idc; +- int max_macroblocks_per_second; +- int max_frame_size_in_macroblocks; +- int max_dpb_macroblocks; +- int maximum_bitrate; +- int max_cpb_size; +- int minimum_compression_ratio; +-} GstVaH264LevelLimits; +- +-static const GstVaH264LevelLimits _h264_level_limits[] = { +- /* *INDENT-OFF* */ +- /*level, idc, max_macroblocks_per_second, max_frame_size_in_macroblocks, +- max_dpb_macroblocks, max_bitrate, max_cpb_size, minimum_compression_ratio */ +- {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2}, +- {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2}, +- {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2}, +- {"1.2", GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384, 1000, 2}, +- {"1.3", GST_H264_LEVEL_L1_3, 11880, 396, 2376, 768, 2000, 2}, +- {"2", GST_H264_LEVEL_L2, 11880, 396, 2376, 2000, 2000, 2}, +- {"2.1", GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000, 4000, 2}, +- {"2.2", GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000, 4000, 2}, +- {"3", GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000, 10000, 2}, +- {"3.1", GST_H264_LEVEL_L3_1, 108000, 3600, 18000, 14000, 14000, 4}, +- {"3.2", GST_H264_LEVEL_L3_2, 216000, 5120, 20480, 20000, 20000, 4}, +- {"4", GST_H264_LEVEL_L4, 245760, 8192, 32768, 20000, 25000, 4}, +- {"4.1", GST_H264_LEVEL_L4_1, 245760, 8192, 32768, 50000, 62500, 2}, +- {"4.2", GST_H264_LEVEL_L4_2, 522240, 8704, 34816, 50000, 62500, 2}, +- {"5", GST_H264_LEVEL_L5, 589824, 22080, 110400, 135000, 135000, 2}, +- {"5.1", GST_H264_LEVEL_L5_1, 983040, 36864, 184320, 240000, 240000, 2}, +- {"5.2", GST_H264_LEVEL_L5_2, 2073600, 36864, 184320, 240000, 240000, 2}, +- {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2}, +- {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2}, +- {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2}, +- /* *INDENT-ON* */ +-}; +- +-/* Enf of code taken from VA Plugin */ +- + static void + gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + GstVideoCodecState * state) + { +- gint maximum_bitrate, frame_size_in_macroblocks, +- macroblocks_per_second, dpb_macroblocks, cpb_size, compression_ratio, +- minimum_level_index; +- + memset (&self->sps, 0, sizeof (self->sps)); + memset (&self->sps, 0, sizeof (self->pps)); + +@@ -474,41 +429,10 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->sps.direct_8x8_inference_flag = 1; + self->sps.frame_mbs_only_flag = 1; + +- // Calculate lowest acceptable level +- gint n_levels = G_N_ELEMENTS (_h264_level_limits); +- GValue bitrate = G_VALUE_INIT; +- g_object_get_property (G_OBJECT (self), "bitrate", &bitrate); +- self->targeted_bitrate = g_value_get_uint (&bitrate); +- maximum_bitrate = self->targeted_bitrate; +- frame_size_in_macroblocks = self->width * self->height / 256; +- macroblocks_per_second = frame_size_in_macroblocks * state->info.fps_n / state->info.fps_d; // Assuming each macroblock is 16x16 +- dpb_macroblocks = frame_size_in_macroblocks; +- cpb_size = maximum_bitrate; +- compression_ratio = state->info.fps_n / state->info.fps_d; +- minimum_level_index = 0; +- for (; minimum_level_index < n_levels; ++minimum_level_index) { +- const GstVaH264LevelLimits *level = +- &_h264_level_limits[minimum_level_index]; +- if (macroblocks_per_second <= level->max_macroblocks_per_second +- && frame_size_in_macroblocks <= level->max_frame_size_in_macroblocks +- && dpb_macroblocks <= level->max_dpb_macroblocks +- && maximum_bitrate <= level->maximum_bitrate +- && cpb_size <= level->max_cpb_size +- && compression_ratio >= level->minimum_compression_ratio) { +- break; +- } +- } +- self->sps.level_idc = _h264_level_limits[minimum_level_index].idc; +- if (self->sps.level_idc == GST_H264_LEVEL_L1B) { ++ /* Add level specific constraint */ ++ self->sps.level_idc = self->level_idc; ++ if (self->sps.level_idc == GST_H264_LEVEL_L1B) + self->sps.constraint_set3_flag = 1; +- } +- GST_DEBUG_OBJECT (self, "width:%d height:%d targeted_bitrate:%ld", +- self->height, self->height, self->targeted_bitrate); +- GST_DEBUG_OBJECT (self, +- "macroblocks_per_second=%d,frame_size_in_macroblocks=%d,dpb_macroblocks=%d,maximum_bitrate=%d,cpb_size=%d,compression_ratio=%d", +- macroblocks_per_second, frame_size_in_macroblocks, dpb_macroblocks, +- maximum_bitrate, cpb_size, compression_ratio); +- GST_DEBUG_OBJECT (self, "level_idc: %d", self->sps.level_idc); + + /* Crop unaligned videos */ + if (self->width & 15 || self->height & 15) { +@@ -582,13 +506,57 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.entropy_coding_mode_flag = self->cabac; + } + +-static gchar * +-gst_v4l2_codec_h264_enc_decide_profile (GstV4l2CodecH264Enc * self) ++/* Begin of code taken from VA plugin */ ++typedef struct ++{ ++ const char *level; ++ int idc; ++ int max_mbs_per_second; ++ int max_frame_size_in_mbs; ++ int max_dpb_mbs; ++ int max_bitrate; ++ int max_cpb_size; ++ int minimum_compression_ratio; ++} GstH264LevelLimits; ++ ++static const GstH264LevelLimits _h264_level_limits[] = { ++ /* *INDENT-OFF* */ ++ // level, idc, mbs, frame, dpb, bitrate, cpb, cr ++ {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2}, ++ {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2}, ++ {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2}, ++ {"1.2", GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384, 1000, 2}, ++ {"1.3", GST_H264_LEVEL_L1_3, 11880, 396, 2376, 768, 2000, 2}, ++ {"2", GST_H264_LEVEL_L2, 11880, 396, 2376, 2000, 2000, 2}, ++ {"2.1", GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000, 4000, 2}, ++ {"2.2", GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000, 4000, 2}, ++ {"3", GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000, 10000, 2}, ++ {"3.1", GST_H264_LEVEL_L3_1, 108000, 3600, 18000, 14000, 14000, 4}, ++ {"3.2", GST_H264_LEVEL_L3_2, 216000, 5120, 20480, 20000, 20000, 4}, ++ {"4", GST_H264_LEVEL_L4, 245760, 8192, 32768, 20000, 25000, 4}, ++ {"4.1", GST_H264_LEVEL_L4_1, 245760, 8192, 32768, 50000, 62500, 2}, ++ {"4.2", GST_H264_LEVEL_L4_2, 522240, 8704, 34816, 50000, 62500, 2}, ++ {"5", GST_H264_LEVEL_L5, 589824, 22080, 110400, 135000, 135000, 2}, ++ {"5.1", GST_H264_LEVEL_L5_1, 983040, 36864, 184320, 240000, 240000, 2}, ++ {"5.2", GST_H264_LEVEL_L5_2, 2073600, 36864, 184320, 240000, 240000, 2}, ++ {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2}, ++ {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2}, ++ {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2}, ++ /* *INDENT-ON* */ ++}; ++ ++/* Enf of code taken from VA Plugin */ ++ ++static gboolean ++gst_v4l2_codec_h264_enc_decide_profile_and_level (GstV4l2CodecH264Enc * self, ++ GstVideoCodecState * state) + { + GstCaps *allowed_caps = NULL; + const gchar *profile_name; ++ const gchar *level_name; + GstStructure *structure; +- gchar *ret; ++ gint minimum_level_index, caps_level_index; ++ guint bitrate, frame_size_in_mbs, mbs_per_second, dpb_mbs; + + g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", + &self->cabac_init_idc, NULL); +@@ -613,10 +581,77 @@ gst_v4l2_codec_h264_enc_decide_profile (GstV4l2CodecH264Enc * self) + } + } + +- ret = g_strdup (profile_name); +- gst_caps_unref (allowed_caps); ++ g_free (self->profile_name); ++ self->profile_name = g_strdup (profile_name); + +- return ret; ++ /* Calculate lowest acceptable level */ ++ g_object_get (self, "bitrate", &bitrate, NULL); ++ if (bitrate == G_MAXUINT) ++ bitrate = 0; ++ ++ /* table is in Kb/s, translate that number */ ++ bitrate /= 1000; ++ ++ frame_size_in_mbs = self->width_in_macroblocks * self->height_in_macroblocks; ++ mbs_per_second = frame_size_in_mbs * state->info.fps_n / state->info.fps_d; ++ ++ /* Only one reference is supported at the moment */ ++ dpb_mbs = frame_size_in_mbs; ++ ++ /* Ignoring CPB as we don't implement HRD */ ++ ++ for (minimum_level_index = 0; ++ minimum_level_index < G_N_ELEMENTS (_h264_level_limits); ++ minimum_level_index++) { ++ const GstH264LevelLimits *level = &_h264_level_limits[minimum_level_index]; ++ if (mbs_per_second <= level->max_mbs_per_second ++ && frame_size_in_mbs <= level->max_frame_size_in_mbs ++ && dpb_mbs <= level->max_dpb_mbs && bitrate <= level->max_bitrate) { ++ break; ++ } ++ } ++ ++ if (minimum_level_index >= G_N_ELEMENTS (_h264_level_limits)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported H.264 level."), ++ ("The minimum level required for this stream is not supported.")); ++ gst_caps_unref (allowed_caps); ++ return FALSE; ++ } ++ ++ level_name = gst_structure_get_string (structure, "level"); ++ caps_level_index = 0; ++ if (level_name) { ++ for (; caps_level_index < G_N_ELEMENTS (_h264_level_limits); ++ caps_level_index++) { ++ const GstH264LevelLimits *level = &_h264_level_limits[caps_level_index]; ++ if (g_str_equal (level->level, level_name)) ++ break; ++ } ++ ++ if (caps_level_index >= G_N_ELEMENTS (_h264_level_limits)) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ++ ("Unsupported H.264 level '%s'.", level_name), ++ ("The H.264 level provided in caps is not supported.")); ++ gst_caps_unref (allowed_caps); ++ return FALSE; ++ } ++ ++ if (caps_level_index < minimum_level_index) { ++ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ++ ("H.264 level '%s' too low. At least '%s' is needed.", level_name, ++ _h264_level_limits[minimum_level_index].level), ++ ("The H.264 level provided in caps is too low.")); ++ gst_caps_unref (allowed_caps); ++ return FALSE; ++ } ++ ++ self->level_idc = _h264_level_limits[caps_level_index].idc; ++ } else { ++ self->level_idc = _h264_level_limits[minimum_level_index].idc; ++ } ++ ++ gst_caps_unref (allowed_caps); ++ return TRUE; + } + + static gboolean +@@ -663,8 +698,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + if (self->output_state) + gst_video_codec_state_unref (self->output_state); + +- g_free (self->profile_name); +- self->profile_name = gst_v4l2_codec_h264_enc_decide_profile (self); ++ if (!gst_v4l2_codec_h264_enc_decide_profile_and_level (self, state)) ++ return FALSE; + + caps = gst_caps_new_simple ("video/x-h264", + "stream-format", G_TYPE_STRING, "byte-stream", +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0067-v4l2codecs-h264enc-De-emulate-SPS-and-PPS-bitstream.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0067-v4l2codecs-h264enc-De-emulate-SPS-and-PPS-bitstream.patch new file mode 100644 index 000000000..0aeb598ba --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0067-v4l2codecs-h264enc-De-emulate-SPS-and-PPS-bitstream.patch @@ -0,0 +1,86 @@ +From 3e690d2af6788e94fad1d92e04bd08da14035c5a Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Tue, 28 Nov 2023 12:28:32 -0500 +Subject: [PATCH 67/68] v4l2codecs: h264enc: De-emulate SPS and PPS bitstream + +The bit writer does not do de-emulation since its only purpose is to +serve bits to VA for which drivers are mandated to take care of that. +Implement minimal de-emulation method using memmove in a post-processing +fashion. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 37 ++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 911ed34..0a195c8 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -26,6 +26,7 @@ + #include "gstv4l2codecpool.h" + #include "gstv4l2format.h" + #include ++#include + + #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +@@ -310,6 +311,38 @@ gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) + return TRUE; + } + ++static guint ++gst_v4l2_codec_deemulate_nal (guint8 * data, guint size, guint maxsize) ++{ ++ GstByteReader br; ++ /* skip initial start code */ ++ guint offset = 3; ++ ++ gst_byte_reader_init (&br, data, maxsize); ++ ++ do { ++ offset = gst_byte_reader_masked_scan_uint32 (&br, ++ 0xfffffc00, 0x00000000, offset, size); ++ if (offset == -1 || offset >= size) ++ break; ++ ++ if (size == maxsize) ++ break; ++ ++ /* Make 1 byte space after the two leading zeros */ ++ offset += 2; ++ memmove (data + offset + 1, data + offset, size - offset); ++ ++ /* Write the emulation byte */ ++ data[offset] = 0x03; ++ ++ size++; ++ offset += 3; ++ } while (TRUE); ++ ++ return size; ++} ++ + static gboolean + gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, + GstBuffer * codec_data, guint * data_size) +@@ -332,6 +365,8 @@ gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, + return FALSE; + } + ++ size = gst_v4l2_codec_deemulate_nal (data, size, SPS_SIZE); ++ + data += size; + *data_size = size; + +@@ -342,6 +377,8 @@ gst_v4l2_codec_h264_enc_set_codec_data (GstVideoEncoder * encoder, + return FALSE; + } + ++ size = gst_v4l2_codec_deemulate_nal (data, size, PPS_SIZE); ++ + *data_size += size; + + gst_buffer_unmap (codec_data, &info); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0068-v4l2codecs-vp8-fix-support-of-unaligned-videos.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0068-v4l2codecs-vp8-fix-support-of-unaligned-videos.patch new file mode 100644 index 000000000..6c8d42f83 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0068-v4l2codecs-vp8-fix-support-of-unaligned-videos.patch @@ -0,0 +1,47 @@ +From 4d51af92e3bfc5c6093807645244d719150b99e4 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 21 Nov 2023 10:51:39 +0100 +Subject: [PATCH 68/68] v4l2codecs: vp8: fix support of unaligned videos + +Fix gst_v4l2_codec_vp8_enc_copy_input_buffer() to support copy of +unaligned video. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index af84a44..42c2b1b 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -440,11 +440,11 @@ gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self, + { + GstVideoFrame src_frame; + GstVideoFrame dest_frame; +- GstVideoInfo dest_vinfo; ++ GstVideoInfo src_vinfo; + GstBuffer *buffer; + GstFlowReturn flow_ret; + +- gst_video_info_set_format (&dest_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), ++ gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), + self->width, self->height); + + flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), +@@ -461,11 +461,11 @@ gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self, + if (!buffer) + goto fail; + +- if (!gst_video_frame_map (&src_frame, &self->vinfo, ++ if (!gst_video_frame_map (&src_frame, &src_vinfo, + frame->input_buffer, GST_MAP_READ)) + goto fail; + +- if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) { ++ if (!gst_video_frame_map (&dest_frame, &self->vinfo, buffer, GST_MAP_WRITE)) { + gst_video_frame_unmap (&dest_frame); + goto fail; + } +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0069-Force-to-add-math-as-library-dependency.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0069-Force-to-add-math-as-library-dependency.patch new file mode 100644 index 000000000..dfc9fcc80 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0069-Force-to-add-math-as-library-dependency.patch @@ -0,0 +1,36 @@ +From a6dc255e58014d396a4b42a01c51c18fc383d49f Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 18 Dec 2023 17:42:00 +0100 +Subject: [PATCH] Force to add math as library dependency + +Signed-off-by: Christophe Priouzeau +Upstream-Status: Pending +--- + gst-libs/gst/codecs/meson.build | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build +index c04600e..7c4040c 100644 +--- a/gst-libs/gst/codecs/meson.build ++++ b/gst-libs/gst/codecs/meson.build +@@ -45,6 +45,8 @@ cp_args = [ + '-DG_LOG_DOMAIN="GStreamer-Codecs"' + ] + ++mathlib = cc.find_library('m', required : false) ++ + gstcodecs = library('gstcodecs-' + api_version, + codecs_sources, + c_args : gst_plugins_bad_args + cp_args, +@@ -53,7 +55,7 @@ gstcodecs = library('gstcodecs-' + api_version, + soversion : soversion, + darwin_versions : osxversion, + install : true, +- dependencies : [gstvideo_dep, gstcodecparsers_dep], ++ dependencies : [gstvideo_dep, gstcodecparsers_dep, mathlib], + ) + + library_def = {'lib': gstcodecs} +-- +2.34.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-encoder-add-rotation-support.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-encoder-add-rotation-support.patch new file mode 100644 index 000000000..1a82b048f --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-encoder-add-rotation-support.patch @@ -0,0 +1,248 @@ +From 6d9de5b60a9f26822ae28bb9d5fa788a80963ccd Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 12 Mar 2024 15:27:33 +0100 +Subject: [PATCH] v4l2codecs: encoder: add rotation support + +Add rotation support based on V4L2_CID_ROTATE control. +90 and 270 degrees rotation requires swap of src width & height +which must be take into account when allocating src pool. +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2codech264enc.c | 15 ++++++---- + sys/v4l2codecs/gstv4l2codecvp8enc.c | 15 ++++++---- + sys/v4l2codecs/gstv4l2encoder.c | 41 ++++++++++++++++++++++++++++ + 3 files changed, 60 insertions(+), 11 deletions(-) + +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 0a195c8..921302a 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -76,6 +76,7 @@ struct _GstV4l2CodecH264Enc + GstV4l2Encoder *encoder; + GstVideoCodecState *output_state; + GstVideoInfo vinfo; ++ GstVideoInfo sink_info; + gint width; + gint height; + gint width_in_macroblocks; +@@ -276,7 +277,8 @@ gst_v4l2_codec_h264_enc_propose_allocation (GstVideoEncoder * encoder, + } + + static gboolean +-gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) ++gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) + { + GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); + +@@ -306,7 +308,7 @@ gst_v4l2_codec_h264_enc_buffers_allocation (GstVideoEncoder * encoder) + return FALSE; + } + +- self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); ++ self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &state->info); + + return TRUE; + } +@@ -705,6 +707,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + + gst_v4l2_codec_h264_enc_reset_allocation (self); + ++ self->sink_info = state->info; ++ + if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, + V4L2_PIX_FMT_H264_SLICE)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), +@@ -713,7 +717,7 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + } + +- if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, ++ if (!gst_v4l2_encoder_select_sink_format (self->encoder, &self->sink_info, + &self->vinfo)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, + ("Failed to configure H264 encoder"), +@@ -727,7 +731,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, + self->height = state->info.height; + GST_VIDEO_ENCODER_CLASS (parent_class)->set_format (encoder, state); + +- gst_v4l2_codec_h264_enc_buffers_allocation (encoder); ++ gst_v4l2_codec_h264_enc_buffers_allocation (encoder, state); ++ + self->width_in_macroblocks = (self->width + 15) / 16; + self->height_in_macroblocks = (self->height + 15) / 16; + self->first_frame = TRUE; +@@ -864,7 +869,7 @@ gst_v4l2_codec_h264_enc_copy_input_buffer (GstV4l2CodecH264Enc * self, + GstFlowReturn flow_ret; + + gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), +- self->width, self->height); ++ self->sink_info.width, self->sink_info.height); + + flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), + &buffer, NULL); +diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c +index 42c2b1b..4ec141c 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c +@@ -72,6 +72,7 @@ struct _GstV4l2CodecVp8Enc + GstV4l2Encoder *encoder; + GstVideoCodecState *output_state; + GstVideoInfo vinfo; ++ GstVideoInfo sink_info; + gint width; + gint height; + guint qp_max, qp_min; +@@ -249,7 +250,8 @@ gst_v4l2_codec_vp8_enc_propose_allocation (GstVideoEncoder * encoder, + } + + static gboolean +-gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) ++gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder, ++ GstVideoCodecState * state) + { + GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder); + +@@ -279,8 +281,7 @@ gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder) + return FALSE; + } + +- self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo); +- ++ self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &state->info); + return TRUE; + } + +@@ -298,6 +299,8 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + + gst_v4l2_codec_vp8_enc_reset_allocation (self); + ++ self->sink_info = state->info; ++ + if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info, + V4L2_PIX_FMT_VP8_FRAME)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"), +@@ -306,7 +309,7 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + return FALSE; + } + +- if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info, ++ if (!gst_v4l2_encoder_select_sink_format (self->encoder, &self->sink_info, + &self->vinfo)) { + GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, + ("Failed to configure VP8 encoder"), +@@ -318,7 +321,7 @@ gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder, + + self->width = state->info.width; + self->height = state->info.height; +- gst_v4l2_codec_vp8_enc_buffers_allocation (encoder); ++ gst_v4l2_codec_vp8_enc_buffers_allocation (encoder, state); + + if (self->output_state) + gst_video_codec_state_unref (self->output_state); +@@ -445,7 +448,7 @@ gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self, + GstFlowReturn flow_ret; + + gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo), +- self->width, self->height); ++ self->sink_info.width, self->sink_info.height); + + flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool), + &buffer, NULL); +diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c +index 27f03e9..2532dc3 100644 +--- a/sys/v4l2codecs/gstv4l2encoder.c ++++ b/sys/v4l2codecs/gstv4l2encoder.c +@@ -44,6 +44,7 @@ enum + PROP_0, + PROP_MEDIA_DEVICE, + PROP_VIDEO_DEVICE, ++ PROP_ROTATION, + }; + + struct _GstV4l2Request +@@ -95,6 +96,7 @@ struct _GstV4l2Encoder + gchar *media_device; + gchar *video_device; + guint render_delay; ++ guint rotation; + + /* detected features */ + gboolean supports_holding_capture; +@@ -687,6 +689,29 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + gint width = info->width; + gint height = info->height; + ++ /* Apply rotation, this will affect src width/height */ ++ if (self->rotation) { ++ struct v4l2_control ctl = { 0, }; ++ ++ ctl.id = V4L2_CID_ROTATE; ++ ctl.value = self->rotation; ++ ret = ioctl (self->video_fd, VIDIOC_S_CTRL, &ctl); ++ if (ret < 0) { ++ GST_ERROR_OBJECT (self, "VIDIOC_S_CTRL failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (self->rotation == 90 || self->rotation == 270) { ++ /* ++ * Swap width & height, this will trigger a call ++ * to S_FMT to let driver know the final ++ * rotated resolution ++ */ ++ width = info->height; ++ height = info->width; ++ } ++ } ++ + ret = ioctl (self->video_fd, VIDIOC_G_FMT, &fmt); + if (ret < 0) { + GST_ERROR_OBJECT (self, "VIDIOC_G_FMT failed: %s", g_strerror (errno)); +@@ -718,6 +743,10 @@ gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, + return FALSE; + } + ++ /* Stick to driver negotiated resolution */ ++ info->width = fmt.fmt.pix_mp.width; ++ info->height = fmt.fmt.pix_mp.height; ++ + return TRUE; + } + +@@ -1114,6 +1143,12 @@ gst_v4l2_encoder_install_properties (GObjectClass * gobject_class, + g_param_spec_string ("video-device", "Video Device Path", + "Path to the video device node", video_device_path, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_ROTATION, ++ g_param_spec_uint ("rotation", "Rotation", ++ "Set rotation angle in degrees", ++ 0, 270, 0, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + } + + void +@@ -1131,6 +1166,9 @@ gst_v4l2_encoder_set_property (GObject * object, guint prop_id, + g_free (self->video_device); + self->video_device = g_value_dup_string (value); + break; ++ case PROP_ROTATION: ++ self->rotation = g_value_get_uint (value); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -1150,6 +1188,9 @@ gst_v4l2_encoder_get_property (GObject * object, guint prop_id, + case PROP_VIDEO_DEVICE: + g_value_set_string (value, self->video_device); + break; ++ case PROP_ROTATION: ++ g_value_set_uint (value, self->rotation); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-sync-to-kernel-v6.6-headers.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-sync-to-kernel-v6.6-headers.patch new file mode 100644 index 000000000..89ace6d6b --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0070-v4l2codecs-sync-to-kernel-v6.6-headers.patch @@ -0,0 +1,1197 @@ +From 6734210758421261a02a8aa2fbdadf4bbd4ca80f Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 21 Feb 2024 15:27:33 +0100 +Subject: [PATCH] v4l2codecs: sync to kernel v6.6 headers + +Upstream-Status: Pending +--- + sys/v4l2codecs/linux/v4l2-controls.h | 880 ++++++++++++++++++++++++--- + sys/v4l2codecs/linux/videodev2.h | 87 ++- + 2 files changed, 884 insertions(+), 83 deletions(-) + +diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h +index 9c83596..7a94437 100644 +--- a/sys/v4l2codecs/linux/v4l2-controls.h ++++ b/sys/v4l2codecs/linux/v4l2-controls.h +@@ -823,6 +823,88 @@ enum v4l2_mpeg_video_frame_skip_mode { + #define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY (V4L2_CID_CODEC_BASE + 653) + #define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE (V4L2_CID_CODEC_BASE + 654) + ++#define V4L2_CID_MPEG_VIDEO_AV1_PROFILE (V4L2_CID_CODEC_BASE + 655) ++/** ++ * enum v4l2_mpeg_video_av1_profile - AV1 profiles ++ * ++ * @V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN: compliant decoders must be able to decode ++ * streams with seq_profile equal to 0. ++ * @V4L2_MPEG_VIDEO_AV1_PROFILE_HIGH: compliant decoders must be able to decode ++ * streams with seq_profile equal less than or equal to 1. ++ * @V4L2_MPEG_VIDEO_AV1_PROFILE_PROFESSIONAL: compliant decoders must be able to ++ * decode streams with seq_profile less than or equal to 2. ++ * ++ * Conveys the highest profile a decoder can work with. ++ */ ++enum v4l2_mpeg_video_av1_profile { ++ V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN = 0, ++ V4L2_MPEG_VIDEO_AV1_PROFILE_HIGH = 1, ++ V4L2_MPEG_VIDEO_AV1_PROFILE_PROFESSIONAL = 2, ++}; ++ ++#define V4L2_CID_MPEG_VIDEO_AV1_LEVEL (V4L2_CID_CODEC_BASE + 656) ++/** ++ * enum v4l2_mpeg_video_av1_level - AV1 levels ++ * ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_0: Level 2.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_1: Level 2.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_2: Level 2.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_2_3: Level 2.3. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_0: Level 3.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_1: Level 3.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_2: Level 3.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_3_3: Level 3.3. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_0: Level 4.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_1: Level 4.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_2: Level 4.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_4_3: Level 4.3. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_0: Level 5.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_1: Level 5.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_2: Level 5.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_5_3: Level 5.3. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_0: Level 6.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_1: Level 6.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_2: Level 6.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_6_3: Level 6.3. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_0: Level 7.0. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_1: Level 7.1. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_2: Level 7.2. ++ * @V4L2_MPEG_VIDEO_AV1_LEVEL_7_3: Level 7.3. ++ * ++ * Conveys the highest level a decoder can work with. ++ */ ++enum v4l2_mpeg_video_av1_level { ++ V4L2_MPEG_VIDEO_AV1_LEVEL_2_0 = 0, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_2_1 = 1, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_2_2 = 2, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_2_3 = 3, ++ ++ V4L2_MPEG_VIDEO_AV1_LEVEL_3_0 = 4, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_3_1 = 5, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_3_2 = 6, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_3_3 = 7, ++ ++ V4L2_MPEG_VIDEO_AV1_LEVEL_4_0 = 8, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_4_1 = 9, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_4_2 = 10, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_4_3 = 11, ++ ++ V4L2_MPEG_VIDEO_AV1_LEVEL_5_0 = 12, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_5_1 = 13, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_5_2 = 14, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_5_3 = 15, ++ ++ V4L2_MPEG_VIDEO_AV1_LEVEL_6_0 = 16, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_6_1 = 17, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_6_2 = 18, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_6_3 = 19, ++ ++ V4L2_MPEG_VIDEO_AV1_LEVEL_7_0 = 20, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_7_1 = 21, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_7_2 = 22, ++ V4L2_MPEG_VIDEO_AV1_LEVEL_7_3 = 23 ++}; ++ + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ + #define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000) + #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0) +@@ -1010,6 +1092,8 @@ enum v4l2_auto_focus_range { + + #define V4L2_CID_CAMERA_SENSOR_ROTATION (V4L2_CID_CAMERA_CLASS_BASE+35) + ++#define V4L2_CID_HDR_SENSOR_MODE (V4L2_CID_CAMERA_CLASS_BASE+36) ++ + /* FM Modulator class control IDs */ + + #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) +@@ -1812,7 +1896,7 @@ struct v4l2_vp8_segment { + * @sharpness_level: matches sharpness_level syntax element. + * @level: matches loop_filter_level syntax element. + * @padding: padding field. Should be zeroed by applications. +- * @flags: see V4L2_VP8_LF_FLAG_{}. ++ * @flags: see V4L2_VP8_LF_{}. + * + * This structure contains loop filter related parameters. + * See the 'mb_lf_adjustments()' part of the frame header syntax, +@@ -1967,6 +2051,97 @@ struct v4l2_ctrl_vp8_frame { + __u64 flags; + }; + ++#define V4L2_CID_STATELESS_VP8_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 201) ++ ++#define V4L2_VP8_FRAME_FLAG_SHOWFRAME 0x1 ++#define V4L2_VP8_FRAME_FLAG_GOLDEN_REFRESH 0x2 ++#define V4L2_VP8_FRAME_FLAG_ALTREF_REFRESH 0x4 ++#define V4L2_VP8_FRAME_FLAG_SEGMENT_ENABLED 0x8 ++#define V4L2_VP8_FRAME_FLAG_LOOP_FILTER_ADJ_ENABLED 0x10 ++#define V4L2_VP8_FRAME_FLAG_REFRESH_ENTROPY_PROBS 0x20 ++ ++#define V4L2_VP8_FRAME_TYPE_KEYFRAME 0 ++#define V4L2_VP8_FRAME_TYPE_INTER 1 ++ ++#define V4L2_VP8_FRAME_COLOR_SPACE_YUV 0 ++#define V4L2_VP8_FRAME_COLOR_SPACE_RESERVED 1 ++ ++#define V4L2_VP8_FRAME_CLAMPING_REQUIRED 0 ++#define V4L2_VP8_FRAME_CLAMPING_NO 1 ++ ++#define V4L2_VP8_FRAME_FILTER_TYPE_NORMAL 0 ++#define V4L2_VP8_FRAME_FILTER_TYPE_SIMPLE 1 ++ ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_1 0 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_2 1 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_4 2 ++#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_8 3 ++ ++#define V4L2_VP8_FRAME_GOLDEN_KEEP 0 ++#define V4L2_VP8_FRAME_GOLDEN_LASTFRAME 1 ++#define V4L2_VP8_FRAME_GOLDEN_ALTREF 2 ++ ++#define V4L2_VP8_FRAME_ALTREF_KEEP 0 ++#define V4L2_VP8_FRAME_ALTREF_LASTFRAME 1 ++#define V4L2_VP8_FRAME_ALTREF_GOLDEN 2 ++ ++#define V4L2_VP8_FRAME_REF_LAST 0 ++#define V4L2_VP8_FRAME_REF_GOLDEN 1 ++#define V4L2_VP8_FRAME_REF_ALT 2 ++ ++/** ++ * struct v4l2_ctrl_vp8_encode_params - VP8 encode parameters ++ * @flags: combination of V4L2_VP8_FRAME_FLAG_{} flags. ++ * @frame_type: specifies the frame type (key or inter). ++ * Set to one of V4L2_VP8_FRAME_TYPE_{}. ++ * @color_space: defines the YUV color space of the sequence. ++ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_COLOR_SPACE_{}. ++ * @clamping_type: defines pixel value clamping type. ++ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_CLAMPING_{}. ++ * @loop_filter_type: selects the type of loop filter applied. ++ * Set to one of V4L2_VP8_FRAME_FILTER_TYPE_{}. ++ * @loop_filter_level: sets the strength of the applied loop filter. ++ * Set to a value from the rage 0..63. ++ * @sharpness_level: sets the sharpness of the applied loop filter. ++ * Set to a value from the range 0..7. ++ * @log2_nbr_of_dct_partitions: determines the number of separate partitions ++ * containing the DCT coefficients of macroblocks. ++ * Set to one of V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_{}. ++ * @prob_intra: indicates the probability of an intra macroblock. ++ * Set to a value from the range 0..255. ++ * @prob_last: indicates the probability that the last reference frame is used for inter-prediction. ++ * Set to a value from the range 0..255. ++ * @prob_gf: indicates the probability that the golden reference frame is used for inter-prediction. ++ * Set to a value from the range 0..255. ++ * @copy_buffer_to_golden: specifies the golden frame refresh strategy. ++ * Set to one of V4L2_VP8_FRAME_FLAG_GOLDEN_{}. ++ * @copy_buffer_to_alternate: specifies the atlref frame refresh strategy. ++ * Set to one of V4L2_VP8_FRAME_FLAG_ALTREF_{}. ++ * @reference_type: specifies what kind of reference to use for current inter frame. ++ * V4L2_VP8_FRAME_TYPE_KEYFRAME shall set this field to zero. ++ * Set to one of V4L2_VP8_FRAME_REF_{}. ++ */ ++struct v4l2_ctrl_vp8_encode_params { ++ __u32 flags; ++ __u8 frame_type; ++ __u8 color_space; ++ __u8 clamping_type; ++ __u8 loop_filter_type; ++ __u8 loop_filter_level; ++ __u8 sharpness_level; ++ __u8 log2_nbr_of_dct_partitions; ++ __u8 prob_intra; ++ __u8 prob_last; ++ __u8 prob_gf; ++ __u8 copy_buffer_to_golden; ++ __u8 copy_buffer_to_alternate; ++ __u8 reference_type; ++}; ++ ++#define V4L2_CID_STATELESS_VP8_ENCODE_QP (V4L2_CID_CODEC_STATELESS_BASE + 202) ++ + /* Stateless MPEG-2 controls */ + + #define V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE 0x01 +@@ -2487,6 +2662,9 @@ struct v4l2_ctrl_hevc_slice_params { + * @poc_st_curr_after: provides the index of the short term after references + * in DPB array + * @poc_lt_curr: provides the index of the long term references in DPB array ++ * @num_delta_pocs_of_ref_rps_idx: same as the derived value NumDeltaPocs[RefRpsIdx], ++ * can be used to parse the RPS data in slice headers ++ * instead of skipping it with @short_term_ref_pic_set_size. + * @reserved: padding field. Should be zeroed by applications. + * @dpb: the decoded picture buffer, for meta-data about reference frames + * @flags: see V4L2_HEVC_DECODE_PARAM_FLAG_{} +@@ -2502,7 +2680,8 @@ struct v4l2_ctrl_hevc_decode_params { + __u8 poc_st_curr_before[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + __u8 poc_st_curr_after[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + __u8 poc_lt_curr[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; +- __u8 reserved[4]; ++ __u8 num_delta_pocs_of_ref_rps_idx; ++ __u8 reserved[3]; + struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + __u64 flags; + }; +@@ -2856,97 +3035,644 @@ struct v4l2_ctrl_vp9_compressed_hdr { + struct v4l2_vp9_mv_probs mv; + }; + +-#define V4L2_CID_STATELESS_VP8_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 501) ++/* Stateless AV1 controls */ ++ ++#define V4L2_AV1_TOTAL_REFS_PER_FRAME 8 ++#define V4L2_AV1_CDEF_MAX 8 ++#define V4L2_AV1_NUM_PLANES_MAX 3 /* 1 if monochrome, 3 otherwise */ ++#define V4L2_AV1_MAX_SEGMENTS 8 ++#define V4L2_AV1_MAX_OPERATING_POINTS (1 << 5) /* 5 bits to encode */ ++#define V4L2_AV1_REFS_PER_FRAME 7 ++#define V4L2_AV1_MAX_NUM_Y_POINTS (1 << 4) /* 4 bits to encode */ ++#define V4L2_AV1_MAX_NUM_CB_POINTS (1 << 4) /* 4 bits to encode */ ++#define V4L2_AV1_MAX_NUM_CR_POINTS (1 << 4) /* 4 bits to encode */ ++#define V4L2_AV1_AR_COEFFS_SIZE 25 /* (2 * 3 * (3 + 1)) + 1 */ ++#define V4L2_AV1_MAX_NUM_PLANES 3 ++#define V4L2_AV1_MAX_TILE_COLS 64 ++#define V4L2_AV1_MAX_TILE_ROWS 64 ++#define V4L2_AV1_MAX_TILE_COUNT 512 ++ ++#define V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE 0x00000001 ++#define V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK 0x00000002 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA 0x00000004 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER 0x00000008 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND 0x00000010 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND 0x00000020 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION 0x00000040 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER 0x00000080 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT 0x00000100 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP 0x00000200 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS 0x00000400 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES 0x00000800 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF 0x00001000 ++#define V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION 0x00002000 ++#define V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME 0x00004000 ++#define V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE 0x00008000 ++#define V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X 0x00010000 ++#define V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y 0x00020000 ++#define V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT 0x00040000 ++#define V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q 0x00080000 ++ ++#define V4L2_CID_STATELESS_AV1_SEQUENCE (V4L2_CID_CODEC_STATELESS_BASE + 500) ++/** ++ * struct v4l2_ctrl_av1_sequence - AV1 Sequence ++ * ++ * Represents an AV1 Sequence OBU. See section 5.5 "Sequence header OBU syntax" ++ * for more details. ++ * ++ * @flags: See V4L2_AV1_SEQUENCE_FLAG_{}. ++ * @seq_profile: specifies the features that can be used in the coded video ++ * sequence. ++ * @order_hint_bits: specifies the number of bits used for the order_hint field ++ * at each frame. ++ * @bit_depth: the bitdepth to use for the sequence as described in section ++ * 5.5.2 "Color config syntax". ++ * @reserved: padding field. Should be zeroed by applications. ++ * @max_frame_width_minus_1: specifies the maximum frame width minus 1 for the ++ * frames represented by this sequence header. ++ * @max_frame_height_minus_1: specifies the maximum frame height minus 1 for the ++ * frames represented by this sequence header. ++ */ ++struct v4l2_ctrl_av1_sequence { ++ __u32 flags; ++ __u8 seq_profile; ++ __u8 order_hint_bits; ++ __u8 bit_depth; ++ __u8 reserved; ++ __u16 max_frame_width_minus_1; ++ __u16 max_frame_height_minus_1; ++}; + +-#define V4L2_VP8_FRAME_FLAG_SHOWFRAME 0x1 +-#define V4L2_VP8_FRAME_FLAG_GOLDEN_REFRESH 0x2 +-#define V4L2_VP8_FRAME_FLAG_ALTREF_REFRESH 0x4 +-#define V4L2_VP8_FRAME_FLAG_SEGMENT_ENABLED 0x8 +-#define V4L2_VP8_FRAME_FLAG_LOOP_FILTER_ADJ_ENABLED 0x10 +-#define V4L2_VP8_FRAME_FLAG_REFRESH_ENTROPY_PROBS 0x20 ++#define V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY (V4L2_CID_CODEC_STATELESS_BASE + 501) ++/** ++ * struct v4l2_ctrl_av1_tile_group_entry - AV1 Tile Group entry ++ * ++ * Represents a single AV1 tile inside an AV1 Tile Group. Note that MiRowStart, ++ * MiRowEnd, MiColStart and MiColEnd can be retrieved from struct ++ * v4l2_av1_tile_info in struct v4l2_ctrl_av1_frame using tile_row and ++ * tile_col. See section 6.10.1 "General tile group OBU semantics" for more ++ * details. ++ * ++ * @tile_offset: offset from the OBU data, i.e. where the coded tile data ++ * actually starts. ++ * @tile_size: specifies the size in bytes of the coded tile. Equivalent to ++ * "TileSize" in the AV1 Specification. ++ * @tile_row: specifies the row of the current tile. Equivalent to "TileRow" in ++ * the AV1 Specification. ++ * @tile_col: specifies the col of the current tile. Equivalent to "TileCol" in ++ * the AV1 Specification. ++ */ ++struct v4l2_ctrl_av1_tile_group_entry { ++ __u32 tile_offset; ++ __u32 tile_size; ++ __u32 tile_row; ++ __u32 tile_col; ++}; + +-#define V4L2_VP8_FRAME_TYPE_KEYFRAME 0 +-#define V4L2_VP8_FRAME_TYPE_INTER 1 ++/** ++ * enum v4l2_av1_warp_model - AV1 Warp Model as described in section 3 ++ * "Symbols and abbreviated terms" of the AV1 Specification. ++ * ++ * @V4L2_AV1_WARP_MODEL_IDENTITY: Warp model is just an identity transform. ++ * @V4L2_AV1_WARP_MODEL_TRANSLATION: Warp model is a pure translation. ++ * @V4L2_AV1_WARP_MODEL_ROTZOOM: Warp model is a rotation + symmetric zoom + ++ * translation. ++ * @V4L2_AV1_WARP_MODEL_AFFINE: Warp model is a general affine transform. ++ */ ++enum v4l2_av1_warp_model { ++ V4L2_AV1_WARP_MODEL_IDENTITY = 0, ++ V4L2_AV1_WARP_MODEL_TRANSLATION = 1, ++ V4L2_AV1_WARP_MODEL_ROTZOOM = 2, ++ V4L2_AV1_WARP_MODEL_AFFINE = 3, ++}; + +-#define V4L2_VP8_FRAME_COLOR_SPACE_YUV 0 +-#define V4L2_VP8_FRAME_COLOR_SPACE_RESERVED 1 ++/** ++ * enum v4l2_av1_reference_frame - AV1 reference frames ++ * ++ * @V4L2_AV1_REF_INTRA_FRAME: Intra Frame Reference ++ * @V4L2_AV1_REF_LAST_FRAME: Last Reference Frame ++ * @V4L2_AV1_REF_LAST2_FRAME: Last2 Reference Frame ++ * @V4L2_AV1_REF_LAST3_FRAME: Last3 Reference Frame ++ * @V4L2_AV1_REF_GOLDEN_FRAME: Golden Reference Frame ++ * @V4L2_AV1_REF_BWDREF_FRAME: BWD Reference Frame ++ * @V4L2_AV1_REF_ALTREF2_FRAME: Alternative2 Reference Frame ++ * @V4L2_AV1_REF_ALTREF_FRAME: Alternative Reference Frame ++ */ ++enum v4l2_av1_reference_frame { ++ V4L2_AV1_REF_INTRA_FRAME = 0, ++ V4L2_AV1_REF_LAST_FRAME = 1, ++ V4L2_AV1_REF_LAST2_FRAME = 2, ++ V4L2_AV1_REF_LAST3_FRAME = 3, ++ V4L2_AV1_REF_GOLDEN_FRAME = 4, ++ V4L2_AV1_REF_BWDREF_FRAME = 5, ++ V4L2_AV1_REF_ALTREF2_FRAME = 6, ++ V4L2_AV1_REF_ALTREF_FRAME = 7, ++}; ++ ++#define V4L2_AV1_GLOBAL_MOTION_IS_INVALID(ref) (1 << (ref)) ++ ++#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_GLOBAL 0x1 ++#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_ROT_ZOOM 0x2 ++#define V4L2_AV1_GLOBAL_MOTION_FLAG_IS_TRANSLATION 0x4 ++/** ++ * struct v4l2_av1_global_motion - AV1 Global Motion parameters as described in ++ * section 6.8.17 "Global motion params semantics" of the AV1 specification. ++ * ++ * @flags: A bitfield containing the flags per reference frame. See ++ * V4L2_AV1_GLOBAL_MOTION_FLAG_{} ++ * @type: The type of global motion transform used. ++ * @params: this field has the same meaning as "gm_params" in the AV1 ++ * specification. ++ * @invalid: bitfield indicating whether the global motion params are invalid ++ * for a given reference frame. See section 7.11.3.6 Setup shear process and ++ * the variable "warpValid". Use V4L2_AV1_GLOBAL_MOTION_IS_INVALID(ref) to ++ * create a suitable mask. ++ * @reserved: padding field. Should be zeroed by applications. ++ */ + +-#define V4L2_VP8_FRAME_CLAMPING_REQUIRED 0 +-#define V4L2_VP8_FRAME_CLAMPING_NO 1 ++struct v4l2_av1_global_motion { ++ __u8 flags[V4L2_AV1_TOTAL_REFS_PER_FRAME]; ++ enum v4l2_av1_warp_model type[V4L2_AV1_TOTAL_REFS_PER_FRAME]; ++ __s32 params[V4L2_AV1_TOTAL_REFS_PER_FRAME][6]; ++ __u8 invalid; ++ __u8 reserved[3]; ++}; + +-#define V4L2_VP8_FRAME_FILTER_TYPE_NORMAL 0 +-#define V4L2_VP8_FRAME_FILTER_TYPE_SIMPLE 1 ++/** ++ * enum v4l2_av1_frame_restoration_type - AV1 Frame Restoration Type ++ * @V4L2_AV1_FRAME_RESTORE_NONE: no filtering is applied. ++ * @V4L2_AV1_FRAME_RESTORE_WIENER: Wiener filter process is invoked. ++ * @V4L2_AV1_FRAME_RESTORE_SGRPROJ: self guided filter process is invoked. ++ * @V4L2_AV1_FRAME_RESTORE_SWITCHABLE: restoration filter is swichtable. ++ */ ++enum v4l2_av1_frame_restoration_type { ++ V4L2_AV1_FRAME_RESTORE_NONE = 0, ++ V4L2_AV1_FRAME_RESTORE_WIENER = 1, ++ V4L2_AV1_FRAME_RESTORE_SGRPROJ = 2, ++ V4L2_AV1_FRAME_RESTORE_SWITCHABLE = 3, ++}; + +-#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_1 0 +-#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_2 1 +-#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_4 2 +-#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_8 3 ++#define V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR 0x1 ++#define V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR 0x2 + +-#define V4L2_VP8_FRAME_GOLDEN_KEEP 0 +-#define V4L2_VP8_FRAME_GOLDEN_LASTFRAME 1 +-#define V4L2_VP8_FRAME_GOLDEN_ALTREF 2 ++/** ++ * struct v4l2_av1_loop_restoration - AV1 Loop Restauration as described in ++ * section 6.10.15 "Loop restoration params semantics" of the AV1 specification. ++ * ++ * @flags: See V4L2_AV1_LOOP_RESTORATION_FLAG_{}. ++ * @lr_unit_shift: specifies if the luma restoration size should be halved. ++ * @lr_uv_shift: specifies if the chroma size should be half the luma size. ++ * @reserved: padding field. Should be zeroed by applications. ++ * @frame_restoration_type: specifies the type of restoration used for each ++ * plane. See enum v4l2_av1_frame_restoration_type. ++ * @loop_restoration_size: specifies the size of loop restoration units in units ++ * of samples in the current plane. ++ */ ++struct v4l2_av1_loop_restoration { ++ __u8 flags; ++ __u8 lr_unit_shift; ++ __u8 lr_uv_shift; ++ __u8 reserved; ++ enum v4l2_av1_frame_restoration_type frame_restoration_type[V4L2_AV1_NUM_PLANES_MAX]; ++ __u32 loop_restoration_size[V4L2_AV1_MAX_NUM_PLANES]; ++}; + +-#define V4L2_VP8_FRAME_ALTREF_KEEP 0 +-#define V4L2_VP8_FRAME_ALTREF_LASTFRAME 1 +-#define V4L2_VP8_FRAME_ALTREF_GOLDEN 2 ++/** ++ * struct v4l2_av1_cdef - AV1 CDEF params semantics as described in section ++ * 6.10.14 "CDEF params semantics" of the AV1 specification ++ * ++ * @damping_minus_3: controls the amount of damping in the deringing filter. ++ * @bits: specifies the number of bits needed to specify which CDEF filter to ++ * apply. ++ * @y_pri_strength: specifies the strength of the primary filter. ++ * @y_sec_strength: specifies the strength of the secondary filter. ++ * @uv_pri_strength: specifies the strength of the primary filter. ++ * @uv_sec_strength: specifies the strength of the secondary filter. ++ */ ++struct v4l2_av1_cdef { ++ __u8 damping_minus_3; ++ __u8 bits; ++ __u8 y_pri_strength[V4L2_AV1_CDEF_MAX]; ++ __u8 y_sec_strength[V4L2_AV1_CDEF_MAX]; ++ __u8 uv_pri_strength[V4L2_AV1_CDEF_MAX]; ++ __u8 uv_sec_strength[V4L2_AV1_CDEF_MAX]; ++}; + +-#define V4L2_VP8_FRAME_REF_LAST 0 +-#define V4L2_VP8_FRAME_REF_GOLDEN 1 +-#define V4L2_VP8_FRAME_REF_ALT 2 ++#define V4L2_AV1_SEGMENTATION_FLAG_ENABLED 0x1 ++#define V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP 0x2 ++#define V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE 0x4 ++#define V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA 0x8 ++#define V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP 0x10 + + /** +- * struct v4l2_ctrl_vp8_encode_params - VP8 encode parameters +- * @flags: combination of V4L2_VP8_FRAME_FLAG_{} flags. +- * @frame_type: specifies the frame type (key or inter). +- * Set to one of V4L2_VP8_FRAME_TYPE_{}. +- * @color_space: defines the YUV color space of the sequence. +- * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. +- * Set to one of V4L2_VP8_FRAME_COLOR_SPACE_{}. +- * @clamping_type: defines pixel value clamping type. +- * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero. +- * Set to one of V4L2_VP8_FRAME_CLAMPING_{}. +- * @loop_filter_type: selects the type of loop filter applied. +- * Set to one of V4L2_VP8_FRAME_FILTER_TYPE_{}. +- * @loop_filter_level: sets the strength of the applied loop filter. +- * Set to a value from the rage 0..63. +- * @sharpness_level: sets the sharpness of the applied loop filter. +- * Set to a value from the range 0..7. +- * @log2_nbr_of_dct_partitions: determines the number of separate partitions +- * containing the DCT coefficients of macroblocks. +- * Set to one of V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_{}. +- * @prob_intra: indicates the probability of an intra macroblock. +- * Set to a value from the range 0..255. +- * @prob_last: indicates the probability that the last reference frame is used for inter-prediction. +- * Set to a value from the range 0..255. +- * @prob_gf: indicates the probability that the golden reference frame is used for inter-prediction. +- * Set to a value from the range 0..255. +- * @copy_buffer_to_golden: specifies the golden frame refresh strategy. +- * Set to one of V4L2_VP8_FRAME_FLAG_GOLDEN_{}. +- * @copy_buffer_to_alternate: specifies the atlref frame refresh strategy. +- * Set to one of V4L2_VP8_FRAME_FLAG_ALTREF_{}. +- * @reference_type: specifies what kind of reference to use for current inter frame. +- * V4L2_VP8_FRAME_TYPE_KEYFRAME shall set this field to zero. +- * Set to one of V4L2_VP8_FRAME_REF_{}. ++ * enum v4l2_av1_segment_feature - AV1 segment features as described in section ++ * 3 "Symbols and abbreviated terms" of the AV1 specification. ++ * ++ * @V4L2_AV1_SEG_LVL_ALT_Q: Index for quantizer segment feature. ++ * @V4L2_AV1_SEG_LVL_ALT_LF_Y_V: Index for vertical luma loop filter segment ++ * feature. ++ * @V4L2_AV1_SEG_LVL_REF_FRAME: Index for reference frame segment feature. ++ * @V4L2_AV1_SEG_LVL_REF_SKIP: Index for skip segment feature. ++ * @V4L2_AV1_SEG_LVL_REF_GLOBALMV: Index for global mv feature. ++ * @V4L2_AV1_SEG_LVL_MAX: Number of segment features. + */ +-struct v4l2_ctrl_vp8_encode_params { ++enum v4l2_av1_segment_feature { ++ V4L2_AV1_SEG_LVL_ALT_Q = 0, ++ V4L2_AV1_SEG_LVL_ALT_LF_Y_V = 1, ++ V4L2_AV1_SEG_LVL_REF_FRAME = 5, ++ V4L2_AV1_SEG_LVL_REF_SKIP = 6, ++ V4L2_AV1_SEG_LVL_REF_GLOBALMV = 7, ++ V4L2_AV1_SEG_LVL_MAX = 8 ++}; ++ ++#define V4L2_AV1_SEGMENT_FEATURE_ENABLED(id) (1 << (id)) ++ ++/** ++ * struct v4l2_av1_segmentation - AV1 Segmentation params as defined in section ++ * 6.8.13 "Segmentation params semantics" of the AV1 specification. ++ * ++ * @flags: see V4L2_AV1_SEGMENTATION_FLAG_{}. ++ * @last_active_seg_id: indicates the highest numbered segment id that has some ++ * enabled feature. This is used when decoding the segment id to only decode ++ * choices corresponding to used segments. ++ * @feature_enabled: bitmask defining which features are enabled in each ++ * segment. Use V4L2_AV1_SEGMENT_FEATURE_ENABLED to build a suitable mask. ++ * @feature_data: data attached to each feature. Data entry is only valid if the ++ * feature is enabled ++ */ ++struct v4l2_av1_segmentation { ++ __u8 flags; ++ __u8 last_active_seg_id; ++ __u8 feature_enabled[V4L2_AV1_MAX_SEGMENTS]; ++ __s16 feature_data[V4L2_AV1_MAX_SEGMENTS][V4L2_AV1_SEG_LVL_MAX]; ++}; ++ ++#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED 0x1 ++#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE 0x2 ++#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT 0x4 ++#define V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI 0x8 ++ ++/** ++ * struct v4l2_av1_loop_filter - AV1 Loop filter params as defined in section ++ * 6.8.10 "Loop filter semantics" and 6.8.16 "Loop filter delta parameters ++ * semantics" of the AV1 specification. ++ * ++ * @flags: see V4L2_AV1_LOOP_FILTER_FLAG_{} ++ * @level: an array containing loop filter strength values. Different loop ++ * filter strength values from the array are used depending on the image plane ++ * being filtered, and the edge direction (vertical or horizontal) being ++ * filtered. ++ * @sharpness: indicates the sharpness level. The loop_filter_level and ++ * loop_filter_sharpness together determine when a block edge is filtered, and ++ * by how much the filtering can change the sample values. The loop filter ++ * process is described in section 7.14 of the AV1 specification. ++ * @ref_deltas: contains the adjustment needed for the filter level based on the ++ * chosen reference frame. If this syntax element is not present, it maintains ++ * its previous value. ++ * @mode_deltas: contains the adjustment needed for the filter level based on ++ * the chosen mode. If this syntax element is not present, it maintains its ++ * previous value. ++ * @delta_lf_res: specifies the left shift which should be applied to decoded ++ * loop filter delta values. ++ */ ++struct v4l2_av1_loop_filter { ++ __u8 flags; ++ __u8 level[4]; ++ __u8 sharpness; ++ __s8 ref_deltas[V4L2_AV1_TOTAL_REFS_PER_FRAME]; ++ __s8 mode_deltas[2]; ++ __u8 delta_lf_res; ++}; ++ ++#define V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA 0x1 ++#define V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX 0x2 ++#define V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT 0x4 ++ ++/** ++ * struct v4l2_av1_quantization - AV1 Quantization params as defined in section ++ * 6.8.11 "Quantization params semantics" of the AV1 specification. ++ * ++ * @flags: see V4L2_AV1_QUANTIZATION_FLAG_{} ++ * @base_q_idx: indicates the base frame qindex. This is used for Y AC ++ * coefficients and as the base value for the other quantizers. ++ * @delta_q_y_dc: indicates the Y DC quantizer relative to base_q_idx. ++ * @delta_q_u_dc: indicates the U DC quantizer relative to base_q_idx. ++ * @delta_q_u_ac: indicates the U AC quantizer relative to base_q_idx. ++ * @delta_q_v_dc: indicates the V DC quantizer relative to base_q_idx. ++ * @delta_q_v_ac: indicates the V AC quantizer relative to base_q_idx. ++ * @qm_y: specifies the level in the quantizer matrix that should be used for ++ * luma plane decoding. ++ * @qm_u: specifies the level in the quantizer matrix that should be used for ++ * chroma U plane decoding. ++ * @qm_v: specifies the level in the quantizer matrix that should be used for ++ * chroma V plane decoding. ++ * @delta_q_res: specifies the left shift which should be applied to decoded ++ * quantizer index delta values. ++ */ ++struct v4l2_av1_quantization { ++ __u8 flags; ++ __u8 base_q_idx; ++ __s8 delta_q_y_dc; ++ __s8 delta_q_u_dc; ++ __s8 delta_q_u_ac; ++ __s8 delta_q_v_dc; ++ __s8 delta_q_v_ac; ++ __u8 qm_y; ++ __u8 qm_u; ++ __u8 qm_v; ++ __u8 delta_q_res; ++}; ++ ++#define V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING 0x1 ++ ++/** ++ * struct v4l2_av1_tile_info - AV1 Tile info as defined in section 6.8.14 "Tile ++ * info semantics" of the AV1 specification. ++ * ++ * @flags: see V4L2_AV1_TILE_INFO_FLAG_{} ++ * @context_update_tile_id: specifies which tile to use for the CDF update. ++ * @tile_rows: specifies the number of tiles down the frame. ++ * @tile_cols: specifies the number of tiles across the frame. ++ * @mi_col_starts: an array specifying the start column (in units of 4x4 luma ++ * samples) for each tile across the image. ++ * @mi_row_starts: an array specifying the start row (in units of 4x4 luma ++ * samples) for each tile down the image. ++ * @width_in_sbs_minus_1: specifies the width of a tile minus 1 in units of ++ * superblocks. ++ * @height_in_sbs_minus_1: specifies the height of a tile minus 1 in units of ++ * superblocks. ++ * @tile_size_bytes: specifies the number of bytes needed to code each tile ++ * size. ++ * @reserved: padding field. Should be zeroed by applications. ++ */ ++struct v4l2_av1_tile_info { ++ __u8 flags; ++ __u8 context_update_tile_id; ++ __u8 tile_cols; ++ __u8 tile_rows; ++ __u32 mi_col_starts[V4L2_AV1_MAX_TILE_COLS + 1]; ++ __u32 mi_row_starts[V4L2_AV1_MAX_TILE_ROWS + 1]; ++ __u32 width_in_sbs_minus_1[V4L2_AV1_MAX_TILE_COLS]; ++ __u32 height_in_sbs_minus_1[V4L2_AV1_MAX_TILE_ROWS]; ++ __u8 tile_size_bytes; ++ __u8 reserved[3]; ++}; ++ ++/** ++ * enum v4l2_av1_frame_type - AV1 Frame Type ++ * ++ * @V4L2_AV1_KEY_FRAME: Key frame ++ * @V4L2_AV1_INTER_FRAME: Inter frame ++ * @V4L2_AV1_INTRA_ONLY_FRAME: Intra-only frame ++ * @V4L2_AV1_SWITCH_FRAME: Switch frame ++ */ ++enum v4l2_av1_frame_type { ++ V4L2_AV1_KEY_FRAME = 0, ++ V4L2_AV1_INTER_FRAME = 1, ++ V4L2_AV1_INTRA_ONLY_FRAME = 2, ++ V4L2_AV1_SWITCH_FRAME = 3 ++}; ++ ++/** ++ * enum v4l2_av1_interpolation_filter - AV1 interpolation filter types ++ * ++ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP: eight tap filter ++ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH: eight tap smooth filter ++ * @V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP: eight tap sharp filter ++ * @V4L2_AV1_INTERPOLATION_FILTER_BILINEAR: bilinear filter ++ * @V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE: filter selection is signaled at ++ * the block level ++ * ++ * See section 6.8.9 "Interpolation filter semantics" of the AV1 specification ++ * for more details. ++ */ ++enum v4l2_av1_interpolation_filter { ++ V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP = 0, ++ V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH = 1, ++ V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP = 2, ++ V4L2_AV1_INTERPOLATION_FILTER_BILINEAR = 3, ++ V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE = 4, ++}; ++ ++/** ++ * enum v4l2_av1_tx_mode - AV1 Tx mode as described in section 6.8.21 "TX mode ++ * semantics" of the AV1 specification. ++ * @V4L2_AV1_TX_MODE_ONLY_4X4: the inverse transform will use only 4x4 ++ * transforms ++ * @V4L2_AV1_TX_MODE_LARGEST: the inverse transform will use the largest ++ * transform size that fits inside the block ++ * @V4L2_AV1_TX_MODE_SELECT: the choice of transform size is specified ++ * explicitly for each block. ++ */ ++enum v4l2_av1_tx_mode { ++ V4L2_AV1_TX_MODE_ONLY_4X4 = 0, ++ V4L2_AV1_TX_MODE_LARGEST = 1, ++ V4L2_AV1_TX_MODE_SELECT = 2 ++}; ++ ++#define V4L2_AV1_FRAME_FLAG_SHOW_FRAME 0x00000001 ++#define V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME 0x00000002 ++#define V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE 0x00000004 ++#define V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE 0x00000008 ++#define V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS 0x00000010 ++#define V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV 0x00000020 ++#define V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC 0x00000040 ++#define V4L2_AV1_FRAME_FLAG_USE_SUPERRES 0x00000080 ++#define V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV 0x00000100 ++#define V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE 0x00000200 ++#define V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS 0x00000400 ++#define V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF 0x00000800 ++#define V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION 0x00001000 ++#define V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT 0x00002000 ++#define V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET 0x00004000 ++#define V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED 0x00008000 ++#define V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT 0x00010000 ++#define V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE 0x00020000 ++#define V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT 0x00040000 ++#define V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING 0x00080000 ++ ++#define V4L2_CID_STATELESS_AV1_FRAME (V4L2_CID_CODEC_STATELESS_BASE + 502) ++/** ++ * struct v4l2_ctrl_av1_frame - Represents an AV1 Frame Header OBU. ++ * ++ * @tile_info: tile info ++ * @quantization: quantization params ++ * @segmentation: segmentation params ++ * @superres_denom: the denominator for the upscaling ratio. ++ * @loop_filter: loop filter params ++ * @cdef: cdef params ++ * @skip_mode_frame: specifies the frames to use for compound prediction when ++ * skip_mode is equal to 1. ++ * @primary_ref_frame: specifies which reference frame contains the CDF values ++ * and other state that should be loaded at the start of the frame. ++ * @loop_restoration: loop restoration params ++ * @global_motion: global motion params ++ * @flags: see V4L2_AV1_FRAME_FLAG_{} ++ * @frame_type: specifies the AV1 frame type ++ * @order_hint: specifies OrderHintBits least significant bits of the expected ++ * output order for this frame. ++ * @upscaled_width: the upscaled width. ++ * @interpolation_filter: specifies the filter selection used for performing ++ * inter prediction. ++ * @tx_mode: specifies how the transform size is determined. ++ * @frame_width_minus_1: add 1 to get the frame's width. ++ * @frame_height_minus_1: add 1 to get the frame's height ++ * @render_width_minus_1: add 1 to get the render width of the frame in luma ++ * samples. ++ * @render_height_minus_1: add 1 to get the render height of the frame in luma ++ * samples. ++ * @current_frame_id: specifies the frame id number for the current frame. Frame ++ * id numbers are additional information that do not affect the decoding ++ * process, but provide decoders with a way of detecting missing reference ++ * frames so that appropriate action can be taken. ++ * @buffer_removal_time: specifies the frame removal time in units of DecCT clock ++ * ticks counted from the removal time of the last random access point for ++ * operating point opNum. ++ * @reserved: padding field. Should be zeroed by applications. ++ * @order_hints: specifies the expected output order hint for each reference ++ * frame. This field corresponds to the OrderHints variable from the ++ * specification (section 5.9.2 "Uncompressed header syntax"). As such, this is ++ * only used for non-intra frames and ignored otherwise. order_hints[0] is ++ * always ignored. ++ * @reference_frame_ts: the V4L2 timestamp of the reference frame slots. ++ * @ref_frame_idx: used to index into @reference_frame_ts when decoding ++ * inter-frames. The meaning of this array is the same as in the specification. ++ * The timestamp refers to the timestamp field in struct v4l2_buffer. Use ++ * v4l2_timeval_to_ns() to convert the struct timeval to a __u64. ++ * @refresh_frame_flags: contains a bitmask that specifies which reference frame ++ * slots will be updated with the current frame after it is decoded. ++ */ ++struct v4l2_ctrl_av1_frame { ++ struct v4l2_av1_tile_info tile_info; ++ struct v4l2_av1_quantization quantization; ++ __u8 superres_denom; ++ struct v4l2_av1_segmentation segmentation; ++ struct v4l2_av1_loop_filter loop_filter; ++ struct v4l2_av1_cdef cdef; ++ __u8 skip_mode_frame[2]; ++ __u8 primary_ref_frame; ++ struct v4l2_av1_loop_restoration loop_restoration; ++ struct v4l2_av1_global_motion global_motion; + __u32 flags; +- __u8 frame_type; +- __u8 color_space; +- __u8 clamping_type; +- __u8 loop_filter_type; +- __u8 loop_filter_level; +- __u8 sharpness_level; +- __u8 log2_nbr_of_dct_partitions; +- __u8 prob_intra; +- __u8 prob_last; +- __u8 prob_gf; +- __u8 copy_buffer_to_golden; +- __u8 copy_buffer_to_alternate; +- __u8 reference_type; ++ enum v4l2_av1_frame_type frame_type; ++ __u32 order_hint; ++ __u32 upscaled_width; ++ enum v4l2_av1_interpolation_filter interpolation_filter; ++ enum v4l2_av1_tx_mode tx_mode; ++ __u32 frame_width_minus_1; ++ __u32 frame_height_minus_1; ++ __u16 render_width_minus_1; ++ __u16 render_height_minus_1; ++ ++ __u32 current_frame_id; ++ __u32 buffer_removal_time[V4L2_AV1_MAX_OPERATING_POINTS]; ++ __u8 reserved[4]; ++ __u32 order_hints[V4L2_AV1_TOTAL_REFS_PER_FRAME]; ++ __u64 reference_frame_ts[V4L2_AV1_TOTAL_REFS_PER_FRAME]; ++ __s8 ref_frame_idx[V4L2_AV1_REFS_PER_FRAME]; ++ __u8 refresh_frame_flags; + }; + +-#define V4L2_CID_STATELESS_VP8_ENCODE_QP (V4L2_CID_CODEC_STATELESS_BASE + 502) ++#define V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN 0x1 ++#define V4L2_AV1_FILM_GRAIN_FLAG_UPDATE_GRAIN 0x2 ++#define V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA 0x4 ++#define V4L2_AV1_FILM_GRAIN_FLAG_OVERLAP 0x8 ++#define V4L2_AV1_FILM_GRAIN_FLAG_CLIP_TO_RESTRICTED_RANGE 0x10 + ++#define V4L2_CID_STATELESS_AV1_FILM_GRAIN (V4L2_CID_CODEC_STATELESS_BASE + 505) ++/** ++ * struct v4l2_ctrl_av1_film_grain - AV1 Film Grain parameters. ++ * ++ * Film grain parameters as specified by section 6.8.20 of the AV1 Specification. ++ * ++ * @flags: see V4L2_AV1_FILM_GRAIN_{}. ++ * @cr_mult: represents a multiplier for the cr component used in derivation of ++ * the input index to the cr component scaling function. ++ * @grain_seed: specifies the starting value for the pseudo-random numbers used ++ * during film grain synthesis. ++ * @film_grain_params_ref_idx: indicates which reference frame contains the ++ * film grain parameters to be used for this frame. ++ * @num_y_points: specifies the number of points for the piece-wise linear ++ * scaling function of the luma component. ++ * @point_y_value: represents the x (luma value) coordinate for the i-th point ++ * of the piecewise linear scaling function for luma component. The values are ++ * signaled on the scale of 0..255. In case of 10 bit video, these values ++ * correspond to luma values divided by 4. In case of 12 bit video, these values ++ * correspond to luma values divided by 16. ++ * @point_y_scaling: represents the scaling (output) value for the i-th point ++ * of the piecewise linear scaling function for luma component. ++ * @num_cb_points: specifies the number of points for the piece-wise linear ++ * scaling function of the cb component. ++ * @point_cb_value: represents the x coordinate for the i-th point of the ++ * piece-wise linear scaling function for cb component. The values are signaled ++ * on the scale of 0..255. ++ * @point_cb_scaling: represents the scaling (output) value for the i-th point ++ * of the piecewise linear scaling function for cb component. ++ * @num_cr_points: specifies represents the number of points for the piece-wise ++ * linear scaling function of the cr component. ++ * @point_cr_value: represents the x coordinate for the i-th point of the ++ * piece-wise linear scaling function for cr component. The values are signaled ++ * on the scale of 0..255. ++ * @point_cr_scaling: represents the scaling (output) value for the i-th point ++ * of the piecewise linear scaling function for cr component. ++ * @grain_scaling_minus_8: represents the shift – 8 applied to the values of the ++ * chroma component. The grain_scaling_minus_8 can take values of 0..3 and ++ * determines the range and quantization step of the standard deviation of film ++ * grain. ++ * @ar_coeff_lag: specifies the number of auto-regressive coefficients for luma ++ * and chroma. ++ * @ar_coeffs_y_plus_128: specifies auto-regressive coefficients used for the Y ++ * plane. ++ * @ar_coeffs_cb_plus_128: specifies auto-regressive coefficients used for the U ++ * plane. ++ * @ar_coeffs_cr_plus_128: specifies auto-regressive coefficients used for the V ++ * plane. ++ * @ar_coeff_shift_minus_6: specifies the range of the auto-regressive ++ * coefficients. Values of 0, 1, 2, and 3 correspond to the ranges for ++ * auto-regressive coefficients of [-2, 2), [-1, 1), [-0.5, 0.5) and [-0.25, ++ * 0.25) respectively. ++ * @grain_scale_shift: specifies how much the Gaussian random numbers should be ++ * scaled down during the grain synthesis process. ++ * @cb_mult: represents a multiplier for the cb component used in derivation of ++ * the input index to the cb component scaling function. ++ * @cb_luma_mult: represents a multiplier for the average luma component used in ++ * derivation of the input index to the cb component scaling function. ++ * @cr_luma_mult: represents a multiplier for the average luma component used in ++ * derivation of the input index to the cr component scaling function. ++ * @cb_offset: represents an offset used in derivation of the input index to the ++ * cb component scaling function. ++ * @cr_offset: represents an offset used in derivation of the input index to the ++ * cr component scaling function. ++ * @reserved: padding field. Should be zeroed by applications. ++ */ ++struct v4l2_ctrl_av1_film_grain { ++ __u8 flags; ++ __u8 cr_mult; ++ __u16 grain_seed; ++ __u8 film_grain_params_ref_idx; ++ __u8 num_y_points; ++ __u8 point_y_value[V4L2_AV1_MAX_NUM_Y_POINTS]; ++ __u8 point_y_scaling[V4L2_AV1_MAX_NUM_Y_POINTS]; ++ __u8 num_cb_points; ++ __u8 point_cb_value[V4L2_AV1_MAX_NUM_CB_POINTS]; ++ __u8 point_cb_scaling[V4L2_AV1_MAX_NUM_CB_POINTS]; ++ __u8 num_cr_points; ++ __u8 point_cr_value[V4L2_AV1_MAX_NUM_CR_POINTS]; ++ __u8 point_cr_scaling[V4L2_AV1_MAX_NUM_CR_POINTS]; ++ __u8 grain_scaling_minus_8; ++ __u8 ar_coeff_lag; ++ __u8 ar_coeffs_y_plus_128[V4L2_AV1_AR_COEFFS_SIZE]; ++ __u8 ar_coeffs_cb_plus_128[V4L2_AV1_AR_COEFFS_SIZE]; ++ __u8 ar_coeffs_cr_plus_128[V4L2_AV1_AR_COEFFS_SIZE]; ++ __u8 ar_coeff_shift_minus_6; ++ __u8 grain_scale_shift; ++ __u8 cb_mult; ++ __u8 cb_luma_mult; ++ __u8 cr_luma_mult; ++ __u16 cb_offset; ++ __u16 cr_offset; ++ __u8 reserved[4]; ++}; + /* MPEG-compression definitions kept for backwards compatibility */ + #define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC + #define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS +diff --git a/sys/v4l2codecs/linux/videodev2.h b/sys/v4l2codecs/linux/videodev2.h +index efaa797..7be6238 100644 +--- a/sys/v4l2codecs/linux/videodev2.h ++++ b/sys/v4l2codecs/linux/videodev2.h +@@ -339,6 +339,13 @@ enum v4l2_ycbcr_encoding { + + /* SMPTE 240M -- Obsolete HDTV */ + V4L2_YCBCR_ENC_SMPTE240M = 8, ++ ++ /* ++ * Largest supported encoding value, assigned by the compiler, used by ++ * the framework to check for invalid values. ++ */ ++ V4L2_YCBCR_ENC_LAST, ++ + }; + + /* +@@ -474,7 +481,6 @@ struct v4l2_capability { + #define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */ + + #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ +-#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ + #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ + #define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */ + +@@ -549,6 +555,13 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_RGBX32 v4l2_fourcc('X', 'B', '2', '4') /* 32 RGBX-8-8-8-8 */ + #define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */ + #define V4L2_PIX_FMT_XRGB32 v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */ ++#define V4L2_PIX_FMT_RGBX1010102 v4l2_fourcc('R', 'X', '3', '0') /* 32 RGBX-10-10-10-2 */ ++#define V4L2_PIX_FMT_RGBA1010102 v4l2_fourcc('R', 'A', '3', '0') /* 32 RGBA-10-10-10-2 */ ++#define V4L2_PIX_FMT_ARGB2101010 v4l2_fourcc('A', 'R', '3', '0') /* 32 ARGB-2-10-10-10 */ ++ ++/* RGB formats (6 or 8 bytes per pixel) */ ++#define V4L2_PIX_FMT_BGR48_12 v4l2_fourcc('B', '3', '1', '2') /* 48 BGR 12-bit per component */ ++#define V4L2_PIX_FMT_ABGR64_12 v4l2_fourcc('B', '4', '1', '2') /* 64 BGRA 12-bit per component */ + + /* Grey formats */ + #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ +@@ -556,6 +569,7 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */ + #define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */ + #define V4L2_PIX_FMT_Y12 v4l2_fourcc('Y', '1', '2', ' ') /* 12 Greyscale */ ++#define V4L2_PIX_FMT_Y012 v4l2_fourcc('Y', '0', '1', '2') /* 12 Greyscale */ + #define V4L2_PIX_FMT_Y14 v4l2_fourcc('Y', '1', '4', ' ') /* 14 Greyscale */ + #define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ + #define V4L2_PIX_FMT_Y16_BE v4l2_fourcc_be('Y', '1', '6', ' ') /* 16 Greyscale BE */ +@@ -563,6 +577,7 @@ struct v4l2_pix_format { + /* Grey bit-packed formats */ + #define V4L2_PIX_FMT_Y10BPACK v4l2_fourcc('Y', '1', '0', 'B') /* 10 Greyscale bit-packed */ + #define V4L2_PIX_FMT_Y10P v4l2_fourcc('Y', '1', '0', 'P') /* 10 Greyscale, MIPI RAW10 packed */ ++#define V4L2_PIX_FMT_IPU3_Y10 v4l2_fourcc('i', 'p', '3', 'y') /* IPU3 packed 10-bit greyscale */ + + /* Palette formats */ + #define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ +@@ -586,7 +601,18 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */ + #define V4L2_PIX_FMT_VUYA32 v4l2_fourcc('V', 'U', 'Y', 'A') /* 32 VUYA-8-8-8-8 */ + #define V4L2_PIX_FMT_VUYX32 v4l2_fourcc('V', 'U', 'Y', 'X') /* 32 VUYX-8-8-8-8 */ ++#define V4L2_PIX_FMT_YUVA32 v4l2_fourcc('Y', 'U', 'V', 'A') /* 32 YUVA-8-8-8-8 */ ++#define V4L2_PIX_FMT_YUVX32 v4l2_fourcc('Y', 'U', 'V', 'X') /* 32 YUVX-8-8-8-8 */ + #define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ ++#define V4L2_PIX_FMT_YUV48_12 v4l2_fourcc('Y', '3', '1', '2') /* 48 YUV 4:4:4 12-bit per component */ ++ ++/* ++ * YCbCr packed format. For each Y2xx format, xx bits of valid data occupy the MSBs ++ * of the 16 bit components, and 16-xx bits of zero padding occupy the LSBs. ++ */ ++#define V4L2_PIX_FMT_Y210 v4l2_fourcc('Y', '2', '1', '0') /* 32 YUYV 4:2:2 */ ++#define V4L2_PIX_FMT_Y212 v4l2_fourcc('Y', '2', '1', '2') /* 32 YUYV 4:2:2 */ ++#define V4L2_PIX_FMT_Y216 v4l2_fourcc('Y', '2', '1', '6') /* 32 YUYV 4:2:2 */ + + /* two planes -- one Y, one Cr + Cb interleaved */ + #define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +@@ -596,12 +622,14 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */ + #define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */ + #define V4L2_PIX_FMT_P010 v4l2_fourcc('P', '0', '1', '0') /* 24 Y/CbCr 4:2:0 10-bit per component */ ++#define V4L2_PIX_FMT_P012 v4l2_fourcc('P', '0', '1', '2') /* 24 Y/CbCr 4:2:0 12-bit per component */ + + /* two non contiguous planes - one Y, one Cr + Cb interleaved */ + #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ + #define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */ + #define V4L2_PIX_FMT_NV16M v4l2_fourcc('N', 'M', '1', '6') /* 16 Y/CbCr 4:2:2 */ + #define V4L2_PIX_FMT_NV61M v4l2_fourcc('N', 'M', '6', '1') /* 16 Y/CrCb 4:2:2 */ ++#define V4L2_PIX_FMT_P012M v4l2_fourcc('P', 'M', '1', '2') /* 24 Y/CbCr 4:2:0 12-bit per component */ + + /* three planes - Y Cb, Cr */ + #define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */ +@@ -623,10 +651,16 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_NV12_4L4 v4l2_fourcc('V', 'T', '1', '2') /* 12 Y/CbCr 4:2:0 4x4 tiles */ + #define V4L2_PIX_FMT_NV12_16L16 v4l2_fourcc('H', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 tiles */ + #define V4L2_PIX_FMT_NV12_32L32 v4l2_fourcc('S', 'T', '1', '2') /* 12 Y/CbCr 4:2:0 32x32 tiles */ ++#define V4L2_PIX_FMT_NV15_4L4 v4l2_fourcc('V', 'T', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit 4x4 tiles */ ++#define V4L2_PIX_FMT_P010_4L4 v4l2_fourcc('T', '0', '1', '0') /* 12 Y/CbCr 4:2:0 10-bit 4x4 macroblocks */ ++#define V4L2_PIX_FMT_NV12_8L128 v4l2_fourcc('A', 'T', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */ ++#define V4L2_PIX_FMT_NV12_10BE_8L128 v4l2_fourcc_be('A', 'X', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */ + + /* Tiled YUV formats, non contiguous planes */ + #define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 tiles */ + #define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 tiles */ ++#define V4L2_PIX_FMT_NV12M_8L128 v4l2_fourcc('N', 'A', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */ ++#define V4L2_PIX_FMT_NV12M_10BE_8L128 v4l2_fourcc_be('N', 'T', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */ + + /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ + #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ +@@ -704,6 +738,10 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_FWHT_STATELESS v4l2_fourcc('S', 'F', 'W', 'H') /* Stateless FWHT (vicodec) */ + #define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ + #define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */ ++#define V4L2_PIX_FMT_AV1_FRAME v4l2_fourcc('A', 'V', '1', 'F') /* AV1 parsed frame */ ++#define V4L2_PIX_FMT_SPK v4l2_fourcc('S', 'P', 'K', '0') /* Sorenson Spark */ ++#define V4L2_PIX_FMT_RV30 v4l2_fourcc('R', 'V', '3', '0') /* RealVideo 8 */ ++#define V4L2_PIX_FMT_RV40 v4l2_fourcc('R', 'V', '4', '0') /* RealVideo 9 & 10 */ + + /* Vendor-specific formats */ + #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ +@@ -737,9 +775,14 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_Z16 v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */ + #define V4L2_PIX_FMT_MT21C v4l2_fourcc('M', 'T', '2', '1') /* Mediatek compressed block mode */ + #define V4L2_PIX_FMT_MM21 v4l2_fourcc('M', 'M', '2', '1') /* Mediatek 8-bit block mode, two non-contiguous planes */ ++#define V4L2_PIX_FMT_MT2110T v4l2_fourcc('M', 'T', '2', 'T') /* Mediatek 10-bit block tile mode */ ++#define V4L2_PIX_FMT_MT2110R v4l2_fourcc('M', 'T', '2', 'R') /* Mediatek 10-bit block raster mode */ + #define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */ + #define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */ + #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */ ++#define V4L2_PIX_FMT_QC08C v4l2_fourcc('Q', '0', '8', 'C') /* Qualcomm 8-bit compressed */ ++#define V4L2_PIX_FMT_QC10C v4l2_fourcc('Q', '1', '0', 'C') /* Qualcomm 10-bit compressed */ ++#define V4L2_PIX_FMT_AJPG v4l2_fourcc('A', 'J', 'P', 'G') /* Aspeed JPEG */ + + /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */ + #define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */ +@@ -933,6 +976,21 @@ struct v4l2_jpegcompression { + * M E M O R Y - M A P P I N G B U F F E R S + */ + ++/* ++ * This corresponds to the user space version of timeval ++ * for 64-bit time_t. sparc64 is different from everyone ++ * else, using the microseconds in the wrong half of the ++ * second 64-bit word. ++ */ ++struct __kernel_v4l2_timeval { ++ long long tv_sec; ++#if defined(__sparc__) && defined(__arch64__) ++ int tv_usec; ++ int __pad; ++#else ++ long long tv_usec; ++#endif ++}; + + struct v4l2_requestbuffers { + __u32 count; +@@ -1543,7 +1601,8 @@ struct v4l2_bt_timings { + ((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt)) + #define V4L2_DV_BT_BLANKING_HEIGHT(bt) \ + ((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + \ +- (bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch) ++ ((bt)->interlaced ? \ ++ ((bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch) : 0)) + #define V4L2_DV_BT_FRAME_HEIGHT(bt) \ + ((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt)) + +@@ -1721,6 +1780,8 @@ struct v4l2_ext_control { + __u8 *p_u8; + __u16 *p_u16; + __u32 *p_u32; ++ __s32 *p_s32; ++ __s64 *p_s64; + struct v4l2_area *p_area; + struct v4l2_ctrl_h264_sps *p_h264_sps; + struct v4l2_ctrl_h264_pps *p_h264_pps; +@@ -1740,6 +1801,11 @@ struct v4l2_ext_control { + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; + struct v4l2_ctrl_hevc_scaling_matrix *p_hevc_scaling_matrix; + struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; ++ struct v4l2_ctrl_av1_sequence *p_av1_sequence; ++ struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry; ++ struct v4l2_ctrl_av1_frame *p_av1_frame; ++ struct v4l2_ctrl_av1_film_grain *p_av1_film_grain; ++ struct v4l2_ctrl_vp8_encode_params *p_vp8_encode_params; + void *ptr; + }; + } __attribute__ ((packed)); +@@ -1794,13 +1860,13 @@ enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_H264_SLICE_PARAMS = 0x0203, + V4L2_CTRL_TYPE_H264_DECODE_PARAMS = 0x0204, + V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205, +- V4L2_CTRL_TYPE_H264_ENCODE_PARAMS = 0x0206, +- V4L2_CTRL_TYPE_H264_ENCODE_RC = 0x0207, +- V4L2_CTRL_TYPE_H264_ENCODE_FEEDBACK = 0x0208, ++ V4L2_CTRL_TYPE_H264_ENCODE_PARAMS = 0x0210, ++ V4L2_CTRL_TYPE_H264_ENCODE_RC = 0x0211, + + V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220, + + V4L2_CTRL_TYPE_VP8_FRAME = 0x0240, ++ V4L2_CTRL_TYPE_VP8_ENCODE_PARAMS = 0x0241, + + V4L2_CTRL_TYPE_MPEG2_QUANTISATION = 0x0250, + V4L2_CTRL_TYPE_MPEG2_SEQUENCE = 0x0251, +@@ -1815,7 +1881,10 @@ enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX = 0x0273, + V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS = 0x0274, + +- V4L2_CTRL_TYPE_VP8_ENCODE_PARAMS = 0x0280, ++ V4L2_CTRL_TYPE_AV1_SEQUENCE = 0x280, ++ V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY = 0x281, ++ V4L2_CTRL_TYPE_AV1_FRAME = 0x282, ++ V4L2_CTRL_TYPE_AV1_FILM_GRAIN = 0x283, + }; + + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ +@@ -2379,6 +2448,7 @@ struct v4l2_event_vsync { + #define V4L2_EVENT_CTRL_CH_VALUE (1 << 0) + #define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1) + #define V4L2_EVENT_CTRL_CH_RANGE (1 << 2) ++#define V4L2_EVENT_CTRL_CH_DIMENSIONS (1 << 3) + + struct v4l2_event_ctrl { + __u32 changes; +@@ -2621,5 +2691,10 @@ struct v4l2_create_buffers { + /* Deprecated definitions kept for backwards compatibility */ + #define V4L2_PIX_FMT_HM12 V4L2_PIX_FMT_NV12_16L16 + #define V4L2_PIX_FMT_SUNXI_TILED_NV12 V4L2_PIX_FMT_NV12_32L32 ++/* ++ * This capability was never implemented, anyone using this cap should drop it ++ * from their code. ++ */ ++#define V4L2_CAP_ASYNCIO 0x02000000 + + #endif /* __LINUX_VIDEODEV2_H */ +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0071-v4l2codecs-vp8-add-webp-image-support.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0071-v4l2codecs-vp8-add-webp-image-support.patch new file mode 100644 index 000000000..9a2b89a79 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0071-v4l2codecs-vp8-add-webp-image-support.patch @@ -0,0 +1,252 @@ +From 6adaff584bc42f9bdf2e22aa3dab5f3f72626c03 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 21 Aug 2024 17:35:34 +0200 +Subject: [PATCH] v4l2codecs: vp8: add webp image support + +Add WebP image decoding support to stateless V4L2 VP8 decoder. +A WebP image is composed of a RIFF header followed by a VP8 chunk. + +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gstvp8decoder.c | 10 +++ + sys/v4l2codecs/gstv4l2codecvp8dec.c | 127 ++++++++++++++++++++++++++- + sys/v4l2codecs/linux/v4l2-controls.h | 1 + + 3 files changed, 136 insertions(+), 2 deletions(-) + +diff --git a/gst-libs/gst/codecs/gstvp8decoder.c b/gst-libs/gst/codecs/gstvp8decoder.c +index 411f272..9ef51cf 100644 +--- a/gst-libs/gst/codecs/gstvp8decoder.c ++++ b/gst-libs/gst/codecs/gstvp8decoder.c +@@ -35,6 +35,8 @@ + GST_DEBUG_CATEGORY (gst_vp8_decoder_debug); + #define GST_CAT_DEFAULT gst_vp8_decoder_debug + ++#define WEBP_RIFF_HEADER_SIZE 20 ++ + struct _GstVp8DecoderPrivate + { + gint width; +@@ -376,6 +378,8 @@ gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder, + GstVp8Picture *picture = NULL; + GstFlowReturn ret = GST_FLOW_OK; + GstVp8DecoderOutputFrame output_frame; ++ GstCaps *webp_caps = gst_caps_intersect (gst_caps_new_empty_simple ("image/webp"), ++ self->input_state->caps); + + GST_LOG_OBJECT (self, + "handle frame, PTS: %" GST_TIME_FORMAT ", DTS: %" +@@ -388,6 +392,12 @@ gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder, + goto error; + } + ++ if (!gst_caps_is_empty (webp_caps)) { ++ /* Drop WebP RIFF header */ ++ map.data += WEBP_RIFF_HEADER_SIZE; ++ map.size -= WEBP_RIFF_HEADER_SIZE; ++ } ++ + pres = gst_vp8_parser_parse_frame_header (&priv->parser, + &frame_hdr, map.data, map.size); + +diff --git a/sys/v4l2codecs/gstv4l2codecvp8dec.c b/sys/v4l2codecs/gstv4l2codecvp8dec.c +index ba63a02..6cc5824 100644 +--- a/sys/v4l2codecs/gstv4l2codecvp8dec.c ++++ b/sys/v4l2codecs/gstv4l2codecvp8dec.c +@@ -26,6 +26,8 @@ + #include "gstv4l2codecpool.h" + #include "gstv4l2codecvp8dec.h" + #include "gstv4l2format.h" ++#include ++#include + + #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +@@ -45,7 +47,8 @@ enum + static GstStaticPadTemplate sink_template = + GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SINK_NAME, + GST_PAD_SINK, GST_PAD_ALWAYS, +- GST_STATIC_CAPS ("video/x-vp8") ++ GST_STATIC_CAPS ("video/x-vp8;" ++ "image/webp;") + ); + + static GstStaticPadTemplate alpha_template = +@@ -80,6 +83,10 @@ struct _GstV4l2CodecVp8Dec + + GstMemory *bitstream; + GstMapInfo bitstream_map; ++ ++ gboolean saw_header; ++ guint frame_size; ++ gboolean webp; + }; + + G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecVp8Dec, gst_v4l2_codec_vp8_dec, +@@ -428,7 +435,8 @@ gst_v4l2_codec_vp8_dec_fill_frame_header (GstV4l2CodecVp8Dec * self, + (frame_hdr->show_frame ? V4L2_VP8_FRAME_FLAG_SHOW_FRAME : 0) | + (frame_hdr->mb_no_skip_coeff ? V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF : 0) | + (frame_hdr->sign_bias_golden ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN : 0) | +- (frame_hdr->sign_bias_alternate ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT : 0), ++ (frame_hdr->sign_bias_alternate ? V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT : 0) | ++ (self->webp ? V4L2_VP8_FRAME_FLAG_WEBP : 0), + }; + /* *INDENT-ON* */ + +@@ -579,6 +587,7 @@ gst_v4l2_codec_vp8_dec_decode_picture (GstVp8Decoder * decoder, + static void + gst_v4l2_codec_vp8_dec_reset_picture (GstV4l2CodecVp8Dec * self) + { ++ self->saw_header = FALSE; + if (self->bitstream) { + if (self->bitstream_map.memory) + gst_memory_unmap (self->bitstream, &self->bitstream_map); +@@ -854,6 +863,7 @@ gst_v4l2_codec_vp8_dec_get_property (GObject * object, guint prop_id, + static void + gst_v4l2_codec_vp8_dec_init (GstV4l2CodecVp8Dec * self) + { ++ self->saw_header = FALSE; + } + + static void +@@ -879,6 +889,116 @@ gst_v4l2_codec_vp8_dec_class_init (GstV4l2CodecVp8DecClass * klass) + { + } + ++static gboolean ++gst_v4l2_codec_vp8_dec_set_format (GstVideoDecoder * decoder, ++ GstVideoCodecState * state) ++{ ++ GstV4l2CodecVp8Dec *self = GST_V4L2_CODEC_VP8_DEC (decoder); ++ GstVp8Decoder *vp8dec = GST_VP8_DECODER (decoder); ++ GstCaps *webp_caps = gst_caps_intersect (gst_caps_new_empty_simple ("image/webp"), ++ state->caps); ++ ++ if (vp8dec->input_state) ++ gst_video_codec_state_unref (vp8dec->input_state); ++ vp8dec->input_state = gst_video_codec_state_ref (state); ++ ++ if (!gst_caps_is_empty (webp_caps)) { ++ /* WebP requires parsing for right packetization */ ++ gst_video_decoder_set_packetized (decoder, FALSE); ++ self->webp = TRUE; ++ } ++ ++ return TRUE; ++} ++ ++/* ++ * Webp image RIFF header ++ * ++ * 52 49 46 46 f6 00 00 00 57 45 42 50 56 50 38 20 RIFF....WEBPVP8 ++ * ea 00 00 00 90 09 00 9d 01 2a 30 00 30 00 3e 35 .........*0.0.>5 ++ * | \______/ \______/ ++ * | | \__VP8 startcode ++ * | \__VP8 frame_tag ++ * | ++ * \__End of WebP RIFF header: 20 bytes, then VP8 chunk ++ * ++ * copied from gstwebpdec.c: gst_webp_dec_parse() ++ */ ++static GstFlowReturn ++gst_v4l2_codec_vp8_dec_parse (GstVideoDecoder * decoder, ++ GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos) ++{ ++ gsize toadd = 0; ++ gsize size; ++ gconstpointer data; ++ GstByteReader reader; ++ GstV4l2CodecVp8Dec *self = (GstV4l2CodecVp8Dec *) decoder; ++ ++ size = gst_adapter_available (adapter); ++ GST_DEBUG_OBJECT (decoder, ++ "parsing webp image data (%" G_GSIZE_FORMAT " bytes)", size); ++ ++ if (at_eos) { ++ GST_DEBUG ("Flushing all data out"); ++ toadd = size; ++ ++ /* If we have leftover data, throw it away */ ++ if (!self->saw_header) ++ goto drop_frame; ++ goto have_full_frame; ++ } ++ ++ if (!self->saw_header) { ++ guint32 code; ++ ++ if (size < 12) ++ goto need_more_data; ++ ++ data = gst_adapter_map (adapter, size); ++ gst_byte_reader_init (&reader, data, size); ++ ++ if (!gst_byte_reader_get_uint32_le (&reader, &code)) ++ goto error; ++ ++ if (code == GST_MAKE_FOURCC ('R', 'I', 'F', 'F')) { ++ if (!gst_byte_reader_get_uint32_le (&reader, &self->frame_size)) ++ goto error; ++ ++ if (!gst_byte_reader_get_uint32_le (&reader, &code)) ++ goto error; ++ ++ if (code == GST_MAKE_FOURCC ('W', 'E', 'B', 'P')) ++ self->saw_header = TRUE; ++ } ++ } ++ ++ if (!self->saw_header) ++ goto error; ++ ++ if (size >= (self->frame_size + 8)) { ++ toadd = self->frame_size + 8; ++ self->saw_header = FALSE; ++ ++ goto have_full_frame; ++ } ++ ++need_more_data: ++ return GST_VIDEO_DECODER_FLOW_NEED_DATA; ++ ++have_full_frame: ++ if (toadd) ++ gst_video_decoder_add_to_frame (decoder, toadd); ++ return gst_video_decoder_have_frame (decoder); ++ ++drop_frame: ++ gst_adapter_flush (adapter, size); ++ return GST_FLOW_OK; ++ ++error: ++ GST_WARNING("parsing webp GST_FLOW_ERROR"); ++ return GST_FLOW_ERROR; ++} ++ + static void + gst_v4l2_codec_vp8_dec_subclass_init (GstV4l2CodecVp8DecClass * klass, + GstV4l2CodecDevice * device) +@@ -906,6 +1026,9 @@ gst_v4l2_codec_vp8_dec_subclass_init (GstV4l2CodecVp8DecClass * klass, + decoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_close); + decoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_stop); ++ decoder_class->parse = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_parse); ++ decoder_class->set_format = ++ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_set_format); + decoder_class->negotiate = + GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_dec_negotiate); + decoder_class->decide_allocation = +diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h +index 7a94437..aaeaa58 100644 +--- a/sys/v4l2codecs/linux/v4l2-controls.h ++++ b/sys/v4l2codecs/linux/v4l2-controls.h +@@ -1987,6 +1987,7 @@ struct v4l2_vp8_entropy_coder_state { + #define V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF 0x08 + #define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN 0x10 + #define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT 0x20 ++#define V4L2_VP8_FRAME_FLAG_WEBP 0x40 + + #define V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) \ + (!!((hdr)->flags & V4L2_VP8_FRAME_FLAG_KEY_FRAME)) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0072-jpegparse-don-t-trigger-message-for-failed-com-marke.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0072-jpegparse-don-t-trigger-message-for-failed-com-marke.patch new file mode 100644 index 000000000..2251375db --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0072-jpegparse-don-t-trigger-message-for-failed-com-marke.patch @@ -0,0 +1,47 @@ +From 2331231d12d6865289f2bfc17218e4cb0bc08783 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?= + +Date: Tue, 19 Dec 2023 13:16:34 +0100 +Subject: [PATCH] jpegparse: don't trigger message for failed com marker + +Part-of: +Upstream-Status: Backport +--- + .../gst-plugins-bad/gst/jpegformat/gstjpegparse.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/gst/jpegformat/gstjpegparse.c b/subprojects/gst-plugins-bad/gst/jpegformat/gstjpegparse.c +index 6be24a2879..712ced6efd 100644 +--- a/gst/jpegformat/gstjpegparse.c ++++ b/gst/jpegformat/gstjpegparse.c +@@ -569,8 +569,13 @@ get_utf8_from_data (const guint8 * data, guint16 size) + "GST_TAG_ENCODING", NULL + }; + const char *str = (gchar *) data; ++ char *ret; + +- return gst_tag_freeform_string_to_utf8 (str, size, env_vars); ++ ret = gst_tag_freeform_string_to_utf8 (str, size, env_vars); ++ if (!ret) ++ GST_MEMDUMP ("non-parsed marker data", data, size); ++ ++ return ret; + } + + /* read comment and post as tag */ +@@ -794,10 +799,8 @@ gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame, + parse->state |= GST_JPEG_PARSER_STATE_GOT_SOS; + break; + case GST_JPEG_MARKER_COM: +- if (!gst_jpeg_parse_com (parse, &seg)) { +- GST_ELEMENT_WARNING (parse, STREAM, FORMAT, +- ("Failed to parse com segment"), ("Invalid data")); +- } ++ if (!gst_jpeg_parse_com (parse, &seg)) ++ GST_WARNING_OBJECT (parse, "Failed to parse com segment"); + break; + case GST_JPEG_MARKER_APP0: + if (!gst_jpeg_parse_app0 (parse, &seg)) { +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0073-kmsallocator-fix-stride-with-planar-formats.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0073-kmsallocator-fix-stride-with-planar-formats.patch new file mode 100644 index 000000000..38b2a3b2a --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0073-kmsallocator-fix-stride-with-planar-formats.patch @@ -0,0 +1,32 @@ +From 0707a1a86ca1fbb09b9788087c9af62343f0cb1c Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 24 Sep 2024 17:01:10 +0200 +Subject: [PATCH] kmsallocator: fix stride with planar formats + +This fixes a regression introduced by 37f2d2ac42b4e8033291742b17d304cd46fc66c46c4f52ea20ce27730501348c6e575b8d35580239 + +Fixes #3801 + +Upstream-Status: Backport + +https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7566 +--- + sys/kms/gstkmsallocator.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c +index 3ec2fdb32d..84359b0782 100644 +--- a/sys/kms/gstkmsallocator.c ++++ b/sys/kms/gstkmsallocator.c +@@ -122,7 +122,7 @@ gst_kms_allocator_memory_create (GstKMSAllocator * allocator, + /* Overwrite the video info's stride and offset using the pitch calculcated + * by the kms driver. */ + stride = gst_video_format_info_extrapolate_stride (vinfo->finfo, i, pitch); +- GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = pitch; ++ GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = stride; + GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = offs; + + /* Note that we cannot negotiate special padding betweem each planes, +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0074-Revert-waylandsink-Fix-rendering-of-unaligned-conten.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0074-Revert-waylandsink-Fix-rendering-of-unaligned-conten.patch new file mode 100644 index 000000000..ff9599e2c --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0074-Revert-waylandsink-Fix-rendering-of-unaligned-conten.patch @@ -0,0 +1,29 @@ +From c52e3c333b62e85a7a7f43b17aa9c63516b0f3f0 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Thu, 26 Sep 2024 11:33:06 +0200 +Subject: [PATCH 1/7] Revert "waylandsink: Fix rendering of unaligned content" + +This reverts commit b51049be129e69ed347d36118751dbb6f8c1a36e. + +Upstream-Status: Pending +--- + gst-libs/gst/wayland/gstwllinuxdmabuf.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +index 2fbe57e..deb5d32 100644 +--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c ++++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +@@ -154,9 +154,6 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + } + } + +- //FIXME, otherwise stride effect with non 16-pixels aligned videos +- width = GST_ROUND_UP_16(width); +- + /* Request buffer creation */ + zwp_linux_buffer_params_v1_add_listener (params, ¶ms_listener, &data); + zwp_linux_buffer_params_v1_create (params, width, height, format, flags); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0075-waylandsink-match-drm-kernel-driver-alignment.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0075-waylandsink-match-drm-kernel-driver-alignment.patch new file mode 100644 index 000000000..2057386bc --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0075-waylandsink-match-drm-kernel-driver-alignment.patch @@ -0,0 +1,53 @@ +From 50890a27e2333e3471fe37f05371b4959cf2a363 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 8 Oct 2024 10:51:13 +0200 +Subject: [PATCH 2/7] waylandsink: match drm kernel driver alignment + +This workaround is needed to render not 128 bytes aligned YUV420 +planar content. + +For example: +$> gst-launch-1.0 videotestsrc ! video/x-raw, format=I420 ! waylandsink + +This workaround may be dropped when switching to DRM modifier +implementation introduced in GStreamer-1.24. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + ext/wayland/gstwaylandsink.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index d98c53f..9254b73 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -692,6 +692,8 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + GstWaylandSink *self = GST_WAYLAND_SINK (bsink);; + gboolean use_dmabuf; + GstVideoFormat format; ++ GstVideoAlignment alignment; ++ guint width; + + GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps); + +@@ -699,6 +701,16 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + if (!gst_video_info_from_caps (&self->video_info, caps)) + goto invalid_format; + ++ /* ++ * stick to DRM kernel driver alignment (128 bytes) to avoid ++ * any board effects due to size mismatch when switching ++ * later-on with DRM dumb allocated buffers ++ */ ++ gst_video_alignment_reset (&alignment); ++ width = GST_VIDEO_INFO_WIDTH (&self->video_info); ++ alignment.padding_right = GST_ROUND_UP_128 (width) - width; ++ gst_video_info_align (&self->video_info, &alignment); ++ + format = GST_VIDEO_INFO_FORMAT (&self->video_info); + self->video_info_changed = TRUE; + self->skip_dumb_buffer_copy = FALSE; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0076-gtkwaylandsink-match-drm-kernel-driver-alignment.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0076-gtkwaylandsink-match-drm-kernel-driver-alignment.patch new file mode 100644 index 000000000..212cbf44f --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0076-gtkwaylandsink-match-drm-kernel-driver-alignment.patch @@ -0,0 +1,53 @@ +From 924ff90f6a62112db1eadbcb6808cdbbc6fcfa27 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 8 Oct 2024 17:55:17 +0200 +Subject: [PATCH 3/7] gtkwaylandsink: match drm kernel driver alignment + +This workaround is needed to render not 128 bytes aligned YUV420 +planar content. + +For example: +$> gst-launch-1.0 videotestsrc ! video/x-raw, format=I420 ! gtkwaylandsink + +This workaround may be dropped when switching to DRM modifier +implementation introduced in GStreamer-1.24. + +Signed-off-by: Hugues Fruchet +Upstream-Status: Pending +--- + ext/gtk/gstgtkwaylandsink.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index b55fc3e..1497a11 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -949,6 +949,8 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + gst_gtk_wayland_sink_get_instance_private (self); + gboolean use_dmabuf; + GstVideoFormat format; ++ GstVideoAlignment alignment; ++ guint width; + + GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps); + +@@ -956,6 +958,16 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + if (!gst_video_info_from_caps (&priv->video_info, caps)) + goto invalid_format; + ++ /* ++ * stick to DRM kernel driver alignment (128 bytes) to avoid ++ * any board effects due to size mismatch when switching ++ * later-on with DRM dumb allocated buffers ++ */ ++ gst_video_alignment_reset (&alignment); ++ width = GST_VIDEO_INFO_WIDTH (&priv->video_info); ++ alignment.padding_right = GST_ROUND_UP_128 (width) - width; ++ gst_video_info_align (&priv->video_info, &alignment); ++ + format = GST_VIDEO_INFO_FORMAT (&priv->video_info); + priv->video_info_changed = TRUE; + priv->skip_dumb_buffer_copy = FALSE; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0077-waylandsink-Add-gst_buffer_pool_config_set_params-to.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0077-waylandsink-Add-gst_buffer_pool_config_set_params-to.patch new file mode 100644 index 000000000..c8e949b57 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0077-waylandsink-Add-gst_buffer_pool_config_set_params-to.patch @@ -0,0 +1,29 @@ +From 200e52ab34720d1df1e82a0a51c9ae9c84d20572 Mon Sep 17 00:00:00 2001 +From: "Cheah, Vincent Beng Keat" +Date: Thu, 27 Jul 2023 15:53:55 +0800 +Subject: [PATCH 4/7] waylandsink: Add gst_buffer_pool_config_set_params() to a + pool + +Part-of: + +Upstream-Status: Backport +--- + ext/wayland/gstwaylandsink.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 9254b73..4e6a459 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -768,6 +768,8 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + GstStructure *config; + pool = gst_wl_video_buffer_pool_new (); + config = gst_buffer_pool_get_config (pool); ++ gst_buffer_pool_config_set_params (config, ++ caps, self->video_info.size, 2, 0); + gst_buffer_pool_config_set_allocator (config, + gst_wl_shm_allocator_get (), NULL); + gst_buffer_pool_set_config (pool, config); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch new file mode 100644 index 000000000..ad1cf3d4e --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch @@ -0,0 +1,29 @@ +From 79e5f04fb120c6b1f9fd9e9227aa98036cb6d25f Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 9 Oct 2024 12:06:31 +0200 +Subject: [PATCH 5/7] gtkwaylandsink: Add gst_buffer_pool_config_set_params() + to a pool + +Backport of part of 6e22846301c waylandsink: Add DRM modifiers support + +Upstream-Status: Backport +--- + ext/gtk/gstgtkwaylandsink.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 1497a11..c198954 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1051,6 +1051,8 @@ gst_gtk_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + GstStructure *config; + pool = gst_wl_video_buffer_pool_new (); + config = gst_buffer_pool_get_config (pool); ++ gst_buffer_pool_config_set_params (config, ++ caps, priv->video_info.size, 2, 0); + gst_buffer_pool_config_set_allocator (config, + gst_wl_shm_allocator_get (), NULL); + gst_buffer_pool_set_config (pool, config); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0079-waylandsink-config-buffer-pool-with-query-size-when-.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0079-waylandsink-config-buffer-pool-with-query-size-when-.patch new file mode 100644 index 000000000..dd721b717 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0079-waylandsink-config-buffer-pool-with-query-size-when-.patch @@ -0,0 +1,39 @@ +From ec3edddc3a734784ada9f6a98481c80195b90e75 Mon Sep 17 00:00:00 2001 +From: Hou Qi +Date: Wed, 17 Apr 2024 10:58:00 +0900 +Subject: [PATCH 6/7] waylandsink: config buffer pool with query size when + propose_allocation + +If propose_allocation comes before set_caps, self->video_info +has not been extracted from caps and self->video_info.size is 0. +It causes buffer pool fail to set config . So need to use info +size got from query instead when propose_allocation. + +Part-of: + +Upstream-Status: Backport +--- + ext/wayland/gstwaylandsink.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 4e6a459..066f05e 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -764,6 +764,13 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + + gst_query_parse_allocation (query, &caps, &need_pool); + ++ if (caps == NULL) ++ return FALSE; ++ ++ /* extract info from caps */ ++ if (!gst_video_info_from_caps (&self->video_info, caps)) ++ return FALSE; ++ + if (need_pool) { + GstStructure *config; + pool = gst_wl_video_buffer_pool_new (); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch new file mode 100644 index 000000000..5cc2ef469 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch @@ -0,0 +1,35 @@ +From 657828bf677e2c49e45aed0da180665c2c238232 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 9 Oct 2024 12:08:06 +0200 +Subject: [PATCH 7/7] gtkwaylandsink: config buffer pool with query size when + propose_allocation + +Backport of waylandsink: config buffer pool with query size when propose_allocation +on gtkwaylandsink (was missing upstream). + +Upstream-Status: Submitted +--- + ext/gtk/gstgtkwaylandsink.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index c198954..fbe05be 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1047,6 +1047,13 @@ gst_gtk_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) + + gst_query_parse_allocation (query, &caps, &need_pool); + ++ if (caps == NULL) ++ return FALSE; ++ ++ /* extract info from caps */ ++ if (!gst_video_info_from_caps (&priv->video_info, caps)) ++ return FALSE; ++ + if (need_pool) { + GstStructure *config; + pool = gst_wl_video_buffer_pool_new (); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.20.%.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.20.%.bbappend deleted file mode 100644 index 492211afd..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.20.%.bbappend +++ /dev/null @@ -1,41 +0,0 @@ -FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" - -SRC_URI:append = " \ - file://0001-waylandsink-add-dmabuf-bufferpool.patch \ - file://0002-waylandsink-fix-error-when-mmapping-dmabuf-buffers.patch \ - file://0003-waylandsink-fix-wrong-width-when-creating-dmabuf-dum.patch \ - file://0004-waylandsink-do-not-hardcode-dmabuf-bufferpool-info-s.patch \ - file://0005-waylandsink-increase-max-buffers-to-32-to-enable-dma.patch \ - file://0006-waylandsink-always-select-dmabuf-buffer-pool.patch \ - file://0007-waylandsink-do-not-destroy-pool-twice.patch \ - file://0008-waylandsink-HACK-disable-frame-dropping-while-redraw.patch \ - file://0009-waylandsink-Uprank-to-secondary.patch \ - file://0010-waylandsink-set-video-alignment-to-32-bytes.patch \ - file://0011-waylandsink-fallback-to-shm-if-display-does-not-supp.patch \ - file://0012-waylandsink-XDG-protocol-does-not-work-in-fullscreen.patch \ - file://0013-waylandsink-silently-drop-erroneous-frame.patch \ - file://0014-waylandsink-add-waylandpool-on-meson-build.patch \ - \ - file://0016-Add-new-gtkwaylandsink-element.patch \ - file://0017-waylandsink-fix-shm-pool-wrongly-selected-with-some-.patch \ -" - -PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" -PACKAGECONFIG[gtk3] = "-Dgtk3=enabled,-Dgtk3=disabled,gtk+3" - -PACKAGECONFIG ?= " \ - ${GSTREAMER_ORC} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \ - ${@bb.utils.filter('DISTRO_FEATURES', 'directfb vulkan', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland gtk3', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gl', '', d)} \ - bz2 closedcaption curl dash dtls hls rsvg sbc smoothstreaming sndfile \ - ttml uvch264 webp \ - kms \ - v4l2codecs \ -" - -do_install:append() { - install -d ${D}${includedir}/gstreamer-1.0/wayland - install -m 644 ${S}/gst-libs/gst/wayland/wayland.h ${D}${includedir}/gstreamer-1.0/wayland -} diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend new file mode 100644 index 000000000..fb0a82ad1 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -0,0 +1,98 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI:append = " \ + file://0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch \ + file://0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch \ + file://0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch \ + file://0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch \ + file://0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch \ + file://0006-waylandsink-Refactor-internal-pool-handling.patch \ + file://0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch \ + file://0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch \ + file://0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch \ + file://0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch \ + file://0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch \ + file://0012-bad-Update-doc-cache-for-waylandsink-changes.patch \ + file://0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch \ + file://0015-waylandsink-Uprank-to-secondary.patch \ + file://0016-gstwlshmallocator-correct-WL-API-declaration.patch \ + file://0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch \ + file://0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch \ + file://0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch \ + file://0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch \ + file://0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch \ + file://0024-v4l2codecs-Add-v4l2-encoder-class.patch \ + file://0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch \ + file://0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch \ + file://0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch \ + file://0028-v4l2codecs-add-key-frame-signaling.patch \ + file://0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch \ + file://0031-v4l2codecs-fix-support-of-unaligned-videos.patch \ + file://0033-waylandsink-Fix-rendering-of-unaligned-content.patch \ + file://0035-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch \ + file://0036-v4l2codecs-fix-gst_v4l2_encoder_set_src_fmt-call.patch \ + file://0037-v4l2codecs-add-support-of-encoding-from-RGB.patch \ + file://0038-v4l2codecs-add-colorimetry-support-at-sink-pad.patch \ + file://0040-Revert-v4l2codecs-fix-support-of-unaligned-videos.patch \ + file://0041-h264parser-Define-level-enum-values.patch \ + file://0042-codecparsers-keep-naming-consistency-in-GST_H264_LEV.patch \ + file://0043-v4l2codecs-encoder-No-need-to-set-num_planes-of-plan.patch \ + file://0044-v4l2codecs-add-NV12M-format-to-gstv4l2format.patch \ + file://0045-v4l2codecs-Add-V4L2-H264-stateless-encode-uAPI.patch \ + file://0046-codecs-Add-base-class-for-stateless-h264-encoder.patch \ + file://0047-v4l2codecs-encoder-Add-h264-code-to-v4l2-encoder-cla.patch \ + file://0048-v4l2codecs-Add-V4L2-stateless-H264-encoder.patch \ + file://0049-v4l2codecs-Register-V4L2-stateless-H264-encoder.patch \ + file://0050-v4l2codecs-encoders-Implement-propose-allocation.patch \ + file://0051-v4l2codecs-format-Allow-matching-planar-format-varia.patch \ + file://0052-v4l2codecs-encoder-Save-the-sink-format.patch \ + file://0053-v4l2codecs-format-Prefer-mplane-variants.patch \ + file://0054-v4l2codecs-encoder-Implement-zero-copy-support.patch \ + file://0055-codecs-h264enc-Fix-indentation.patch \ + file://0056-v4l2codecs-h264enc-Fix-indentation.patch \ + file://0057-v4l2codecs-h264enc-Cleanup-caps-colorimetry-framerat.patch \ + file://0058-v4l2codecs-h264enc-add-CABAC-enable-disable-control.patch \ + file://0059-v4l2codecs-h264enc-Don-t-call-virtual-negotiate-dire.patch \ + file://0060-v4l2codecs-vp8enc-Don-t-call-virtual-negotiate-direc.patch \ + file://0061-v4l2codecs-h264enc-Add-profile-negotiation.patch \ + file://0062-codecs-h264encoder-Fix-H.264-QP-max-value.patch \ + file://0063-v4l2codecs-h264dec-Remove-dead-and-broken-code.patch \ + file://0064-codecs-Introduce-a-PID-based-rate-controller.patch \ + file://0065-codecs-h264encoder-Use-the-PID-base-rate-controller.patch \ + file://0066-v4l2codecs-h264enc-Rewrite-profile-level-negotiation.patch \ + file://0067-v4l2codecs-h264enc-De-emulate-SPS-and-PPS-bitstream.patch \ + file://0068-v4l2codecs-vp8-fix-support-of-unaligned-videos.patch \ + file://0069-Force-to-add-math-as-library-dependency.patch \ + file://0070-v4l2codecs-sync-to-kernel-v6.6-headers.patch \ + file://0070-v4l2codecs-encoder-add-rotation-support.patch \ + file://0071-v4l2codecs-vp8-add-webp-image-support.patch \ + file://0072-jpegparse-don-t-trigger-message-for-failed-com-marke.patch \ + file://0073-kmsallocator-fix-stride-with-planar-formats.patch \ + file://0074-Revert-waylandsink-Fix-rendering-of-unaligned-conten.patch \ + file://0075-waylandsink-match-drm-kernel-driver-alignment.patch \ + file://0076-gtkwaylandsink-match-drm-kernel-driver-alignment.patch \ + file://0077-waylandsink-Add-gst_buffer_pool_config_set_params-to.patch \ + file://0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch \ + file://0079-waylandsink-config-buffer-pool-with-query-size-when-.patch \ + file://0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch \ +" + +PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" +PACKAGECONFIG[gtk3] = "-Dgtk3=enabled,-Dgtk3=disabled,gtk+3" + +PACKAGECONFIG ?= " \ + ${GSTREAMER_ORC} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'directfb vulkan', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland gtk3', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gl', '', d)} \ + bz2 closedcaption curl dash dtls hls rsvg sbc smoothstreaming sndfile \ + ttml uvch264 webp \ + faac kms \ + v4l2codecs \ +" + +do_install:append() { + install -d ${D}${includedir}/gstreamer-1.0/wayland + install -m 644 ${S}/gst-libs/gst/wayland/wayland.h ${D}${includedir}/gstreamer-1.0/wayland +} diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-allocators-Add-a-DRM-Dumb-Allocator.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-allocators-Add-a-DRM-Dumb-Allocator.patch new file mode 100644 index 000000000..de64bd0e6 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-allocators-Add-a-DRM-Dumb-Allocator.patch @@ -0,0 +1,955 @@ +From 3923d3d4c64870a02f6f219c3444fdcb196dca23 Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 8 Feb 2023 16:50:10 -0500 +Subject: [PATCH] allocators: Add a DRM Dumb Allocator + +This allow allocating memory from any DRM driver that supports this +method. It additionally allow exporting DMABuf. This allocator depends +on libdrm and will be stubbed if the dependency is missing. This is derived +from kmssink dumb allocator. + +Part-of: +Upstream-Status: Backport +--- + gst-libs/gst/allocators/allocators.h | 1 + + gst-libs/gst/allocators/gstdrmdumb.c | 755 +++++++++++++++++++++++++++ + gst-libs/gst/allocators/gstdrmdumb.h | 88 ++++ + gst-libs/gst/allocators/meson.build | 7 +- + meson.build | 7 + + meson_options.txt | 1 + + 6 files changed, 856 insertions(+), 3 deletions(-) + create mode 100644 gst-libs/gst/allocators/gstdrmdumb.c + create mode 100644 gst-libs/gst/allocators/gstdrmdumb.h + +diff --git a/gst-libs/gst/allocators/allocators.h b/gst-libs/gst/allocators/allocators.h +index b616aa2..65e7e81 100644 +--- a/gst-libs/gst/allocators/allocators.h ++++ b/gst-libs/gst/allocators/allocators.h +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #endif /* __GST_ALLOCATORS_H__ */ + +diff --git a/gst-libs/gst/allocators/gstdrmdumb.c b/gst-libs/gst/allocators/gstdrmdumb.c +new file mode 100644 +index 0000000..c5c4834 +--- /dev/null ++++ b/gst-libs/gst/allocators/gstdrmdumb.c +@@ -0,0 +1,755 @@ ++/* GStreamer ++ * ++ * Copyright (C) 2023 Collabora ++ * Copyright (C) 2016 Igalia ++ * ++ * Authors: ++ * Víctor Manuel Jáquez Leal ++ * Javier Martin ++ * Colin Kinloch ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gstdrmdumb.h" ++#include "gstdmabuf.h" ++ ++/** ++ * SECTION:gstdrmdumb ++ * @title: GstDRMDumbAllocator ++ * @short_description: Memory wrapper for Linux DRM Dumb memory ++ * @see_also: #GstMemory ++ * ++ * Since: 1.24 ++ */ ++ ++#ifdef HAVE_LIBDRM ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#endif ++ ++#define GST_CAT_DEFAULT drmdumballocator_debug ++GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); ++ ++#define GST_DRM_DUMB_MEMORY_TYPE "DRMDumbMemory" ++ ++typedef struct _GstDRMDumbMemory GstDRMDumbMemory; ++struct _GstDRMDumbMemory ++{ ++ GstMemory parent; ++ ++ gpointer ptr; ++ gsize size; ++ guint32 handle; ++ guint refs; ++}; ++ ++struct _GstDRMDumbAllocator ++{ ++ GstAllocator parent; ++ ++ gint drm_fd; ++ gchar *drm_device_path; ++ ++ /* protected by GstDRMDumbAllocator object lock */ ++ GstAllocator *dmabuf_alloc; ++}; ++ ++#define parent_class gst_drm_dumb_allocator_parent_class ++G_DEFINE_TYPE_WITH_CODE (GstDRMDumbAllocator, gst_drm_dumb_allocator, ++ GST_TYPE_ALLOCATOR, ++ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "drmdumballocator", 0, ++ "DRM dumb buffer allocator")); ++enum ++{ ++ PROP_DRM_FD = 1, ++ PROP_DRM_DEVICE_PATH, ++ PROP_N, ++}; ++ ++static GParamSpec *g_props[PROP_N] = { NULL, }; ++ ++/** ++ * gst_is_drm_dumb_memory: ++ * @mem: the memory to be checked ++ * ++ * Returns: %TRUE if @mem is DRM Dumb memory, otherwise %FALSE ++ * ++ * Since: 1.24 ++ */ ++gboolean ++gst_is_drm_dumb_memory (GstMemory * mem) ++{ ++ return gst_memory_is_type (mem, GST_DRM_DUMB_MEMORY_TYPE); ++} ++ ++/** ++ * gst_drm_dumb_memory_get_handle: ++ * @mem: the memory to get the handle from ++ * ++ * Return the DRM buffer object handle associated with @mem. ++ * ++ * Returns: the DRM buffer object handle associated with the memory, or 0. ++ * The handle is still owned by the GstMemory and cannot be used ++ * beyond the lifetime of this GstMemory unless it is being passed ++ * to DRM driver, which does handle a refcount internally. ++ * ++ * Since: 1.24 ++ */ ++guint32 ++gst_drm_dumb_memory_get_handle (GstMemory * mem) ++{ ++ if (!gst_is_drm_dumb_memory (mem)) ++ return 0; ++ ++ return ((GstDRMDumbMemory *) mem)->handle; ++} ++ ++/** ++ * gst_drm_dumb_memory_export_dmabuf: ++ * @mem: the memory to export from ++ * ++ * Exports a DMABuf from the DRM Bumb buffer object. One can check if this ++ * feature is supported using gst_drm_dumb_allocator_has_prime_export(); ++ * ++ * Returns: a #GstMemory from #GstDmaBufAllocator wrapping the exported dma-buf ++ * file descriptor. ++ * ++ * Since: 1.24 ++ */ ++GstMemory * ++gst_drm_dumb_memory_export_dmabuf (GstMemory * mem) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbMemory *drmmem = (GstDRMDumbMemory *) mem; ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (mem->allocator); ++ GstMemory *dmamem; ++ gint ret; ++ gint prime_fd; ++ ++ ret = drmPrimeHandleToFD (alloc->drm_fd, drmmem->handle, ++ DRM_CLOEXEC | DRM_RDWR, &prime_fd); ++ if (ret) ++ goto export_fd_failed; ++ ++ if (G_UNLIKELY (alloc->dmabuf_alloc == NULL)) ++ alloc->dmabuf_alloc = gst_dmabuf_allocator_new (); ++ ++ dmamem = gst_dmabuf_allocator_alloc (alloc->dmabuf_alloc, prime_fd, ++ gst_memory_get_sizes (mem, NULL, NULL)); ++ ++ GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d", drmmem->handle, ++ prime_fd); ++ ++ return dmamem; ++ ++ /* ERRORS */ ++export_fd_failed: ++ { ++ GST_ERROR_OBJECT (alloc, "Failed to export bo handle %d: %s (%d)", ++ drmmem->handle, g_strerror (errno), ret); ++ return NULL; ++ } ++ ++#else ++ return NULL; ++#endif ++} ++ ++#ifdef HAVE_LIBDRM ++static guint32 ++gst_drm_height_from_drm (guint32 drmfmt, guint32 height) ++{ ++ guint32 ret; ++ ++ switch (drmfmt) { ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_P010: ++ case DRM_FORMAT_P016: ++ ret = height * 3 / 2; ++ break; ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ ret = height * 2; ++ break; ++ case DRM_FORMAT_NV24: ++ ret = height * 3; ++ break; ++ default: ++ ret = height; ++ break; ++ } ++ ++ return ret; ++} ++ ++static guint32 ++gst_drm_bpp_from_drm (guint32 drm_fourcc) ++{ ++ guint32 bpp; ++ ++ switch (drm_fourcc) { ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ case DRM_FORMAT_NV24: ++ bpp = 8; ++ break; ++ case DRM_FORMAT_P010: ++ bpp = 10; ++ break; ++ case DRM_FORMAT_UYVY: ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YVYU: ++ case DRM_FORMAT_P016: ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ bpp = 16; ++ break; ++ case DRM_FORMAT_BGR888: ++ case DRM_FORMAT_RGB888: ++ bpp = 24; ++ break; ++ default: ++ bpp = 32; ++ break; ++ } ++ ++ return bpp; ++} ++ ++static gboolean ++check_drm_fd (GstDRMDumbAllocator * alloc) ++{ ++ return alloc->drm_fd > -1; ++} ++#endif ++ ++static void ++gst_drm_dumb_allocator_open_device (GstDRMDumbAllocator * alloc, ++ const gchar * path) ++{ ++#ifdef HAVE_LIBDRM ++ gint fd = -1; ++ ++ /* Ignore default constructor call */ ++ if (path == NULL) ++ return; ++ ++ /* construct only */ ++ g_assert (alloc->drm_fd == -1); ++ g_assert (alloc->drm_device_path == NULL); ++ ++ fd = open (path, O_RDWR | O_CLOEXEC); ++ ++ if (fd < 0) { ++ GST_WARNING_OBJECT (alloc, "Failed to open DRM device at %s", path); ++ return; ++ } ++ ++ alloc->drm_device_path = g_strdup (path); ++ alloc->drm_fd = fd; ++#endif ++} ++ ++static void ++gst_drm_dumb_allocator_set_fd (GstDRMDumbAllocator * alloc, gint fd) ++{ ++#ifdef HAVE_LIBDRM ++ /* Ignore default constructor call */ ++ if (fd == -1) ++ return; ++ ++ /* construct only */ ++ g_assert (alloc->drm_fd == -1); ++ g_assert (alloc->drm_device_path == NULL); ++ ++ if (fd >= 0) { ++ alloc->drm_device_path = drmGetDeviceNameFromFd2 (fd); ++ if (!alloc->drm_device_path) { ++ GST_WARNING_OBJECT (alloc, "Failed to verify DRM fd."); ++ return; ++ } ++ ++ GST_DEBUG_OBJECT (alloc, "Using external FD for %s", ++ alloc->drm_device_path); ++ ++ alloc->drm_fd = dup (fd); ++ } ++#endif ++} ++ ++static void ++gst_drm_dumb_allocator_memory_reset (GstDRMDumbAllocator * alloc, ++ GstDRMDumbMemory * mem) ++{ ++#ifdef HAVE_LIBDRM ++ gint err; ++ struct drm_mode_destroy_dumb arg = { 0, }; ++ ++ if (!mem->size) ++ return; ++ ++ if (!check_drm_fd (alloc)) ++ return; ++ ++ if (mem->ptr != NULL) { ++ GST_WARNING_OBJECT (alloc, "destroying mapped bo (refcount=%d)", mem->refs); ++ munmap (mem->ptr, mem->size); ++ mem->ptr = NULL; ++ } ++ ++ arg.handle = mem->handle; ++ ++ err = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg); ++ if (err) ++ GST_WARNING_OBJECT (alloc, ++ "Failed to destroy dumb buffer object: %s %d", ++ g_strerror (errno), errno); ++ ++ mem->handle = -1; ++ mem->size = 0; ++#endif ++} ++ ++static gboolean ++gst_drm_dumb_allocator_memory_create (GstDRMDumbAllocator * alloc, ++ GstDRMDumbMemory * drmmem, ++ guint32 drm_fourcc, guint32 width, guint32 height, guint32 * out_pitch) ++{ ++#ifdef HAVE_LIBDRM ++ gint ret; ++ struct drm_mode_create_dumb arg = { 0, }; ++ ++ if (drmmem->size) ++ return TRUE; ++ ++ if (!check_drm_fd (alloc)) ++ return FALSE; ++ ++ arg.bpp = gst_drm_bpp_from_drm (drm_fourcc); ++ arg.width = width; ++ arg.height = gst_drm_height_from_drm (drm_fourcc, height); ++ ++ ret = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg); ++ if (ret) ++ goto create_failed; ++ ++ if (!arg.pitch) { ++ GST_DEBUG_OBJECT (alloc, ++ "DRM dumb buffer pitch not set, no need to modify vinfo"); ++ goto done; ++ } ++ ++ GST_DEBUG_OBJECT (alloc, ++ "DRM dumb buffer pitch is set, vinfo modification required"); ++ *out_pitch = arg.pitch; ++ ++done: ++ drmmem->handle = arg.handle; ++ /* will be used used as maxsize of GstMemory */ ++ drmmem->size = arg.size; ++ ++ return TRUE; ++ ++ /* ERRORS */ ++create_failed: ++ { ++ GST_ERROR_OBJECT (alloc, "Failed to create buffer object: %s (%d)", ++ g_strerror (errno), errno); ++ return FALSE; ++ } ++ ++#else ++ return FALSE; ++#endif ++} ++ ++static void ++gst_drm_dumb_allocator_free (GstAllocator * base_alloc, GstMemory * mem) ++{ ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc); ++ GstDRMDumbMemory *drmmem; ++ ++ drmmem = (GstDRMDumbMemory *) mem; ++ ++ gst_drm_dumb_allocator_memory_reset (alloc, drmmem); ++ g_free (drmmem); ++} ++ ++static void ++gst_drm_dumb_allocator_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (object); ++ ++ switch (prop_id) { ++ case PROP_DRM_FD: ++ gst_drm_dumb_allocator_set_fd (alloc, g_value_get_int (value)); ++ break; ++ case PROP_DRM_DEVICE_PATH: ++ gst_drm_dumb_allocator_open_device (alloc, g_value_get_string (value)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_drm_dumb_allocator_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (object); ++ ++ switch (prop_id) { ++ case PROP_DRM_FD: ++ g_value_set_int (value, alloc->drm_fd); ++ break; ++ case PROP_DRM_DEVICE_PATH: ++ g_value_set_string (value, alloc->drm_device_path); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_drm_dumb_allocator_finalize (GObject * obj) ++{ ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (obj); ++ ++ if (alloc->dmabuf_alloc) ++ gst_object_unref (alloc->dmabuf_alloc); ++ ++ g_free (alloc->drm_device_path); ++ alloc->drm_device_path = NULL; ++ ++#ifdef HAVE_LIBDRM ++ if (alloc->drm_fd >= 0) { ++ close (alloc->drm_fd); ++ alloc->drm_fd = -1; ++ } ++#endif ++ ++ G_OBJECT_CLASS (parent_class)->finalize (obj); ++} ++ ++static void ++gst_drm_dumb_allocator_class_init (GstDRMDumbAllocatorClass * klass) ++{ ++ GObjectClass *gobject_class; ++ GstAllocatorClass *allocator_class; ++ ++ allocator_class = GST_ALLOCATOR_CLASS (klass); ++ gobject_class = G_OBJECT_CLASS (klass); ++ ++ allocator_class->free = gst_drm_dumb_allocator_free; ++ ++ gobject_class->set_property = gst_drm_dumb_allocator_set_property; ++ gobject_class->get_property = gst_drm_dumb_allocator_get_property; ++ gobject_class->finalize = gst_drm_dumb_allocator_finalize; ++ ++ /** ++ * GstDRMDumbAllocator:drm-fd: ++ * ++ * Since: 1.24 ++ */ ++ g_props[PROP_DRM_FD] = g_param_spec_int ("drm-fd", "DRM fd", ++ "DRM file descriptor", -1, G_MAXINT, -1, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ ++ /** ++ * GstDRMDumbAllocator:drm-device-path: ++ * ++ * Since: 1.24 ++ */ ++ g_props[PROP_DRM_DEVICE_PATH] = g_param_spec_string ("drm-device-path", ++ "DRM device path", "DRM device path", NULL, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); ++ ++ g_object_class_install_properties (gobject_class, PROP_N, g_props); ++} ++ ++static gpointer ++gst_drm_dumb_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (mem->allocator); ++ GstDRMDumbMemory *drmmem; ++ gint err; ++ gpointer out; ++ struct drm_mode_map_dumb arg = { 0, }; ++ ++ ++ if (!check_drm_fd (alloc)) ++ return NULL; ++ ++ drmmem = (GstDRMDumbMemory *) mem; ++ if (!drmmem->size) ++ return NULL; ++ ++ /* Reuse existing buffer object mapping if possible */ ++ if (drmmem->ptr != NULL) { ++ goto out; ++ } ++ ++ arg.handle = drmmem->handle; ++ ++ err = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); ++ if (err) { ++ GST_ERROR_OBJECT (alloc, "Failed to get offset of buffer object: %s %d", ++ g_strerror (errno), errno); ++ return NULL; ++ } ++ ++ out = mmap (0, drmmem->size, ++ PROT_READ | PROT_WRITE, MAP_SHARED, alloc->drm_fd, arg.offset); ++ if (out == MAP_FAILED) { ++ GST_ERROR_OBJECT (alloc, "Failed to map dumb buffer object: %s %d", ++ g_strerror (errno), errno); ++ return NULL; ++ } ++ drmmem->ptr = out; ++ ++out: ++ g_atomic_int_inc (&drmmem->refs); ++ return drmmem->ptr; ++ ++#else ++ return NULL; ++#endif ++} ++ ++static void ++gst_drm_dumb_memory_unmap (GstMemory * mem) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbMemory *drmmem; ++ ++ if (!check_drm_fd ((GstDRMDumbAllocator *) mem->allocator)) ++ return; ++ ++ drmmem = (GstDRMDumbMemory *) mem; ++ if (!drmmem->size) ++ return; ++ ++ if (g_atomic_int_dec_and_test (&drmmem->refs)) { ++ munmap (drmmem->ptr, drmmem->size); ++ drmmem->ptr = NULL; ++ } ++#endif ++} ++ ++static void ++gst_drm_dumb_allocator_init (GstDRMDumbAllocator * alloc) ++{ ++ GstAllocator *base_alloc = GST_ALLOCATOR_CAST (alloc); ++ ++ alloc->drm_fd = -1; ++ alloc->drm_device_path = NULL; ++ ++ base_alloc->mem_type = GST_DRM_DUMB_MEMORY_TYPE; ++ base_alloc->mem_map = gst_drm_dumb_memory_map; ++ base_alloc->mem_unmap = gst_drm_dumb_memory_unmap; ++ /* Use the default, fallback copy function */ ++ ++ GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); ++} ++ ++#ifdef HAVE_LIBDRM ++static gboolean ++check_cap (GstDRMDumbAllocator * alloc) ++{ ++ gint ret; ++ guint64 has_dumb = 0; ++ ++ if (!alloc) ++ return FALSE; ++ ++ if (!check_drm_fd (alloc)) ++ return FALSE; ++ ++ ret = drmGetCap (alloc->drm_fd, DRM_CAP_DUMB_BUFFER, &has_dumb); ++ if (ret) ++ GST_WARNING_OBJECT (alloc, "could not get dumb buffer capability"); ++ ++ return ! !(has_dumb); ++} ++#endif ++ ++/** ++ * gst_drm_dumb_allocator_new_with_fd: ++ * @drm_fd: file descriptor of the DRM device ++ * ++ * Creates a new #GstDRMDumbAllocator for the specific file desciptor. This ++ * function can fail if the file descriptor is not a DRM device or if ++ * the DRM device does not support DUMB allocation. ++ * ++ * Returns: (transfer full) (nullable): a new DRM Dumb allocator. Use gst_object_unref() ++ * to release the allocator after usage. ++ * ++ * Since: 1.24 ++ ++ */ ++GstAllocator * ++gst_drm_dumb_allocator_new_with_fd (gint drm_fd) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbAllocator *alloc; ++ ++ alloc = g_object_new (GST_TYPE_DRM_DUMB_ALLOCATOR, "drm-fd", drm_fd, NULL); ++ gst_object_ref_sink (alloc); ++ ++ if (!check_drm_fd (alloc)) ++ g_clear_object (&alloc); ++ ++ if (!check_cap (alloc)) ++ g_clear_object (&alloc); ++ ++ return alloc ? GST_ALLOCATOR (alloc) : NULL; ++ ++#else ++ return NULL; ++#endif ++} ++ ++/** ++ * gst_drm_dumb_allocator_new_with_device_path: ++ * @drm_device_path: path to the DRM device to open ++ * ++ * Creates a new #GstDRMDumbAllocator for the specific device path. This ++ * function can fail if the path does not exist, is not a DRM device or if ++ * the DRM device doesnot support DUMB allocation. ++ * ++ * Returns: (transfer full) (nullable): a new DRM Dumb allocator. Use gst_object_unref() ++ * to release the allocator after usage. ++ * ++ * Since: 1.24 ++ */ ++GstAllocator * ++gst_drm_dumb_allocator_new_with_device_path (const gchar * drm_device_path) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbAllocator *alloc; ++ ++ alloc = g_object_new (GST_TYPE_DRM_DUMB_ALLOCATOR, ++ "drm-device-path", drm_device_path, NULL); ++ gst_object_ref_sink (alloc); ++ ++ if (!check_drm_fd (alloc)) ++ g_clear_object (&alloc); ++ ++ if (!check_cap (alloc)) ++ g_clear_object (&alloc); ++ ++ return alloc ? GST_ALLOCATOR (alloc) : NULL; ++ ++#else ++ return NULL; ++#endif ++} ++ ++/** ++ * gst_drm_dumb_allocator_alloc: ++ * @allocator: the allocator instance ++ * @drm_fourcc: the DRM format to allocate for ++ * @width: padded width for this allocation ++ * @height: padded height for this allocation ++ * @out_pitch: (out): the pitch as returned by the driver ++ * ++ * Allocated a DRM buffer object for the specific @drm_fourcc, @width and ++ * @height. Note that the DRM Dumb allocation interface is agnostic to the ++ * pixel format. This @drm_fourcc is converted into a bpp (bit-per-pixel) ++ * number and the height is scaled according to the sub-sampling. ++ * ++ * Returns: (transfer full): a new DRM Dumb #GstMemory. Use gst_memory_unref() ++ * to release the memory after usage. ++ * ++ * Since: 1.24 ++ */ ++GstMemory * ++gst_drm_dumb_allocator_alloc (GstAllocator * base_alloc, ++ guint32 drm_fourcc, guint32 width, guint32 height, guint32 * out_pitch) ++{ ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc); ++ GstDRMDumbMemory *drmmem; ++ GstMemory *mem; ++ ++ drmmem = g_new0 (GstDRMDumbMemory, 1); ++ mem = GST_MEMORY_CAST (drmmem); ++ ++ if (!gst_drm_dumb_allocator_memory_create (alloc, drmmem, ++ drm_fourcc, width, height, out_pitch)) { ++ g_free (drmmem); ++ return NULL; ++ } ++ ++ gst_memory_init (mem, 0, base_alloc, NULL, drmmem->size, 0, 0, drmmem->size); ++ return mem; ++} ++ ++/** ++ * gst_drm_dumb_allocator_has_prime_export: ++ * @allocator: the #GstAllocator ++ * ++ * This function allow verifying if the driver support dma-buf exportation. ++ * ++ * Returns: %TRUE if the allocator support exporting dma-buf. ++ * ++ * Since: 1.24 ++ */ ++gboolean ++gst_drm_dumb_allocator_has_prime_export (GstAllocator * base_alloc) ++{ ++#ifdef HAVE_LIBDRM ++ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc); ++ gint ret; ++ guint64 has_prime = 0; ++ ++ if (!check_drm_fd (alloc)) ++ return FALSE; ++ ++ ret = drmGetCap (alloc->drm_fd, DRM_CAP_PRIME, &has_prime); ++ if (ret) ++ GST_WARNING_OBJECT (alloc, "could not get prime capability"); ++ ++ return ! !(has_prime & DRM_PRIME_CAP_EXPORT); ++ ++#else ++ return FALSE; ++#endif ++} +diff --git a/gst-libs/gst/allocators/gstdrmdumb.h b/gst-libs/gst/allocators/gstdrmdumb.h +new file mode 100644 +index 0000000..fa5f5ed +--- /dev/null ++++ b/gst-libs/gst/allocators/gstdrmdumb.h +@@ -0,0 +1,88 @@ ++/* GStreamer ++ * ++ * Copyright (C) 2016 Igalia ++ * Copyright (C) 2023 Collabora ++ * ++ * Authors: ++ * Víctor Manuel Jáquez Leal ++ * Javier Martin ++ * Colin Kinloch ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++/** ++ * GstDRMDumbAllocator: ++ * ++ * Private intance object for #GstDRMDumbAllocator. ++ * ++ * Since: 1.24 ++ */ ++ ++/** ++ * GstDRMDumbAllocatorClass.parent_class: ++ * ++ * Parent Class. ++ * ++ * Since: 1.24 ++ */ ++ ++/** ++ * GST_TYPE_DRM_DUMB_ALLOCATOR: ++ * ++ * Macro that returns the #GstDRMDumbAllocator type. ++ * ++ * Since: 1.24 ++ */ ++#define GST_TYPE_DRM_DUMB_ALLOCATOR gst_drm_dumb_allocator_get_type () ++GST_ALLOCATORS_API ++G_DECLARE_FINAL_TYPE (GstDRMDumbAllocator, gst_drm_dumb_allocator, ++ GST, DRM_DUMB_ALLOCATOR, GstAllocator); ++ ++GST_ALLOCATORS_API ++gboolean gst_is_drm_dumb_memory (GstMemory * mem); ++ ++GST_ALLOCATORS_API ++guint32 gst_drm_dumb_memory_get_handle (GstMemory * mem); ++ ++GST_ALLOCATORS_API ++GstMemory* gst_drm_dumb_memory_export_dmabuf (GstMemory * mem); ++ ++GST_ALLOCATORS_API ++GstAllocator * gst_drm_dumb_allocator_new_with_fd (gint drm_fd); ++ ++GST_ALLOCATORS_API ++GstAllocator * gst_drm_dumb_allocator_new_with_device_path (const gchar *drm_device_path); ++ ++GST_ALLOCATORS_API ++GstMemory * gst_drm_dumb_allocator_alloc (GstAllocator * allocator, ++ guint32 drm_fourcc, ++ guint32 width, ++ guint32 height, ++ guint32 *out_pitch); ++ ++GST_ALLOCATORS_API ++gboolean gst_drm_dumb_allocator_has_prime_export (GstAllocator * allocator); ++ ++G_END_DECLS +diff --git a/gst-libs/gst/allocators/meson.build b/gst-libs/gst/allocators/meson.build +index 6ee0bfd..879c2fc 100644 +--- a/gst-libs/gst/allocators/meson.build ++++ b/gst-libs/gst/allocators/meson.build +@@ -4,10 +4,11 @@ gst_allocators_headers = files([ + 'gstfdmemory.h', + 'gstphysmemory.h', + 'gstdmabuf.h', ++ 'gstdrmdumb.h', + ]) + install_headers(gst_allocators_headers, subdir : 'gstreamer-1.0/gst/allocators/') + +-gst_allocators_sources = files([ 'gstdmabuf.c', 'gstfdmemory.c', 'gstphysmemory.c']) ++gst_allocators_sources = files([ 'gstdrmdumb.c', 'gstdmabuf.c', 'gstfdmemory.c', 'gstphysmemory.c']) + gstallocators = library('gstallocators-@0@'.format(api_version), + gst_allocators_sources, + c_args : gst_plugins_base_args + ['-DBUILDING_GST_ALLOCATORS', '-DG_LOG_DOMAIN="GStreamer-Allocators"'], +@@ -16,12 +17,12 @@ gstallocators = library('gstallocators-@0@'.format(api_version), + soversion : soversion, + darwin_versions : osxversion, + install : true, +- dependencies : [gst_dep], ++ dependencies : [libdrm_dep, gst_dep], + ) + + pkg_name = 'gstreamer-allocators-1.0' + pkgconfig.generate(gstallocators, +- libraries : [gst_dep], ++ libraries : [libdrm_dep, gst_dep], + variables : pkgconfig_variables, + subdirs : pkgconfig_subdirs, + name : pkg_name, +diff --git a/meson.build b/meson.build +index 19a8347..699af91 100644 +--- a/meson.build ++++ b/meson.build +@@ -305,6 +305,13 @@ if get_option('default_library') == 'static' + gst_plugins_base_args += ['-DGST_STATIC_COMPILATION'] + endif + ++libdrm_dep = dependency('libdrm', version : '>= 2.4.98', ++ required : get_option('drm'), ++ fallback: ['libdrm', 'ext_libdrm'] ++) ++ ++core_conf.set('HAVE_LIBDRM', libdrm_dep.found()) ++ + # X11 checks are for sys/ and tests/ + x11_dep = dependency('x11', required : get_option('x11')) + # GIO is used by the GIO plugin, and by the TCP, SDP, and RTSP plugins +diff --git a/meson_options.txt b/meson_options.txt +index 50ec6aa..0db1840 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -36,6 +36,7 @@ option('audiorate', type : 'feature', value : 'auto') + option('audioresample', type : 'feature', value : 'auto') + option('audiotestsrc', type : 'feature', value : 'auto') + option('compositor', type : 'feature', value : 'auto') ++option('drm', type : 'feature', value : 'auto') + option('encoding', type : 'feature', value : 'auto') + option('gio', type : 'feature', value : 'auto') + option('gio-typefinder', type : 'feature', value : 'auto') +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-playbin2-disable-any-default-video-processing.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-playbin2-disable-any-default-video-processing.patch deleted file mode 100644 index 09e250734..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0001-playbin2-disable-any-default-video-processing.patch +++ /dev/null @@ -1,34 +0,0 @@ -From a698ef5806e50b614f49decb4eb2a2b4404f56e5 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Wed, 23 Jan 2019 14:18:28 +0100 -Subject: [PATCH 1/2] playbin2: disable any default video processing - -Do not enable by default software video processing -to not degrade performances. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - gst/playback/gstplaybin2.c | 5 ++--- - 1 file changed, 2 insertions(+), 3 deletions(-) - -diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c -index a2e9ed4..4c0163e 100644 ---- a/gst/playback/gstplaybin2.c -+++ b/gst/playback/gstplaybin2.c -@@ -499,9 +499,8 @@ struct _GstPlayBinClass - #define DEFAULT_URI NULL - #define DEFAULT_SUBURI NULL - #define DEFAULT_SOURCE NULL --#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \ -- GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \ -- GST_PLAY_FLAG_SOFT_COLORBALANCE -+#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_SOFT_VOLUME -+ - #define DEFAULT_N_VIDEO 0 - #define DEFAULT_CURRENT_VIDEO -1 - #define DEFAULT_N_AUDIO 0 --- -2.7.4 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-DRAFT-video-Allow-frame-copy-from-different-dimensio.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-DRAFT-video-Allow-frame-copy-from-different-dimensio.patch new file mode 100644 index 000000000..c545b8afc --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-DRAFT-video-Allow-frame-copy-from-different-dimensio.patch @@ -0,0 +1,78 @@ +From 4e3dc15635d1a3fbd49fca2c5478bd10623fac7d Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Mon, 6 Nov 2023 16:35:47 -0500 +Subject: [PATCH] DRAFT: video: Allow frame copy from different dimensions + +This removes the limitation regarding the frame dimensions when copying. +This leaves it to the implementation to decided if doing so is +acceptable or not. A flag is introduced to allow filling the padding +when a smaller images is copied into a bigger one. + +TODO: + - Add filling flag, needed by encoders + - Add some unit tests + +Upstream-Status: Backport +--- + gst-libs/gst/video/video-frame.c | 19 +++++++------------ + 1 file changed, 7 insertions(+), 12 deletions(-) + +diff --git a/gst-libs/gst/video/video-frame.c b/gst-libs/gst/video/video-frame.c +index bfb57e4..787af67 100644 +--- a/gst-libs/gst/video/video-frame.c ++++ b/gst-libs/gst/video/video-frame.c +@@ -319,8 +319,6 @@ gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src, + + finfo = dinfo->finfo; + +- g_return_val_if_fail (dinfo->width <= sinfo->width +- && dinfo->height <= sinfo->height, FALSE); + g_return_val_if_fail (finfo->n_planes > plane, FALSE); + + sp = src->data[plane]; +@@ -333,15 +331,19 @@ gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src, + } + + gst_video_format_info_component (finfo, plane, comp); +- w = GST_VIDEO_FRAME_COMP_WIDTH (dest, +- comp[0]) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]); ++ w = MIN (GST_VIDEO_FRAME_COMP_WIDTH (dest, comp[0]), ++ GST_VIDEO_FRAME_COMP_WIDTH (src, comp[0])) * ++ GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]); ++ ++ + /* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have + * pstride == 0 */ + if (w == 0) + w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane), + GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane)); + +- h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]); ++ h = MIN (GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]), ++ GST_VIDEO_FRAME_COMP_HEIGHT (src, comp[0])); + + ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane); + ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane); +@@ -409,19 +411,12 @@ gboolean + gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src) + { + guint i, n_planes; +- const GstVideoInfo *sinfo; + GstVideoInfo *dinfo; + + g_return_val_if_fail (dest != NULL, FALSE); + g_return_val_if_fail (src != NULL, FALSE); + +- sinfo = &src->info; + dinfo = &dest->info; +- +- g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE); +- g_return_val_if_fail (dinfo->width <= sinfo->width +- && dinfo->height <= sinfo->height, FALSE); +- + n_planes = dinfo->finfo->n_planes; + + for (i = 0; i < n_planes; i++) +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-playbin3-disable-any-default-video-processing.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-playbin3-disable-any-default-video-processing.patch deleted file mode 100644 index 314443d97..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base/0002-playbin3-disable-any-default-video-processing.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 17a847850f31be11c0c98d31cdd4d62856bfa313 Mon Sep 17 00:00:00 2001 -From: Hugues Fruchet -Date: Wed, 23 Jan 2019 14:16:36 +0100 -Subject: [PATCH 2/2] playbin3: disable any default video processing - -Do not enable by default software video processing -to not degrade performances. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Hugues Fruchet ---- - gst/playback/gstplaybin3.c | 5 ++--- - 1 file changed, 2 insertions(+), 3 deletions(-) - -diff --git a/gst/playback/gstplaybin3.c b/gst/playback/gstplaybin3.c -index 027766b..9103cb1 100644 ---- a/gst/playback/gstplaybin3.c -+++ b/gst/playback/gstplaybin3.c -@@ -520,9 +520,8 @@ struct _GstPlayBin3Class - /* props */ - #define DEFAULT_URI NULL - #define DEFAULT_SUBURI NULL --#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \ -- GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \ -- GST_PLAY_FLAG_SOFT_COLORBALANCE | GST_PLAY_FLAG_BUFFERING -+#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | \ -+ GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_BUFFERING - #define DEFAULT_CURRENT_VIDEO -1 - #define DEFAULT_CURRENT_AUDIO -1 - #define DEFAULT_CURRENT_TEXT -1 --- -2.7.4 - diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.20.%.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.22.12.bbappend similarity index 87% rename from meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.20.%.bbappend rename to meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.22.12.bbappend index e557486c3..4d6c8663e 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.20.%.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.22.12.bbappend @@ -1,7 +1,9 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}/:" -SRC_URI:append = " file://0001-playbin2-disable-any-default-video-processing.patch " -SRC_URI:append = " file://0002-playbin3-disable-any-default-video-processing.patch " +SRC_URI:append = " \ + file://0001-allocators-Add-a-DRM-Dumb-Allocator.patch \ + file://0002-DRAFT-video-Allow-frame-copy-from-different-dimensio.patch \ + " PACKAGECONFIG ?= " \ ${GSTREAMER_ORC} \ diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.20.%.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.22.12.bbappend similarity index 76% rename from meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.20.%.bbappend rename to meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.22.12.bbappend index d6ce07961..4eb778aca 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.20.%.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.22.12.bbappend @@ -2,8 +2,9 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}/:" PACKAGECONFIG ?= " \ ${GSTREAMER_ORC} \ + ${PACKAGECONFIG_SOUP} \ ${@bb.utils.filter('DISTRO_FEATURES', 'pulseaudio x11', d)} \ - bz2 cairo flac gdk-pixbuf gudev jpeg lame libpng mpg123 soup speex taglib v4l2 \ + bz2 cairo flac gdk-pixbuf gudev jpeg lame libpng mpg123 speex taglib v4l2 \ libv4l2 \ ${@bb.utils.contains_any('DISTRO_FEATURES', '${GTK3DISTROFEATURES}', 'gtk', '', d)} \ " @@ -15,5 +16,3 @@ EXTRA_OEMESON += " \ # remove qt5 for the moment PACKAGECONFIG:remove = " qt5" - -RDEPENDS:${PN}-soup += "libsoup-2.4" diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0/0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0/0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch new file mode 100644 index 000000000..3fb5dfce1 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0/0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch @@ -0,0 +1,69 @@ +From c590304b77a723b9b69f3d2d2f777dc27ac4901c Mon Sep 17 00:00:00 2001 +From: Nicolas Dufresne +Date: Wed, 15 Feb 2023 13:10:25 -0500 +Subject: [PATCH] basesink: Add GST_BASE_SINK_FLOW_DROPPED return value + +This new flow return value can be used in ::render virtual method +to signal that a frame is not being rendered. + +Part-of: +Upstream-Status: Backport +--- + libs/gst/base/gstbasesink.c | 8 ++++++++ + libs/gst/base/gstbasesink.h | 14 ++++++++++++++ + 2 files changed, 22 insertions(+) + +diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c +index 294d946..a3abcc7 100644 +--- a/libs/gst/base/gstbasesink.c ++++ b/libs/gst/base/gstbasesink.c +@@ -3950,6 +3950,11 @@ again: + + if (bclass->render) + ret = bclass->render (basesink, GST_BUFFER_CAST (obj)); ++ ++ if (ret == GST_BASE_SINK_FLOW_DROPPED) { ++ ret = GST_FLOW_OK; ++ goto dropped; ++ } + } else { + GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj); + +@@ -3959,6 +3964,9 @@ again: + /* Set the first buffer and buffer list to be included in last sample */ + gst_base_sink_set_last_buffer (basesink, sync_buf); + gst_base_sink_set_last_buffer_list (basesink, buffer_list); ++ ++ /* Not currently supported */ ++ g_assert (ret != GST_BASE_SINK_FLOW_DROPPED); + } + + if (ret == GST_FLOW_STEP) +diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h +index 8edae03..2cf799a 100644 +--- a/libs/gst/base/gstbasesink.h ++++ b/libs/gst/base/gstbasesink.h +@@ -58,6 +58,20 @@ G_BEGIN_DECLS + #define GST_BASE_SINK_PREROLL_SIGNAL(obj) g_cond_signal (GST_BASE_SINK_GET_PREROLL_COND (obj)); + #define GST_BASE_SINK_PREROLL_BROADCAST(obj) g_cond_broadcast (GST_BASE_SINK_GET_PREROLL_COND (obj)); + ++/** ++ * GST_BASE_SINK_FLOW_DROPPED: ++ * ++ * A #GstFlowReturn that can be returned from ++ * #GstBaseSinkClass::render to indicate that the output buffer was not ++ * rendered. ++ * ++ * Note that this is currently not support for #GstBaseSinkClass::render_list ++ * virtual method. ++ * ++ * Since: 1.24 ++ */ ++#define GST_BASE_SINK_FLOW_DROPPED GST_FLOW_CUSTOM_SUCCESS ++ + typedef struct _GstBaseSink GstBaseSink; + typedef struct _GstBaseSinkClass GstBaseSinkClass; + typedef struct _GstBaseSinkPrivate GstBaseSinkPrivate; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0_1.22.12.bbappend new file mode 100644 index 000000000..6c7087aa0 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0_1.22.12.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI:append = " \ + file://0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch \ + "