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