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