1 /* ********************************************************************* *\
2 
3 Copyright (C) 2013 Intel Corporation.  All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 - Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9 - Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12 - Neither the name of Intel Corporation nor the names of its contributors
13 may be used to endorse or promote products derived from this software
14 without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 \* ********************************************************************* */
28 
29 #include "handbrake/project.h"
30 
31 #if HB_PROJECT_FEATURE_QSV
32 
33 #include "handbrake/handbrake.h"
34 #include "handbrake/hbffmpeg.h"
35 #include "handbrake/qsv_filter.h"
36 #include "handbrake/qsv_libav.h"
37 
38 struct hb_filter_private_s
39 {
40     hb_job_t                     * job;
41     hb_buffer_list_t               list;
42 
43     int                            width_in;
44     int                            height_in;
45     int                            pix_fmt;
46     int                            pix_fmt_out;
47     int                            width_out;
48     int                            height_out;
49     int                            crop[4];
50     int                            deinterlace;
51     int                            is_frc_used;
52 
53     // set during init, used to configure input surfaces' "area of interest"
54     mfxU16                         CropX;
55     mfxU16                         CropY;
56     mfxU16                         CropH;
57     mfxU16                         CropW;
58 
59     hb_qsv_space                 * vpp_space;
60 
61     // FRC param(s)
62     mfxExtVPPFrameRateConversion   frc_config;
63 };
64 
65 static int hb_qsv_filter_init( hb_filter_object_t * filter,
66                                hb_filter_init_t * init );
67 
68 static int hb_qsv_filter_work( hb_filter_object_t * filter,
69                                hb_buffer_t ** buf_in,
70                                hb_buffer_t ** buf_out );
71 
72 static hb_filter_info_t * hb_qsv_filter_info( hb_filter_object_t * filter );
73 
74 static void hb_qsv_filter_close( hb_filter_object_t * filter );
75 
76 static const char qsv_filter_template[] =
77     "width=^"HB_INT_REG"$:height=^"HB_INT_REG"$:"
78     "crop-top=^"HB_INT_REG"$:crop-bottom=^"HB_INT_REG"$:"
79     "crop-left=^"HB_INT_REG"$:crop-right=^"HB_INT_REG"$:"
80     "deinterlace=^([01])$";
81 
82 hb_filter_object_t hb_filter_qsv =
83 {
84     .id                = HB_FILTER_QSV,
85     .enforce_order     = 1,
86     .name              = "Quick Sync Video VPP",
87     .settings          = NULL,
88     .init              = hb_qsv_filter_init,
89     .work              = hb_qsv_filter_work,
90     .close             = hb_qsv_filter_close,
91     .info              = hb_qsv_filter_info,
92     .settings_template = qsv_filter_template,
93 };
94 
filter_init(hb_qsv_context * qsv,hb_filter_private_t * pv)95 static int filter_init( hb_qsv_context* qsv, hb_filter_private_t * pv ){
96     mfxStatus sts;
97     int i=0;
98 
99     if(!qsv) return 3;
100 
101 
102     if(!qsv->vpp_space){
103         qsv->vpp_space = hb_qsv_list_init(HAVE_THREADS);
104     }
105     if(!pv->vpp_space){
106         for(i=0; i<hb_qsv_list_count(qsv->vpp_space);i++){
107             hb_qsv_space *qsv_vpp = hb_qsv_list_item( qsv->vpp_space, i );
108             if(qsv_vpp->type == HB_QSV_VPP_DEFAULT){
109                 pv->vpp_space = qsv_vpp;
110                 break;
111             }
112         }
113     }
114 
115     if(!pv->vpp_space){
116         pv->vpp_space = calloc( 1, sizeof( hb_qsv_space ));
117         pv->vpp_space->type = HB_QSV_VPP_DEFAULT;
118         hb_qsv_list_add( qsv->vpp_space, pv->vpp_space );
119     }
120     else
121         if(pv->vpp_space->is_init_done ) return 1;
122 
123     if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2;
124 
125     // we need to know final output settings before we can properly configure
126     if (!pv->job->qsv.enc_info.is_init_done)
127     {
128         return 2;
129     }
130 
131     hb_qsv_add_context_usage(qsv,HAVE_THREADS);
132 
133     // see params needed like at mediasdk-man.pdf:"Appendix A: Configuration Parameter Constraints"
134     // for now - most will take from the decode
135     {
136         hb_qsv_space *qsv_vpp = pv->vpp_space;
137         HB_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
138 
139         if (pv->deinterlace)
140         {
141             /*
142              * Input may be progressive, interlaced or even mixed, so init with
143              * MFX_PICSTRUCT_UNKNOWN and use per-frame field order information
144              * (mfxFrameSurface1.Info.PicStruct)
145              */
146             qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct  = MFX_PICSTRUCT_UNKNOWN;
147             qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
148         }
149         else
150         {
151             /* Same PicStruct in/out: no filtering */
152             qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct  = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
153             qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
154         }
155 
156         // FrameRate is important for VPP to start with
157         if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 &&
158             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){
159             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->vrate.num;
160             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->vrate.den;
161         }
162 
163         /*
164          * In theory, input width/height and decode CropW/CropH should be the
165          * same; however, due to some versions of FFmpeg not applying the H.264
166          * "crop rect" properly, there can be a mismatch.
167          *
168          * Since we want the same behavior regardless of whether we're using
169          * software or hardware-accelerated decoding, prefer the FFmpeg values.
170          *
171          * Note that since CropW/CropH may be higher than the decode values, we
172          * need to adjust  CropX/CropY to make sure we don't exceed the input's
173          * Width/Height boundaries.
174          */
175         pv->CropW = pv-> width_in;
176         pv->CropH = pv->height_in;
177         pv->CropX = FFMIN(qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX,
178                           qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width  - pv->CropW);
179         pv->CropY = FFMIN(qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY,
180                           qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height - pv->CropH);
181         /* Then, apply additional cropping requested by the user, if any */
182         pv->CropX += pv->crop[2];
183         pv->CropY += pv->crop[0];
184         pv->CropW -= pv->crop[2] + pv->crop[3];
185         pv->CropH -= pv->crop[0] + pv->crop[1];
186 
187 
188         qsv_vpp->m_mfxVideoParam.vpp.In.FourCC          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC;
189         qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
190         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN   = pv->job->vrate.num;
191         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD   = pv->job->vrate.den;
192         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
193         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
194         qsv_vpp->m_mfxVideoParam.vpp.In.Width           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width;
195         qsv_vpp->m_mfxVideoParam.vpp.In.Height          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height;
196         qsv_vpp->m_mfxVideoParam.vpp.In.CropX           = pv->CropX;
197         qsv_vpp->m_mfxVideoParam.vpp.In.CropY           = pv->CropY;
198         qsv_vpp->m_mfxVideoParam.vpp.In.CropW           = pv->CropW;
199         qsv_vpp->m_mfxVideoParam.vpp.In.CropH           = pv->CropH;
200 
201         qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC;
202         qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
203         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN   = pv->job->vrate.num;
204         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD   = pv->job->vrate.den;
205         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
206         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
207         qsv_vpp->m_mfxVideoParam.vpp.Out.Width           = pv->job->qsv.enc_info.align_width;
208         qsv_vpp->m_mfxVideoParam.vpp.Out.Height          = pv->job->qsv.enc_info.align_height;
209         qsv_vpp->m_mfxVideoParam.vpp.Out.CropX           = 0; // no letterboxing
210         qsv_vpp->m_mfxVideoParam.vpp.Out.CropY           = 0; // no pillarboxing
211         qsv_vpp->m_mfxVideoParam.vpp.Out.CropW           = pv-> width_out;
212         qsv_vpp->m_mfxVideoParam.vpp.Out.CropH           = pv->height_out;
213 
214         qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
215 
216         qsv_vpp->m_mfxVideoParam.AsyncDepth = pv->job->qsv.async_depth;
217 
218         memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2);
219 
220         sts = MFXVideoVPP_QueryIOSurf(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam, qsv_vpp->request );
221         HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
222 
223         int num_surfaces_in  = qsv_vpp->request[0].NumFrameSuggested;
224         int num_surfaces_out = qsv_vpp->request[1].NumFrameSuggested;
225 
226         hb_qsv_config *config = qsv->qsv_config;
227 
228 
229         qsv_vpp->surface_num = FFMIN( num_surfaces_in + num_surfaces_out + qsv_vpp->m_mfxVideoParam.AsyncDepth + config ? config->additional_buffers/2 :0 , HB_QSV_SURFACE_NUM );
230         if(qsv_vpp->surface_num <= 0 )
231             qsv_vpp->surface_num = HB_QSV_SURFACE_NUM;
232 
233         int i = 0;
234         for (i = 0; i < qsv_vpp->surface_num; i++){
235             qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) );
236             HB_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC);
237             memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo));
238         }
239 
240         qsv_vpp->sync_num = FFMIN( qsv_vpp->surface_num, HB_QSV_SYNC_NUM );
241 
242         for (i = 0; i < qsv_vpp->sync_num; i++){
243             qsv_vpp->p_syncp[i] = av_mallocz(sizeof(hb_qsv_sync));
244             HB_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC);
245             qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint));
246             HB_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
247         }
248 /*
249     about available VPP filters, see "Table 4 Configurable VPP filters", mediasdk-man.pdf
250     Hints (optional feature) IDs:
251     MFX_EXTBUFF_VPP_DENOISE                 // Remove noise
252                                             //    Value of 0-100 (inclusive) indicates
253                                             //    the level of noise to remove.
254     MFX_EXTBUFF_VPP_DETAIL                  // Enhance picture details/edges:
255                                             //    0-100 value (inclusive) to indicate
256                                             //    the level of details to be enhanced.
257     MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION   // Convert input frame rate to match the output, based on frame interpolation:
258                                             //    MFX_FRCALGM_PRESERVE_TIMESTAMP,
259                                             //    MFX_FRCALGM_DISTRIBUTED_TIMESTAMP,
260                                             //    MFX_FRCALGM_FRAME_INTERPOLATION
261     MFX_EXTBUFF_VPP_IMAGE_STABILIZATION     // Perform image stabilization
262                                             //  Stabilization modes:
263                                             //      MFX_IMAGESTAB_MODE_UPSCALE
264                                             //      MFX_IMAGESTAB_MODE_BOXING
265     MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION     // Perform detection of picture structure:
266                                             //    Detected picture structure - top field first, bottom field first, progressive or unknown
267                                             //    if video processor cannot detect picture structure.
268     MFX_EXTBUFF_VPP_PROCAMP                 // Adjust the brightness, contrast, saturation, and hue settings
269 
270     // Initialize extended buffer for frame processing
271     // - Process amplifier (ProcAmp) used to control brightness
272     // - mfxExtVPPDoUse:   Define the processing algorithm to be used
273     // - mfxExtVPPProcAmp: ProcAmp configuration
274     // - mfxExtBuffer:     Add extended buffers to VPP parameter configuration
275     mfxExtVPPDoUse extDoUse;
276     mfxU32 tabDoUseAlg[1];
277     extDoUse.Header.BufferId = MFX_EXTBUFF_VPP_DOUSE;
278     extDoUse.Header.BufferSz = sizeof(mfxExtVPPDoUse);
279     extDoUse.NumAlg  = 1;
280     extDoUse.AlgList = tabDoUseAlg;
281     tabDoUseAlg[0] = MFX_EXTBUFF_VPP_PROCAMP;
282 
283     mfxExtVPPProcAmp procampConfig;
284     procampConfig.Header.BufferId = MFX_EXTBUFF_VPP_PROCAMP;
285     procampConfig.Header.BufferSz = sizeof(mfxExtVPPProcAmp);
286     procampConfig.Hue        = 0.0f;  // Default
287     procampConfig.Saturation = 1.0f;  // Default
288     procampConfig.Contrast   = 1.0;   // Default
289     procampConfig.Brightness = 40.0;  // Adjust brightness
290 
291     mfxExtBuffer* ExtBuffer[2];
292     ExtBuffer[0] = (mfxExtBuffer*)&extDoUse;
293     ExtBuffer[1] = (mfxExtBuffer*)&procampConfig;
294     VPPParams.NumExtParam = 2;
295     VPPParams.ExtParam = (mfxExtBuffer**)&ExtBuffer[0];
296 */
297         memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(qsv_vpp->ext_opaque_alloc));
298 
299         if( (qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN  /  qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD ) !=
300             (qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN /  qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD) )
301         {
302             pv->is_frc_used = 1;
303         }
304 
305         qsv_vpp->m_mfxVideoParam.NumExtParam        = qsv_vpp->p_ext_param_num = 1 + pv->is_frc_used;
306 
307         qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num);
308         HB_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC);
309 
310         qsv_vpp->m_mfxVideoParam.ExtParam           = qsv_vpp->p_ext_params;
311 
312         qsv_vpp->ext_opaque_alloc.In.Surfaces       = qsv->dec_space->p_surfaces;
313         qsv_vpp->ext_opaque_alloc.In.NumSurface     = qsv->dec_space->surface_num;
314         qsv_vpp->ext_opaque_alloc.In.Type           = qsv->dec_space->request[0].Type;
315 
316         qsv_vpp->ext_opaque_alloc.Out.Surfaces      = qsv_vpp->p_surfaces;
317         qsv_vpp->ext_opaque_alloc.Out.NumSurface    = qsv_vpp->surface_num;
318         qsv_vpp->ext_opaque_alloc.Out.Type          = qsv->dec_space->request[0].Type;
319 
320         qsv_vpp->ext_opaque_alloc.Header.BufferId   = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
321         qsv_vpp->ext_opaque_alloc.Header.BufferSz   = sizeof(mfxExtOpaqueSurfaceAlloc);
322         qsv_vpp->p_ext_params[0]                    = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc;
323 
324         if(pv->is_frc_used)
325         {
326             pv->frc_config.Header.BufferId  = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION;
327             pv->frc_config.Header.BufferSz  = sizeof(mfxExtVPPFrameRateConversion);
328             pv->frc_config.Algorithm        = MFX_FRCALGM_PRESERVE_TIMESTAMP;
329 
330             qsv_vpp->p_ext_params[1] = (mfxExtBuffer*)&pv->frc_config;
331         }
332 
333         sts = MFXVideoVPP_Init(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam);
334 
335         HB_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
336         HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
337 
338         qsv_vpp->is_init_done = 1;
339     }
340     return 0;
341 }
342 
hb_qsv_filter_init(hb_filter_object_t * filter,hb_filter_init_t * init)343 static int hb_qsv_filter_init( hb_filter_object_t * filter,
344                                hb_filter_init_t * init )
345 {
346 
347     filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
348     hb_filter_private_t * pv = filter->private_data;
349 
350     hb_buffer_list_clear(&pv->list);
351     // list of init params provided at work.c:~700
352     pv->width_in  = init->geometry.width;
353     pv->height_in = init->geometry.height;
354     pv->width_out = init->geometry.width;
355     pv->height_out = init->geometry.height;
356     memcpy( pv->crop, init->crop, sizeof( int[4] ) );
357 
358     hb_dict_extract_int(&pv->width_out, filter->settings, "width");
359     hb_dict_extract_int(&pv->height_out, filter->settings, "height");
360     hb_dict_extract_int(&pv->crop[0], filter->settings, "crop-top");
361     hb_dict_extract_int(&pv->crop[1], filter->settings, "crop-bottom");
362     hb_dict_extract_int(&pv->crop[2], filter->settings, "crop-left");
363     hb_dict_extract_int(&pv->crop[3], filter->settings, "crop-right");
364     hb_dict_extract_bool(&pv->deinterlace, filter->settings, "deinterlace");
365 
366     pv->job = init->job;
367 
368     // will be later as more params will be known
369     // filter_init(pv->job->qsv, pv);
370 
371     // framerate shaping not yet supported
372     init->cfr = 0;
373 
374     init->pix_fmt = pv->pix_fmt;
375     init->geometry.width = pv->width_out;
376     init->geometry.height = pv->height_out;
377     memcpy( init->crop, pv->crop, sizeof( int[4] ) );
378 
379     return 0;
380 }
381 
hb_qsv_filter_info(hb_filter_object_t * filter)382 static hb_filter_info_t * hb_qsv_filter_info( hb_filter_object_t * filter )
383 {
384     hb_filter_private_t *pv = filter->private_data;
385     hb_filter_info_t    * info;
386 
387     if( !pv )
388         return NULL;
389 
390     info = calloc(1, sizeof(hb_filter_info_t));
391     info->human_readable_desc = malloc(128);
392     info->human_readable_desc[0] = 0;
393 
394     snprintf(info->human_readable_desc, 128,
395             "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d",
396             pv->width_in, pv->height_in,
397             pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3],
398             pv->width_in  - pv->crop[2] - pv->crop[3],
399             pv->height_in - pv->crop[0] - pv->crop[1],
400             pv->width_out, pv->height_out);
401 
402     if (pv->deinterlace)
403     {
404         int len = strlen(info->human_readable_desc);
405         snprintf(info->human_readable_desc + len, 128 - len, ", deinterlace");
406     }
407 
408     return info;
409 }
410 
qsv_filter_close(hb_qsv_context * qsv,HB_QSV_STAGE_TYPE vpp_type)411 void qsv_filter_close( hb_qsv_context* qsv, HB_QSV_STAGE_TYPE vpp_type ){
412     int i = 0;
413     int x = 0;
414     hb_qsv_space* vpp_space = 0;
415 
416     if(qsv && qsv->is_context_active && qsv->vpp_space)
417     for(i=hb_qsv_list_count( qsv->vpp_space);i>0;i--){
418 
419         vpp_space = hb_qsv_list_item( qsv->vpp_space, i-1 );
420         if( vpp_space->type == vpp_type && vpp_space->is_init_done){
421 
422             hb_log( "qsv_filter[%s] done: max_surfaces: %u/%u , max_syncs: %u/%u", ((vpp_type == HB_QSV_VPP_DEFAULT)?"Default": "User") ,vpp_space->surface_num_max_used, vpp_space->surface_num, vpp_space->sync_num_max_used, vpp_space->sync_num );
423 
424             for (x = 0; x < vpp_space->surface_num; x++){
425                 av_freep(&vpp_space->p_surfaces[x]);
426             }
427             vpp_space->surface_num = 0;
428 
429             if( vpp_space->p_ext_param_num || vpp_space->p_ext_params )
430                 av_freep(&vpp_space->p_ext_params);
431             vpp_space->p_ext_param_num = 0;
432 
433             for (x = 0; x < vpp_space->sync_num; x++){
434                 av_freep(&vpp_space->p_syncp[x]->p_sync);
435                 av_freep(&vpp_space->p_syncp[x]);
436             }
437             vpp_space->sync_num = 0;
438 
439             hb_qsv_list_rem(qsv->vpp_space,vpp_space);
440             if( hb_qsv_list_count(qsv->vpp_space) == 0 )
441                 hb_qsv_list_close(&qsv->vpp_space);
442 
443             vpp_space->is_init_done = 0;
444             break;
445         }
446     }
447 }
448 
hb_qsv_filter_close(hb_filter_object_t * filter)449 static void hb_qsv_filter_close( hb_filter_object_t * filter )
450 {
451     hb_filter_private_t * pv = filter->private_data;
452 
453     if ( !pv )
454     {
455         return;
456     }
457 
458     hb_qsv_context* qsv = pv->job->qsv.ctx;
459     if(qsv && qsv->vpp_space && hb_qsv_list_count(qsv->vpp_space) > 0){
460 
461         // closing local stuff
462         qsv_filter_close(qsv,HB_QSV_VPP_DEFAULT);
463 
464         // closing the common stuff
465         hb_qsv_context_clean(qsv,hb_qsv_full_path_is_enabled(pv->job));
466     }
467     hb_buffer_list_close(&pv->list);
468     free( pv );
469     filter->private_data = NULL;
470 }
471 
process_frame(hb_qsv_list * received_item,hb_qsv_context * qsv,hb_filter_private_t * pv)472 int process_frame(hb_qsv_list* received_item, hb_qsv_context* qsv, hb_filter_private_t * pv ){
473 
474     // 1 if have results , 0 - otherwise
475     int ret = 1;
476 
477     mfxStatus sts = MFX_ERR_NONE;
478     mfxFrameSurface1 *work_surface = NULL;
479     hb_qsv_stage* stage = 0;
480 
481     hb_qsv_space *qsv_vpp = pv->vpp_space;
482 
483     if(received_item){
484         stage = hb_qsv_get_last_stage( received_item );
485         work_surface = stage->out.p_surface;
486     }
487 
488     int sync_idx = hb_qsv_get_free_sync(qsv_vpp, qsv);
489     int surface_idx = -1;
490 
491     for(;;)
492     {
493             if (sync_idx == -1)
494             {
495                 hb_error("qsv: Not enough resources allocated for QSV filter");
496                 ret = 0;
497                 break;
498             }
499             if( sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE )
500                surface_idx = hb_qsv_get_free_surface(qsv_vpp, qsv,  &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY);
501             if (surface_idx == -1) {
502                 hb_error("qsv: Not enough resources allocated for QSV filter");
503                 ret = 0;
504                 break;
505             }
506             if (work_surface != NULL)
507             {
508                 work_surface->Info.CropX = pv->CropX;
509                 work_surface->Info.CropY = pv->CropY;
510                 work_surface->Info.CropW = pv->CropW;
511                 work_surface->Info.CropH = pv->CropH;
512             }
513 
514             sts = MFXVideoVPP_RunFrameVPPAsync(qsv->mfx_session, work_surface, qsv_vpp->p_surfaces[surface_idx] , NULL, qsv_vpp->p_syncp[sync_idx]->p_sync);
515 
516             if( MFX_ERR_MORE_DATA == sts ){
517                 if(!qsv_vpp->pending){
518                     qsv_vpp->pending = hb_qsv_list_init(0);
519                 }
520 
521                 // if we have no results, we should not miss resource(s)
522                 hb_qsv_list_add( qsv_vpp->pending, received_item);
523 
524                 ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
525 
526                 ret = 0;
527                 break;
528             }
529 
530             if( MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)){
531                 if (work_surface){
532                    ff_qsv_atomic_dec(&work_surface->Data.Locked);
533                }
534             }
535 
536             if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){
537                 if( MFX_ERR_MORE_SURFACE == sts )
538                     continue;
539 
540                 if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts )
541                    ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked);
542             }
543 
544             HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
545 
546             if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output
547             {
548                 if (MFX_WRN_DEVICE_BUSY == sts){
549                     hb_qsv_sleep(10); // wait if device is busy
550                     continue;
551                 }
552 
553                 // shouldn't be a case but drain
554                 if(stage){
555                         hb_qsv_stage* new_stage = hb_qsv_stage_init();
556 
557                         new_stage->type = HB_QSV_VPP_DEFAULT;
558                         new_stage->in.p_surface  =  work_surface;
559                         new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx];
560                         new_stage->out.sync      = qsv_vpp->p_syncp[sync_idx];
561                         hb_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS );
562 
563                         // add pending resources for the proper reclaim later
564                         if( qsv_vpp->pending ){
565                             if( hb_qsv_list_count(qsv_vpp->pending)>0 ){
566                                 new_stage->pending = qsv_vpp->pending;
567                 }
568                             qsv_vpp->pending = 0;
569 
570                             // making free via decrement for all pending
571                             int i = 0;
572                             for (i = hb_qsv_list_count(new_stage->pending); i > 0; i--){
573                                 hb_qsv_list *atom_list = hb_qsv_list_item(new_stage->pending, i-1);
574                                 hb_qsv_stage *stage = hb_qsv_get_last_stage( atom_list );
575                                 mfxFrameSurface1 *work_surface = stage->out.p_surface;
576                                 if (work_surface)
577                                    ff_qsv_atomic_dec(&work_surface->Data.Locked);
578                             }
579                         }
580                 }
581                 break;
582             }
583 
584             ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
585 
586             if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
587                 HB_DEBUG_ASSERT(1, "The bitstream buffer size is insufficient.");
588 
589             break;
590     }
591 
592     return ret;
593 }
594 
hb_qsv_filter_work(hb_filter_object_t * filter,hb_buffer_t ** buf_in,hb_buffer_t ** buf_out)595 static int hb_qsv_filter_work( hb_filter_object_t * filter,
596                                hb_buffer_t ** buf_in,
597                                hb_buffer_t ** buf_out )
598 {
599 
600     hb_filter_private_t * pv = filter->private_data;
601     hb_buffer_t * in = *buf_in;
602     hb_buffer_t * out = *buf_out;
603     int sts = 0;
604 
605     if ( !pv )
606     {
607         *buf_out = in;
608         *buf_in = NULL;
609         return HB_FILTER_OK;
610     }
611 
612     hb_qsv_context* qsv = pv->job->qsv.ctx;
613 
614     while(1)
615     {
616         int ret = filter_init(qsv,pv);
617         if(ret >= 2)
618             hb_qsv_sleep(1);
619         else
620             break;
621     }
622 
623     *buf_in = NULL;
624 
625     if (in->s.flags & HB_BUF_FLAG_EOF)
626     {
627         while(1)
628         {
629             sts = process_frame(in->qsv_details.qsv_atom, qsv, pv);
630             if(sts)
631                 hb_buffer_list_append(&pv->list, in);
632             else
633                 break;
634         }
635 
636         hb_buffer_list_append(&pv->list, in);
637         *buf_out = hb_buffer_list_clear(&pv->list);
638         return HB_FILTER_DONE;
639     }
640 
641     sts = process_frame(in->qsv_details.qsv_atom, qsv, pv);
642 
643     if(sts)
644     {
645         hb_buffer_list_append(&pv->list, in);
646     }
647 
648     out = *buf_out = hb_buffer_list_rem_head(&pv->list);
649     if (pv->is_frc_used && out != NULL)
650     {
651         if (out->qsv_details.qsv_atom)
652         {
653             hb_qsv_stage* stage;
654             mfxFrameSurface1 *work_surface;
655             int64_t duration;
656             hb_qsv_space *qsv_vpp;
657 
658             stage        = hb_qsv_get_last_stage(out->qsv_details.qsv_atom);
659             work_surface = stage->out.p_surface;
660 
661             hb_qsv_wait_on_sync( qsv,stage );
662 
663             qsv_vpp  = pv->vpp_space;
664             duration =
665                 ((double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD /
666                  (double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN ) *
667                 90000.;
668             out->s.start = work_surface->Data.TimeStamp;
669             out->s.stop  = work_surface->Data.TimeStamp + duration;
670         }
671     }
672 
673     return HB_FILTER_OK;
674 }
675 
676 #endif // HB_PROJECT_FEATURE_QSV
677 
678