msdk: vpp:Add more filters
[platform/upstream/gstreamer.git] / sys / msdk / gstmsdkvpp.c
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 #include "msdk-enums.h"
47
48 GST_DEBUG_CATEGORY_EXTERN (gst_msdkvpp_debug);
49 #define GST_CAT_DEFAULT gst_msdkvpp_debug
50
51 static GstStaticPadTemplate gst_msdkvpp_sink_factory =
52 GST_STATIC_PAD_TEMPLATE ("sink",
53     GST_PAD_SINK,
54     GST_PAD_ALWAYS,
55     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, I420, YUY2, UYVY, BGRA }")
56         ", " "interlace-mode = (string){ progressive, interleaved, mixed }"));
57
58 static GstStaticPadTemplate gst_msdkvpp_src_factory =
59 GST_STATIC_PAD_TEMPLATE ("src",
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, BGRA }") ", "
63         "interlace-mode = (string){ progressive, interleaved, mixed }"));
64
65 enum
66 {
67   PROP_0,
68   PROP_HARDWARE,
69   PROP_ASYNC_DEPTH,
70   PROP_DENOISE,
71   PROP_ROTATION,
72   PROP_N,
73 };
74
75 #define PROP_HARDWARE_DEFAULT            TRUE
76 #define PROP_ASYNC_DEPTH_DEFAULT         1
77 #define PROP_DENOISE_DEFAULT             0
78 #define PROP_ROTATION_DEFAULT            MFX_ANGLE_0
79
80 #define gst_msdkvpp_parent_class parent_class
81 G_DEFINE_TYPE (GstMsdkVPP, gst_msdkvpp, GST_TYPE_BASE_TRANSFORM);
82
83 typedef struct
84 {
85   mfxFrameSurface1 *surface;
86   GstBuffer *buf;
87 } MsdkSurface;
88
89 static void
90 free_msdk_surface (MsdkSurface * surface)
91 {
92   if (surface->buf)
93     gst_buffer_unref (surface->buf);
94   g_slice_free (MsdkSurface, surface);
95 }
96
97 static void
98 gst_msdkvpp_add_extra_param (GstMsdkVPP * thiz, mfxExtBuffer * param)
99 {
100   if (thiz->num_extra_params < MAX_EXTRA_PARAMS) {
101     thiz->extra_params[thiz->num_extra_params] = param;
102     thiz->num_extra_params++;
103   }
104 }
105
106 static gboolean
107 ensure_context (GstBaseTransform * trans)
108 {
109   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
110
111   if (gst_msdk_context_prepare (GST_ELEMENT_CAST (thiz), &thiz->context)) {
112     GST_INFO_OBJECT (thiz, "Found context from neighbour %" GST_PTR_FORMAT,
113         thiz->context);
114
115     if (gst_msdk_context_get_job_type (thiz->context) & GST_MSDK_JOB_VPP) {
116       GstMsdkContext *parent_context;
117
118       parent_context = thiz->context;
119       thiz->context = gst_msdk_context_new_with_parent (parent_context);
120       gst_object_unref (parent_context);
121
122       GST_INFO_OBJECT (thiz,
123           "Creating new context %" GST_PTR_FORMAT " with joined session",
124           thiz->context);
125     } else {
126       gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_VPP);
127     }
128   } else {
129     if (!gst_msdk_context_ensure_context (GST_ELEMENT_CAST (thiz),
130             thiz->hardware, GST_MSDK_JOB_VPP))
131       return FALSE;
132     GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT,
133         thiz->context);
134   }
135
136   gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth);
137
138   return TRUE;
139 }
140
141 static GstBuffer *
142 create_output_buffer (GstMsdkVPP * thiz)
143 {
144   GstBuffer *outbuf;
145   GstFlowReturn ret;
146   GstBufferPool *pool = thiz->srcpad_buffer_pool;
147
148   g_return_val_if_fail (pool != NULL, NULL);
149
150   if (!gst_buffer_pool_is_active (pool) &&
151       !gst_buffer_pool_set_active (pool, TRUE))
152     goto error_activate_pool;
153
154   outbuf = NULL;
155   ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
156   if (ret != GST_FLOW_OK || !outbuf)
157     goto error_create_buffer;
158
159   return outbuf;
160
161   /* ERRORS */
162 error_activate_pool:
163   {
164     GST_ERROR_OBJECT (thiz, "failed to activate output video buffer pool");
165     return NULL;
166   }
167 error_create_buffer:
168   {
169     GST_ERROR_OBJECT (thiz, "failed to create output video buffer");
170     return NULL;
171   }
172 }
173
174 static GstFlowReturn
175 gst_msdkvpp_prepare_output_buffer (GstBaseTransform * trans,
176     GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
177 {
178   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
179
180   if (gst_base_transform_is_passthrough (trans)) {
181     *outbuf_ptr = inbuf;
182     return GST_FLOW_OK;
183   }
184
185   *outbuf_ptr = create_output_buffer (thiz);
186   return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
187 }
188
189 static GstBufferPool *
190 gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
191     GstCaps * caps, guint min_num_buffers)
192 {
193   GstBufferPool *pool = NULL;
194   GstStructure *config;
195   GstAllocator *allocator = NULL;
196   GstVideoInfo info;
197   GstVideoInfo *pool_info = NULL;
198   GstVideoAlignment align;
199   GstAllocationParams params = { 0, 31, 0, 0, };
200   mfxFrameAllocResponse *alloc_resp = NULL;
201
202   if (direction == GST_PAD_SINK) {
203     alloc_resp = &thiz->in_alloc_resp;
204     pool_info = &thiz->sinkpad_buffer_pool_info;
205   } else if (direction == GST_PAD_SRC) {
206     alloc_resp = &thiz->out_alloc_resp;
207     pool_info = &thiz->srcpad_buffer_pool_info;
208   }
209
210   pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
211   if (!pool)
212     goto error_no_pool;
213
214   if (!gst_video_info_from_caps (&info, caps))
215     goto error_no_video_info;
216
217   gst_msdk_set_video_alignment (&info, &align);
218   gst_video_info_align (&info, &align);
219
220   if (thiz->use_video_memory)
221     allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
222   else
223     allocator = gst_msdk_system_allocator_new (&info);
224
225   if (!allocator)
226     goto error_no_allocator;
227
228   config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
229   gst_buffer_pool_config_set_params (config, caps, info.size, min_num_buffers,
230       0);
231
232   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
233   gst_buffer_pool_config_add_option (config,
234       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
235   if (thiz->use_video_memory)
236     gst_buffer_pool_config_add_option (config,
237         GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
238
239   gst_buffer_pool_config_set_video_alignment (config, &align);
240   gst_buffer_pool_config_set_allocator (config, allocator, &params);
241   gst_object_unref (allocator);
242
243   if (!gst_buffer_pool_set_config (pool, config))
244     goto error_pool_config;
245
246   /* Updating pool_info with algined info of allocator */
247   *pool_info = info;
248
249   return pool;
250
251 error_no_pool:
252   {
253     GST_INFO_OBJECT (thiz, "Failed to create bufferpool");
254     return NULL;
255   }
256 error_no_video_info:
257   {
258     GST_INFO_OBJECT (thiz, "Failed to get Video info from caps");
259     return NULL;
260   }
261 error_no_allocator:
262   {
263     GST_INFO_OBJECT (thiz, "Failed to create allocator");
264     if (pool)
265       gst_object_unref (pool);
266     return NULL;
267   }
268 error_pool_config:
269   {
270     GST_INFO_OBJECT (thiz, "Failed to set config");
271     if (pool)
272       gst_object_unref (pool);
273     if (allocator)
274       gst_object_unref (allocator);
275     return NULL;
276   }
277 }
278
279 static gboolean
280 gst_msdkvpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
281 {
282   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
283   GstVideoInfo info;
284   GstBufferPool *pool = NULL;
285   GstStructure *config = NULL;
286   GstCaps *caps;
287   guint size = 0, min_buffers = 0, max_buffers = 0;
288   GstAllocator *allocator = NULL;
289   GstAllocationParams params;
290   gboolean update_pool = FALSE;
291
292   gst_query_parse_allocation (query, &caps, NULL);
293   if (!caps) {
294     GST_ERROR_OBJECT (thiz, "Failed to parse the decide_allocation caps");
295     return FALSE;
296   }
297   if (!gst_video_info_from_caps (&info, caps)) {
298     GST_ERROR_OBJECT (thiz, "Failed to get video info");
299     return FALSE;
300   }
301
302   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL))
303     thiz->add_video_meta = TRUE;
304   else
305     thiz->add_video_meta = FALSE;
306
307   if (gst_query_get_n_allocation_pools (query) > 0) {
308     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min_buffers,
309         &max_buffers);
310     update_pool = TRUE;
311     size = MAX (size, GST_VIDEO_INFO_SIZE (&info));
312
313     if (pool && !GST_IS_MSDK_BUFFER_POOL (pool)) {
314       GST_INFO_OBJECT (thiz, "ignoring non-msdk pool: %" GST_PTR_FORMAT, pool);
315       g_clear_object (&pool);
316     }
317   }
318
319   if (!pool) {
320     gst_object_unref (thiz->srcpad_buffer_pool);
321     pool =
322         gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, caps, min_buffers);
323     thiz->srcpad_buffer_pool = pool;
324
325     /* get the configured pool properties inorder to set in query */
326     config = gst_buffer_pool_get_config (pool);
327     gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
328         &max_buffers);
329     if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
330       gst_query_add_allocation_param (query, allocator, &params);
331     gst_structure_free (config);
332   }
333
334   if (update_pool)
335     gst_query_set_nth_allocation_pool (query, 0, pool, size, min_buffers,
336         max_buffers);
337   else
338     gst_query_add_allocation_pool (query, pool, size, min_buffers, max_buffers);
339
340   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
341
342   /* Fixme if downstream doesn't have videometa support, msdkvpp should
343    * copy the output buffers */
344
345   return TRUE;
346 }
347
348 static gboolean
349 gst_msdkvpp_propose_allocation (GstBaseTransform * trans,
350     GstQuery * decide_query, GstQuery * query)
351 {
352   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
353   GstVideoInfo info;
354   GstBufferPool *pool = NULL;
355   GstAllocator *allocator = NULL;
356   GstCaps *caps;
357   GstStructure *config;
358   gboolean need_pool;
359   guint size;
360   GstAllocationParams params;
361
362   gst_query_parse_allocation (query, &caps, &need_pool);
363   if (!caps) {
364     GST_ERROR_OBJECT (thiz, "Failed to parse the allocation caps");
365     return FALSE;
366   }
367
368   if (!gst_video_info_from_caps (&info, caps)) {
369     GST_ERROR_OBJECT (thiz, "Failed to get video info");
370     return FALSE;
371   }
372
373   size = MAX (info.size, GST_VIDEO_INFO_SIZE (&thiz->sinkpad_buffer_pool_info));
374
375   /* We already created a pool while setting the caps
376    * just to make sure the pipeline works even if there is
377    * no allocation query from upstream (theoratical ??).Provide the
378    * same pool in query if required/possible */
379   if (!gst_video_info_is_equal (&thiz->sinkpad_buffer_pool_info, &info)) {
380     gst_object_unref (thiz->sinkpad_buffer_pool);
381     thiz->sinkpad_buffer_pool =
382         gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
383         thiz->in_num_surfaces);
384   }
385
386   pool = thiz->sinkpad_buffer_pool;
387
388   config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
389
390   gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
391
392   if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
393     gst_query_add_allocation_param (query, allocator, &params);
394   gst_structure_free (config);
395
396   /* if upstream does't have a pool requirement, set only
397    *  size, min_buffers and max_buffers in query */
398   if (!need_pool)
399     pool = NULL;
400
401   gst_query_add_allocation_pool (query, pool, size, thiz->in_num_surfaces, 0);
402   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
403
404   return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
405       decide_query, query);
406 }
407
408 static MsdkSurface *
409 get_surface_from_pool (GstMsdkVPP * thiz, GstBufferPool * pool,
410     GstBufferPoolAcquireParams * params)
411 {
412   GstBuffer *new_buffer;
413   mfxFrameSurface1 *new_surface;
414   MsdkSurface *msdk_surface;
415
416   if (!gst_buffer_pool_is_active (pool) &&
417       !gst_buffer_pool_set_active (pool, TRUE)) {
418     GST_ERROR_OBJECT (pool, "failed to activate buffer pool");
419     return NULL;
420   }
421
422   if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) {
423     GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool");
424     return NULL;
425   }
426
427   if (gst_msdk_is_msdk_buffer (new_buffer))
428     new_surface = gst_msdk_get_surface_from_buffer (new_buffer);
429   else {
430     GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory");
431     return NULL;
432   }
433
434   msdk_surface = g_slice_new0 (MsdkSurface);
435   msdk_surface->surface = new_surface;
436   msdk_surface->buf = new_buffer;
437
438   return msdk_surface;
439 }
440
441 static MsdkSurface *
442 get_msdk_surface_from_input_buffer (GstMsdkVPP * thiz, GstBuffer * inbuf)
443 {
444   GstVideoFrame src_frame, out_frame;
445   MsdkSurface *msdk_surface;
446
447   if (gst_msdk_is_msdk_buffer (inbuf)) {
448     msdk_surface = g_slice_new0 (MsdkSurface);
449     msdk_surface->surface = gst_msdk_get_surface_from_buffer (inbuf);
450     msdk_surface->buf = gst_buffer_ref (inbuf);
451     return msdk_surface;
452   }
453
454   /* If upstream hasn't accpeted the proposed msdk bufferpool,
455    * just copy frame to msdk buffer and take a surface from it.
456    */
457   if (!(msdk_surface =
458           get_surface_from_pool (thiz, thiz->sinkpad_buffer_pool, NULL)))
459     goto error;
460
461   if (!gst_video_frame_map (&src_frame, &thiz->sinkpad_info, inbuf,
462           GST_MAP_READ)) {
463     GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
464     goto error;
465   }
466
467   if (!gst_video_frame_map (&out_frame, &thiz->sinkpad_buffer_pool_info,
468           msdk_surface->buf, GST_MAP_WRITE)) {
469     GST_ERROR_OBJECT (thiz, "failed to map the frame for destination");
470     gst_video_frame_unmap (&src_frame);
471     goto error;
472   }
473
474   if (!gst_video_frame_copy (&out_frame, &src_frame)) {
475     GST_ERROR_OBJECT (thiz, "failed to copy frame");
476     gst_video_frame_unmap (&out_frame);
477     gst_video_frame_unmap (&src_frame);
478     goto error;
479   }
480
481   gst_video_frame_unmap (&out_frame);
482   gst_video_frame_unmap (&src_frame);
483
484   return msdk_surface;
485
486 error:
487   return NULL;
488 }
489
490 static GstFlowReturn
491 gst_msdkvpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
492     GstBuffer * outbuf)
493 {
494   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
495   mfxSession session;
496   mfxSyncPoint sync_point = NULL;
497   mfxStatus status;
498   MsdkSurface *in_surface = NULL;
499   MsdkSurface *out_surface = NULL;
500
501   in_surface = get_msdk_surface_from_input_buffer (thiz, inbuf);
502   if (!in_surface)
503     return GST_FLOW_ERROR;
504
505   if (gst_msdk_is_msdk_buffer (outbuf)) {
506     out_surface = g_slice_new0 (MsdkSurface);
507     out_surface->surface = gst_msdk_get_surface_from_buffer (outbuf);
508   } else {
509     GST_ERROR ("Failed to get msdk outsurface!");
510     return GST_FLOW_ERROR;
511   }
512
513   session = gst_msdk_context_get_session (thiz->context);
514   for (;;) {
515     status =
516         MFXVideoVPP_RunFrameVPPAsync (session, in_surface->surface,
517         out_surface->surface, NULL, &sync_point);
518     if (status != MFX_WRN_DEVICE_BUSY)
519       break;
520     /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
521     g_usleep (1000);
522   };
523
524   if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA
525       && status != MFX_ERR_MORE_SURFACE)
526     goto vpp_error;
527
528   /* No output generated */
529   if (status == MFX_ERR_MORE_DATA)
530     goto error_more_data;
531   if (sync_point)
532     MFXVideoCORE_SyncOperation (session, sync_point, 10000);
533
534   /* More than one output buffers are generated */
535   if (status == MFX_ERR_MORE_SURFACE)
536     status = MFX_ERR_NONE;
537
538   gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
539
540   free_msdk_surface (in_surface);
541   return GST_FLOW_OK;
542
543 vpp_error:
544   GST_ERROR_OBJECT (thiz, "MSDK Failed to do VPP");
545   free_msdk_surface (in_surface);
546   free_msdk_surface (out_surface);
547   return GST_FLOW_ERROR;
548
549 error_more_data:
550   GST_WARNING_OBJECT (thiz,
551       "MSDK Requries additional input for processing, "
552       "Retruning FLOW_DROPPED since no output buffer was generated");
553   free_msdk_surface (in_surface);
554   return GST_BASE_TRANSFORM_FLOW_DROPPED;
555 }
556
557 static void
558 gst_msdkvpp_close (GstMsdkVPP * thiz)
559 {
560   mfxStatus status;
561
562   if (!thiz->context)
563     return;
564
565   GST_DEBUG_OBJECT (thiz, "Closing VPP 0x%p", thiz->context);
566   status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
567   if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
568     GST_WARNING_OBJECT (thiz, "Encoder close failed (%s)",
569         msdk_status_to_string (status));
570   }
571
572   if (thiz->context)
573     gst_object_replace ((GstObject **) & thiz->context, NULL);
574
575   memset (&thiz->param, 0, sizeof (thiz->param));
576
577   if (thiz->sinkpad_buffer_pool)
578     gst_object_unref (thiz->sinkpad_buffer_pool);
579   thiz->sinkpad_buffer_pool = NULL;
580   if (thiz->srcpad_buffer_pool)
581     gst_object_unref (thiz->srcpad_buffer_pool);
582   thiz->srcpad_buffer_pool = NULL;
583
584   gst_video_info_init (&thiz->sinkpad_info);
585   gst_video_info_init (&thiz->srcpad_info);
586 }
587
588 static void
589 ensure_filters (GstMsdkVPP * thiz)
590 {
591   guint n_filters = 0;
592
593   /* Denoise */
594   if (thiz->flags & GST_MSDK_FLAG_DENOISE) {
595     mfxExtVPPDenoise *mfx_denoise = &thiz->mfx_denoise;
596     mfx_denoise->Header.BufferId = MFX_EXTBUFF_VPP_DENOISE;
597     mfx_denoise->Header.BufferSz = sizeof (mfxExtVPPDenoise);
598     mfx_denoise->DenoiseFactor = thiz->denoise_factor;
599     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_denoise);
600     thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_DENOISE;
601     n_filters++;
602   }
603
604   /* Rotation */
605   if (thiz->flags & GST_MSDK_FLAG_ROTATION) {
606     mfxExtVPPRotation *mfx_rotation = &thiz->mfx_rotation;
607     mfx_rotation->Header.BufferId = MFX_EXTBUFF_VPP_ROTATION;
608     mfx_rotation->Header.BufferSz = sizeof (mfxExtVPPRotation);
609     mfx_rotation->Angle = thiz->rotation;
610     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_rotation);
611     thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_ROTATION;
612     n_filters++;
613   }
614
615   /* mfxExtVPPDoUse */
616   if (n_filters) {
617     mfxExtVPPDoUse *mfx_vpp_douse = &thiz->mfx_vpp_douse;
618     mfx_vpp_douse->Header.BufferId = MFX_EXTBUFF_VPP_DOUSE;
619     mfx_vpp_douse->Header.BufferSz = sizeof (mfxExtVPPDoUse);
620     mfx_vpp_douse->NumAlg = n_filters;
621     mfx_vpp_douse->AlgList = thiz->max_filter_algorithms;
622     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_vpp_douse);
623   }
624 }
625
626 static void
627 gst_msdkvpp_set_passthrough (GstMsdkVPP * thiz)
628 {
629   gboolean passthrough = TRUE;
630
631   /* no passthrough if any of the filter algorithm is enabled */
632   if (thiz->flags)
633     passthrough = FALSE;
634
635   /* no passthrough if there is change in out width,height or format */
636   if (GST_VIDEO_INFO_WIDTH (&thiz->sinkpad_info) !=
637       GST_VIDEO_INFO_WIDTH (&thiz->srcpad_info)
638       || GST_VIDEO_INFO_HEIGHT (&thiz->sinkpad_info) !=
639       GST_VIDEO_INFO_HEIGHT (&thiz->srcpad_info)
640       || GST_VIDEO_INFO_FORMAT (&thiz->sinkpad_info) !=
641       GST_VIDEO_INFO_FORMAT (&thiz->srcpad_info))
642     passthrough = FALSE;
643
644   GST_OBJECT_UNLOCK (thiz);
645   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (thiz), passthrough);
646   GST_OBJECT_LOCK (thiz);
647 }
648
649 static gboolean
650 gst_msdkvpp_initialize (GstMsdkVPP * thiz)
651 {
652   mfxSession session;
653   mfxStatus status;
654   mfxFrameAllocRequest request[2];
655
656   if (!thiz->context) {
657     GST_WARNING_OBJECT (thiz, "No MSDK Context");
658     return FALSE;
659   }
660
661   GST_OBJECT_LOCK (thiz);
662   session = gst_msdk_context_get_session (thiz->context);
663
664   if (thiz->use_video_memory) {
665     gst_msdk_set_frame_allocator (thiz->context);
666     thiz->param.IOPattern =
667         MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
668   } else {
669     thiz->param.IOPattern =
670         MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
671   }
672
673   /* update input video attributes */
674   gst_msdk_set_mfx_frame_info_from_video_info (&thiz->param.vpp.In,
675       &thiz->sinkpad_info);
676
677   /* update output video attributes, only CSC and Scaling are supported for now */
678   gst_msdk_set_mfx_frame_info_from_video_info (&thiz->param.vpp.Out,
679       &thiz->srcpad_info);
680   thiz->param.vpp.Out.FrameRateExtN =
681       GST_VIDEO_INFO_FPS_N (&thiz->sinkpad_info);
682   thiz->param.vpp.Out.FrameRateExtD =
683       GST_VIDEO_INFO_FPS_D (&thiz->sinkpad_info);
684
685   /* validate parameters and allow the Media SDK to make adjustments */
686   status = MFXVideoVPP_Query (session, &thiz->param, &thiz->param);
687   if (status < MFX_ERR_NONE) {
688     GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)",
689         msdk_status_to_string (status));
690     goto no_vpp;
691   } else if (status > MFX_ERR_NONE) {
692     GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s",
693         msdk_status_to_string (status));
694   }
695
696   /* Enable the required filters */
697   ensure_filters (thiz);
698
699   /* set passthrough according to filter operation change */
700   gst_msdkvpp_set_passthrough (thiz);
701
702   /* Add exteneded buffers */
703   if (thiz->num_extra_params) {
704     thiz->param.NumExtParam = thiz->num_extra_params;
705     thiz->param.ExtParam = thiz->extra_params;
706   }
707
708   status = MFXVideoVPP_QueryIOSurf (session, &thiz->param, request);
709   if (status < MFX_ERR_NONE) {
710     GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
711         msdk_status_to_string (status));
712     goto no_vpp;
713   } else if (status > MFX_ERR_NONE) {
714     GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s",
715         msdk_status_to_string (status));
716   }
717
718   if (thiz->use_video_memory) {
719     /* Input surface pool pre-allocation */
720     gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->in_alloc_resp);
721     /* Output surface pool pre-allocation */
722     gst_msdk_frame_alloc (thiz->context, &(request[1]), &thiz->out_alloc_resp);
723   }
724
725   thiz->in_num_surfaces = request[0].NumFrameSuggested;
726   thiz->out_num_surfaces = request[1].NumFrameSuggested;
727
728
729   status = MFXVideoVPP_Init (session, &thiz->param);
730   if (status < MFX_ERR_NONE) {
731     GST_ERROR_OBJECT (thiz, "Init failed (%s)", msdk_status_to_string (status));
732     goto no_vpp;
733   } else if (status > MFX_ERR_NONE) {
734     GST_WARNING_OBJECT (thiz, "Init returned: %s",
735         msdk_status_to_string (status));
736   }
737
738   GST_OBJECT_UNLOCK (thiz);
739   return TRUE;
740
741 no_vpp:
742   GST_OBJECT_UNLOCK (thiz);
743   if (thiz->context)
744     gst_object_replace ((GstObject **) & thiz->context, NULL);
745   return FALSE;
746 }
747
748 static gboolean
749 gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
750     GstCaps * out_caps)
751 {
752   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
753   GstVideoInfo in_info, out_info;
754   gboolean sinkpad_info_changed = FALSE;
755   gboolean srcpad_info_changed = FALSE;
756
757   gst_video_info_from_caps (&in_info, caps);
758   gst_video_info_from_caps (&out_info, out_caps);
759
760   if (!gst_video_info_is_equal (&in_info, &thiz->sinkpad_info))
761     sinkpad_info_changed = TRUE;
762   if (!gst_video_info_is_equal (&out_info, &thiz->srcpad_info))
763     srcpad_info_changed = TRUE;
764
765   thiz->sinkpad_info = in_info;
766   thiz->srcpad_info = out_info;
767 #ifndef _WIN32
768   thiz->use_video_memory = TRUE;
769 #else
770   thiz->use_video_memory = FALSE;
771 #endif
772
773   if (!sinkpad_info_changed && !srcpad_info_changed)
774     return TRUE;
775
776   if (!gst_msdkvpp_initialize (thiz))
777     return FALSE;
778
779   /* Ensure sinkpad buffer pool */
780   thiz->sinkpad_buffer_pool =
781       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
782       thiz->in_num_surfaces);
783   if (!thiz->sinkpad_buffer_pool) {
784     GST_ERROR_OBJECT (thiz, "Failed to ensure the sinkpad buffer pool");
785     return FALSE;
786   }
787   /* Ensure a srcpad buffer pool */
788   thiz->srcpad_buffer_pool =
789       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, out_caps,
790       thiz->out_num_surfaces);
791   if (!thiz->srcpad_buffer_pool) {
792     GST_ERROR_OBJECT (thiz, "Failed to ensure the srcpad buffer pool");
793     return FALSE;
794   }
795
796   return TRUE;
797 }
798
799 static GstCaps *
800 gst_msdkvpp_fixate_caps (GstBaseTransform * trans,
801     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
802 {
803   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
804   GstCaps *result = NULL;
805
806   if (direction == GST_PAD_SRC)
807     result = gst_caps_fixate (result);
808   else {
809     result = gst_msdkvpp_fixate_srccaps (thiz, caps, othercaps);
810   }
811
812   GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, result);
813   gst_caps_unref (othercaps);
814   return result;
815 }
816
817 /* Generic code for now, requires changes in future when we
818  * add hardware query for supported formats, Framerate control etc */
819 static GstCaps *
820 gst_msdkvpp_transform_caps (GstBaseTransform * trans,
821     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
822 {
823   GstCaps *out_caps;
824
825   GST_DEBUG_OBJECT (trans,
826       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
827       (direction == GST_PAD_SINK) ? "sink" : "src");
828
829   if (direction == GST_PAD_SRC)
830     out_caps = gst_static_pad_template_get_caps (&gst_msdkvpp_sink_factory);
831   else
832     out_caps = gst_static_pad_template_get_caps (&gst_msdkvpp_src_factory);
833
834   if (out_caps && filter) {
835     GstCaps *intersection;
836
837     intersection = gst_caps_intersect_full (out_caps, filter,
838         GST_CAPS_INTERSECT_FIRST);
839     gst_caps_unref (out_caps);
840     out_caps = intersection;
841   }
842
843   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
844   return out_caps;
845 }
846
847 static gboolean
848 gst_msdkvpp_start (GstBaseTransform * trans)
849 {
850   if (!ensure_context (trans))
851     return FALSE;
852   return TRUE;
853 }
854
855 static gboolean
856 gst_msdkvpp_stop (GstBaseTransform * trans)
857 {
858   gst_msdkvpp_close (GST_MSDKVPP (trans));
859   return TRUE;
860 }
861
862 static void
863 gst_msdkvpp_set_property (GObject * object, guint prop_id,
864     const GValue * value, GParamSpec * pspec)
865 {
866   GstMsdkVPP *thiz = GST_MSDKVPP (object);
867
868   switch (prop_id) {
869     case PROP_HARDWARE:
870       thiz->hardware = g_value_get_boolean (value);
871       break;
872     case PROP_ASYNC_DEPTH:
873       thiz->async_depth = g_value_get_uint (value);
874       break;
875     case PROP_DENOISE:
876       thiz->denoise_factor = g_value_get_uint (value);
877       thiz->flags |= GST_MSDK_FLAG_DENOISE;
878       break;
879     case PROP_ROTATION:
880       thiz->rotation = g_value_get_enum (value);
881       thiz->flags |= GST_MSDK_FLAG_ROTATION;
882       break;
883     default:
884       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
885       break;
886   }
887 }
888
889 static void
890 gst_msdkvpp_get_property (GObject * object, guint prop_id,
891     GValue * value, GParamSpec * pspec)
892 {
893   GstMsdkVPP *thiz = GST_MSDKVPP (object);
894
895   switch (prop_id) {
896     case PROP_HARDWARE:
897       g_value_set_boolean (value, thiz->hardware);
898       break;
899     case PROP_ASYNC_DEPTH:
900       g_value_set_uint (value, thiz->async_depth);
901       break;
902     case PROP_DENOISE:
903       g_value_set_uint (value, thiz->denoise_factor);
904       break;
905     case PROP_ROTATION:
906       g_value_set_enum (value, thiz->rotation);
907       break;
908     default:
909       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
910       break;
911   }
912 }
913
914 static void
915 gst_msdkvpp_finalize (GObject * object)
916 {
917   G_OBJECT_CLASS (parent_class)->finalize (object);
918 }
919
920 static void
921 gst_msdkvpp_set_context (GstElement * element, GstContext * context)
922 {
923   GstMsdkContext *msdk_context = NULL;
924   GstMsdkVPP *thiz = GST_MSDKVPP (element);
925
926   if (gst_msdk_context_get_context (context, &msdk_context)) {
927     gst_object_replace ((GstObject **) & thiz->context,
928         (GstObject *) msdk_context);
929     gst_object_unref (msdk_context);
930   }
931
932   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
933 }
934
935 static void
936 gst_msdkvpp_class_init (GstMsdkVPPClass * klass)
937 {
938   GObjectClass *gobject_class;
939   GstElementClass *element_class;
940   GstBaseTransformClass *trans_class;
941   GParamSpec *obj_properties[PROP_N] = { NULL, };
942
943   gobject_class = G_OBJECT_CLASS (klass);
944   element_class = GST_ELEMENT_CLASS (klass);
945   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
946
947   gobject_class->set_property = gst_msdkvpp_set_property;
948   gobject_class->get_property = gst_msdkvpp_get_property;
949   gobject_class->finalize = gst_msdkvpp_finalize;
950
951   element_class->set_context = gst_msdkvpp_set_context;
952
953   gst_element_class_add_static_pad_template (element_class,
954       &gst_msdkvpp_src_factory);
955   gst_element_class_add_static_pad_template (element_class,
956       &gst_msdkvpp_sink_factory);
957
958   gst_element_class_set_static_metadata (element_class,
959       "MSDK Video Postprocessor",
960       "Filter/Converter/Video;Filter/Converter/Video/Scaler;"
961       "Filter/Effect/Video;Filter/Effect/Video/Deinterlace",
962       "A MediaSDK Video Postprocessing Filter",
963       "Sreerenj Balachandrn <sreerenj.balachandran@intel.com>");
964
965   trans_class->start = GST_DEBUG_FUNCPTR (gst_msdkvpp_start);
966   trans_class->stop = GST_DEBUG_FUNCPTR (gst_msdkvpp_stop);
967   trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_transform_caps);
968   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_fixate_caps);
969   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_msdkvpp_set_caps);
970   trans_class->transform = GST_DEBUG_FUNCPTR (gst_msdkvpp_transform);
971   trans_class->propose_allocation =
972       GST_DEBUG_FUNCPTR (gst_msdkvpp_propose_allocation);
973   trans_class->decide_allocation =
974       GST_DEBUG_FUNCPTR (gst_msdkvpp_decide_allocation);
975   trans_class->prepare_output_buffer =
976       GST_DEBUG_FUNCPTR (gst_msdkvpp_prepare_output_buffer);
977
978   obj_properties[PROP_HARDWARE] =
979       g_param_spec_boolean ("hardware", "Hardware", "Enable hardware VPP",
980       PROP_HARDWARE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
981
982   obj_properties[PROP_ASYNC_DEPTH] =
983       g_param_spec_uint ("async-depth", "Async Depth",
984       "Depth of asynchronous pipeline",
985       1, 1, PROP_ASYNC_DEPTH_DEFAULT,
986       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
987
988   obj_properties[PROP_DENOISE] =
989       g_param_spec_uint ("denoise", "Denoising factor",
990       "Denoising Factor",
991       0, 100, PROP_DENOISE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
992
993   obj_properties[PROP_ROTATION] =
994       g_param_spec_enum ("rotation", "Rotation",
995       "Rotation Angle", gst_msdkvpp_rotation_get_type (),
996       PROP_ROTATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
997
998   g_object_class_install_properties (gobject_class, PROP_N, obj_properties);
999 }
1000
1001 static void
1002 gst_msdkvpp_init (GstMsdkVPP * thiz)
1003 {
1004   thiz->hardware = PROP_HARDWARE_DEFAULT;
1005   thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
1006   thiz->denoise_factor = PROP_DENOISE_DEFAULT;
1007   thiz->rotation = PROP_ROTATION_DEFAULT;
1008   gst_video_info_init (&thiz->sinkpad_info);
1009   gst_video_info_init (&thiz->srcpad_info);
1010 }