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
This commit is contained in:
Seungha Yang 2018-11-04 22:41:50 +09:00 committed by Sebastian Dröge
parent dee5201392
commit 6c6d351665

View file

@ -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);