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_pp.h"
36 #include "handbrake/qsv_filter.h"
37 #include "handbrake/qsv_libav.h"
38 #include "handbrake/qsv_memory.h"
39 #include "handbrake/qsv_common.h"
40 
41 static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
42                                hb_filter_init_t * init );
43 static int hb_qsv_filter_pre_work( hb_filter_object_t * filter,
44                                hb_buffer_t ** buf_in,
45                                hb_buffer_t ** buf_out );
46 static hb_filter_info_t * hb_qsv_filter_pre_info( hb_filter_object_t * filter );
47 static void hb_qsv_filter_pre_close( hb_filter_object_t * filter );
48 
49 static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
50                                hb_filter_init_t * init );
51 static int hb_qsv_filter_post_work( hb_filter_object_t * filter,
52                                hb_buffer_t ** buf_in,
53                                hb_buffer_t ** buf_out );
54 static hb_filter_info_t * hb_qsv_filter_post_info(hb_filter_object_t * filter);
55 static void hb_qsv_filter_post_close( hb_filter_object_t * filter );
56 
57 
58 hb_filter_object_t hb_filter_qsv_pre =
59 {
60     .id            = HB_FILTER_QSV_PRE,
61     .enforce_order = 1,
62     .name          = "Quick Sync Video user filter (pre)",
63     .settings      = NULL,
64     .init          = hb_qsv_filter_pre_init,
65     .work          = hb_qsv_filter_pre_work,
66     .close         = hb_qsv_filter_pre_close,
67     .info          = hb_qsv_filter_pre_info,
68 };
69 
70 hb_filter_object_t hb_filter_qsv_post =
71 {
72     .id            = HB_FILTER_QSV_POST,
73     .enforce_order = 1,
74     .name          = "Quick Sync Video user filter (post)",
75     .settings      = NULL,
76     .init          = hb_qsv_filter_post_init,
77     .work          = hb_qsv_filter_post_work,
78     .close         = hb_qsv_filter_post_close,
79     .info          = hb_qsv_filter_post_info,
80 };
81 
82 
filter_pre_init(hb_qsv_context * qsv,hb_filter_private_t * pv)83 static int filter_pre_init( hb_qsv_context* qsv, hb_filter_private_t * pv ){
84     mfxStatus sts = MFX_ERR_NONE;
85     int i=0;
86 
87     if(!qsv) return 3;
88 
89     hb_qsv_space *prev_vpp = 0;
90 
91     if(!qsv->vpp_space){
92         qsv->vpp_space = hb_qsv_list_init(HAVE_THREADS);
93         // note some change as : when no size changes -> no VPP used
94         // impact on : prev_vpp
95     }
96 
97     if(!pv->vpp_space){
98         for(i=0; i<hb_qsv_list_count(qsv->vpp_space);i++){
99             hb_qsv_space *qsv_vpp = hb_qsv_list_item( qsv->vpp_space, i );
100             if(qsv_vpp->type == HB_QSV_VPP_USER){
101                 pv->vpp_space = qsv_vpp;
102                 break;
103             }
104             else
105             if(qsv_vpp->type == HB_QSV_VPP_DEFAULT){
106                 prev_vpp = qsv_vpp;
107             }
108 
109         }
110     }
111 
112     if(!pv->vpp_space){
113         pv->vpp_space = calloc( 1, sizeof( hb_qsv_space ));
114         pv->vpp_space->type = HB_QSV_VPP_USER;
115         hb_qsv_list_add( qsv->vpp_space, pv->vpp_space );
116         hb_qsv_add_context_usage(qsv,HAVE_THREADS);
117     }
118     else
119         if(pv->vpp_space->is_init_done ) return 1;
120 
121     if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2;
122 
123     hb_qsv_space *qsv_vpp = pv->vpp_space;
124 
125     HB_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
126 
127 
128     if (prev_vpp)
129     {
130         memcpy( &qsv_vpp->m_mfxVideoParam.vpp,  &prev_vpp->m_mfxVideoParam.vpp, sizeof(prev_vpp->m_mfxVideoParam.vpp));
131     }
132     else
133     {
134         HB_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
135 
136         // FrameRate is important for VPP to start with
137         if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 &&
138             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){
139             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->vrate.num;
140             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->vrate.den;
141         }
142 
143         qsv_vpp->m_mfxVideoParam.vpp.In.FourCC          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC;
144         qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
145         qsv_vpp->m_mfxVideoParam.vpp.In.CropX           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX;
146         qsv_vpp->m_mfxVideoParam.vpp.In.CropY           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY;
147         qsv_vpp->m_mfxVideoParam.vpp.In.CropW           = pv->job->title->geometry.width;
148         qsv_vpp->m_mfxVideoParam.vpp.In.CropH           = pv->job->title->geometry.height;
149         qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct       = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
150         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN;
151         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD;
152         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
153         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
154         qsv_vpp->m_mfxVideoParam.vpp.In.Width           = HB_QSV_ALIGN16(pv->job->title->geometry.width);
155         qsv_vpp->m_mfxVideoParam.vpp.In.Height          = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)?
156                                                             HB_QSV_ALIGN16(pv->job->title->geometry.height) : HB_QSV_ALIGN32(pv->job->title->geometry.height);
157 
158         qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC;
159         qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
160         qsv_vpp->m_mfxVideoParam.vpp.Out.CropX           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX;
161         qsv_vpp->m_mfxVideoParam.vpp.Out.CropY           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY;
162         qsv_vpp->m_mfxVideoParam.vpp.Out.CropW           = pv->job->title->geometry.width;
163         qsv_vpp->m_mfxVideoParam.vpp.Out.CropH           = pv->job->title->geometry.height;
164         qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct       = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
165         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN;
166         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD;
167         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
168         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
169         qsv_vpp->m_mfxVideoParam.vpp.Out.Width           = HB_QSV_ALIGN16(pv->job->title->geometry.width);
170         qsv_vpp->m_mfxVideoParam.vpp.Out.Height          = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)?
171                                                             HB_QSV_ALIGN16(pv->job->title->geometry.height) : HB_QSV_ALIGN32(pv->job->title->geometry.height);
172 
173         memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2);
174     }
175 
176     qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
177 
178     qsv_vpp->surface_num = FFMIN(prev_vpp ? prev_vpp->surface_num : qsv->dec_space->surface_num/2, HB_QSV_SURFACE_NUM);
179 
180     for(i = 0; i < qsv_vpp->surface_num; i++){
181         qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) );
182         HB_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC);
183         memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo));
184     }
185 
186     qsv_vpp->sync_num = FFMIN(prev_vpp ? prev_vpp->sync_num : qsv->dec_space->sync_num, HB_QSV_SYNC_NUM);
187     for (i = 0; i < qsv_vpp->sync_num; i++){
188         qsv_vpp->p_syncp[i] = av_mallocz(sizeof(hb_qsv_sync));
189         HB_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC);
190         qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint));
191         HB_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
192     }
193 
194     memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc));
195     qsv_vpp->m_mfxVideoParam.NumExtParam        = qsv_vpp->p_ext_param_num = 1;
196 
197     qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num);
198     HB_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC);
199 
200     qsv_vpp->m_mfxVideoParam.ExtParam           = qsv_vpp->p_ext_params;
201 
202     qsv_vpp->ext_opaque_alloc.Header.BufferId   = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
203     qsv_vpp->ext_opaque_alloc.Header.BufferSz   = sizeof(mfxExtOpaqueSurfaceAlloc);
204     qsv_vpp->p_ext_params[0]                    = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc;
205 
206     if(prev_vpp){
207         qsv_vpp->ext_opaque_alloc.In.Surfaces       = prev_vpp->p_surfaces;
208         qsv_vpp->ext_opaque_alloc.In.NumSurface     = prev_vpp->surface_num;
209     }
210     else{
211         qsv_vpp->ext_opaque_alloc.In.Surfaces       = qsv->dec_space->p_surfaces;
212         qsv_vpp->ext_opaque_alloc.In.NumSurface     = qsv->dec_space->surface_num;
213     }
214     qsv_vpp->ext_opaque_alloc.In.Type           = qsv->dec_space->request[0].Type;
215 
216     qsv_vpp->ext_opaque_alloc.Out.Surfaces      = qsv_vpp->p_surfaces;
217     qsv_vpp->ext_opaque_alloc.Out.NumSurface    = qsv_vpp->surface_num;
218     qsv_vpp->ext_opaque_alloc.Out.Type          = qsv->dec_space->request[0].Type;
219 
220     pv->qsv_user = hb_list_init();
221 
222     qsv_filter_t *plugin = av_mallocz( sizeof(qsv_filter_t) );
223 
224     plugin->pv               = pv;
225     plugin->plug.pthis       = plugin;
226     plugin->plug.PluginInit  = qsv_PluginInit;
227     plugin->plug.PluginClose = qsv_PluginClose;
228     plugin->plug.GetPluginParam = qsv_GetPluginParam;
229     plugin->plug.Submit      = qsv_Submit;
230     plugin->plug.Execute     = qsv_Execute;
231     plugin->plug.FreeResources = qsv_FreeResources;
232 
233     hb_list_add(pv->qsv_user,plugin);
234 
235     sts=MFXVideoUSER_Register(qsv->mfx_session,0,&plugin->plug);
236     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
237 
238     plugin_init(plugin,&qsv_vpp->m_mfxVideoParam);
239 
240     qsv_vpp->is_init_done = 1;
241 
242     return 0;
243 }
244 
hb_qsv_filter_pre_info(hb_filter_object_t * filter)245 static hb_filter_info_t * hb_qsv_filter_pre_info( hb_filter_object_t * filter )
246 {
247     hb_filter_private_t * pv = filter->private_data;
248     hb_filter_info_t    * info;
249 
250     if( !pv )
251         return NULL;
252 
253     info = calloc(1, sizeof(hb_filter_info_t));
254     info->human_readable_desc = malloc(128);
255     info->human_readable_desc[0] = 0;
256 
257     snprintf(info->human_readable_desc, 128, "copy data to system memory");
258 
259     return info;
260 }
hb_qsv_filter_pre_init(hb_filter_object_t * filter,hb_filter_init_t * init)261 static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
262                                hb_filter_init_t * init ){
263     filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
264     hb_filter_private_t * pv = filter->private_data;
265     pv->job = init->job;
266 
267     pv->pre.frame_go             = 0;
268     pv->pre.frame_completed      = hb_cond_init();
269     pv->pre.frame_completed_lock = hb_lock_init();
270 
271     pv->post.frame_go            = 0;
272     pv->post.frame_completed     = hb_cond_init();
273     pv->post.frame_completed_lock = hb_lock_init();
274 
275     pv->pre_busy.frame_go            = 0;
276     pv->pre_busy.frame_completed     = hb_cond_init();
277     pv->pre_busy.frame_completed_lock = hb_lock_init();
278 
279     pv->post_busy.frame_go               = 0;
280     pv->post_busy.frame_completed        = hb_cond_init();
281     pv->post_busy.frame_completed_lock   = hb_lock_init();
282 
283     pv->list = hb_list_init();
284 
285     // just to remind:
286     // PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) , 3 planes: Y, U, V
287     // PIX_FMT_NV12,      ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
288     pv->sws_context_from_nv12 = hb_sws_get_context(
289         pv->job->title->geometry.width, pv->job->title->geometry.height,
290         AV_PIX_FMT_NV12, pv->job->color_range,
291         pv->job->title->geometry.width, pv->job->title->geometry.height,
292         AV_PIX_FMT_YUV420P, pv->job->color_range,
293         SWS_LANCZOS|SWS_ACCURATE_RND, SWS_CS_DEFAULT);
294     pv->sws_context_to_nv12 = hb_sws_get_context(
295         pv->job->title->geometry.width, pv->job->title->geometry.height,
296         AV_PIX_FMT_YUV420P, pv->job->color_range,
297         pv->job->title->geometry.width, pv->job->title->geometry.height,
298         AV_PIX_FMT_NV12, pv->job->color_range,
299         SWS_LANCZOS|SWS_ACCURATE_RND, SWS_CS_DEFAULT);
300     return 0;
301 }
pre_process_frame(hb_buffer_t * in,hb_qsv_context * qsv,hb_filter_private_t * pv)302 int pre_process_frame(hb_buffer_t *in, hb_qsv_context* qsv, hb_filter_private_t * pv ){
303 
304     // 1 if have results , 0 otherwise
305     int ret = 1;
306 
307     hb_qsv_list* received_item = in->qsv_details.qsv_atom;
308 
309     mfxStatus sts = MFX_ERR_NONE;
310     mfxFrameSurface1 *work_surface = NULL;
311     hb_qsv_stage* stage = 0;
312 
313     hb_qsv_space *qsv_vpp = pv->vpp_space;
314 
315     if (received_item)
316     {
317         stage = hb_qsv_get_last_stage( received_item );
318         work_surface = stage->out.p_surface;
319     }
320 
321     int sync_idx = hb_qsv_get_free_sync(qsv_vpp, qsv);
322     int surface_idx = -1;
323 
324     for (;;)
325     {
326             if (sync_idx == -1)
327             {
328                 hb_error("qsv: Not enough resources allocated for the preprocessing filter");
329                 ret = 0;
330                 break;
331             }
332 
333             if (sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE)
334                surface_idx = hb_qsv_get_free_surface(qsv_vpp, qsv,  &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY);
335             if (surface_idx == -1) {
336                 hb_error("qsv: Not enough resources allocated for the preprocessing filter");
337                 ret = 0;
338                 break;
339             }
340 
341             sts = MFXVideoUSER_ProcessFrameAsync(qsv->mfx_session, (void * const*)&work_surface, 1, (void * const*)&qsv_vpp->p_surfaces[surface_idx] , 1, qsv_vpp->p_syncp[sync_idx]->p_sync);
342 
343             if (MFX_ERR_MORE_DATA == sts)
344             {
345                 if (!qsv_vpp->pending)
346                 {
347                     qsv_vpp->pending = hb_qsv_list_init(0);
348                 }
349 
350                 // if we have no results, we should not miss resource(s)
351                 hb_qsv_list_add( qsv_vpp->pending, received_item);
352 
353                 ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
354 
355                 ret = 0;
356                 break;
357             }
358 
359             if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){
360                 if( MFX_ERR_MORE_SURFACE == sts )
361                     continue;
362 
363                 if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts )
364                    ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked);
365             }
366 
367             HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
368 
369             if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output
370             {
371                 if (MFX_WRN_DEVICE_BUSY == sts){
372                     hb_lock(pv->pre_busy.frame_completed_lock);
373                     while(!pv->pre_busy.frame_go){
374                         hb_cond_timedwait(pv->pre_busy.frame_completed,pv->pre_busy.frame_completed_lock,1000);
375                         if(*pv->job->die)
376                             break;
377                     }
378                     pv->pre_busy.frame_go = 0;
379                     hb_unlock(pv->pre_busy.frame_completed_lock);
380 
381                     continue;
382                 }
383                 hb_lock(pv->pre.frame_completed_lock);
384                 while(!pv->pre.frame_go){
385                     hb_cond_timedwait(pv->pre.frame_completed,pv->pre.frame_completed_lock,1000);
386                     if(*pv->job->die)
387                         break;
388                 }
389                 pv->pre.frame_go = 0;
390                 hb_unlock(pv->pre.frame_completed_lock);
391 
392                 in = pv->pre.out;
393 
394                 if (work_surface){
395                    ff_qsv_atomic_dec(&work_surface->Data.Locked);
396                 }
397 
398                 // inserting for the future, will be locked until very ready
399                 if(stage){
400                         hb_qsv_stage* new_stage = hb_qsv_stage_init();
401 
402                         new_stage->type = HB_QSV_VPP_USER;
403                         new_stage->in.p_surface  =  work_surface;
404                         new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx];
405                         new_stage->out.sync = qsv_vpp->p_syncp[sync_idx];
406                         hb_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS );
407 
408                         // add pending resources for the proper reclaim later
409                         if( qsv_vpp->pending ){
410                             if( hb_qsv_list_count(qsv_vpp->pending)>0 ){
411                                 new_stage->pending = qsv_vpp->pending;
412                 }
413                             qsv_vpp->pending = 0;
414 
415                             // making free via decrement for all pending
416                             int i = 0;
417                             for (i = hb_qsv_list_count(new_stage->pending); i > 0; i--){
418                                 hb_qsv_list *atom_list = hb_qsv_list_item(new_stage->pending, i-1);
419                                 hb_qsv_stage *stage = hb_qsv_get_last_stage( atom_list );
420                                 mfxFrameSurface1 *work_surface = stage->out.p_surface;
421                                 if (work_surface)
422                                    ff_qsv_atomic_dec(&work_surface->Data.Locked);
423                             }
424                         }
425                 }
426 
427                 break;
428             }
429 
430             ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
431 
432             if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
433                 HB_DEBUG_ASSERT(1, "The bitstream buffer size is insufficient.");
434 
435             break;
436     }
437 
438     return ret;
439 }
440 
hb_qsv_filter_pre_work(hb_filter_object_t * filter,hb_buffer_t ** buf_in,hb_buffer_t ** buf_out)441 static int hb_qsv_filter_pre_work( hb_filter_object_t * filter,
442                                hb_buffer_t ** buf_in,
443                                hb_buffer_t ** buf_out ){
444     hb_filter_private_t * pv = filter->private_data;
445     hb_buffer_t * in = *buf_in;
446     hb_buffer_t * out = *buf_out;
447     int sts = 0;
448 
449     hb_qsv_context* qsv = pv->job->qsv.ctx;
450 
451     if(!in->qsv_details.filter_details)
452         in->qsv_details.filter_details = pv;
453 
454     if (in->s.flags & HB_BUF_FLAG_EOF)
455     {
456         *buf_out = in;
457         *buf_in = NULL;
458         return HB_FILTER_DONE;
459     }
460 
461     while(1){
462         int ret = filter_pre_init(qsv,pv);
463         if(ret >= 2)
464             hb_qsv_sleep(1);
465         else
466             break;
467     }
468 
469     pv->pre.in  = in;
470     pv->pre.out = in;
471 
472     sts = pre_process_frame(in, qsv, pv);
473 
474     if(sts){
475         hb_list_add(pv->list,out);
476     }
477 
478     if( hb_list_count(pv->list) ){
479         *buf_out = hb_list_item(pv->list,0);
480         hb_list_rem(pv->list,*buf_out);
481          *buf_in = NULL;
482     }
483     else{
484         *buf_in = NULL;
485         *buf_out = in;
486     }
487 
488     return HB_FILTER_OK;
489 }
hb_qsv_filter_pre_close(hb_filter_object_t * filter)490 static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){
491     int i = 0;
492     mfxStatus sts = MFX_ERR_NONE;
493 
494     hb_filter_private_t * pv = filter->private_data;
495 
496     if ( !pv )
497     {
498         return;
499     }
500 
501     sws_freeContext(pv->sws_context_to_nv12);
502     sws_freeContext(pv->sws_context_from_nv12);
503 
504     hb_qsv_context* qsv = pv->job->qsv.ctx;
505     if(qsv && qsv->vpp_space && hb_qsv_list_count(qsv->vpp_space) > 0 ){
506         if(pv->qsv_user && qsv->mfx_session){
507 
508             sts=MFXVideoUSER_Unregister(qsv->mfx_session,0);
509             HB_QSV_CHECK_RET(sts, MFX_ERR_NONE, sts);
510 
511             for(i=hb_list_count(pv->qsv_user);i>0;i--){
512                 qsv_filter_t *plugin = hb_list_item(pv->qsv_user,i-1);
513                 hb_list_rem(pv->qsv_user,plugin);
514                 plugin_close(plugin);
515             }
516             hb_list_close(&pv->qsv_user);
517         }
518 
519         // closing local stuff
520         qsv_filter_close(qsv,HB_QSV_VPP_USER);
521 
522         // closing the common stuff
523         hb_qsv_context_clean(qsv,hb_qsv_full_path_is_enabled(pv->job));
524     }
525     hb_cond_close(&pv->pre.frame_completed);
526     hb_lock_close(&pv->pre.frame_completed_lock);
527 
528     hb_cond_close(&pv->post.frame_completed);
529     hb_lock_close(&pv->post.frame_completed_lock);
530 
531     hb_cond_close(&pv->pre_busy.frame_completed);
532     hb_lock_close(&pv->pre_busy.frame_completed_lock);
533 
534     hb_cond_close(&pv->post_busy.frame_completed);
535     hb_lock_close(&pv->post_busy.frame_completed_lock);
536 
537     hb_list_close( &pv->list );
538 
539     free( pv );
540     filter->private_data = NULL;
541 }
542 
543 
hb_qsv_filter_post_info(hb_filter_object_t * filter)544 static hb_filter_info_t * hb_qsv_filter_post_info( hb_filter_object_t * filter )
545 {
546     hb_filter_private_t * pv = filter->private_data;
547     hb_filter_info_t    * info;
548 
549     if( !pv )
550         return NULL;
551 
552     info = calloc(1, sizeof(hb_filter_info_t));
553     info->human_readable_desc = malloc(128);
554     info->human_readable_desc[0] = 0;
555 
556     snprintf(info->human_readable_desc, 128, "copy data to opaque memory");
557 
558     return info;
559 }
hb_qsv_filter_post_init(hb_filter_object_t * filter,hb_filter_init_t * init)560 static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
561                                hb_filter_init_t * init ){
562     filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
563     hb_filter_private_t * pv = filter->private_data;
564     pv->job = init->job;
565     return 0;
566 }
hb_qsv_filter_post_work(hb_filter_object_t * filter,hb_buffer_t ** buf_in,hb_buffer_t ** buf_out)567 static int hb_qsv_filter_post_work( hb_filter_object_t * filter,
568                                hb_buffer_t ** buf_in,
569                                hb_buffer_t ** buf_out ){
570     hb_filter_private_t * pv = filter->private_data;
571     hb_buffer_t * in = *buf_in;
572     hb_buffer_t * out = *buf_out;
573 
574     if (in->s.flags & HB_BUF_FLAG_EOF)
575     {
576         *buf_out = in;
577         *buf_in = NULL;
578         return HB_FILTER_DONE;
579     }
580 
581     hb_qsv_context* qsv = pv->job->qsv.ctx;
582     pv = in->qsv_details.filter_details;
583 
584     if (!pv)
585     {
586         *buf_out = NULL;
587         *buf_in = NULL;
588         return HB_FILTER_OK;
589     }
590 
591     while(1){
592         int ret = filter_pre_init(qsv,pv);
593         if(ret >= 2)
594             hb_qsv_sleep(1);
595         else
596             break;
597     }
598 
599     pv->post.in = in;
600     pv->post.out = out;
601 
602     // signal: input is prepared, can start inserting data back into pipeline
603     hb_lock(pv->post.frame_completed_lock);
604     pv->post.frame_go = 1;
605     hb_cond_broadcast(pv->post.frame_completed);
606     hb_unlock(pv->post.frame_completed_lock);
607 
608     // wait: on signal that data is ready
609     hb_lock(pv->post_busy.frame_completed_lock);
610     while(!pv->post_busy.frame_go){
611         hb_cond_timedwait(pv->post_busy.frame_completed,pv->post_busy.frame_completed_lock,1000);
612         if(*pv->job->die)
613             break;
614     }
615     pv->post_busy.frame_go = 0;
616     hb_unlock(pv->post_busy.frame_completed_lock);
617 
618     if (pv->post.status == HB_FILTER_OK || pv->post.status == HB_FILTER_DONE)
619     {
620     *buf_out = in;
621     }
622     else
623     {
624         *buf_out = NULL;
625         pv->post.status = HB_FILTER_OK;
626     }
627     *buf_in = NULL;
628 
629     return HB_FILTER_OK;
630 }
hb_qsv_filter_post_close(hb_filter_object_t * filter)631 static void hb_qsv_filter_post_close( hb_filter_object_t * filter ){
632     hb_filter_private_t * pv = filter->private_data;
633 
634     if ( !pv )
635     {
636         return;
637     }
638 
639     free( pv );
640     filter->private_data = NULL;
641 }
642 
643 
qsv_PluginInit(mfxHDL pthis,mfxCoreInterface * core)644 mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core){
645     mfxStatus sts = MFX_ERR_NONE;
646 
647     if(core && pthis){
648         qsv_filter_t *plugin = pthis;
649         plugin->core = core;
650 
651         plugin->pluginparam.MaxThreadNum = 1;
652         plugin->pluginparam.ThreadPolicy = MFX_THREADPOLICY_SERIAL;
653     }
654     else
655         sts = MFX_ERR_NULL_PTR;
656 
657     return sts;
658 }
qsv_PluginClose(mfxHDL pthis)659 mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis){
660     mfxStatus sts = MFX_ERR_NONE;
661     return sts;
662 }
qsv_GetPluginParam(mfxHDL pthis,mfxPluginParam * par)663 mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par){
664     mfxStatus sts = MFX_ERR_NONE;
665 
666     if(pthis){
667         qsv_filter_t *plugin = pthis;
668         *par = plugin->pluginparam;
669     }
670     else
671         sts = MFX_ERR_NULL_PTR;
672     return sts;
673 }
qsv_Submit(mfxHDL pthis,const mfxHDL * in,mfxU32 in_num,const mfxHDL * out,mfxU32 out_num,mfxThreadTask * task)674 mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task){
675     mfxStatus sts = MFX_ERR_NONE;
676 
677     qsv_filter_t *plugin = pthis;
678 
679     mfxFrameSurface1 *surface_in = (mfxFrameSurface1 *)in[0];
680     mfxFrameSurface1 *surface_out = (mfxFrameSurface1 *)out[0];
681     mfxFrameSurface1 *real_surface_in = surface_in;
682     mfxFrameSurface1 *real_surface_out = surface_out;
683 
684     sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_in, &real_surface_in);
685     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC);
686 
687     sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_out, &real_surface_out);
688     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC);
689 
690     int task_idx = get_free_task(plugin->tasks);
691 
692     if (task_idx == -1)
693     {
694         return MFX_WRN_DEVICE_BUSY;
695     }
696 
697     plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_in->Data));
698     plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_out->Data));
699 
700     // to preserve timing if other filters are used in-between
701     surface_out->Data.TimeStamp  = surface_in->Data.TimeStamp;
702     surface_out->Data.FrameOrder = surface_in->Data.FrameOrder;
703 
704     qsv_filter_task_t *current_task = hb_list_item(plugin->tasks,task_idx);
705     current_task->in    = real_surface_in;
706     current_task->out   = real_surface_out;
707     current_task->busy  = 1;
708     current_task->pv    = plugin->pv;
709 
710     *task = (mfxThreadTask)current_task;
711 
712     return sts;
713 }
qsv_Execute(mfxHDL pthis,mfxThreadTask task,mfxU32 uid_p,mfxU32 uid_a)714 mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a){
715     mfxStatus sts = MFX_ERR_NONE;
716 
717     qsv_filter_task_t *current_task = (qsv_filter_task_t *)task;
718 
719     sts = (current_task->processor.process)(current_task,0);
720     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
721 
722     sts =  MFX_TASK_DONE;
723     return sts;
724 }
qsv_FreeResources(mfxHDL pthis,mfxThreadTask task,mfxStatus sts)725 mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts){
726 
727     qsv_filter_t *plugin = pthis;
728     qsv_filter_task_t *current_task = (qsv_filter_task_t *)task;
729 
730     plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->in->Data));
731     plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->out->Data));
732 
733     current_task->busy  = 0;
734 
735     hb_lock(plugin->pv->pre_busy.frame_completed_lock);
736     plugin->pv->pre_busy.frame_go = 1;
737     hb_cond_broadcast(plugin->pv->pre_busy.frame_completed);
738     hb_unlock(plugin->pv->pre_busy.frame_completed_lock);
739 
740     return MFX_ERR_NONE;
741 }
742 
plugin_init(qsv_filter_t * plugin,mfxVideoParam * param)743 mfxStatus plugin_init(qsv_filter_t* plugin, mfxVideoParam *param){
744     mfxStatus sts = MFX_ERR_NONE;
745 
746     if(plugin->is_init_done) return sts;
747 
748     plugin->videoparam = param;
749 
750     mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL;
751 
752     plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam,
753                                     plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION);
754 
755     if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces)
756         return MFX_ERR_INVALID_VIDEO_PARAM;
757 
758     sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface,
759             plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces);
760     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
761 
762 
763     sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface,
764             plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces);
765     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
766 
767 
768     plugin->tasks = hb_list_init();
769     qsv_filter_task_t *task = calloc( 1, sizeof( qsv_filter_task_t ));
770 
771     task->processor.process = process_filter;
772     task->processor.alloc   = &plugin->core->FrameAllocator;
773     task->processor.core   = plugin->core;
774 
775     hb_list_add(plugin->tasks,task);
776 
777     plugin->is_init_done = 1;
778 
779     return sts;
780 }
781 
plugin_close(qsv_filter_t * plugin)782 mfxStatus plugin_close(qsv_filter_t* plugin){
783     int i = 0;
784     mfxStatus sts = MFX_ERR_NONE;
785 
786     if(!plugin->is_init_done) return sts;
787 
788     mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL;
789 
790     plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam,
791                                     plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION);
792 
793     if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces)
794         return MFX_ERR_INVALID_VIDEO_PARAM;
795 
796     sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface,
797             plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces);
798     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
799 
800 
801     sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface,
802             plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces);
803     HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
804 
805     if(plugin->tasks){
806         for(i=hb_list_count(plugin->tasks);i>0;i--){
807             qsv_filter_task_t *task = hb_list_item(plugin->tasks,i-1);
808             hb_list_rem(plugin->tasks,task);
809             free(task);
810         }
811         hb_list_close(&plugin->tasks);
812     }
813 
814     plugin->is_init_done = 0;
815 
816     return sts;
817 }
818 
get_ext_buffer(mfxExtBuffer ** buffers,mfxU32 buffers_num,mfxU32 buffer_id)819 mfxExtBuffer* get_ext_buffer(mfxExtBuffer** buffers, mfxU32 buffers_num, mfxU32 buffer_id){
820     int i = 0;
821     if(!buffers) return 0;
822     for(i=0;i<buffers_num;i++){
823         if(!buffers[i]) continue;
824         if(buffers[i]->BufferId == buffer_id)
825             return buffers[i];
826     }
827     return 0;
828 }
829 
get_free_task(hb_list_t * tasks)830 int get_free_task(hb_list_t* tasks){
831     int ret = -1;
832     int i = 0;
833     for(i=0;i<hb_list_count(tasks);i++){
834         qsv_filter_task_t* task = hb_list_item(tasks,i);
835         if(!task->busy){
836             ret = i;
837             break;
838         }
839     }
840     return ret;
841 }
842 
lock_frame(mfxFrameAllocator * alloc,mfxFrameSurface1 * surface)843 mfxStatus lock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){
844     mfxStatus sts = MFX_ERR_NONE;
845     // prevent double lock
846     if (surface->Data.Y != 0 && surface->Data.MemId !=0){
847         return MFX_ERR_UNSUPPORTED;
848     }
849     // not allocated, therefore no lock
850     if (surface->Data.Y != 0){
851         return MFX_ERR_NONE;
852     }
853     sts = alloc->Lock(alloc->pthis,surface->Data.MemId,&surface->Data);
854     return sts;
855 }
856 
unlock_frame(mfxFrameAllocator * alloc,mfxFrameSurface1 * surface)857 mfxStatus unlock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){
858     mfxStatus sts = MFX_ERR_NONE;
859     // not allocated
860     if (surface->Data.Y != 0 && surface->Data.MemId == 0){
861         return MFX_ERR_NONE;
862     }
863     // not locked
864     if (surface->Data.Y == 0){
865         return MFX_ERR_NONE;
866     }
867     sts = alloc->Unlock(alloc->pthis,surface->Data.MemId,&surface->Data);
868     return sts;
869 }
870 
871 
process_filter(qsv_filter_task_t * task,void * params)872 int process_filter(qsv_filter_task_t* task, void* params){
873     mfxStatus sts = MFX_ERR_NONE;
874 
875     if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->in)))return sts;
876     if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->out)))
877     {
878         unlock_frame(task->processor.alloc,task->in);
879         return sts;
880     }
881 
882     qsv_nv12_to_yuv420(task->pv->sws_context_from_nv12,task->pv->pre.out, task->in, task->processor.core);
883 
884     // signal: input is prepared, converted from pipeline into internal buffer
885     hb_lock(task->pv->pre.frame_completed_lock);
886     task->pv->pre.frame_go = 1;
887     hb_cond_broadcast(task->pv->pre.frame_completed);
888     hb_unlock(task->pv->pre.frame_completed_lock);
889 
890     // wait: input is prepared, converted from pipeline into internal buffer
891     hb_lock(task->pv->post.frame_completed_lock);
892     while(!task->pv->post.frame_go){
893         hb_cond_timedwait(task->pv->post.frame_completed,task->pv->post.frame_completed_lock,1000);
894         if(*task->pv->job->die)
895             break;
896     }
897     task->pv->post.frame_go = 0;
898     hb_unlock(task->pv->post.frame_completed_lock);
899 
900 // this is just a simple fun/test case
901 #if 0
902     {
903         int i = 0;
904         char *cur_line;
905         char* luma =  task->pv->post.in->plane[0].data;
906         int pitch =  task->pv->post.in->plane[0].stride;
907         int h = task->pv->post.in->plane[0].height;
908         int w = task->pv->post.in->plane[0].width;
909         for (i = 0; i < h; i++){
910 
911             cur_line = luma + i * pitch;
912             if(i>h/4 && i < 3*h/4 && i % 5 == 0 )
913                 memset(cur_line, 0 , w );
914         }
915     }
916 #endif
917 
918     if(task->pv->post.in)
919     {
920     qsv_yuv420_to_nv12(task->pv->sws_context_to_nv12, task->out, task->pv->post.in);
921     }
922 
923     // signal: output is prepared, converted from internal buffer into pipeline
924     hb_lock(task->pv->post_busy.frame_completed_lock);
925     task->pv->post_busy.frame_go = 1;
926     hb_cond_broadcast(task->pv->post_busy.frame_completed);
927     hb_unlock(task->pv->post_busy.frame_completed_lock);
928 
929     unlock_frame(task->processor.alloc,task->in);
930     unlock_frame(task->processor.alloc,task->out);
931 
932     return sts;
933 }
934 
935 #endif // HB_PROJECT_FEATURE_QSV
936