From 6c6d351665e6c1184874e27fcfadf138ac802c5f Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sun, 4 Nov 2018 22:41:50 +0900 Subject: [PATCH] d3dvideosink: Set actual buffer size to buffer pool If configured size of buffer pool and actual buffer size are mismatched, pool will do unwanted buffer resize. Fixes #801 --- sys/d3dvideosink/d3dhelpers.c | 192 +++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 70 deletions(-) diff --git a/sys/d3dvideosink/d3dhelpers.c b/sys/d3dvideosink/d3dhelpers.c index d5a14bb4c6..43a9668a2a 100644 --- a/sys/d3dvideosink/d3dhelpers.c +++ b/sys/d3dvideosink/d3dhelpers.c @@ -519,18 +519,97 @@ gst_d3dsurface_buffer_pool_get_options (GstBufferPool * pool) return options; } +/* Calculate actual required buffer size from D3DLOCKED_RECT structure. + * Note that D3D could require larger Pitch value than minimum required one in theory. + * See also + * https://docs.microsoft.com/en-us/windows/desktop/direct3d9/width-vs--pitch */ +static gboolean +d3d_calculate_buffer_size (GstVideoInfo * info, D3DLOCKED_RECT * lr, + gsize * offset, gint * stride, gsize * size) +{ + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_BGR: + offset[0] = 0; + stride[0] = lr->Pitch; + *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info) * 3; + break; + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_RGBA: + offset[0] = 0; + stride[0] = lr->Pitch; + *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info) * 4; + break; + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_RGB15: + offset[0] = 0; + stride[0] = lr->Pitch; + *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info) * 2; + break; + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + offset[0] = 0; + stride[0] = lr->Pitch; + *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info) * 2; + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + offset[0] = 0; + stride[0] = lr->Pitch; + if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_YV12) { + offset[1] = + offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0); + stride[1] = lr->Pitch / 2; + offset[2] = + offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1); + stride[2] = lr->Pitch / 2; + *size = offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2); + } else { + offset[2] = + offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0); + stride[2] = lr->Pitch / 2; + offset[1] = + offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2); + stride[1] = lr->Pitch / 2; + *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1); + } + break; + case GST_VIDEO_FORMAT_NV12: + offset[0] = 0; + stride[0] = lr->Pitch; + offset[1] = offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0); + stride[1] = lr->Pitch; + *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1); + break; + default: + return FALSE; + } + + GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT + " (%s %dx%d, Pitch %d)", *size, + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)), + GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), lr->Pitch); + + return TRUE; +} + static gboolean gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) { GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool); + GstD3DVideoSink *sink = pool->sink; + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); GstCaps *caps; GstVideoInfo info; - - if (!GST_BUFFER_POOL_CLASS - (gst_d3dsurface_buffer_pool_parent_class)->set_config (bpool, config)) { - return FALSE; - } + LPDIRECT3DSURFACE9 surface; + D3DFORMAT d3dformat; + gint stride[GST_VIDEO_MAX_PLANES] = { 0, }; + gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; + D3DLOCKED_RECT lr; + HRESULT hr; + gsize size; if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL) || !caps) { @@ -544,8 +623,8 @@ gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool, return FALSE; } - if (gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&info)) == - D3DFMT_UNKNOWN) { + d3dformat = gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&info)); + if (d3dformat == D3DFMT_UNKNOWN) { GST_ERROR_OBJECT (pool, "Unsupported video format in caps %" GST_PTR_FORMAT, caps); return FALSE; @@ -554,6 +633,33 @@ gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool, GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height, caps); + /* Create a surface to get exact buffer size */ + hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d. + device.d3d_device, GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), d3dformat, D3DPOOL_DEFAULT, &surface, + NULL); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to create D3D surface"); + return FALSE; + } + + IDirect3DSurface9_LockRect (surface, &lr, NULL, 0); + if (!lr.pBits) { + GST_ERROR_OBJECT (sink, "Failed to lock D3D surface"); + IDirect3DSurface9_Release (surface); + return FALSE; + } + + if (!d3d_calculate_buffer_size (&info, &lr, offset, stride, &size)) { + GST_ERROR_OBJECT (sink, "Failed to get buffer size"); + IDirect3DSurface9_UnlockRect (surface); + IDirect3DSurface9_Release (surface); + return FALSE; + } + + IDirect3DSurface9_UnlockRect (surface); + IDirect3DSurface9_Release (surface); + pool->info = info; pool->add_metavideo = @@ -566,7 +672,10 @@ gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool, gst_object_ref_sink (pool->allocator); } - return TRUE; + gst_buffer_pool_config_set_params (config, caps, size, 2, 0); + + return GST_BUFFER_POOL_CLASS + (gst_d3dsurface_buffer_pool_parent_class)->set_config (bpool, config); } static GstFlowReturn @@ -609,68 +718,11 @@ gst_d3dsurface_buffer_pool_alloc_buffer (GstBufferPool * bpool, goto fallback; } - switch (GST_VIDEO_INFO_FORMAT (&pool->info)) { - case GST_VIDEO_FORMAT_BGR: - offset[0] = 0; - stride[0] = lr.Pitch; - size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 3; - break; - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_RGBA: - offset[0] = 0; - stride[0] = lr.Pitch; - size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 4; - break; - case GST_VIDEO_FORMAT_RGB16: - case GST_VIDEO_FORMAT_RGB15: - offset[0] = 0; - stride[0] = lr.Pitch; - size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 2; - break; - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - offset[0] = 0; - stride[0] = lr.Pitch; - size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 2; - break; - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - offset[0] = 0; - stride[0] = lr.Pitch; - if (GST_VIDEO_INFO_FORMAT (&pool->info) == GST_VIDEO_FORMAT_YV12) { - offset[1] = - offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 0); - stride[1] = lr.Pitch / 2; - offset[2] = - offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 1); - stride[2] = lr.Pitch / 2; - size = - offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 2); - } else { - offset[2] = - offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 0); - stride[2] = lr.Pitch / 2; - offset[1] = - offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 2); - stride[1] = lr.Pitch / 2; - size = - offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 1); - } - break; - case GST_VIDEO_FORMAT_NV12: - offset[0] = 0; - stride[0] = lr.Pitch; - offset[1] = - offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 0); - stride[1] = lr.Pitch; - size = - offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 1); - break; - default: - g_assert_not_reached (); - break; + if (!d3d_calculate_buffer_size (&pool->info, &lr, offset, stride, &size)) { + GST_ERROR_OBJECT (sink, "Failed to get buffer size"); + IDirect3DSurface9_UnlockRect (surface); + IDirect3DSurface9_Release (surface); + return GST_FLOW_ERROR; } IDirect3DSurface9_UnlockRect (surface);