1 /* GStreamer Intel MSDK plugin
2  * Copyright (c) 2018, Intel Corporation
3  * All rights reserved.
4  *
5  * Author: Sreerenj Balachaandran <sreerenj.balachandran@intel.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37 
38 #include <stdlib.h>
39 
40 #include "gstmsdkvpp.h"
41 #include "gstmsdkbufferpool.h"
42 #include "gstmsdkvideomemory.h"
43 #include "gstmsdksystemmemory.h"
44 #include "gstmsdkcontextutil.h"
45 #include "gstmsdkvpputil.h"
46 
47 #define EXT_FORMATS     ""
48 
49 #ifndef _WIN32
50 #include "gstmsdkallocator_libva.h"
51 #if VA_CHECK_VERSION(1, 4, 1)
52 #undef EXT_FORMATS
53 #define EXT_FORMATS     ", BGR10A2_LE"
54 #endif
55 #endif
56 
57 GST_DEBUG_CATEGORY_EXTERN (gst_msdkvpp_debug);
58 #define GST_CAT_DEFAULT gst_msdkvpp_debug
59 
60 #if (MFX_VERSION >= 1028)
61 #define SUPPORTED_SYSTEM_FORMAT \
62     "{ NV12, YV12, I420, YUY2, UYVY, VUYA, BGRA, BGRx, RGB16, P010_10LE }"
63 #define SUPPORTED_DMABUF_FORMAT \
64     "{ NV12, BGRA, YUY2, UYVY, VUYA, RGB16, P010_10LE}"
65 #else
66 #define SUPPORTED_SYSTEM_FORMAT \
67     "{ NV12, YV12, I420, YUY2, UYVY, VUYA, BGRA, BGRx, P010_10LE }"
68 #define SUPPORTED_DMABUF_FORMAT \
69     "{ NV12, BGRA, YUY2, UYVY, VUYA, P010_10LE}"
70 #endif
71 
72 static GstStaticPadTemplate gst_msdkvpp_sink_factory =
73     GST_STATIC_PAD_TEMPLATE ("sink",
74     GST_PAD_SINK,
75     GST_PAD_ALWAYS,
76     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (SUPPORTED_SYSTEM_FORMAT)
77         ", " "interlace-mode = (string){ progressive, interleaved, mixed }" ";"
78         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF,
79             SUPPORTED_DMABUF_FORMAT)));
80 
81 static GstStaticPadTemplate gst_msdkvpp_src_factory =
82     GST_STATIC_PAD_TEMPLATE ("src",
83     GST_PAD_SRC,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
86         (GST_CAPS_FEATURE_MEMORY_DMABUF,
87             "{ BGRA, YUY2, UYVY, NV12, VUYA, BGRx, P010_10LE" EXT_FORMATS "}")
88         ";"
89         GST_VIDEO_CAPS_MAKE ("{ BGRA, NV12, YUY2, UYVY, VUYA, BGRx, P010_10LE"
90             EXT_FORMATS "}") ", "
91         "interlace-mode = (string){ progressive, interleaved, mixed }" ";"));
92 
93 enum
94 {
95   PROP_0,
96   PROP_HARDWARE,
97   PROP_ASYNC_DEPTH,
98   PROP_DENOISE,
99   PROP_ROTATION,
100   PROP_DEINTERLACE_MODE,
101   PROP_DEINTERLACE_METHOD,
102   PROP_HUE,
103   PROP_SATURATION,
104   PROP_BRIGHTNESS,
105   PROP_CONTRAST,
106   PROP_DETAIL,
107   PROP_MIRRORING,
108   PROP_SCALING_MODE,
109   PROP_FORCE_ASPECT_RATIO,
110   PROP_FRC_ALGORITHM,
111   PROP_N,
112 };
113 
114 #define PROP_HARDWARE_DEFAULT            TRUE
115 #define PROP_ASYNC_DEPTH_DEFAULT         1
116 #define PROP_DENOISE_DEFAULT             0
117 #define PROP_ROTATION_DEFAULT            MFX_ANGLE_0
118 #define PROP_DEINTERLACE_MODE_DEFAULT    GST_MSDKVPP_DEINTERLACE_MODE_AUTO
119 #define PROP_DEINTERLACE_METHOD_DEFAULT  MFX_DEINTERLACING_BOB
120 #define PROP_HUE_DEFAULT                 0
121 #define PROP_SATURATION_DEFAULT          1
122 #define PROP_BRIGHTNESS_DEFAULT          0
123 #define PROP_CONTRAST_DEFAULT            1
124 #define PROP_DETAIL_DEFAULT              0
125 #define PROP_MIRRORING_DEFAULT           MFX_MIRRORING_DISABLED
126 #define PROP_SCALING_MODE_DEFAULT        MFX_SCALING_MODE_DEFAULT
127 #define PROP_FORCE_ASPECT_RATIO_DEFAULT  TRUE
128 #define PROP_FRC_ALGORITHM_DEFAULT       _MFX_FRC_ALGORITHM_NONE
129 
130 #define gst_msdkvpp_parent_class parent_class
131 G_DEFINE_TYPE (GstMsdkVPP, gst_msdkvpp, GST_TYPE_BASE_TRANSFORM);
132 
133 typedef struct
134 {
135   mfxFrameSurface1 *surface;
136   GstBuffer *buf;
137 } MsdkSurface;
138 
139 static void
free_msdk_surface(MsdkSurface * surface)140 free_msdk_surface (MsdkSurface * surface)
141 {
142   if (surface->buf)
143     gst_buffer_unref (surface->buf);
144   g_slice_free (MsdkSurface, surface);
145 }
146 
147 static void
gst_msdkvpp_add_extra_param(GstMsdkVPP * thiz,mfxExtBuffer * param)148 gst_msdkvpp_add_extra_param (GstMsdkVPP * thiz, mfxExtBuffer * param)
149 {
150   if (thiz->num_extra_params < MAX_EXTRA_PARAMS) {
151     thiz->extra_params[thiz->num_extra_params] = param;
152     thiz->num_extra_params++;
153   }
154 }
155 
156 static gboolean
ensure_context(GstBaseTransform * trans)157 ensure_context (GstBaseTransform * trans)
158 {
159   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
160 
161   if (gst_msdk_context_prepare (GST_ELEMENT_CAST (thiz), &thiz->context)) {
162     GST_INFO_OBJECT (thiz, "Found context from neighbour %" GST_PTR_FORMAT,
163         thiz->context);
164 
165     if (gst_msdk_context_get_job_type (thiz->context) & GST_MSDK_JOB_VPP) {
166       GstMsdkContext *parent_context, *msdk_context;
167 
168       parent_context = thiz->context;
169       msdk_context = gst_msdk_context_new_with_parent (parent_context);
170 
171       if (!msdk_context) {
172         GST_ERROR_OBJECT (thiz, "Context creation failed");
173         return FALSE;
174       }
175 
176       thiz->context = msdk_context;
177       gst_object_unref (parent_context);
178 
179       GST_INFO_OBJECT (thiz,
180           "Creating new context %" GST_PTR_FORMAT " with joined session",
181           thiz->context);
182     } else {
183       gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_VPP);
184     }
185   } else {
186     if (!gst_msdk_context_ensure_context (GST_ELEMENT_CAST (thiz),
187             thiz->hardware, GST_MSDK_JOB_VPP))
188       return FALSE;
189     GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT,
190         thiz->context);
191   }
192 
193   gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth);
194 
195   return TRUE;
196 }
197 
198 static GstBuffer *
create_output_buffer(GstMsdkVPP * thiz)199 create_output_buffer (GstMsdkVPP * thiz)
200 {
201   GstBuffer *outbuf;
202   GstFlowReturn ret;
203   GstBufferPool *pool = thiz->srcpad_buffer_pool;
204 
205   g_return_val_if_fail (pool != NULL, NULL);
206 
207   if (!gst_buffer_pool_is_active (pool) &&
208       !gst_buffer_pool_set_active (pool, TRUE))
209     goto error_activate_pool;
210 
211   outbuf = NULL;
212   ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
213   if (ret != GST_FLOW_OK || !outbuf)
214     goto error_create_buffer;
215 
216   return outbuf;
217 
218   /* ERRORS */
219 error_activate_pool:
220   {
221     GST_ERROR_OBJECT (thiz, "failed to activate output video buffer pool");
222     return NULL;
223   }
224 error_create_buffer:
225   {
226     GST_ERROR_OBJECT (thiz, "failed to create output video buffer");
227     return NULL;
228   }
229 }
230 
231 static GstFlowReturn
gst_msdkvpp_prepare_output_buffer(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer ** outbuf_ptr)232 gst_msdkvpp_prepare_output_buffer (GstBaseTransform * trans,
233     GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
234 {
235   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
236 
237   if (gst_base_transform_is_passthrough (trans)) {
238     *outbuf_ptr = inbuf;
239     return GST_FLOW_OK;
240   }
241 
242   *outbuf_ptr = create_output_buffer (thiz);
243   return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
244 }
245 
246 static GstBufferPool *
gst_msdkvpp_create_buffer_pool(GstMsdkVPP * thiz,GstPadDirection direction,GstCaps * caps,guint min_num_buffers)247 gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
248     GstCaps * caps, guint min_num_buffers)
249 {
250   GstBufferPool *pool = NULL;
251   GstStructure *config;
252   GstAllocator *allocator = NULL;
253   GstVideoInfo info;
254   GstVideoInfo *pool_info = NULL;
255   GstVideoAlignment align;
256   GstAllocationParams params = { 0, 31, 0, 0, };
257   mfxFrameAllocResponse *alloc_resp = NULL;
258   gboolean use_dmabuf = FALSE;
259 
260   if (direction == GST_PAD_SINK) {
261     alloc_resp = &thiz->in_alloc_resp;
262     pool_info = &thiz->sinkpad_buffer_pool_info;
263     use_dmabuf = thiz->use_sinkpad_dmabuf;
264   } else if (direction == GST_PAD_SRC) {
265     alloc_resp = &thiz->out_alloc_resp;
266     pool_info = &thiz->srcpad_buffer_pool_info;
267     use_dmabuf = thiz->use_srcpad_dmabuf;
268   }
269 
270   pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
271   if (!pool)
272     goto error_no_pool;
273 
274   if (!gst_video_info_from_caps (&info, caps))
275     goto error_no_video_info;
276 
277   gst_msdk_set_video_alignment (&info, &align);
278   gst_video_info_align (&info, &align);
279 
280   if (use_dmabuf)
281     allocator =
282         gst_msdk_dmabuf_allocator_new (thiz->context, &info, alloc_resp);
283   else if (thiz->use_video_memory)
284     allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
285   else
286     allocator = gst_msdk_system_allocator_new (&info);
287 
288   if (!allocator)
289     goto error_no_allocator;
290 
291   config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
292   gst_buffer_pool_config_set_params (config, caps, info.size, min_num_buffers,
293       0);
294 
295   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
296   gst_buffer_pool_config_add_option (config,
297       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
298   if (thiz->use_video_memory) {
299     gst_buffer_pool_config_add_option (config,
300         GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
301     if (use_dmabuf)
302       gst_buffer_pool_config_add_option (config,
303           GST_BUFFER_POOL_OPTION_MSDK_USE_DMABUF);
304   }
305 
306   gst_buffer_pool_config_set_video_alignment (config, &align);
307   gst_buffer_pool_config_set_allocator (config, allocator, &params);
308   gst_object_unref (allocator);
309 
310   if (!gst_buffer_pool_set_config (pool, config))
311     goto error_pool_config;
312 
313   /* Updating pool_info with algined info of allocator */
314   *pool_info = info;
315 
316   return pool;
317 
318 error_no_pool:
319   {
320     GST_INFO_OBJECT (thiz, "Failed to create bufferpool");
321     return NULL;
322   }
323 error_no_video_info:
324   {
325     GST_INFO_OBJECT (thiz, "Failed to get Video info from caps");
326     gst_object_unref (pool);
327     return NULL;
328   }
329 error_no_allocator:
330   {
331     GST_INFO_OBJECT (thiz, "Failed to create allocator");
332     gst_object_unref (pool);
333     return NULL;
334   }
335 error_pool_config:
336   {
337     GST_INFO_OBJECT (thiz, "Failed to set config");
338     gst_object_unref (pool);
339     gst_object_unref (allocator);
340     return NULL;
341   }
342 }
343 
344 static gboolean
_gst_caps_has_feature(const GstCaps * caps,const gchar * feature)345 _gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
346 {
347   guint i;
348 
349   for (i = 0; i < gst_caps_get_size (caps); i++) {
350     GstCapsFeatures *const features = gst_caps_get_features (caps, i);
351     /* Skip ANY features, we need an exact match for correct evaluation */
352     if (gst_caps_features_is_any (features))
353       continue;
354     if (gst_caps_features_contains (features, feature))
355       return TRUE;
356   }
357   return FALSE;
358 }
359 
360 static gboolean
gst_msdkvpp_decide_allocation(GstBaseTransform * trans,GstQuery * query)361 gst_msdkvpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
362 {
363   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
364   GstVideoInfo info;
365   GstBufferPool *pool = NULL;
366   GstStructure *config = NULL;
367   GstCaps *caps;
368   guint size = 0, min_buffers = 0, max_buffers = 0;
369   GstAllocator *allocator = NULL;
370   GstAllocationParams params;
371   gboolean update_pool = FALSE;
372 
373   gst_query_parse_allocation (query, &caps, NULL);
374   if (!caps) {
375     GST_ERROR_OBJECT (thiz, "Failed to parse the decide_allocation caps");
376     return FALSE;
377   }
378   if (!gst_video_info_from_caps (&info, caps)) {
379     GST_ERROR_OBJECT (thiz, "Failed to get video info");
380     return FALSE;
381   }
382   /* if downstream allocation query supports dmabuf-capsfeatures,
383    * we do allocate dmabuf backed memory */
384   if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
385     GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
386     thiz->use_srcpad_dmabuf = TRUE;
387   }
388 
389   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL))
390     thiz->add_video_meta = TRUE;
391   else
392     thiz->add_video_meta = FALSE;
393 
394   /* Check whether the query has pool */
395   if (gst_query_get_n_allocation_pools (query) > 0)
396     update_pool = TRUE;
397 
398   /* increase the min_buffers with number of concurrent vpp operations */
399   min_buffers += thiz->async_depth;
400 
401   /* invalidate the cached pool if there is an allocation_query */
402   if (thiz->srcpad_buffer_pool)
403     gst_object_unref (thiz->srcpad_buffer_pool);
404 
405   /* Always create a pool for vpp out buffers. Each of the msdk element
406    * has to create it's own mfxsurfacepool which is an msdk contraint.
407    * For eg: Each Msdk component (vpp, dec and enc) will invoke the external
408    * Frame allocator for video-memory usage.So sharing the pool between
409    * gst-msdk elements might not be a good idea, rather each element
410    * can check the buffer type (whether it is from msdk-buffer pool)
411    * to make sure there is no copy. Since we share the context between
412    * msdk elements, using buffers from one sdk's framealloator in another
413    * sdk-components is perfectly fine */
414   pool = gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, caps, min_buffers);
415   thiz->srcpad_buffer_pool = pool;
416 
417   /* get the configured pool properties inorder to set in query */
418   config = gst_buffer_pool_get_config (pool);
419   gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
420       &max_buffers);
421   if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
422     gst_query_add_allocation_param (query, allocator, &params);
423   gst_structure_free (config);
424 
425   if (update_pool)
426     gst_query_set_nth_allocation_pool (query, 0, pool, size, min_buffers,
427         max_buffers);
428   else
429     gst_query_add_allocation_pool (query, pool, size, min_buffers, max_buffers);
430 
431   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
432 
433   /* Fixme if downstream doesn't have videometa support, msdkvpp should
434    * copy the output buffers */
435 
436   return TRUE;
437 }
438 
439 static gboolean
gst_msdkvpp_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)440 gst_msdkvpp_propose_allocation (GstBaseTransform * trans,
441     GstQuery * decide_query, GstQuery * query)
442 {
443   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
444   GstVideoInfo info;
445   GstBufferPool *pool = NULL;
446   GstAllocator *allocator = NULL;
447   GstCaps *caps;
448   GstStructure *config;
449   gboolean need_pool;
450   GstAllocationParams params;
451   guint size;
452   guint min_buffers = thiz->async_depth + 1;
453 
454   gst_query_parse_allocation (query, &caps, &need_pool);
455   if (!caps) {
456     GST_ERROR_OBJECT (thiz, "Failed to parse the allocation caps");
457     return FALSE;
458   }
459 
460   if (!gst_video_info_from_caps (&info, caps)) {
461     GST_ERROR_OBJECT (thiz, "Failed to get video info");
462     return FALSE;
463   }
464 
465   /* if upstream allocation query supports dmabuf-capsfeatures,
466    * we do allocate dmabuf backed memory */
467   if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
468     GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
469     thiz->use_sinkpad_dmabuf = TRUE;
470   }
471 
472   if (need_pool) {
473     /* alwys provide a new pool for upstream to help re-negotiation
474      * more info here: https://bugzilla.gnome.org/show_bug.cgi?id=748344 */
475     pool = gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
476         min_buffers);
477   }
478 
479   /* Update the internal pool if any allocation attribute changed */
480   if (!gst_video_info_is_equal (&thiz->sinkpad_buffer_pool_info, &info)) {
481     gst_object_unref (thiz->sinkpad_buffer_pool);
482     thiz->sinkpad_buffer_pool = gst_msdkvpp_create_buffer_pool (thiz,
483         GST_PAD_SINK, caps, min_buffers);
484   }
485 
486   /* get the size and allocator params from configured pool and set it in query */
487   if (!need_pool)
488     pool = gst_object_ref (thiz->sinkpad_buffer_pool);
489   config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
490   gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
491   if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
492     gst_query_add_allocation_param (query, allocator, &params);
493   gst_structure_free (config);
494 
495   /* if upstream does't have a pool requirement, set only
496    *  size, min_buffers and max_buffers in query */
497   gst_query_add_allocation_pool (query, need_pool ? pool : NULL, size,
498       min_buffers, 0);
499   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
500 
501   gst_object_unref (pool);
502 
503   return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
504       decide_query, query);
505 }
506 
507 static MsdkSurface *
get_surface_from_pool(GstMsdkVPP * thiz,GstBufferPool * pool,GstBufferPoolAcquireParams * params)508 get_surface_from_pool (GstMsdkVPP * thiz, GstBufferPool * pool,
509     GstBufferPoolAcquireParams * params)
510 {
511   GstBuffer *new_buffer;
512   mfxFrameSurface1 *new_surface;
513   MsdkSurface *msdk_surface;
514 
515   if (!gst_buffer_pool_is_active (pool) &&
516       !gst_buffer_pool_set_active (pool, TRUE)) {
517     GST_ERROR_OBJECT (pool, "failed to activate buffer pool");
518     return NULL;
519   }
520 
521   if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) {
522     GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool");
523     return NULL;
524   }
525 
526   if (gst_msdk_is_msdk_buffer (new_buffer))
527     new_surface = gst_msdk_get_surface_from_buffer (new_buffer);
528   else {
529     GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory");
530     return NULL;
531   }
532 
533   msdk_surface = g_slice_new0 (MsdkSurface);
534   msdk_surface->surface = new_surface;
535   msdk_surface->buf = new_buffer;
536 
537   return msdk_surface;
538 }
539 
540 #ifndef _WIN32
541 static gboolean
import_dmabuf_to_msdk_surface(GstMsdkVPP * thiz,GstBuffer * buf,MsdkSurface * msdk_surface)542 import_dmabuf_to_msdk_surface (GstMsdkVPP * thiz, GstBuffer * buf,
543     MsdkSurface * msdk_surface)
544 {
545   GstMemory *mem = NULL;
546   GstVideoInfo vinfo;
547   GstVideoMeta *vmeta;
548   GstMsdkMemoryID *msdk_mid = NULL;
549   mfxFrameSurface1 *mfx_surface = NULL;
550   gint fd, i;
551 
552   mem = gst_buffer_peek_memory (buf, 0);
553   fd = gst_dmabuf_memory_get_fd (mem);
554   if (fd < 0)
555     return FALSE;
556 
557   vinfo = thiz->sinkpad_info;
558 
559   /* Update offset/stride/size if there is VideoMeta attached to
560    * the buffer */
561   vmeta = gst_buffer_get_video_meta (buf);
562   if (vmeta) {
563     if (GST_VIDEO_INFO_FORMAT (&vinfo) != vmeta->format ||
564         GST_VIDEO_INFO_WIDTH (&vinfo) != vmeta->width ||
565         GST_VIDEO_INFO_HEIGHT (&vinfo) != vmeta->height ||
566         GST_VIDEO_INFO_N_PLANES (&vinfo) != vmeta->n_planes) {
567       GST_ERROR_OBJECT (thiz, "VideoMeta attached to buffer is not matching"
568           "the negotiated width/height/format");
569       return FALSE;
570     }
571     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vinfo); ++i) {
572       GST_VIDEO_INFO_PLANE_OFFSET (&vinfo, i) = vmeta->offset[i];
573       GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, i) = vmeta->stride[i];
574     }
575     GST_VIDEO_INFO_SIZE (&vinfo) = gst_buffer_get_size (buf);
576   }
577 
578   /* Upstream neither accepted the msdk pool nor the msdk buffer size restrictions.
579    * Current media-driver and GMMLib will fail due to strict memory size restrictions.
580    * Ideally, media-driver should accept what ever memory coming from other drivers
581    * in case of dmabuf-import and this is how the intel-vaapi-driver works.
582    * For now, in order to avoid any crash we check the buffer size and fallback
583    * to copy frame method.
584    *
585    * See this: https://github.com/intel/media-driver/issues/169
586    * */
587   if (GST_VIDEO_INFO_SIZE (&vinfo) <
588       GST_VIDEO_INFO_SIZE (&thiz->sinkpad_buffer_pool_info))
589     return FALSE;
590 
591   mfx_surface = msdk_surface->surface;
592   msdk_mid = (GstMsdkMemoryID *) mfx_surface->Data.MemId;
593 
594   /* release the internal memory storage of associated mfxSurface */
595   gst_msdk_replace_mfx_memid (thiz->context, mfx_surface, VA_INVALID_ID);
596 
597   /* export dmabuf to vasurface */
598   if (!gst_msdk_export_dmabuf_to_vasurface (thiz->context, &vinfo, fd,
599           msdk_mid->surface))
600     return FALSE;
601 
602   return TRUE;
603 }
604 #endif
605 
606 static MsdkSurface *
get_msdk_surface_from_input_buffer(GstMsdkVPP * thiz,GstBuffer * inbuf)607 get_msdk_surface_from_input_buffer (GstMsdkVPP * thiz, GstBuffer * inbuf)
608 {
609   GstVideoFrame src_frame, out_frame;
610   MsdkSurface *msdk_surface;
611   GstMemory *mem = NULL;
612 
613   if (gst_msdk_is_msdk_buffer (inbuf)) {
614     msdk_surface = g_slice_new0 (MsdkSurface);
615     msdk_surface->surface = gst_msdk_get_surface_from_buffer (inbuf);
616     msdk_surface->buf = gst_buffer_ref (inbuf);
617     return msdk_surface;
618   }
619 
620   /* If upstream hasn't accpeted the proposed msdk bufferpool,
621    * just copy frame (if not dmabuf backed) to msdk buffer and
622    * take a surface from it.   */
623   if (!(msdk_surface =
624           get_surface_from_pool (thiz, thiz->sinkpad_buffer_pool, NULL)))
625     goto error;
626 
627 #ifndef _WIN32
628   /************ dmabuf-import ************* */
629   /* if upstream provided a dmabuf backed memory, but not an msdk
630    * buffer, we could export the dmabuf to underlined vasurface */
631   mem = gst_buffer_peek_memory (inbuf, 0);
632   if (gst_is_dmabuf_memory (mem)) {
633     if (import_dmabuf_to_msdk_surface (thiz, inbuf, msdk_surface))
634       return msdk_surface;
635     else
636       GST_INFO_OBJECT (thiz, "Upstream dmabuf-backed memory is not imported"
637           "to the msdk surface, fall back to the copy input frame method");
638   }
639 #endif
640 
641   if (!gst_video_frame_map (&src_frame, &thiz->sinkpad_info, inbuf,
642           GST_MAP_READ)) {
643     GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
644     goto error;
645   }
646 
647   if (!gst_video_frame_map (&out_frame, &thiz->sinkpad_buffer_pool_info,
648           msdk_surface->buf, GST_MAP_WRITE)) {
649     GST_ERROR_OBJECT (thiz, "failed to map the frame for destination");
650     gst_video_frame_unmap (&src_frame);
651     goto error;
652   }
653 
654   if (!gst_video_frame_copy (&out_frame, &src_frame)) {
655     GST_ERROR_OBJECT (thiz, "failed to copy frame");
656     gst_video_frame_unmap (&out_frame);
657     gst_video_frame_unmap (&src_frame);
658     goto error;
659   }
660 
661   gst_video_frame_unmap (&out_frame);
662   gst_video_frame_unmap (&src_frame);
663 
664   return msdk_surface;
665 
666 error:
667   return NULL;
668 }
669 
670 static GstFlowReturn
gst_msdkvpp_transform(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)671 gst_msdkvpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
672     GstBuffer * outbuf)
673 {
674   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
675   GstClockTime timestamp;
676   GstFlowReturn ret = GST_FLOW_OK;
677   mfxSession session;
678   mfxSyncPoint sync_point = NULL;
679   mfxStatus status;
680   MsdkSurface *in_surface = NULL;
681   MsdkSurface *out_surface = NULL;
682 
683   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
684 
685   in_surface = get_msdk_surface_from_input_buffer (thiz, inbuf);
686   if (!in_surface)
687     return GST_FLOW_ERROR;
688 
689   if (gst_msdk_is_msdk_buffer (outbuf)) {
690     out_surface = g_slice_new0 (MsdkSurface);
691     out_surface->surface = gst_msdk_get_surface_from_buffer (outbuf);
692   } else {
693     GST_ERROR ("Failed to get msdk outsurface!");
694     return GST_FLOW_ERROR;
695   }
696 
697   session = gst_msdk_context_get_session (thiz->context);
698 
699   /* outer loop is for handling FrameRate Control and deinterlace use cases */
700   do {
701     for (;;) {
702       status =
703           MFXVideoVPP_RunFrameVPPAsync (session, in_surface->surface,
704           out_surface->surface, NULL, &sync_point);
705       if (status != MFX_WRN_DEVICE_BUSY)
706         break;
707       /* If device is busy, wait 1ms and retry, as per MSDK's recommendation */
708       g_usleep (1000);
709     };
710 
711     if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA
712         && status != MFX_ERR_MORE_SURFACE)
713       goto vpp_error;
714 
715     /* No output generated */
716     if (status == MFX_ERR_MORE_DATA)
717       goto error_more_data;
718 
719     /* Wait for vpp operation to complete, the magic number 300000 below
720      * is used in MSDK samples
721      * #define MSDK_VPP_WAIT_INTERVAL 300000
722      */
723     if (sync_point &&
724         MFXVideoCORE_SyncOperation (session, sync_point,
725             300000) != MFX_ERR_NONE)
726       GST_WARNING_OBJECT (thiz, "failed to do sync operation");
727 
728     /* More than one output buffers are generated */
729     if (status == MFX_ERR_MORE_SURFACE) {
730       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
731       GST_BUFFER_DURATION (outbuf) = thiz->buffer_duration;
732       timestamp += thiz->buffer_duration;
733       ret = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (trans), outbuf);
734       if (ret != GST_FLOW_OK)
735         goto error_push_buffer;
736       outbuf = create_output_buffer (thiz);
737     } else {
738       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
739       GST_BUFFER_DURATION (outbuf) = thiz->buffer_duration;
740     }
741   } while (status == MFX_ERR_MORE_SURFACE);
742 
743   free_msdk_surface (in_surface);
744   return ret;
745 
746 vpp_error:
747   GST_ERROR_OBJECT (thiz, "MSDK Failed to do VPP");
748   free_msdk_surface (in_surface);
749   free_msdk_surface (out_surface);
750   return GST_FLOW_ERROR;
751 
752 error_more_data:
753   GST_WARNING_OBJECT (thiz,
754       "MSDK Requries additional input for processing, "
755       "Retruning FLOW_DROPPED since no output buffer was generated");
756   free_msdk_surface (in_surface);
757   return GST_BASE_TRANSFORM_FLOW_DROPPED;
758 
759 error_push_buffer:
760   {
761     free_msdk_surface (in_surface);
762     free_msdk_surface (out_surface);
763     GST_DEBUG_OBJECT (thiz, "failed to push output buffer: %s",
764         gst_flow_get_name (ret));
765     return ret;
766   }
767 }
768 
769 static void
gst_msdkvpp_close(GstMsdkVPP * thiz)770 gst_msdkvpp_close (GstMsdkVPP * thiz)
771 {
772   mfxStatus status;
773 
774   if (!thiz->context)
775     return;
776 
777   GST_DEBUG_OBJECT (thiz, "Closing VPP 0x%p", thiz->context);
778   status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
779   if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
780     GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
781         msdk_status_to_string (status));
782   }
783 
784   if (thiz->context)
785     gst_object_replace ((GstObject **) & thiz->context, NULL);
786 
787   memset (&thiz->param, 0, sizeof (thiz->param));
788 
789   if (thiz->sinkpad_buffer_pool)
790     gst_object_unref (thiz->sinkpad_buffer_pool);
791   thiz->sinkpad_buffer_pool = NULL;
792   if (thiz->srcpad_buffer_pool)
793     gst_object_unref (thiz->srcpad_buffer_pool);
794   thiz->srcpad_buffer_pool = NULL;
795 
796   thiz->buffer_duration = GST_CLOCK_TIME_NONE;
797   gst_video_info_init (&thiz->sinkpad_info);
798   gst_video_info_init (&thiz->srcpad_info);
799 }
800 
801 static void
ensure_filters(GstMsdkVPP * thiz)802 ensure_filters (GstMsdkVPP * thiz)
803 {
804 
805   /* Denoise */
806   if (thiz->flags & GST_MSDK_FLAG_DENOISE) {
807     mfxExtVPPDenoise *mfx_denoise = &thiz->mfx_denoise;
808     mfx_denoise->Header.BufferId = MFX_EXTBUFF_VPP_DENOISE;
809     mfx_denoise->Header.BufferSz = sizeof (mfxExtVPPDenoise);
810     mfx_denoise->DenoiseFactor = thiz->denoise_factor;
811     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_denoise);
812   }
813 
814   /* Rotation */
815   if (thiz->flags & GST_MSDK_FLAG_ROTATION) {
816     mfxExtVPPRotation *mfx_rotation = &thiz->mfx_rotation;
817     mfx_rotation->Header.BufferId = MFX_EXTBUFF_VPP_ROTATION;
818     mfx_rotation->Header.BufferSz = sizeof (mfxExtVPPRotation);
819     mfx_rotation->Angle = thiz->rotation;
820     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_rotation);
821   }
822 
823   /* Deinterlace */
824   if (thiz->flags & GST_MSDK_FLAG_DEINTERLACE) {
825     mfxExtVPPDeinterlacing *mfx_deinterlace = &thiz->mfx_deinterlace;
826     mfx_deinterlace->Header.BufferId = MFX_EXTBUFF_VPP_DEINTERLACING;
827     mfx_deinterlace->Header.BufferSz = sizeof (mfxExtVPPDeinterlacing);
828     mfx_deinterlace->Mode = thiz->deinterlace_method;
829     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_deinterlace);
830   }
831 
832   /* Colorbalance(ProcAmp) */
833   if (thiz->flags & (GST_MSDK_FLAG_HUE | GST_MSDK_FLAG_SATURATION |
834           GST_MSDK_FLAG_BRIGHTNESS | GST_MSDK_FLAG_CONTRAST)) {
835     mfxExtVPPProcAmp *mfx_procamp = &thiz->mfx_procamp;
836     mfx_procamp->Header.BufferId = MFX_EXTBUFF_VPP_PROCAMP;
837     mfx_procamp->Header.BufferSz = sizeof (mfxExtVPPProcAmp);
838     mfx_procamp->Hue = thiz->hue;
839     mfx_procamp->Saturation = thiz->saturation;
840     mfx_procamp->Brightness = thiz->brightness;
841     mfx_procamp->Contrast = thiz->contrast;
842     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_procamp);
843   }
844 
845   /* Detail/Edge enhancement */
846   if (thiz->flags & GST_MSDK_FLAG_DETAIL) {
847     mfxExtVPPDetail *mfx_detail = &thiz->mfx_detail;
848     mfx_detail->Header.BufferId = MFX_EXTBUFF_VPP_DETAIL;
849     mfx_detail->Header.BufferSz = sizeof (mfxExtVPPDetail);
850     mfx_detail->DetailFactor = thiz->detail;
851     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_detail);
852   }
853 
854   /* Mirroring */
855   if (thiz->flags & GST_MSDK_FLAG_MIRRORING) {
856     mfxExtVPPMirroring *mfx_mirroring = &thiz->mfx_mirroring;
857     mfx_mirroring->Header.BufferId = MFX_EXTBUFF_VPP_MIRRORING;
858     mfx_mirroring->Header.BufferSz = sizeof (mfxExtVPPMirroring);
859     mfx_mirroring->Type = thiz->mirroring;
860     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_mirroring);
861   }
862 
863   /* Scaling Mode */
864   if (thiz->flags & GST_MSDK_FLAG_SCALING_MODE) {
865     mfxExtVPPScaling *mfx_scaling = &thiz->mfx_scaling;
866     mfx_scaling->Header.BufferId = MFX_EXTBUFF_VPP_SCALING;
867     mfx_scaling->Header.BufferSz = sizeof (mfxExtVPPScaling);
868     mfx_scaling->ScalingMode = thiz->scaling_mode;
869     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_scaling);
870   }
871 
872   /* FRC */
873   if (thiz->flags & GST_MSDK_FLAG_FRC) {
874     mfxExtVPPFrameRateConversion *mfx_frc = &thiz->mfx_frc;
875     mfx_frc->Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION;
876     mfx_frc->Header.BufferSz = sizeof (mfxExtVPPFrameRateConversion);
877     mfx_frc->Algorithm = thiz->frc_algm;
878     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_frc);
879   }
880 }
881 
882 static void
gst_msdkvpp_set_passthrough(GstMsdkVPP * thiz)883 gst_msdkvpp_set_passthrough (GstMsdkVPP * thiz)
884 {
885   gboolean passthrough = TRUE;
886 
887   /* no passthrough if any of the filter algorithm is enabled */
888   if (thiz->flags)
889     passthrough = FALSE;
890 
891   /* vpp could be needed in some specific circumstances, for eg:
892    * input surface is dmabuf and output must be videomemory. So far
893    * the underline iHD driver doesn't seems to support dmabuf mapping,
894    * so we could explicitly ask msdkvpp to provide non-dambuf videomemory
895    * surfaces as output thourgh capsfileters */
896   if (thiz->need_vpp)
897     passthrough = FALSE;
898 
899   /* no passthrough if there is change in out width,height or format */
900   if (GST_VIDEO_INFO_WIDTH (&thiz->sinkpad_info) !=
901       GST_VIDEO_INFO_WIDTH (&thiz->srcpad_info)
902       || GST_VIDEO_INFO_HEIGHT (&thiz->sinkpad_info) !=
903       GST_VIDEO_INFO_HEIGHT (&thiz->srcpad_info)
904       || GST_VIDEO_INFO_FORMAT (&thiz->sinkpad_info) !=
905       GST_VIDEO_INFO_FORMAT (&thiz->srcpad_info))
906     passthrough = FALSE;
907 
908   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (thiz), passthrough);
909 }
910 
911 static gboolean
gst_msdkvpp_initialize(GstMsdkVPP * thiz)912 gst_msdkvpp_initialize (GstMsdkVPP * thiz)
913 {
914   mfxSession session;
915   mfxStatus status;
916   mfxFrameAllocRequest request[2];
917 
918   if (!thiz->context) {
919     GST_WARNING_OBJECT (thiz, "No MSDK Context");
920     return FALSE;
921   }
922 
923   GST_OBJECT_LOCK (thiz);
924   session = gst_msdk_context_get_session (thiz->context);
925 
926   /* Close the current session if the session has been initialized,
927    * otherwise the subsequent function call of MFXVideoVPP_Init() will
928    * fail
929    */
930   if (thiz->initialized)
931     MFXVideoVPP_Close (session);
932 
933   if (thiz->use_video_memory) {
934     gst_msdk_set_frame_allocator (thiz->context);
935     thiz->param.IOPattern =
936         MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
937   } else {
938     thiz->param.IOPattern =
939         MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
940   }
941 
942   /* update input video attributes */
943   gst_msdk_set_mfx_frame_info_from_video_info (&thiz->param.vpp.In,
944       &thiz->sinkpad_info);
945 
946   /* update output video attributes, only CSC and Scaling are supported for now */
947   gst_msdk_set_mfx_frame_info_from_video_info (&thiz->param.vpp.Out,
948       &thiz->srcpad_info);
949 
950   /* use msdk frame rarte control if there is a mismatch in In & OUt fps  */
951   if (GST_VIDEO_INFO_FPS_N (&thiz->srcpad_info) &&
952       (GST_VIDEO_INFO_FPS_N (&thiz->sinkpad_info) !=
953           GST_VIDEO_INFO_FPS_N (&thiz->srcpad_info)
954           || GST_VIDEO_INFO_FPS_D (&thiz->sinkpad_info) !=
955           GST_VIDEO_INFO_FPS_D (&thiz->srcpad_info))) {
956     thiz->flags |= GST_MSDK_FLAG_FRC;
957     /* So far this is the only algorithm which is working somewhat good */
958     thiz->frc_algm = MFX_FRCALGM_PRESERVE_TIMESTAMP;
959   }
960 
961   /* work-around to avoid zero fps in msdk structure */
962   if (!thiz->param.vpp.In.FrameRateExtN)
963     thiz->param.vpp.In.FrameRateExtN = 30;
964   if (!thiz->param.vpp.Out.FrameRateExtN)
965     thiz->param.vpp.Out.FrameRateExtN = thiz->param.vpp.In.FrameRateExtN;
966 
967   /* set vpp out picstruct as progressive if deinterlacing enabled */
968   if (thiz->flags & GST_MSDK_FLAG_DEINTERLACE)
969     thiz->param.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
970 
971   /* Enable the required filters */
972   ensure_filters (thiz);
973 
974   /* Add exteneded buffers */
975   if (thiz->num_extra_params) {
976     thiz->param.NumExtParam = thiz->num_extra_params;
977     thiz->param.ExtParam = thiz->extra_params;
978   }
979 
980   /* validate parameters and allow the Media SDK to make adjustments */
981   status = MFXVideoVPP_Query (session, &thiz->param, &thiz->param);
982   if (status < MFX_ERR_NONE) {
983     GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)",
984         msdk_status_to_string (status));
985     goto no_vpp;
986   } else if (status > MFX_ERR_NONE) {
987     GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s",
988         msdk_status_to_string (status));
989   }
990 
991   status = MFXVideoVPP_QueryIOSurf (session, &thiz->param, request);
992   if (status < MFX_ERR_NONE) {
993     GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
994         msdk_status_to_string (status));
995     goto no_vpp;
996   } else if (status > MFX_ERR_NONE) {
997     GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s",
998         msdk_status_to_string (status));
999   }
1000 
1001   if (thiz->use_video_memory) {
1002     /* Input surface pool pre-allocation */
1003     request[0].Type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
1004     if (thiz->use_sinkpad_dmabuf)
1005       request[0].Type |= MFX_MEMTYPE_EXPORT_FRAME;
1006     gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->in_alloc_resp);
1007 
1008     /* Output surface pool pre-allocation */
1009     request[1].Type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
1010     if (thiz->use_srcpad_dmabuf)
1011       request[1].Type |= MFX_MEMTYPE_EXPORT_FRAME;
1012     gst_msdk_frame_alloc (thiz->context, &(request[1]), &thiz->out_alloc_resp);
1013   }
1014 
1015   thiz->in_num_surfaces = request[0].NumFrameSuggested;
1016   thiz->out_num_surfaces = request[1].NumFrameSuggested;
1017 
1018 
1019   status = MFXVideoVPP_Init (session, &thiz->param);
1020   if (status < MFX_ERR_NONE) {
1021     GST_ERROR_OBJECT (thiz, "Init failed (%s)", msdk_status_to_string (status));
1022     goto no_vpp;
1023   } else if (status > MFX_ERR_NONE) {
1024     GST_WARNING_OBJECT (thiz, "Init returned: %s",
1025         msdk_status_to_string (status));
1026   }
1027 
1028   thiz->initialized = TRUE;
1029   GST_OBJECT_UNLOCK (thiz);
1030   return TRUE;
1031 
1032 no_vpp:
1033   GST_OBJECT_UNLOCK (thiz);
1034   if (thiz->context)
1035     gst_object_replace ((GstObject **) & thiz->context, NULL);
1036   return FALSE;
1037 }
1038 
1039 static gboolean
gst_msdkvpp_set_caps(GstBaseTransform * trans,GstCaps * caps,GstCaps * out_caps)1040 gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
1041     GstCaps * out_caps)
1042 {
1043   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
1044   GstVideoInfo in_info, out_info;
1045   gboolean sinkpad_info_changed = FALSE;
1046   gboolean srcpad_info_changed = FALSE;
1047   gboolean deinterlace;
1048 
1049   if (gst_caps_get_features (caps, 0) != gst_caps_get_features (out_caps, 0))
1050     thiz->need_vpp = 1;
1051 
1052   gst_video_info_from_caps (&in_info, caps);
1053   gst_video_info_from_caps (&out_info, out_caps);
1054 
1055   if (!gst_video_info_is_equal (&in_info, &thiz->sinkpad_info))
1056     sinkpad_info_changed = TRUE;
1057   if (!gst_video_info_is_equal (&out_info, &thiz->srcpad_info))
1058     srcpad_info_changed = TRUE;
1059 
1060   if (!sinkpad_info_changed && !srcpad_info_changed && thiz->initialized)
1061     return TRUE;
1062 
1063   thiz->sinkpad_info = in_info;
1064   thiz->srcpad_info = out_info;
1065 #ifndef _WIN32
1066   thiz->use_video_memory = TRUE;
1067 #else
1068   thiz->use_video_memory = FALSE;
1069 #endif
1070 
1071   /* check for deinterlace requirement */
1072   deinterlace = gst_msdkvpp_is_deinterlace_enabled (thiz, &in_info);
1073   if (deinterlace)
1074     thiz->flags |= GST_MSDK_FLAG_DEINTERLACE;
1075 
1076   thiz->buffer_duration = GST_VIDEO_INFO_FPS_N (&out_info) > 0 ?
1077       gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&out_info),
1078       GST_VIDEO_INFO_FPS_N (&out_info)) : 0;
1079 
1080   if (!gst_msdkvpp_initialize (thiz))
1081     return FALSE;
1082 
1083   /* set passthrough according to filter operation change */
1084   gst_msdkvpp_set_passthrough (thiz);
1085 
1086   /* Ensure sinkpad buffer pool */
1087   thiz->sinkpad_buffer_pool =
1088       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
1089       thiz->in_num_surfaces);
1090   if (!thiz->sinkpad_buffer_pool) {
1091     GST_ERROR_OBJECT (thiz, "Failed to ensure the sinkpad buffer pool");
1092     return FALSE;
1093   }
1094   /* Ensure a srcpad buffer pool */
1095   thiz->srcpad_buffer_pool =
1096       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, out_caps,
1097       thiz->out_num_surfaces);
1098   if (!thiz->srcpad_buffer_pool) {
1099     GST_ERROR_OBJECT (thiz, "Failed to ensure the srcpad buffer pool");
1100     return FALSE;
1101   }
1102 
1103   return TRUE;
1104 }
1105 
1106 static gboolean
pad_can_dmabuf(GstMsdkVPP * thiz,GstPadDirection direction,GstCaps * filter)1107 pad_can_dmabuf (GstMsdkVPP * thiz, GstPadDirection direction, GstCaps * filter)
1108 {
1109   gboolean ret = FALSE;
1110   GstCaps *caps, *out_caps;
1111   GstPad *pad;
1112   GstBaseTransform *trans = GST_BASE_TRANSFORM (thiz);
1113 
1114   if (direction == GST_PAD_SRC)
1115     pad = GST_BASE_TRANSFORM_SRC_PAD (trans);
1116   else
1117     pad = GST_BASE_TRANSFORM_SINK_PAD (trans);
1118 
1119   /* make a copy of filter caps since we need to alter the structure
1120    * by adding dmabuf-capsfeatures */
1121   caps = gst_caps_copy (filter);
1122   gst_caps_set_features (caps, 0,
1123       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF));
1124 
1125   out_caps = gst_pad_peer_query_caps (pad, caps);
1126   if (!out_caps)
1127     goto done;
1128 
1129   if (gst_caps_is_any (out_caps) || gst_caps_is_empty (out_caps)
1130       || out_caps == caps)
1131     goto done;
1132 
1133   if (_gst_caps_has_feature (out_caps, GST_CAPS_FEATURE_MEMORY_DMABUF))
1134     ret = TRUE;
1135 done:
1136   if (caps)
1137     gst_caps_unref (caps);
1138   if (out_caps)
1139     gst_caps_unref (out_caps);
1140   return ret;
1141 }
1142 
1143 static GstCaps *
gst_msdkvpp_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)1144 gst_msdkvpp_fixate_caps (GstBaseTransform * trans,
1145     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1146 {
1147   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
1148   GstCaps *result = NULL;
1149   gboolean *use_dmabuf;
1150 
1151   if (direction == GST_PAD_SRC) {
1152     result = gst_caps_fixate (result);
1153     use_dmabuf = &thiz->use_sinkpad_dmabuf;
1154   } else {
1155     result = gst_msdkvpp_fixate_srccaps (thiz, caps, othercaps);
1156     use_dmabuf = &thiz->use_srcpad_dmabuf;
1157   }
1158 
1159   GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, result);
1160   gst_caps_unref (othercaps);
1161 
1162   if (pad_can_dmabuf (thiz,
1163           direction == GST_PAD_SRC ? GST_PAD_SINK : GST_PAD_SRC, result)) {
1164     gst_caps_set_features (result, 0,
1165         gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
1166     *use_dmabuf = TRUE;
1167   }
1168 
1169   return result;
1170 }
1171 
1172 /* Generic code for now, requires changes in future when we
1173  * add hardware query for supported formats, Framerate control etc */
1174 static GstCaps *
gst_msdkvpp_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)1175 gst_msdkvpp_transform_caps (GstBaseTransform * trans,
1176     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1177 {
1178   GstCaps *out_caps;
1179 
1180   GST_DEBUG_OBJECT (trans,
1181       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1182       (direction == GST_PAD_SINK) ? "sink" : "src");
1183 
1184   if (direction == GST_PAD_SRC)
1185     out_caps = gst_static_pad_template_get_caps (&gst_msdkvpp_sink_factory);
1186   else
1187     out_caps = gst_static_pad_template_get_caps (&gst_msdkvpp_src_factory);
1188 
1189   if (out_caps && filter) {
1190     GstCaps *intersection;
1191 
1192     intersection = gst_caps_intersect_full (out_caps, filter,
1193         GST_CAPS_INTERSECT_FIRST);
1194     gst_caps_unref (out_caps);
1195     out_caps = intersection;
1196   }
1197 
1198   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
1199   return out_caps;
1200 }
1201 
1202 static gboolean
gst_msdkvpp_start(GstBaseTransform * trans)1203 gst_msdkvpp_start (GstBaseTransform * trans)
1204 {
1205   if (!ensure_context (trans))
1206     return FALSE;
1207   return TRUE;
1208 }
1209 
1210 static gboolean
gst_msdkvpp_stop(GstBaseTransform * trans)1211 gst_msdkvpp_stop (GstBaseTransform * trans)
1212 {
1213   gst_msdkvpp_close (GST_MSDKVPP (trans));
1214   return TRUE;
1215 }
1216 
1217 static void
gst_msdkvpp_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1218 gst_msdkvpp_set_property (GObject * object, guint prop_id,
1219     const GValue * value, GParamSpec * pspec)
1220 {
1221   GstMsdkVPP *thiz = GST_MSDKVPP (object);
1222 
1223   switch (prop_id) {
1224     case PROP_HARDWARE:
1225       thiz->hardware = g_value_get_boolean (value);
1226       break;
1227     case PROP_ASYNC_DEPTH:
1228       thiz->async_depth = g_value_get_uint (value);
1229       break;
1230     case PROP_DENOISE:
1231       thiz->denoise_factor = g_value_get_uint (value);
1232       thiz->flags |= GST_MSDK_FLAG_DENOISE;
1233       break;
1234     case PROP_ROTATION:
1235       thiz->rotation = g_value_get_enum (value);
1236       thiz->flags |= GST_MSDK_FLAG_ROTATION;
1237       break;
1238     case PROP_DEINTERLACE_MODE:
1239       thiz->deinterlace_mode = g_value_get_enum (value);
1240       break;
1241     case PROP_DEINTERLACE_METHOD:
1242       thiz->deinterlace_method = g_value_get_enum (value);
1243       break;
1244     case PROP_HUE:
1245       thiz->hue = g_value_get_float (value);
1246       thiz->flags |= GST_MSDK_FLAG_HUE;
1247       break;
1248     case PROP_SATURATION:
1249       thiz->saturation = g_value_get_float (value);
1250       thiz->flags |= GST_MSDK_FLAG_SATURATION;
1251       break;
1252     case PROP_BRIGHTNESS:
1253       thiz->brightness = g_value_get_float (value);
1254       thiz->flags |= GST_MSDK_FLAG_BRIGHTNESS;
1255       break;
1256     case PROP_CONTRAST:
1257       thiz->contrast = g_value_get_float (value);
1258       thiz->flags |= GST_MSDK_FLAG_CONTRAST;
1259       break;
1260     case PROP_DETAIL:
1261       thiz->detail = g_value_get_uint (value);
1262       thiz->flags |= GST_MSDK_FLAG_DETAIL;
1263       break;
1264     case PROP_MIRRORING:
1265       thiz->mirroring = g_value_get_enum (value);
1266       thiz->flags |= GST_MSDK_FLAG_MIRRORING;
1267       break;
1268     case PROP_SCALING_MODE:
1269       thiz->scaling_mode = g_value_get_enum (value);
1270       thiz->flags |= GST_MSDK_FLAG_SCALING_MODE;
1271       break;
1272     case PROP_FORCE_ASPECT_RATIO:
1273       thiz->keep_aspect = g_value_get_boolean (value);
1274       break;
1275     case PROP_FRC_ALGORITHM:
1276       thiz->frc_algm = g_value_get_enum (value);
1277       break;
1278     default:
1279       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1280       break;
1281   }
1282 }
1283 
1284 static void
gst_msdkvpp_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1285 gst_msdkvpp_get_property (GObject * object, guint prop_id,
1286     GValue * value, GParamSpec * pspec)
1287 {
1288   GstMsdkVPP *thiz = GST_MSDKVPP (object);
1289 
1290   switch (prop_id) {
1291     case PROP_HARDWARE:
1292       g_value_set_boolean (value, thiz->hardware);
1293       break;
1294     case PROP_ASYNC_DEPTH:
1295       g_value_set_uint (value, thiz->async_depth);
1296       break;
1297     case PROP_DENOISE:
1298       g_value_set_uint (value, thiz->denoise_factor);
1299       break;
1300     case PROP_ROTATION:
1301       g_value_set_enum (value, thiz->rotation);
1302       break;
1303     case PROP_DEINTERLACE_MODE:
1304       g_value_set_enum (value, thiz->deinterlace_mode);
1305       break;
1306     case PROP_DEINTERLACE_METHOD:
1307       g_value_set_enum (value, thiz->deinterlace_method);
1308       break;
1309     case PROP_HUE:
1310       g_value_set_float (value, thiz->hue);
1311       break;
1312     case PROP_SATURATION:
1313       g_value_set_float (value, thiz->saturation);
1314       break;
1315     case PROP_BRIGHTNESS:
1316       g_value_set_float (value, thiz->brightness);
1317       break;
1318     case PROP_CONTRAST:
1319       g_value_set_float (value, thiz->contrast);
1320       break;
1321     case PROP_DETAIL:
1322       g_value_set_uint (value, thiz->detail);
1323       break;
1324     case PROP_MIRRORING:
1325       g_value_set_enum (value, thiz->mirroring);
1326       break;
1327     case PROP_SCALING_MODE:
1328       g_value_set_enum (value, thiz->scaling_mode);
1329       break;
1330     case PROP_FORCE_ASPECT_RATIO:
1331       g_value_set_boolean (value, thiz->keep_aspect);
1332       break;
1333     case PROP_FRC_ALGORITHM:
1334       g_value_set_enum (value, thiz->frc_algm);
1335       break;
1336     default:
1337       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1338       break;
1339   }
1340 }
1341 
1342 static void
gst_msdkvpp_finalize(GObject * object)1343 gst_msdkvpp_finalize (GObject * object)
1344 {
1345   G_OBJECT_CLASS (parent_class)->finalize (object);
1346 }
1347 
1348 static void
gst_msdkvpp_set_context(GstElement * element,GstContext * context)1349 gst_msdkvpp_set_context (GstElement * element, GstContext * context)
1350 {
1351   GstMsdkContext *msdk_context = NULL;
1352   GstMsdkVPP *thiz = GST_MSDKVPP (element);
1353 
1354   if (gst_msdk_context_get_context (context, &msdk_context)) {
1355     gst_object_replace ((GstObject **) & thiz->context,
1356         (GstObject *) msdk_context);
1357     gst_object_unref (msdk_context);
1358   }
1359 
1360   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1361 }
1362 
1363 static void
gst_msdkvpp_class_init(GstMsdkVPPClass * klass)1364 gst_msdkvpp_class_init (GstMsdkVPPClass * klass)
1365 {
1366   GObjectClass *gobject_class;
1367   GstElementClass *element_class;
1368   GstBaseTransformClass *trans_class;
1369   GParamSpec *obj_properties[PROP_N] = { NULL, };
1370 
1371   gobject_class = G_OBJECT_CLASS (klass);
1372   element_class = GST_ELEMENT_CLASS (klass);
1373   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1374 
1375   gobject_class->set_property = gst_msdkvpp_set_property;
1376   gobject_class->get_property = gst_msdkvpp_get_property;
1377   gobject_class->finalize = gst_msdkvpp_finalize;
1378 
1379   element_class->set_context = gst_msdkvpp_set_context;
1380 
1381   gst_element_class_add_static_pad_template (element_class,
1382       &gst_msdkvpp_src_factory);
1383   gst_element_class_add_static_pad_template (element_class,
1384       &gst_msdkvpp_sink_factory);
1385 
1386   gst_element_class_set_static_metadata (element_class,
1387       "MSDK Video Postprocessor",
1388       "Filter/Converter/Video;Filter/Converter/Video/Scaler;"
1389       "Filter/Effect/Video;Filter/Effect/Video/Deinterlace",
1390       "A MediaSDK Video Postprocessing Filter",
1391       "Sreerenj Balachandrn <sreerenj.balachandran@intel.com>");
1392 
1393   trans_class->start = GST_DEBUG_FUNCPTR (gst_msdkvpp_start);
1394   trans_class->stop = GST_DEBUG_FUNCPTR (gst_msdkvpp_stop);
1395   trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_transform_caps);
1396   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_fixate_caps);
1397   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_set_caps);
1398   trans_class->transform = GST_DEBUG_FUNCPTR (gst_msdkvpp_transform);
1399   trans_class->propose_allocation =
1400       GST_DEBUG_FUNCPTR (gst_msdkvpp_propose_allocation);
1401   trans_class->decide_allocation =
1402       GST_DEBUG_FUNCPTR (gst_msdkvpp_decide_allocation);
1403   trans_class->prepare_output_buffer =
1404       GST_DEBUG_FUNCPTR (gst_msdkvpp_prepare_output_buffer);
1405 
1406   obj_properties[PROP_HARDWARE] =
1407       g_param_spec_boolean ("hardware", "Hardware", "Enable hardware VPP",
1408       PROP_HARDWARE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1409 
1410   obj_properties[PROP_ASYNC_DEPTH] =
1411       g_param_spec_uint ("async-depth", "Async Depth",
1412       "Depth of asynchronous pipeline",
1413       1, 1, PROP_ASYNC_DEPTH_DEFAULT,
1414       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1415 
1416   obj_properties[PROP_DENOISE] =
1417       g_param_spec_uint ("denoise", "Denoising factor",
1418       "Denoising Factor",
1419       0, 100, PROP_DENOISE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1420 
1421   obj_properties[PROP_ROTATION] =
1422       g_param_spec_enum ("rotation", "Rotation",
1423       "Rotation Angle", gst_msdkvpp_rotation_get_type (),
1424       PROP_ROTATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1425 
1426   obj_properties[PROP_DEINTERLACE_MODE] =
1427       g_param_spec_enum ("deinterlace-mode", "Deinterlace Mode",
1428       "Deinterlace mode to use", gst_msdkvpp_deinterlace_mode_get_type (),
1429       PROP_DEINTERLACE_MODE_DEFAULT,
1430       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1431 
1432   obj_properties[PROP_DEINTERLACE_METHOD] =
1433       g_param_spec_enum ("deinterlace-method", "Deinterlace Method",
1434       "Deinterlace method to use", gst_msdkvpp_deinterlace_method_get_type (),
1435       PROP_DEINTERLACE_METHOD_DEFAULT,
1436       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1437 
1438   obj_properties[PROP_HUE] =
1439       g_param_spec_float ("hue", "Hue",
1440       "The hue of the video",
1441       -180, 180, PROP_HUE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1442 
1443   obj_properties[PROP_SATURATION] =
1444       g_param_spec_float ("saturation", "Saturation",
1445       "The Saturation of the video",
1446       0, 10, PROP_SATURATION_DEFAULT,
1447       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1448 
1449   obj_properties[PROP_BRIGHTNESS] =
1450       g_param_spec_float ("brightness", "Brightness",
1451       "The Brightness of the video",
1452       -100, 100, PROP_BRIGHTNESS_DEFAULT,
1453       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1454 
1455   obj_properties[PROP_CONTRAST] =
1456       g_param_spec_float ("contrast", "Contrast",
1457       "The Contrast of the video",
1458       0, 10, PROP_CONTRAST_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1459 
1460   obj_properties[PROP_DETAIL] =
1461       g_param_spec_uint ("detail", "Detail",
1462       "The factor of detail/edge enhancement filter algorithm",
1463       0, 100, PROP_DETAIL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1464 
1465   obj_properties[PROP_MIRRORING] =
1466       g_param_spec_enum ("mirroring", "Mirroring",
1467       "The Mirroring type", gst_msdkvpp_mirroring_get_type (),
1468       PROP_MIRRORING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1469 
1470   obj_properties[PROP_SCALING_MODE] =
1471       g_param_spec_enum ("scaling-mode", "Scaling Mode",
1472       "The Scaling mode to use", gst_msdkvpp_scaling_mode_get_type (),
1473       PROP_SCALING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1474 
1475   obj_properties[PROP_FORCE_ASPECT_RATIO] =
1476       g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
1477       "When enabled, scaling will respect original aspect ratio",
1478       PROP_FORCE_ASPECT_RATIO_DEFAULT,
1479       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1480 
1481   obj_properties[PROP_FRC_ALGORITHM] =
1482       g_param_spec_enum ("frc-algorithm", "FrameRateControl Algorithm",
1483       "The Framerate Control Alogorithm to use",
1484       gst_msdkvpp_frc_algorithm_get_type (), PROP_FRC_ALGORITHM_DEFAULT,
1485       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1486 
1487   g_object_class_install_properties (gobject_class, PROP_N, obj_properties);
1488 }
1489 
1490 static void
gst_msdkvpp_init(GstMsdkVPP * thiz)1491 gst_msdkvpp_init (GstMsdkVPP * thiz)
1492 {
1493   thiz->initialized = FALSE;
1494   thiz->hardware = PROP_HARDWARE_DEFAULT;
1495   thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
1496   thiz->denoise_factor = PROP_DENOISE_DEFAULT;
1497   thiz->rotation = PROP_ROTATION_DEFAULT;
1498   thiz->deinterlace_mode = PROP_DEINTERLACE_MODE_DEFAULT;
1499   thiz->deinterlace_method = PROP_DEINTERLACE_METHOD_DEFAULT;
1500   thiz->buffer_duration = GST_CLOCK_TIME_NONE;
1501   thiz->hue = PROP_HUE_DEFAULT;
1502   thiz->saturation = PROP_SATURATION_DEFAULT;
1503   thiz->brightness = PROP_BRIGHTNESS_DEFAULT;
1504   thiz->contrast = PROP_CONTRAST_DEFAULT;
1505   thiz->detail = PROP_DETAIL_DEFAULT;
1506   thiz->mirroring = PROP_MIRRORING_DEFAULT;
1507   thiz->scaling_mode = PROP_SCALING_MODE_DEFAULT;
1508   thiz->keep_aspect = PROP_FORCE_ASPECT_RATIO_DEFAULT;
1509   thiz->frc_algm = PROP_FRC_ALGORITHM_DEFAULT;
1510   gst_video_info_init (&thiz->sinkpad_info);
1511   gst_video_info_init (&thiz->srcpad_info);
1512 }
1513