d3d12: Add H.264 video encoder

Adding video encoder element

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5808>
This commit is contained in:
Seungha Yang 2024-01-06 21:26:46 +09:00 committed by GStreamer Marge Bot
parent bf420a3a20
commit 4e1bf149d0
17 changed files with 6151 additions and 1 deletions

View file

@ -11614,6 +11614,290 @@
},
"rank": "none"
},
"d3d12h264enc": {
"author": "Seungha Yang <seungha@centricular.com>",
"description": "Direct3D12 H.264 Video Encoder",
"hierarchy": [
"GstD3D12H264Enc",
"GstD3D12Encoder",
"GstVideoEncoder",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstPreset"
],
"klass": "Codec/Encoder/Video/Hardware",
"pad-templates": {
"sink": {
"caps": "video/x-raw(memory:D3D12Memory):\n format: NV12\n width: [ 16, 4096 ]\n height: [ 16, 4096 ]\n interlace-mode: progressive\nvideo/x-raw:\n format: NV12\n width: [ 16, 4096 ]\n height: [ 16, 4096 ]\n interlace-mode: progressive\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "video/x-h264:\n width: [ 16, 4096 ]\n height: [ 16, 4096 ]\n stream-format: byte-stream\n alignment: au\n profile: { (string)high, (string)main, (string)constrained-baseline }\n",
"direction": "src",
"presence": "always"
}
},
"properties": {
"aud": {
"blurb": "Use AU delimiter",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "true",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"bitrate": {
"blurb": "Target bitrate in kbits/second. Used for \"cbr\", \"vbr\", and \"qvbr\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "2000",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"cc-insert": {
"blurb": "Closed Caption insert mode",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "insert (0)",
"mutable": "null",
"readable": true,
"type": "GstD3D12EncoderSeiInsertMode",
"writable": true
},
"frame-analysis": {
"blurb": "Enable 2 pass encoding if supported by hardware",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"gop-size": {
"blurb": "Size of GOP (0 = infinite)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "60",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"max-bitrate": {
"blurb": "Peak bitrate in kbits/second. Used for \"vbr\", and \"qvbr\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "4000",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-b": {
"blurb": "Constant QP value for B frames. Used for \"cqp\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "23",
"max": "51",
"min": "1",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-i": {
"blurb": "Constant QP value for I frames. Used for \"cqp\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "23",
"max": "51",
"min": "1",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-init": {
"blurb": "Initial QP value. Used for \"cbr\", \"vbr\", and \"qvbr\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "51",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-max": {
"blurb": "Maximum QP value for \"cbr\", \"vbr\", and \"qvbr\" rate control. To enable min/max QP setting, \"qp-max >= qp-min > 0\" condition should be satisfied",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "51",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-min": {
"blurb": "Minimum QP value for \"cbr\", \"vbr\", and \"qvbr\" rate control. To enable min/max QP setting, \"qp-max >= qp-min > 0\" condition should be satisfied",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "51",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qp-p": {
"blurb": "Constant QP value for P frames. Used for \"cqp\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "23",
"max": "51",
"min": "1",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"qvbr-quality": {
"blurb": "Constant quality target value for \"qvbr\" rate control",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "23",
"max": "51",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"rate-control": {
"blurb": "Rate Control Method",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "vbr (3)",
"mutable": "null",
"readable": true,
"type": "GstD3D12EncoderRateControl",
"writable": true
},
"rate-control-support": {
"blurb": "Supported rate control modes",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "(none)",
"mutable": "null",
"readable": true,
"type": "GstD3D12EncoderRateControlSupport",
"writable": false
},
"ref-frames": {
"blurb": "Preferred number of reference frames. Actual number of reference frames can be limited depending on hardware (0 = unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "16",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"slice-mode": {
"blurb": "Slice partiton mode",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "full (0)",
"mutable": "null",
"readable": true,
"type": "GstD3D12EncoderSubregionLayout",
"writable": true
},
"slice-mode-support": {
"blurb": "Supported slice partition modes",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "full",
"mutable": "null",
"readable": true,
"type": "GstD3D12EncoderSubregionLayoutSupport",
"writable": false
},
"slice-partition": {
"blurb": "Slice partition threshold interpreted depending on \"slice-mode\". If set zero, full frame encoding will be selected without partitioning regardless of requested \"slice-mode\"",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
}
},
"rank": "none"
},
"d3d12h265dec": {
"author": "Seungha Yang <seungha@centricualr.com>",
"description": "Direct3D12/DXVA based H.265 video decoder",
@ -12415,6 +12699,194 @@
}
]
},
"GstD3D12Encoder": {
"hierarchy": [
"GstD3D12Encoder",
"GstVideoEncoder",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstPreset"
],
"kind": "object",
"properties": {
"adapter-luid": {
"blurb": "DXGI Adapter LUID (Locally Unique Identifier) of created device",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "9223372036854775807",
"min": "-9223372036854775808",
"mutable": "null",
"readable": true,
"type": "gint64",
"writable": false
},
"device-id": {
"blurb": "DXGI Device ID",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": false
},
"vendor-id": {
"blurb": "DXGI Vendor ID",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": false
}
}
},
"GstD3D12EncoderRateControl": {
"kind": "enum",
"values": [
{
"desc": "Constant QP",
"name": "cqp",
"value": "1"
},
{
"desc": "Constant bitrate",
"name": "cbr",
"value": "2"
},
{
"desc": "Variable bitrate",
"name": "vbr",
"value": "3"
},
{
"desc": "Constant quality variable bitrate",
"name": "qvbr",
"value": "4"
}
]
},
"GstD3D12EncoderRateControlSupport": {
"kind": "flags",
"values": [
{
"desc": "Constant QP",
"name": "cqp",
"value": "0x00000002"
},
{
"desc": "Constant bitrate",
"name": "cbr",
"value": "0x00000004"
},
{
"desc": "Variable bitrate",
"name": "vbr",
"value": "0x00000008"
},
{
"desc": "Constant quality variable bitrate",
"name": "qvbr",
"value": "0x00000010"
}
]
},
"GstD3D12EncoderSeiInsertMode": {
"kind": "enum",
"values": [
{
"desc": "Insert",
"name": "insert",
"value": "0"
},
{
"desc": "Insert and drop",
"name": "insert-and-drop",
"value": "1"
},
{
"desc": "Disabled",
"name": "disabled",
"value": "2"
}
]
},
"GstD3D12EncoderSubregionLayout": {
"kind": "enum",
"values": [
{
"desc": "Full frame without partitioning",
"name": "full",
"value": "0"
},
{
"desc": "Bytes per subregion",
"name": "bytes",
"value": "1"
},
{
"desc": "Coding units (e.g., macroblock) per subregion",
"name": "coding-units",
"value": "2"
},
{
"desc": "Uniform rows per subregion",
"name": "rows",
"value": "3"
},
{
"desc": "Uniform subregions per frame",
"name": "subregions",
"value": "4"
}
]
},
"GstD3D12EncoderSubregionLayoutSupport": {
"kind": "flags",
"values": [
{
"desc": "Full frame without partitioning",
"name": "full",
"value": "0x00000001"
},
{
"desc": "Bytes per subregion",
"name": "bytes",
"value": "0x00000002"
},
{
"desc": "Coding units (e.g., macroblock) per subregion",
"name": "coding-units",
"value": "0x00000004"
},
{
"desc": "Uniform rows (in coding-unit) per subregion",
"name": "rows",
"value": "0x00000008"
},
{
"desc": "Uniform subregions per frame",
"name": "subregions",
"value": "0x00000010"
}
]
},
"GstD3D12MSAAMode": {
"kind": "enum",
"values": [

View file

@ -188,3 +188,7 @@ static const GstD3D12Format g_gst_d3d12_default_format_map[] = {
#undef MAKE_FORMAT_MAP_RGBP
#define GST_D3D12_N_FORMATS G_N_ELEMENTS(g_gst_d3d12_default_format_map)
void gst_d3d12_device_clear_yuv_texture (GstD3D12Device * device,
GstMemory * mem);

View file

@ -24,6 +24,7 @@
#include "gstd3d12.h"
#include "gstd3d12-private.h"
#include "gstd3d11on12.h"
#include <directx/d3dx12.h>
#include <wrl.h>
#include <vector>
#include <string.h>
@ -143,6 +144,8 @@ struct _GstD3D12DevicePrivate
GstD3D12CommandListPool *copy_cl_pool = nullptr;
GstD3D12CommandAllocatorPool *copy_ca_pool = nullptr;
guint rtv_inc_size;
guint adapter_index = 0;
guint device_id = 0;
guint vendor_id = 0;
@ -649,6 +652,9 @@ gst_d3d12_device_new_internal (const GstD3D12DeviceConstructData * data)
if (!priv->copy_ca_pool)
goto error;
priv->rtv_inc_size =
device->GetDescriptorHandleIncrementSize (D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
return self;
error:
@ -1107,3 +1113,75 @@ gst_d3d12_device_d3d12_debug (GstD3D12Device * device, const gchar * file,
info_queue->ClearStoredMessages ();
}
void
gst_d3d12_device_clear_yuv_texture (GstD3D12Device * device, GstMemory * mem)
{
auto priv = device->priv;
auto dmem = GST_D3D12_MEMORY_CAST (mem);
ComPtr < ID3D12DescriptorHeap > heap;
auto resource = gst_d3d12_memory_get_resource_handle (dmem);
auto desc = resource->GetDesc ();
if (desc.Format != DXGI_FORMAT_NV12 && desc.Format != DXGI_FORMAT_P010 &&
desc.Format != DXGI_FORMAT_P016) {
return;
}
gst_d3d12_memory_get_render_target_view_heap (dmem, &heap);
if (!heap)
return;
GstD3D12CommandAllocator *gst_ca = nullptr;
gst_d3d12_command_allocator_pool_acquire (priv->direct_ca_pool, &gst_ca);
if (!gst_ca)
return;
ComPtr < ID3D12CommandAllocator > ca;
gst_d3d12_command_allocator_get_handle (gst_ca, &ca);
GstD3D12CommandList *gst_cl = nullptr;
gst_d3d12_command_list_pool_acquire (priv->direct_cl_pool,
ca.Get (), &gst_cl);
if (!gst_cl) {
gst_d3d12_command_allocator_unref (gst_ca);
return;
}
ComPtr < ID3D12CommandList > cl_base;
ComPtr < ID3D12GraphicsCommandList > cl;
gst_d3d12_command_list_get_handle (gst_cl, &cl_base);
cl_base.As (&cl);
auto rtv_handle =
CD3DX12_CPU_DESCRIPTOR_HANDLE (heap->GetCPUDescriptorHandleForHeapStart
(),
priv->rtv_inc_size);
const FLOAT clear_color[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
cl->ClearRenderTargetView (rtv_handle, clear_color, 0, nullptr);
auto hr = cl->Close ();
if (!gst_d3d12_result (hr, device)) {
gst_clear_d3d12_command_list (&gst_cl);
gst_clear_d3d12_command_allocator (&gst_ca);
return;
}
ID3D12CommandList *cmd_list[] = { cl.Get () };
guint64 fence_val = 0;
hr = gst_d3d12_command_queue_execute_command_lists (priv->direct_queue,
1, cmd_list, &fence_val);
auto ret = gst_d3d12_result (hr, device);
gst_d3d12_command_list_unref (gst_cl);
if (ret) {
gst_d3d12_command_queue_set_notify (priv->direct_queue, fence_val,
gst_ca, (GDestroyNotify) gst_d3d12_command_allocator_unref);
dmem->fence_value = fence_val;
} else {
gst_d3d12_command_allocator_unref (gst_ca);
}
}

View file

@ -0,0 +1,333 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 "gstd3d12dpbstorage.h"
#include <wrl.h>
#include <vector>
#include <directx/d3dx12.h>
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_dpb_storage_debug);
#define GST_CAT_DEFAULT gst_d3d12_dpb_storage_debug
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
struct OwnedTexture
{
ComPtr<ID3D12Resource> texture;
guint subresource = 0;
gboolean is_free = TRUE;
};
struct GstD3D12DpbStoragePrivate
{
gboolean array_of_textures;
DXGI_FORMAT format;
guint width;
guint height;
D3D12_RESOURCE_FLAGS resource_flags;
std::vector<ID3D12Resource *> dpb;
std::vector<guint> dpb_subresource;
std::vector<OwnedTexture> pool;
ComPtr<ID3D12Resource> base_texture;
};
/* *INDENT-ON* */
struct _GstD3D12DpbStorage
{
GstObject parent;
GstD3D12Device *device;
GstD3D12DpbStoragePrivate *priv;
};
static void gst_d3d12_dpb_storage_finalize (GObject * object);
#define gst_d3d12_dpb_storage_parent_class parent_class
G_DEFINE_TYPE (GstD3D12DpbStorage, gst_d3d12_dpb_storage, GST_TYPE_OBJECT);
static void
gst_d3d12_dpb_storage_class_init (GstD3D12DpbStorageClass * klass)
{
auto object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gst_d3d12_dpb_storage_finalize;
GST_DEBUG_CATEGORY_INIT (gst_d3d12_dpb_storage_debug, "d3d12dpbstorage", 0,
"d3d12dpbstorage");
}
static void
gst_d3d12_dpb_storage_init (GstD3D12DpbStorage * self)
{
self->priv = new GstD3D12DpbStoragePrivate ();
}
static void
gst_d3d12_dpb_storage_finalize (GObject * object)
{
auto self = GST_D3D12_DPB_STORAGE (object);
delete self->priv;
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static HRESULT
allocate_texture (ID3D12Device * device, DXGI_FORMAT format, guint width,
guint height, D3D12_RESOURCE_FLAGS resource_flags, guint array_size,
ID3D12Resource ** texture)
{
D3D12_HEAP_PROPERTIES prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D (format,
width, height, array_size, 1, 1, 0, resource_flags);
return device->CreateCommittedResource (&prop, D3D12_HEAP_FLAG_NONE,
&desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS (texture));
}
gboolean
gst_d3d12_dpb_storage_acquire_frame (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE);
g_return_val_if_fail (frame, FALSE);
auto priv = storage->priv;
for (size_t i = 0; i < priv->pool.size (); i++) {
auto & it = priv->pool[i];
if (it.is_free) {
frame->pReconstructedPicture = it.texture.Get ();
frame->ReconstructedPictureSubresource = it.subresource;
it.is_free = FALSE;
return TRUE;
}
}
if (!priv->array_of_textures) {
GST_ERROR_OBJECT (storage, "No available free texture");
frame->pReconstructedPicture = nullptr;
frame->ReconstructedPictureSubresource = 0;
return FALSE;
}
auto device = gst_d3d12_device_get_device_handle (storage->device);
OwnedTexture new_texture;
new_texture.is_free = FALSE;
HRESULT hr = allocate_texture (device, priv->format, priv->width,
priv->height, priv->resource_flags, 1, &new_texture.texture);
if (!gst_d3d12_result (hr, storage->device)) {
GST_ERROR_OBJECT (storage, "Couldn't allocate texture");
frame->pReconstructedPicture = nullptr;
frame->ReconstructedPictureSubresource = 0;
return FALSE;
}
frame->pReconstructedPicture = new_texture.texture.Get ();
frame->ReconstructedPictureSubresource = 0;
priv->pool.push_back (new_texture);
return TRUE;
}
gboolean
gst_d3d12_dpb_storage_add_frame (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE);
g_return_val_if_fail (frame, FALSE);
g_return_val_if_fail (frame->pReconstructedPicture, FALSE);
auto priv = storage->priv;
priv->dpb.insert (priv->dpb.begin (), frame->pReconstructedPicture);
priv->dpb_subresource.insert (priv->dpb_subresource.begin (),
frame->ReconstructedPictureSubresource);
return TRUE;
}
gboolean
gst_d3d12_dpb_storage_get_reference_frames (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES * ref_frames)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE);
g_return_val_if_fail (ref_frames, FALSE);
auto priv = storage->priv;
ref_frames->NumTexture2Ds = priv->dpb.size ();
ref_frames->ppTexture2Ds = priv->dpb.data ();
if (priv->array_of_textures)
ref_frames->pSubresources = nullptr;
else
ref_frames->pSubresources = priv->dpb_subresource.data ();
return TRUE;
}
static void
gst_d3d12_dpb_storage_release_frame (GstD3D12DpbStorage * self,
ID3D12Resource * texture, guint subresource)
{
auto priv = self->priv;
if (priv->array_of_textures) {
for (size_t i = 0; i < priv->pool.size (); i++) {
auto & it = priv->pool[i];
if (texture == it.texture.Get () && it.subresource == subresource) {
it.is_free = TRUE;
return;
}
}
g_assert_not_reached ();
} else {
g_return_if_fail (subresource < priv->pool.size ());
priv->pool[subresource].is_free = TRUE;
}
}
gboolean
gst_d3d12_dpb_storage_remove_oldest_frame (GstD3D12DpbStorage * storage)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), FALSE);
auto priv = storage->priv;
if (priv->dpb.empty ()) {
GST_WARNING_OBJECT (storage, "DPB is empty now");
return FALSE;
}
gst_d3d12_dpb_storage_release_frame (storage,
priv->dpb.back (), priv->dpb_subresource.back ());
priv->dpb.pop_back ();
priv->dpb_subresource.pop_back ();
return TRUE;
}
void
gst_d3d12_dpb_storage_clear_dpb (GstD3D12DpbStorage * storage)
{
g_return_if_fail (GST_IS_D3D12_DPB_STORAGE (storage));
auto priv = storage->priv;
g_assert (priv->dpb.size () == priv->dpb_subresource.size ());
for (size_t i = 0; i < priv->dpb.size (); i++) {
gst_d3d12_dpb_storage_release_frame (storage,
priv->dpb[i], priv->dpb_subresource[i]);
}
priv->dpb.clear ();
priv->dpb_subresource.clear ();
}
guint
gst_d3d12_dpb_storage_get_dpb_size (GstD3D12DpbStorage * storage)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), 0);
auto priv = storage->priv;
return priv->dpb.size ();
}
guint
gst_d3d12_dpb_storage_get_pool_size (GstD3D12DpbStorage * storage)
{
g_return_val_if_fail (GST_IS_D3D12_DPB_STORAGE (storage), 0);
auto priv = storage->priv;
return priv->pool.size ();
}
GstD3D12DpbStorage *
gst_d3d12_dpb_storage_new (GstD3D12Device * device, guint dpb_size,
gboolean use_array_of_textures, DXGI_FORMAT format, guint width,
guint height, D3D12_RESOURCE_FLAGS resource_flags)
{
g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr);
auto self = (GstD3D12DpbStorage *)
g_object_new (GST_TYPE_D3D12_DPB_STORAGE, nullptr);
gst_object_ref_sink (self);
self->device = (GstD3D12Device *) gst_object_ref (device);
auto priv = self->priv;
auto device_handle = gst_d3d12_device_get_device_handle (device);
HRESULT hr;
if (use_array_of_textures) {
for (guint i = 0; i < dpb_size; i++) {
OwnedTexture texture;
hr = allocate_texture (device_handle,
format, width, height, resource_flags, 1, &texture.texture);
if (!gst_d3d12_result (hr, device)) {
GST_ERROR_OBJECT (self, "Couldn't allocate initial texture");
gst_object_unref (self);
return nullptr;
}
priv->pool.push_back (texture);
}
} else {
hr = allocate_texture (device_handle,
format, width, height, resource_flags, dpb_size, &priv->base_texture);
if (!gst_d3d12_result (hr, device)) {
GST_ERROR_OBJECT (self, "Couldn't allocate initial texture");
gst_object_unref (self);
return nullptr;
}
priv->pool.resize (dpb_size);
for (guint i = 0; i < dpb_size; i++) {
priv->pool[i].texture = priv->base_texture;
priv->pool[i].subresource = i;
}
}
priv->width = width;
priv->height = height;
priv->resource_flags = resource_flags;
priv->array_of_textures = use_array_of_textures;
priv->dpb.reserve (dpb_size);
priv->dpb_subresource.reserve (dpb_size);
return self;
}

View file

@ -0,0 +1,57 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d12.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D12_DPB_STORAGE (gst_d3d12_dpb_storage_get_type())
G_DECLARE_FINAL_TYPE (GstD3D12DpbStorage, gst_d3d12_dpb_storage,
GST, D3D12_DPB_STORAGE, GstObject);
GstD3D12DpbStorage * gst_d3d12_dpb_storage_new (GstD3D12Device * device,
guint dpb_size,
gboolean use_array_of_textures,
DXGI_FORMAT format,
guint width,
guint height,
D3D12_RESOURCE_FLAGS resource_flags);
gboolean gst_d3d12_dpb_storage_acquire_frame (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame);
gboolean gst_d3d12_dpb_storage_add_frame (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * frame);
gboolean gst_d3d12_dpb_storage_get_reference_frames (GstD3D12DpbStorage * storage,
D3D12_VIDEO_ENCODE_REFERENCE_FRAMES * ref_frames);
gboolean gst_d3d12_dpb_storage_remove_oldest_frame (GstD3D12DpbStorage * storage);
void gst_d3d12_dpb_storage_clear_dpb (GstD3D12DpbStorage * storage);
guint gst_d3d12_dpb_storage_get_dpb_size (GstD3D12DpbStorage * storage);
guint gst_d3d12_dpb_storage_get_pool_size (GstD3D12DpbStorage * storage);
G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,119 @@
/* GStreamer
* Copyright (C) 2024 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d12.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D12_ENCODER (gst_d3d12_encoder_get_type())
#define GST_D3D12_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D12_ENCODER,GstD3D12Encoder))
#define GST_D3D12_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D12_ENCODER,GstD3D12EncoderClass))
#define GST_D3D12_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_D3D12_ENCODER,GstD3D12EncoderClass))
#define GST_IS_D3D12_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D12_ENCODER))
#define GST_IS_D3D12_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D12_ENCODER))
struct GstD3D12EncoderPrivate;
struct GstD3D12EncoderConfig
{
D3D12_VIDEO_ENCODER_PROFILE_DESC profile_desc;
D3D12_VIDEO_ENCODER_LEVEL_SETTING level;
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codec_config;
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA layout;
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop_struct;
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolution;
D3D12_VIDEO_ENCODER_SUPPORT_FLAGS support_flags;
D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP cqp;;
D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR cbr;
D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR vbr;
D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR qvbr;
D3D12_VIDEO_ENCODER_RATE_CONTROL rate_control;
guint max_subregions;
};
enum GstD3D12EncoderSeiInsertMode
{
GST_D3D12_ENCODER_SEI_INSERT,
GST_D3D12_ENCODER_SEI_INSERT_AND_DROP,
GST_D3D12_ENCODER_SEI_DISABLED,
};
#define GST_TYPE_D3D12_ENCODER_RATE_CONTROL (gst_d3d12_encoder_rate_control_get_type ())
GType gst_d3d12_encoder_rate_control_get_type (void);
#define GST_TYPE_D3D12_ENCODER_RATE_CONTROL_SUPPORT (gst_d3d12_encoder_rate_control_support_get_type ())
GType gst_d3d12_encoder_rate_control_support_get_type (void);
#define GST_TYPE_D3D12_ENCODER_SUBREGION_LAYOUT (gst_d3d12_encoder_subregion_layout_get_type())
GType gst_d3d12_encoder_subregion_layout_get_type (void);
#define GST_TYPE_D3D12_ENCODER_SUBREGION_LAYOUT_SUPPORT (gst_d3d12_encoder_subregion_layout_support_get_type ())
GType gst_d3d12_encoder_subregion_layout_support_get_type (void);
#define GST_TYPE_D3D12_ENCODER_SEI_INSERT_MODE (gst_d3d12_encoder_sei_insert_mode_get_type ())
GType gst_d3d12_encoder_sei_insert_mode_get_type (void);
struct GstD3D12Encoder
{
GstVideoEncoder parent;
GstD3D12Device *device;
GstD3D12EncoderPrivate *priv;
};
struct GstD3D12EncoderClass
{
GstVideoEncoderClass parent_class;
D3D12_VIDEO_ENCODER_CODEC codec;
gint64 adapter_luid;
guint device_id;
guint vendor_id;
gboolean (*new_sequence) (GstD3D12Encoder * encoder,
ID3D12VideoDevice * video_device,
GstVideoCodecState * state,
GstD3D12EncoderConfig * config);
gboolean (*start_frame) (GstD3D12Encoder * encoder,
ID3D12VideoDevice * video_device,
GstVideoCodecFrame * frame,
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_DESC * seq_ctrl,
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_DESC * picture_ctrl,
D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE * recon_pic,
GstD3D12EncoderConfig * config,
gboolean * need_new_session);
gboolean (*end_frame) (GstD3D12Encoder * encoder);
};
GType gst_d3d12_encoder_get_type (void);
gboolean gst_d3d12_encoder_check_needs_new_session (D3D12_VIDEO_ENCODER_SUPPORT_FLAGS support_flags,
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS seq_flags);
#define CHECK_SUPPORT_FLAG(flags, f) \
((flags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_ ##f) != 0)
G_END_DECLS

View file

@ -0,0 +1,326 @@
/* GStreamer
* Copyright (C) 2024 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you cln 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 "gstd3d12encoderbufferpool.h"
#include <directx/d3dx12.h>
#include <wrl.h>
#include <queue>
#include <mutex>
#include <condition_variable>
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_encoder_buffer_pool_debug);
#define GST_CAT_DEFAULT gst_d3d12_encoder_buffer_pool_debug
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
struct _GstD3D12EncoderBuffer : public GstMiniObject
{
GstD3D12EncoderBufferPool *pool = nullptr;
ComPtr<ID3D12Resource> metadata;
ComPtr<ID3D12Resource> resolved_metadata;
ComPtr<ID3D12Resource> bitstream;
};
struct GstD3D12EncoderBufferPoolPrivate
{
~GstD3D12EncoderBufferPoolPrivate ()
{
while (!buffer_pool.empty ()) {
auto buf = buffer_pool.front ();
buffer_pool.pop ();
gst_mini_object_unref (buf);
}
}
ComPtr<ID3D12Device> device;
std::mutex lock;
std::condition_variable cond;
std::queue<GstD3D12EncoderBuffer *>buffer_pool;
guint metadata_size;
guint resolved_metadata_size;
guint bitstream_size;
guint pool_size;
};
/* *INDENT-ON* */
struct _GstD3D12EncoderBufferPool
{
GstObject parent;
GstD3D12EncoderBufferPoolPrivate *priv;
};
GST_DEFINE_MINI_OBJECT_TYPE (GstD3D12EncoderBuffer, gst_d3d12_encoder_buffer);
static void gst_d3d12_encoder_buffer_pool_finalize (GObject * object);
#define gst_d3d12_encoder_buffer_pool_parent_class parent_class
G_DEFINE_TYPE (GstD3D12EncoderBufferPool,
gst_d3d12_encoder_buffer_pool, GST_TYPE_OBJECT);
static void
gst_d3d12_encoder_buffer_pool_class_init (GstD3D12EncoderBufferPoolClass *
klass)
{
auto object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gst_d3d12_encoder_buffer_pool_finalize;
GST_DEBUG_CATEGORY_INIT (gst_d3d12_encoder_buffer_pool_debug,
"d3d12encoderbufferpool", 0, "d3d12encoderbufferpool");
}
static void
gst_d3d12_encoder_buffer_pool_init (GstD3D12EncoderBufferPool * self)
{
self->priv = new GstD3D12EncoderBufferPoolPrivate ();
}
static void
gst_d3d12_encoder_buffer_pool_finalize (GObject * object)
{
auto self = GST_D3D12_ENCODER_BUFFER_POOL (object);
GST_DEBUG_OBJECT (self, "Finalize");
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_d3d12_encoder_buffer_free (GstD3D12EncoderBuffer * buffer)
{
delete buffer;
}
static GstD3D12EncoderBuffer *
gst_d3d12_encoder_buffer_pool_alloc (GstD3D12EncoderBufferPool * self)
{
auto priv = self->priv;
D3D12_HEAP_PROPERTIES prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc =
CD3DX12_RESOURCE_DESC::Buffer (priv->metadata_size);
ComPtr < ID3D12Resource > metadata;
auto hr = priv->device->CreateCommittedResource (&prop,
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
nullptr, IID_PPV_ARGS (&metadata));
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Couldn't metadata buffer, hr: 0x%x", (guint) hr);
return nullptr;
}
prop = CD3DX12_HEAP_PROPERTIES (D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
D3D12_MEMORY_POOL_L0);
desc = CD3DX12_RESOURCE_DESC::Buffer (priv->resolved_metadata_size);
ComPtr < ID3D12Resource > resolved_metadata;
hr = priv->device->CreateCommittedResource (&prop,
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
nullptr, IID_PPV_ARGS (&resolved_metadata));
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Couldn't metadata buffer, hr: 0x%x", (guint) hr);
return nullptr;
}
desc = CD3DX12_RESOURCE_DESC::Buffer (priv->bitstream_size);
ComPtr < ID3D12Resource > bitstream;
hr = priv->device->CreateCommittedResource (&prop,
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
nullptr, IID_PPV_ARGS (&bitstream));
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Couldn't metadata buffer, hr: 0x%x", (guint) hr);
return nullptr;
}
auto new_buf = new GstD3D12EncoderBuffer ();
gst_mini_object_init (new_buf, 0, gst_d3d12_encoder_buffer_get_type (),
nullptr, nullptr,
(GstMiniObjectFreeFunction) gst_d3d12_encoder_buffer_free);
new_buf->metadata = metadata;
new_buf->resolved_metadata = resolved_metadata;
new_buf->bitstream = bitstream;
return new_buf;
}
GstD3D12EncoderBufferPool *
gst_d3d12_encoder_buffer_pool_new (GstD3D12Device * device,
guint metadata_size, guint resolved_metadata_size, guint bitstream_size,
guint pool_size)
{
g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr);
auto self = (GstD3D12EncoderBufferPool *)
g_object_new (GST_TYPE_D3D12_ENCODER_BUFFER_POOL, nullptr);
gst_object_ref_sink (self);
auto priv = self->priv;
priv->device = gst_d3d12_device_get_device_handle (device);;
priv->metadata_size = metadata_size;
priv->resolved_metadata_size = resolved_metadata_size;
priv->bitstream_size = bitstream_size;
priv->pool_size = pool_size;
for (guint i = 0; i < pool_size; i++) {
auto new_buf = gst_d3d12_encoder_buffer_pool_alloc (self);
if (!new_buf) {
gst_object_unref (self);
return nullptr;
}
priv->buffer_pool.push (new_buf);
}
return self;
}
static void
gst_d3d12_encoder_buffer_pool_release (GstD3D12EncoderBufferPool * pool,
GstD3D12EncoderBuffer * buffer)
{
auto priv = pool->priv;
{
std::lock_guard < std::mutex > lk (priv->lock);
buffer->dispose = nullptr;
buffer->pool = nullptr;
priv->buffer_pool.push (buffer);
priv->cond.notify_one ();
}
gst_object_unref (pool);
}
static gboolean
gst_d3d12_encoder_buffer_dispose (GstD3D12EncoderBuffer * buffer)
{
if (!buffer->pool)
return TRUE;
gst_mini_object_ref (buffer);
gst_d3d12_encoder_buffer_pool_release (buffer->pool, buffer);
return FALSE;
}
gboolean
gst_d3d12_encoder_buffer_pool_acquire (GstD3D12EncoderBufferPool * pool,
GstD3D12EncoderBuffer ** buffer)
{
g_return_val_if_fail (GST_IS_D3D12_ENCODER_BUFFER_POOL (pool), FALSE);
g_return_val_if_fail (buffer, FALSE);
*buffer = nullptr;
auto priv = pool->priv;
GstD3D12EncoderBuffer *new_buf = nullptr;
{
std::unique_lock < std::mutex > lk (priv->lock);
if (priv->pool_size > 0) {
while (priv->buffer_pool.empty ())
priv->cond.wait (lk);
}
if (!priv->buffer_pool.empty ()) {
new_buf = priv->buffer_pool.front ();
priv->buffer_pool.pop ();
}
}
if (!new_buf)
new_buf = gst_d3d12_encoder_buffer_pool_alloc (pool);
if (!new_buf)
return FALSE;
new_buf->pool = (GstD3D12EncoderBufferPool *) gst_object_ref (pool);
new_buf->dispose =
(GstMiniObjectDisposeFunction) gst_d3d12_encoder_buffer_dispose;
*buffer = new_buf;
return TRUE;
}
GstD3D12EncoderBuffer *
gst_d3d12_encoder_buffer_ref (GstD3D12EncoderBuffer * buffer)
{
return (GstD3D12EncoderBuffer *) gst_mini_object_ref (buffer);
}
void
gst_d3d12_encoder_buffer_unref (GstD3D12EncoderBuffer * buffer)
{
gst_mini_object_unref (buffer);
}
void
gst_clear_d3d12_encoder_buffer (GstD3D12EncoderBuffer ** buffer)
{
gst_clear_mini_object (buffer);
}
gboolean
gst_d3d12_encoder_buffer_get_metadata (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** metadata)
{
g_return_val_if_fail (buffer, FALSE);
g_return_val_if_fail (metadata, FALSE);
*metadata = buffer->metadata.Get ();
(*metadata)->AddRef ();
return TRUE;
}
gboolean
gst_d3d12_encoder_buffer_get_resolved_metadata (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** resolved_metadata)
{
g_return_val_if_fail (buffer, FALSE);
g_return_val_if_fail (resolved_metadata, FALSE);
*resolved_metadata = buffer->resolved_metadata.Get ();
(*resolved_metadata)->AddRef ();
return TRUE;
}
gboolean
gst_d3d12_encoder_buffer_get_bitstream (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** bitstream)
{
g_return_val_if_fail (buffer, FALSE);
g_return_val_if_fail (bitstream, FALSE);
*bitstream = buffer->bitstream.Get ();
(*bitstream)->AddRef ();
return TRUE;
}

View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) 2024 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d12.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D12_ENCODER_BUFFER_POOL (gst_d3d12_encoder_buffer_pool_get_type())
G_DECLARE_FINAL_TYPE (GstD3D12EncoderBufferPool,
gst_d3d12_encoder_buffer_pool, GST, D3D12_ENCODER_BUFFER_POOL, GstObject);
typedef struct _GstD3D12EncoderBuffer GstD3D12EncoderBuffer;
GstD3D12EncoderBufferPool * gst_d3d12_encoder_buffer_pool_new (GstD3D12Device * device,
guint metadata_size,
guint resolved_metadata_size,
guint bitstream_size,
guint pool_size);
gboolean gst_d3d12_encoder_buffer_pool_acquire (GstD3D12EncoderBufferPool * pool,
GstD3D12EncoderBuffer ** buffer);
GstD3D12EncoderBuffer * gst_d3d12_encoder_buffer_ref (GstD3D12EncoderBuffer * buffer);
void gst_d3d12_encoder_buffer_unref (GstD3D12EncoderBuffer * buffer);
void gst_clear_d3d12_encoder_buffer (GstD3D12EncoderBuffer ** buffer);
gboolean gst_d3d12_encoder_buffer_get_metadata (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** metadata);
gboolean gst_d3d12_encoder_buffer_get_resolved_metadata (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** resolved_metadata);
gboolean gst_d3d12_encoder_buffer_get_bitstream (GstD3D12EncoderBuffer * buffer,
ID3D12Resource ** bitstream);
G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
/* GStreamer
* Copyright (C) 2024 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d12.h"
G_BEGIN_DECLS
void gst_d3d12_h264_enc_register (GstPlugin * plugin,
GstD3D12Device * device,
ID3D12VideoDevice * video_device,
guint rank);
G_END_DECLS

View file

@ -23,6 +23,7 @@
#include "gstd3d12.h"
#include "gstd3d12memory-private.h"
#include "gstd3d12-private.h"
#include <directx/d3dx12.h>
#include <string.h>
#include <wrl.h>
@ -774,7 +775,20 @@ gst_d3d12_allocator_alloc_internal (GstD3D12Allocator * self,
return nullptr;
}
return gst_d3d12_allocator_alloc_wrapped (self, device, resource.Get (), 0);
auto mem =
gst_d3d12_allocator_alloc_wrapped (self, device, resource.Get (), 0);
if (!mem)
return nullptr;
/* Initialize YUV texture with black color */
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) != 0 &&
(heap_flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) == 0 &&
desc->DepthOrArraySize == 1) {
gst_d3d12_device_clear_yuv_texture (device, mem);
}
return mem;
}
GstMemory *

View file

@ -14,10 +14,14 @@ d3d12_sources = [
'gstd3d12descriptorpool.cpp',
'gstd3d12device.cpp',
'gstd3d12download.cpp',
'gstd3d12dpbstorage.cpp',
'gstd3d12dxgicapture.cpp',
'gstd3d12encoder.cpp',
'gstd3d12encoderbufferpool.cpp',
'gstd3d12fencedatapool.cpp',
'gstd3d12format.cpp',
'gstd3d12h264dec.cpp',
'gstd3d12h264enc.cpp',
'gstd3d12h265dec.cpp',
'gstd3d12memory.cpp',
'gstd3d12overlaycompositor.cpp',

View file

@ -38,6 +38,7 @@
#include "gstd3d12screencapturedevice.h"
#include "gstd3d12screencapturesrc.h"
#include "gstd3d12h264dec.h"
#include "gstd3d12h264enc.h"
#include "gstd3d12h265dec.h"
#include "gstd3d12vp9dec.h"
#include "gstd3d12av1dec.h"
@ -106,6 +107,9 @@ plugin_init (GstPlugin * plugin)
gst_d3d12_av1_dec_register (plugin, device, video_device.Get (),
GST_RANK_NONE);
gst_d3d12_h264_enc_register (plugin, device, video_device.Get (),
GST_RANK_NONE);
gst_object_unref (device);
}

View file

@ -0,0 +1,508 @@
/* GStreamer
* Copyright (C) 2024 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include <stdlib.h>
#include "../key-handler.h"
static gchar *rc_modes[] = {
"cqp", "cbr", "vbr", "qvbr"
};
static gchar *slice_modes[] = {
"full", "subregions"
};
static GMainLoop *loop = NULL;
static gint width = 640;
static gint height = 480;
static guint bitrate = 1000;
static guint max_bitrate = 2000;
static guint rc_index = 0;
static guint qp_i = 24;
static guint qp_p = 24;
static guint qp_b = 24;
static guint max_qp = 51;
static guint gop_size = 30;
static guint ref_frames = 0;
static guint slice_mode_index = 0;
static guint num_slices = 2;
#define BITRATE_STEP 100
G_LOCK_DEFINE_STATIC (input_lock);
typedef struct
{
GstElement *pipeline;
GstElement *capsfilter;
GstElement *encoder;
gulong probe_id;
gint prev_width;
gint prev_height;
} TestCallbackData;
static void
print_keyboard_help (void)
{
/* *INDENT-OFF* */
static struct
{
const gchar *key_desc;
const gchar *key_help;
} key_controls[] = {
{
"q", "Quit"}, {
"right arrow", "Increase Width"}, {
"left arrow", "Decrease Width"}, {
"up arrow", "Increase Height"}, {
"down arrow", "Decrease Height"}, {
"f", "Sends force keyunit event"}, {
"]", "Increase bitrate by 100 kbps"}, {
"[", "Decrease bitrate by 100 kbps"}, {
"}", "Increase max-bitrate by 100 kbps"}, {
"{", "Decrease max-bitrate by 100 kbps"}, {
"r", "Toggle rate-control mode"}, {
"<", "Decrease GOP size"}, {
">", "Increase GOP size"}, {
"+", "Incrase ref-frames"}, {
"-", "Decrase ref-frames"}, {
"I", "Increase QP-I"}, {
"i", "Decrease QP-I"}, {
"P", "Increase QP-P"}, {
"p", "Decrease QP-P"}, {
"m", "Toggle slice mode"}, {
"S", "Incrase number of slices"}, {
"s", "Decrease number of slices"}, {
"k", "show keyboard shortcuts"}
};
/* *INDENT-ON* */
guint i, chars_to_pad, desc_len, max_desc_len = 0;
gst_print ("\n\n%s\n\n", "Keyboard controls:");
for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) {
desc_len = g_utf8_strlen (key_controls[i].key_desc, -1);
max_desc_len = MAX (max_desc_len, desc_len);
}
++max_desc_len;
for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) {
chars_to_pad = max_desc_len - g_utf8_strlen (key_controls[i].key_desc, -1);
gst_print ("\t%s", key_controls[i].key_desc);
gst_print ("%-*s: ", chars_to_pad, "");
gst_print ("%s\n", key_controls[i].key_help);
}
gst_print ("\n");
}
static void
keyboard_cb (gchar input, gboolean is_ascii, gpointer user_data)
{
TestCallbackData *data = (TestCallbackData *) user_data;
G_LOCK (input_lock);
if (!is_ascii) {
switch (input) {
case KB_ARROW_UP:
height += 2;
gst_println ("Increase height to %d", height);
break;
case KB_ARROW_DOWN:
height -= 2;
height = MAX (height, 16);
gst_println ("Decrease height to %d", height);
break;
case KB_ARROW_LEFT:
width -= 2;
width = MAX (width, 16);
gst_println ("Decrease width to %d", width);
break;
case KB_ARROW_RIGHT:
width += 2;
gst_println ("Increase width to %d", width);
break;
default:
break;
}
} else {
switch (input) {
case 'k':
case 'K':
print_keyboard_help ();
break;
case 'q':
case 'Q':
gst_element_send_event (data->pipeline, gst_event_new_eos ());
g_main_loop_quit (loop);
break;
case 'f':
{
GstEvent *event =
gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
TRUE, 0);
gst_println ("Sending force keyunit event");
gst_element_send_event (data->encoder, event);
break;
}
case ']':
if (bitrate < G_MAXUINT64 - BITRATE_STEP) {
bitrate += BITRATE_STEP;
max_bitrate = MAX (max_bitrate, bitrate);
gst_println ("Increase bitrate to %" G_GUINT64_FORMAT, bitrate);
g_object_set (data->encoder, "bitrate", bitrate, "max-bitrate",
max_bitrate, NULL);
}
break;
case '[':
if (bitrate > 2 * BITRATE_STEP) {
bitrate -= BITRATE_STEP;
gst_println ("Decrease bitrate to %" G_GUINT64_FORMAT, bitrate);
g_object_set (data->encoder, "bitrate", bitrate, NULL);
}
break;
case '}':
if (max_bitrate < G_MAXUINT64 - BITRATE_STEP) {
max_bitrate += BITRATE_STEP;
gst_println ("Increase max bitrate to %" G_GUINT64_FORMAT,
max_bitrate);
g_object_set (data->encoder, "max-bitrate", max_bitrate, NULL);
}
break;
case '{':
if (max_bitrate > 2 * BITRATE_STEP) {
max_bitrate -= BITRATE_STEP;
bitrate = MAX (bitrate, max_bitrate);
gst_println ("Decrease max bitrate to %" G_GUINT64_FORMAT,
max_bitrate);
g_object_set (data->encoder, "bitrate", bitrate, "max-bitrate",
max_bitrate, NULL);
}
break;
case 'r':
rc_index++;
rc_index %= G_N_ELEMENTS (rc_modes);
gst_println ("Change rate control mode to %s", rc_modes[rc_index]);
gst_util_set_object_arg (G_OBJECT (data->encoder), "rate-control",
rc_modes[rc_index]);
break;
case '<':
gop_size--;
gst_println ("Updating GOP size to %u", gop_size);
g_object_set (data->encoder, "gop-size", gop_size, NULL);
break;
case '>':
gop_size++;
gst_println ("Updating GOP size to %u", gop_size);
g_object_set (data->encoder, "gop-size", gop_size, NULL);
break;
case '+':
ref_frames++;
ref_frames %= 17;
gst_println ("Updating ref frames to %u", ref_frames);
g_object_set (data->encoder, "ref-frames", ref_frames, NULL);
break;
case '-':
ref_frames--;
ref_frames %= 17;
gst_println ("Updating ref frames to %u", ref_frames);
g_object_set (data->encoder, "ref-frames", ref_frames, NULL);
break;
case 'I':
qp_i++;
qp_i %= 52;
if (qp_i == 0)
qp_i++;
gst_println ("Updating QP-I to %d", qp_i);
g_object_set (data->encoder, "qp-i", qp_i, NULL);
break;
case 'i':
qp_i--;
qp_i %= 52;
if (qp_i == 0)
qp_i = 51;
gst_println ("Updating QP-I to %d", qp_i);
g_object_set (data->encoder, "qp-i", qp_i, NULL);
break;
case 'P':
qp_p++;
qp_p %= 52;
if (qp_p == 0)
qp_p++;
gst_println ("Updating QP-P to %d", qp_p);
g_object_set (data->encoder, "qp-p", qp_p, NULL);
break;
case 'p':
qp_p--;
qp_p %= 52;
if (qp_p == 0)
qp_p = 51;
gst_println ("Updating QP-P to %d", qp_i);
g_object_set (data->encoder, "qp-p", qp_i, NULL);
break;
case 'm':
slice_mode_index++;
slice_mode_index %= G_N_ELEMENTS (slice_modes);
gst_println ("Updating slice mode to %s",
slice_modes[slice_mode_index]);
gst_util_set_object_arg (G_OBJECT (data->encoder), "slice-mode",
slice_modes[slice_mode_index]);
break;
case 'S':
num_slices++;
gst_println ("Updating slice partition to %u", num_slices);
g_object_set (data->encoder, "slice-partition", num_slices, NULL);
break;
case 's':
num_slices--;
gst_println ("Updating slice partition to %u", num_slices);
g_object_set (data->encoder, "slice-partition", num_slices, NULL);
break;
default:
break;
}
}
G_UNLOCK (input_lock);
}
static gboolean
bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:{
GError *err;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
gst_printerrln ("ERROR %s", err->message);
if (dbg != NULL)
gst_printerrln ("ERROR debug information: %s", dbg);
g_clear_error (&err);
g_free (dbg);
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static gboolean
check_encoder_available (const gchar * encoder_name)
{
gboolean ret = TRUE;
GstElement *elem;
elem = gst_element_factory_make (encoder_name, NULL);
if (!elem) {
gst_printerrln ("%s is not available", encoder_name);
return FALSE;
}
if (gst_element_set_state (elem,
GST_STATE_PAUSED) != GST_STATE_CHANGE_SUCCESS) {
gst_printerrln ("cannot open device");
ret = FALSE;
}
gst_element_set_state (elem, GST_STATE_NULL);
gst_object_unref (elem);
return ret;
}
static GstPadProbeReturn
resolution_change_probe (GstPad * pad, GstPadProbeInfo * info,
gpointer user_data)
{
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
TestCallbackData *data = (TestCallbackData *) user_data;
G_LOCK (input_lock);
if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))) {
GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
GstPad *peer = gst_pad_get_peer (pad);
GstFlowReturn flow_ret = GST_FLOW_OK;
ret = GST_PAD_PROBE_HANDLED;
if (peer) {
flow_ret = gst_pad_chain (peer, buffer);
if (flow_ret != GST_FLOW_OK) {
gst_pad_remove_probe (pad, data->probe_id);
data->probe_id = 0;
} else {
if (data->prev_width != width || data->prev_height != height) {
GstCaps *caps = NULL;
gint next_width, next_height;
next_width = width;
next_height = height;
g_object_get (data->capsfilter, "caps", &caps, NULL);
caps = gst_caps_make_writable (caps);
gst_caps_set_simple (caps,
"width", G_TYPE_INT, next_width, "height", G_TYPE_INT,
next_height, NULL);
g_object_set (data->capsfilter, "caps", caps, NULL);
gst_caps_unref (caps);
data->prev_width = next_width;
data->prev_height = next_height;
}
}
}
}
G_UNLOCK (input_lock);
return ret;
}
gint
main (gint argc, gchar ** argv)
{
GstElement *pipeline;
GstElement *src, *capsfilter, *enc, *enc_queue, *dec, *parser, *queue, *sink;
GstStateChangeReturn sret;
GError *error = NULL;
GOptionContext *option_ctx;
GstCaps *caps;
TestCallbackData data = { 0, };
GstPad *pad;
gchar *encoder_name = NULL;
/* *INDENT-OFF* */
GOptionEntry options[] = {
{"encoder", 0, 0, G_OPTION_ARG_STRING, &encoder_name,
"Video encoder element to test, default: d3d12h264enc"},
{NULL}
};
/* *INDENT-ON* */
#define MAKE_ELEMENT_AND_ADD(elem, name) G_STMT_START { \
GstElement *_elem = gst_element_factory_make (name, NULL); \
if (!_elem) { \
gst_printerrln ("%s is not available", name); \
exit (1); \
} \
gst_println ("Adding element %s", name); \
elem = _elem; \
gst_bin_add (GST_BIN (pipeline), elem); \
} G_STMT_END
option_ctx =
g_option_context_new ("d3d12 video encoder dynamic reconfigure example");
g_option_context_add_main_entries (option_ctx, options, NULL);
g_option_context_set_help_enabled (option_ctx, TRUE);
if (!g_option_context_parse (option_ctx, &argc, &argv, &error)) {
gst_printerrln ("option parsing failed: %s\n", error->message);
g_clear_error (&error);
exit (1);
}
g_option_context_free (option_ctx);
gst_init (NULL, NULL);
if (!encoder_name)
encoder_name = g_strdup ("d3d12h264enc");
if (!check_encoder_available (encoder_name)) {
gst_printerrln ("Cannot load %s plugin", encoder_name);
exit (1);
}
/* prepare the pipeline */
loop = g_main_loop_new (NULL, FALSE);
pipeline = gst_pipeline_new (NULL);
MAKE_ELEMENT_AND_ADD (src, "videotestsrc");
g_object_set (src, "pattern", 1, "is-live", TRUE, NULL);
MAKE_ELEMENT_AND_ADD (capsfilter, "capsfilter");
MAKE_ELEMENT_AND_ADD (enc, encoder_name);
g_object_set (enc, "bitrate", bitrate, "max-bitrate", max_bitrate,
"qp-i", qp_i, "qp-p", qp_p, "gop-size", 30, NULL);
gst_util_set_object_arg (G_OBJECT (enc), "rate-control", rc_modes[rc_index]);
MAKE_ELEMENT_AND_ADD (enc_queue, "queue");
MAKE_ELEMENT_AND_ADD (parser, "h264parse");
MAKE_ELEMENT_AND_ADD (dec, "d3d12h264dec");
MAKE_ELEMENT_AND_ADD (queue, "queue");
MAKE_ELEMENT_AND_ADD (sink, "d3d12videosink");
if (!gst_element_link_many (src, capsfilter, enc, enc_queue,
parser, dec, queue, sink, NULL)) {
gst_printerrln ("Failed to link element");
exit (1);
}
caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
width, "height", G_TYPE_INT, height, NULL);
g_object_set (capsfilter, "caps", caps, NULL);
gst_caps_unref (caps);
data.pipeline = pipeline;
data.capsfilter = capsfilter;
data.encoder = enc;
pad = gst_element_get_static_pad (capsfilter, "src");
data.probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
(GstPadProbeCallback) resolution_change_probe, &data, NULL);
gst_object_unref (pad);
data.prev_width = width;
data.prev_height = height;
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_msg, &data);
/* run the pipeline */
sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (sret == GST_STATE_CHANGE_FAILURE) {
gst_printerrln ("Pipeline doesn't want to playing\n");
} else {
set_key_handler ((KeyInputCallback) keyboard_cb, &data);
g_main_loop_run (loop);
unset_key_handler ();
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline);
g_main_loop_unref (loop);
g_free (encoder_name);
return 0;
}

View file

@ -0,0 +1,10 @@
if host_system != 'windows'
subdir_done()
endif
executable('d3d12enc-dynamic-reconfigure',
['d3d12enc-dynamic-reconfigure.c', '../key-handler.c'],
include_directories : [configinc],
dependencies: [gst_dep, gstbase_dep, gstvideo_dep],
c_args : gst_plugins_bad_args,
install: false)

View file

@ -4,6 +4,7 @@ subdir('camerabin2')
subdir('codecparsers')
subdir('codecs')
subdir('d3d11')
subdir('d3d12')
subdir('directfb')
subdir('gtk')
subdir('ipcpipeline')