WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / qsv_filter_pp.c
blob250e2b52cc92ff5d334b4f6ab043bd5becda096b
1 /* ********************************************************************* *\
3 Copyright (C) 2013 Intel Corporation. All rights reserved.
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.
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.
27 \* ********************************************************************* */
29 #ifdef USE_QSV
31 #include "hb.h"
32 #include "hbffmpeg.h"
33 #include "libavcodec/qsv.h"
34 #include "qsv_filter_pp.h"
35 #include "qsv_filter.h"
36 #include "qsv_memory.h"
39 static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
40 hb_filter_init_t * init );
41 static int hb_qsv_filter_pre_work( hb_filter_object_t * filter,
42 hb_buffer_t ** buf_in,
43 hb_buffer_t ** buf_out );
44 static int hb_qsv_filter_pre_info( hb_filter_object_t * filter,
45 hb_filter_info_t * info );
46 static void hb_qsv_filter_pre_close( hb_filter_object_t * filter );
48 static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
49 hb_filter_init_t * init );
50 static int hb_qsv_filter_post_work( hb_filter_object_t * filter,
51 hb_buffer_t ** buf_in,
52 hb_buffer_t ** buf_out );
53 static int hb_qsv_filter_post_info( hb_filter_object_t * filter,
54 hb_filter_info_t * info );
55 static void hb_qsv_filter_post_close( hb_filter_object_t * filter );
58 hb_filter_object_t hb_filter_qsv_pre =
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,
70 hb_filter_object_t hb_filter_qsv_post =
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,
83 static int filter_pre_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
84 mfxStatus sts = MFX_ERR_NONE;
85 int i=0;
87 if(!qsv) return 3;
89 av_qsv_space *prev_vpp = 0;
91 if(!qsv->vpp_space){
92 qsv->vpp_space = av_qsv_list_init(HAVE_THREADS);
93 // note some change as : when no size changes -> no VPP used
94 // impact on : prev_vpp
97 if(!pv->vpp_space){
98 for(i=0; i<av_qsv_list_count(qsv->vpp_space);i++){
99 av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i );
100 if(qsv_vpp->type == AV_QSV_VPP_USER){
101 pv->vpp_space = qsv_vpp;
102 break;
104 else
105 if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){
106 prev_vpp = qsv_vpp;
112 if(!pv->vpp_space){
113 pv->vpp_space = calloc( 1, sizeof( av_qsv_space ));
114 pv->vpp_space->type = AV_QSV_VPP_USER;
115 av_qsv_list_add( qsv->vpp_space, pv->vpp_space );
116 av_qsv_add_context_usage(qsv,HAVE_THREADS);
118 else
119 if(pv->vpp_space->is_init_done ) return 1;
121 if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2;
123 av_qsv_space *qsv_vpp = pv->vpp_space;
125 AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
128 if (prev_vpp)
130 memcpy( &qsv_vpp->m_mfxVideoParam.vpp, &prev_vpp->m_mfxVideoParam.vpp, sizeof(prev_vpp->m_mfxVideoParam.vpp));
132 else
134 AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
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;
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 = AV_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 AV_QSV_ALIGN16(pv->job->title->geometry.height) : AV_QSV_ALIGN32(pv->job->title->geometry.height);
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 = AV_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 AV_QSV_ALIGN16(pv->job->title->geometry.height) : AV_QSV_ALIGN32(pv->job->title->geometry.height);
173 memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2);
176 qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
178 qsv_vpp->surface_num = FFMIN(prev_vpp ? prev_vpp->surface_num : qsv->dec_space->surface_num/2, AV_QSV_SURFACE_NUM);
180 for(i = 0; i < qsv_vpp->surface_num; i++){
181 qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) );
182 AV_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));
186 qsv_vpp->sync_num = FFMIN(prev_vpp ? prev_vpp->sync_num : qsv->dec_space->sync_num, AV_QSV_SYNC_NUM);
187 for (i = 0; i < qsv_vpp->sync_num; i++){
188 qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync));
189 AV_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 AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
194 memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc));
195 qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1;
197 qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num);
198 AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC);
200 qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params;
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;
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;
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;
214 qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type;
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;
220 pv->qsv_user = hb_list_init();
222 qsv_filter_t *plugin = av_mallocz( sizeof(qsv_filter_t) );
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;
233 hb_list_add(pv->qsv_user,plugin);
235 sts=MFXVideoUSER_Register(qsv->mfx_session,0,&plugin->plug);
236 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
238 plugin_init(plugin,&qsv_vpp->m_mfxVideoParam);
240 qsv_vpp->is_init_done = 1;
242 return 0;
245 static int hb_qsv_filter_pre_info( hb_filter_object_t * filter,
246 hb_filter_info_t * info ){
247 hb_filter_private_t * pv = filter->private_data;
248 if( !pv )
249 return 0;
251 sprintf(info->human_readable_desc, "copy data to system memory");
253 return 0;
255 static int hb_qsv_filter_pre_init( hb_filter_object_t * filter,
256 hb_filter_init_t * init ){
257 filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
258 hb_filter_private_t * pv = filter->private_data;
259 pv->job = init->job;
261 pv->pre.frame_go = 0;
262 pv->pre.frame_completed = hb_cond_init();
263 pv->pre.frame_completed_lock = hb_lock_init();
265 pv->post.frame_go = 0;
266 pv->post.frame_completed = hb_cond_init();
267 pv->post.frame_completed_lock = hb_lock_init();
269 pv->pre_busy.frame_go = 0;
270 pv->pre_busy.frame_completed = hb_cond_init();
271 pv->pre_busy.frame_completed_lock = hb_lock_init();
273 pv->post_busy.frame_go = 0;
274 pv->post_busy.frame_completed = hb_cond_init();
275 pv->post_busy.frame_completed_lock = hb_lock_init();
277 pv->list = hb_list_init();
279 // just to remind:
280 // PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) , 3 planes: Y, U, V
281 // 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)
282 pv->sws_context_from_nv12 = hb_sws_get_context(
283 pv->job->title->geometry.width, pv->job->title->geometry.height,
284 AV_PIX_FMT_NV12,
285 pv->job->title->geometry.width, pv->job->title->geometry.height,
286 AV_PIX_FMT_YUV420P,
287 SWS_LANCZOS|SWS_ACCURATE_RND);
288 pv->sws_context_to_nv12 = hb_sws_get_context(
289 pv->job->title->geometry.width, pv->job->title->geometry.height,
290 AV_PIX_FMT_YUV420P,
291 pv->job->title->geometry.width, pv->job->title->geometry.height,
292 AV_PIX_FMT_NV12,
293 SWS_LANCZOS|SWS_ACCURATE_RND);
294 return 0;
296 int pre_process_frame(hb_buffer_t *in, av_qsv_context* qsv, hb_filter_private_t * pv ){
298 // 1 if have results , 0 otherwise
299 int ret = 1;
301 av_qsv_list* received_item = in->qsv_details.qsv_atom;
303 mfxStatus sts = MFX_ERR_NONE;
304 mfxFrameSurface1 *work_surface = NULL;
305 av_qsv_stage* stage = 0;
307 av_qsv_space *qsv_vpp = pv->vpp_space;
309 if (received_item)
311 stage = av_qsv_get_last_stage( received_item );
312 work_surface = stage->out.p_surface;
315 int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv);
316 int surface_idx = -1;
318 for (;;)
320 if (sync_idx == -1)
322 hb_error("qsv: Not enough resources allocated for the preprocessing filter");
323 ret = 0;
324 break;
327 if (sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE)
328 surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY);
329 if (surface_idx == -1) {
330 hb_error("qsv: Not enough resources allocated for the preprocessing filter");
331 ret = 0;
332 break;
335 sts = MFXVideoUSER_ProcessFrameAsync(qsv->mfx_session, &work_surface, 1, &qsv_vpp->p_surfaces[surface_idx] , 1, qsv_vpp->p_syncp[sync_idx]->p_sync);
337 if (MFX_ERR_MORE_DATA == sts)
339 if (!qsv_vpp->pending)
341 qsv_vpp->pending = av_qsv_list_init(0);
344 // if we have no results, we should not miss resource(s)
345 av_qsv_list_add( qsv_vpp->pending, received_item);
347 ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
349 ret = 0;
350 break;
353 if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){
354 if( MFX_ERR_MORE_SURFACE == sts )
355 continue;
357 if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts )
358 ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked);
361 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
363 if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output
365 if (MFX_WRN_DEVICE_BUSY == sts){
366 hb_lock(pv->pre_busy.frame_completed_lock);
367 while(!pv->pre_busy.frame_go){
368 hb_cond_timedwait(pv->pre_busy.frame_completed,pv->pre_busy.frame_completed_lock,1000);
369 if(*pv->job->die)
370 break;
372 pv->pre_busy.frame_go = 0;
373 hb_unlock(pv->pre_busy.frame_completed_lock);
375 continue;
377 hb_lock(pv->pre.frame_completed_lock);
378 while(!pv->pre.frame_go){
379 hb_cond_timedwait(pv->pre.frame_completed,pv->pre.frame_completed_lock,1000);
380 if(*pv->job->die)
381 break;
383 pv->pre.frame_go = 0;
384 hb_unlock(pv->pre.frame_completed_lock);
386 in = pv->pre.out;
388 if (work_surface){
389 ff_qsv_atomic_dec(&work_surface->Data.Locked);
392 // inserting for the future, will be locked until very ready
393 if(stage){
394 av_qsv_stage* new_stage = av_qsv_stage_init();
396 new_stage->type = AV_QSV_VPP_USER;
397 new_stage->in.p_surface = work_surface;
398 new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx];
399 new_stage->out.sync = qsv_vpp->p_syncp[sync_idx];
400 av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS );
402 // add pending resources for the proper reclaim later
403 if( qsv_vpp->pending ){
404 if( av_qsv_list_count(qsv_vpp->pending)>0 ){
405 new_stage->pending = qsv_vpp->pending;
407 qsv_vpp->pending = 0;
409 // making free via decrement for all pending
410 int i = 0;
411 for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){
412 av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1);
413 av_qsv_stage *stage = av_qsv_get_last_stage( atom_list );
414 mfxFrameSurface1 *work_surface = stage->out.p_surface;
415 if (work_surface)
416 ff_qsv_atomic_dec(&work_surface->Data.Locked);
421 break;
424 ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use);
426 if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
427 HB_DEBUG_ASSERT(1, "The bitstream buffer size is insufficient.");
429 break;
432 return ret;
435 static int hb_qsv_filter_pre_work( hb_filter_object_t * filter,
436 hb_buffer_t ** buf_in,
437 hb_buffer_t ** buf_out ){
438 hb_filter_private_t * pv = filter->private_data;
439 hb_buffer_t * in = *buf_in;
440 hb_buffer_t * out = *buf_out;
441 int sts = 0;
443 av_qsv_context* qsv = pv->job->qsv.ctx;
445 if(!in->qsv_details.filter_details)
446 in->qsv_details.filter_details = pv;
448 if (in->s.flags & HB_BUF_FLAG_EOF)
450 *buf_out = in;
451 *buf_in = NULL;
452 return HB_FILTER_DONE;
455 while(1){
456 int ret = filter_pre_init(qsv,pv);
457 if(ret >= 2)
458 av_qsv_sleep(1);
459 else
460 break;
463 pv->pre.in = in;
464 pv->pre.out = in;
466 sts = pre_process_frame(in, qsv, pv);
468 if(sts){
469 hb_list_add(pv->list,out);
472 if( hb_list_count(pv->list) ){
473 *buf_out = hb_list_item(pv->list,0);
474 hb_list_rem(pv->list,*buf_out);
475 *buf_in = NULL;
477 else{
478 *buf_in = NULL;
479 *buf_out = in;
482 return HB_FILTER_OK;
484 static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){
485 int i = 0;
486 mfxStatus sts = MFX_ERR_NONE;
488 hb_filter_private_t * pv = filter->private_data;
490 if ( !pv )
492 return;
495 sws_freeContext(pv->sws_context_to_nv12);
496 sws_freeContext(pv->sws_context_from_nv12);
498 av_qsv_context* qsv = pv->job->qsv.ctx;
499 if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0 ){
500 if(pv->qsv_user && qsv->mfx_session){
502 sts=MFXVideoUSER_Unregister(qsv->mfx_session,0);
503 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
505 for(i=hb_list_count(pv->qsv_user);i>0;i--){
506 qsv_filter_t *plugin = hb_list_item(pv->qsv_user,i-1);
507 hb_list_rem(pv->qsv_user,plugin);
508 plugin_close(plugin);
510 hb_list_close(&pv->qsv_user);
513 // closing local stuff
514 qsv_filter_close(qsv,AV_QSV_VPP_USER);
516 // closing the commong stuff
517 av_qsv_context_clean(qsv);
519 hb_cond_close(&pv->pre.frame_completed);
520 hb_lock_close(&pv->pre.frame_completed_lock);
522 hb_cond_close(&pv->post.frame_completed);
523 hb_lock_close(&pv->post.frame_completed_lock);
525 hb_cond_close(&pv->pre_busy.frame_completed);
526 hb_lock_close(&pv->pre_busy.frame_completed_lock);
528 hb_cond_close(&pv->post_busy.frame_completed);
529 hb_lock_close(&pv->post_busy.frame_completed_lock);
531 hb_list_close( &pv->list );
533 free( pv );
534 filter->private_data = NULL;
538 static int hb_qsv_filter_post_info( hb_filter_object_t * filter,
539 hb_filter_info_t * info ){
540 hb_filter_private_t * pv = filter->private_data;
541 if( !pv )
542 return 0;
544 sprintf(info->human_readable_desc, "copy data to opaque memory");
546 return 0;
548 static int hb_qsv_filter_post_init( hb_filter_object_t * filter,
549 hb_filter_init_t * init ){
550 filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
551 hb_filter_private_t * pv = filter->private_data;
552 pv->job = init->job;
553 return 0;
555 static int hb_qsv_filter_post_work( hb_filter_object_t * filter,
556 hb_buffer_t ** buf_in,
557 hb_buffer_t ** buf_out ){
558 hb_filter_private_t * pv = filter->private_data;
559 hb_buffer_t * in = *buf_in;
560 hb_buffer_t * out = *buf_out;
562 if (in->s.flags & HB_BUF_FLAG_EOF)
564 *buf_out = in;
565 *buf_in = NULL;
566 return HB_FILTER_DONE;
569 av_qsv_context* qsv = pv->job->qsv.ctx;
570 pv = in->qsv_details.filter_details;
572 if (!pv)
574 *buf_out = NULL;
575 *buf_in = NULL;
576 return HB_FILTER_OK;
579 while(1){
580 int ret = filter_pre_init(qsv,pv);
581 if(ret >= 2)
582 av_qsv_sleep(1);
583 else
584 break;
587 pv->post.in = in;
588 pv->post.out = out;
590 // signal: input is prepared, can start inserting data back into pipeline
591 hb_lock(pv->post.frame_completed_lock);
592 pv->post.frame_go = 1;
593 hb_cond_broadcast(pv->post.frame_completed);
594 hb_unlock(pv->post.frame_completed_lock);
596 // wait: on signal that data is ready
597 hb_lock(pv->post_busy.frame_completed_lock);
598 while(!pv->post_busy.frame_go){
599 hb_cond_timedwait(pv->post_busy.frame_completed,pv->post_busy.frame_completed_lock,1000);
600 if(*pv->job->die)
601 break;
603 pv->post_busy.frame_go = 0;
604 hb_unlock(pv->post_busy.frame_completed_lock);
606 if (pv->post.status == HB_FILTER_OK || pv->post.status == HB_FILTER_DONE)
608 *buf_out = in;
610 else
612 *buf_out = NULL;
613 pv->post.status = HB_FILTER_OK;
615 *buf_in = NULL;
617 return HB_FILTER_OK;
619 static void hb_qsv_filter_post_close( hb_filter_object_t * filter ){
620 hb_filter_private_t * pv = filter->private_data;
622 if ( !pv )
624 return;
627 free( pv );
628 filter->private_data = NULL;
632 mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core){
633 mfxStatus sts = MFX_ERR_NONE;
635 if(core && pthis){
636 qsv_filter_t *plugin = pthis;
637 plugin->core = core;
639 plugin->pluginparam.MaxThreadNum = 1;
640 plugin->pluginparam.ThreadPolicy = MFX_THREADPOLICY_SERIAL;
642 else
643 sts = MFX_ERR_NULL_PTR;
645 return sts;
647 mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis){
648 mfxStatus sts = MFX_ERR_NONE;
649 return sts;
651 mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par){
652 mfxStatus sts = MFX_ERR_NONE;
654 if(pthis){
655 qsv_filter_t *plugin = pthis;
656 *par = plugin->pluginparam;
658 else
659 sts = MFX_ERR_NULL_PTR;
660 return sts;
662 mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task){
663 mfxStatus sts = MFX_ERR_NONE;
665 qsv_filter_t *plugin = pthis;
667 mfxFrameSurface1 *surface_in = (mfxFrameSurface1 *)in[0];
668 mfxFrameSurface1 *surface_out = (mfxFrameSurface1 *)out[0];
669 mfxFrameSurface1 *real_surface_in = surface_in;
670 mfxFrameSurface1 *real_surface_out = surface_out;
672 sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_in, &real_surface_in);
673 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC);
675 sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_out, &real_surface_out);
676 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC);
678 int task_idx = get_free_task(plugin->tasks);
680 if (task_idx == -1)
682 return MFX_WRN_DEVICE_BUSY;
685 plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_in->Data));
686 plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_out->Data));
688 // to preserve timing if other filters are used in-between
689 surface_out->Data.TimeStamp = surface_in->Data.TimeStamp;
690 surface_out->Data.FrameOrder = surface_in->Data.FrameOrder;
692 qsv_filter_task_t *current_task = hb_list_item(plugin->tasks,task_idx);
693 current_task->in = real_surface_in;
694 current_task->out = real_surface_out;
695 current_task->busy = 1;
696 current_task->pv = plugin->pv;
698 *task = (mfxThreadTask)current_task;
700 return sts;
702 mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a){
703 mfxStatus sts = MFX_ERR_NONE;
705 qsv_filter_task_t *current_task = (qsv_filter_task_t *)task;
706 qsv_filter_t *plugin = pthis;
708 sts = (current_task->processor.process)(current_task,0);
709 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
711 sts = MFX_TASK_DONE;
712 return sts;
714 mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts){
716 qsv_filter_t *plugin = pthis;
717 qsv_filter_task_t *current_task = (qsv_filter_task_t *)task;
719 plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->in->Data));
720 plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->out->Data));
722 current_task->busy = 0;
724 hb_lock(plugin->pv->pre_busy.frame_completed_lock);
725 plugin->pv->pre_busy.frame_go = 1;
726 hb_cond_broadcast(plugin->pv->pre_busy.frame_completed);
727 hb_unlock(plugin->pv->pre_busy.frame_completed_lock);
729 return MFX_ERR_NONE;
732 mfxStatus plugin_init(qsv_filter_t* plugin, mfxVideoParam *param){
733 mfxStatus sts = MFX_ERR_NONE;
735 if(plugin->is_init_done) return sts;
737 plugin->videoparam = param;
739 mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL;
741 plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam,
742 plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION);
744 if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces)
745 return MFX_ERR_INVALID_VIDEO_PARAM;
747 sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface,
748 plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces);
749 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
752 sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface,
753 plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces);
754 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
757 plugin->tasks = hb_list_init();
758 qsv_filter_task_t *task = calloc( 1, sizeof( qsv_filter_task_t ));
760 task->processor.process = process_filter;
761 task->processor.alloc = &plugin->core->FrameAllocator;
762 task->processor.core = plugin->core;
764 hb_list_add(plugin->tasks,task);
766 plugin->is_init_done = 1;
768 return sts;
771 mfxStatus plugin_close(qsv_filter_t* plugin){
772 int i = 0;
773 mfxStatus sts = MFX_ERR_NONE;
775 if(!plugin->is_init_done) return sts;
777 mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL;
779 plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam,
780 plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION);
782 if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces)
783 return MFX_ERR_INVALID_VIDEO_PARAM;
785 sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface,
786 plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces);
787 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
790 sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface,
791 plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces);
792 AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
794 if(plugin->tasks){
795 for(i=hb_list_count(plugin->tasks);i>0;i--){
796 qsv_filter_task_t *task = hb_list_item(plugin->tasks,i-1);
797 hb_list_rem(plugin->tasks,task);
798 free(task);
800 hb_list_close(&plugin->tasks);
803 plugin->is_init_done = 0;
805 return sts;
808 mfxExtBuffer* get_ext_buffer(mfxExtBuffer** buffers, mfxU32 buffers_num, mfxU32 buffer_id){
809 int i = 0;
810 if(!buffers) return 0;
811 for(i=0;i<buffers_num;i++){
812 if(!buffers[i]) continue;
813 if(buffers[i]->BufferId == buffer_id)
814 return buffers[i];
816 return 0;
819 int get_free_task(hb_list_t* tasks){
820 int ret = -1;
821 int i = 0;
822 for(i=0;i<hb_list_count(tasks);i++){
823 qsv_filter_task_t* task = hb_list_item(tasks,i);
824 if(!task->busy){
825 ret = i;
826 break;
829 return ret;
832 mfxStatus lock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){
833 mfxStatus sts = MFX_ERR_NONE;
834 // prevent double lock
835 if (surface->Data.Y != 0 && surface->Data.MemId !=0){
836 return MFX_ERR_UNSUPPORTED;
838 // not allocated, therefore no lock
839 if (surface->Data.Y != 0){
840 return MFX_ERR_NONE;
842 sts = alloc->Lock(alloc->pthis,surface->Data.MemId,&surface->Data);
843 return sts;
846 mfxStatus unlock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){
847 mfxStatus sts = MFX_ERR_NONE;
848 // not allocated
849 if (surface->Data.Y != 0 && surface->Data.MemId == 0){
850 return MFX_ERR_NONE;
852 // not locked
853 if (surface->Data.Y == 0){
854 return MFX_ERR_NONE;
856 sts = alloc->Unlock(alloc->pthis,surface->Data.MemId,&surface->Data);
857 return sts;
861 int process_filter(qsv_filter_task_t* task, void* params){
862 mfxStatus sts = MFX_ERR_NONE;
864 if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->in)))return sts;
865 if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->out)))
867 unlock_frame(task->processor.alloc,task->in);
868 return sts;
871 qsv_nv12_to_yuv420(task->pv->sws_context_from_nv12,task->pv->pre.out, task->in, task->processor.core);
873 // signal: input is prepared, converted from pipeline into internal buffer
874 hb_lock(task->pv->pre.frame_completed_lock);
875 task->pv->pre.frame_go = 1;
876 hb_cond_broadcast(task->pv->pre.frame_completed);
877 hb_unlock(task->pv->pre.frame_completed_lock);
879 // wait: input is prepared, converted from pipeline into internal buffer
880 hb_lock(task->pv->post.frame_completed_lock);
881 while(!task->pv->post.frame_go){
882 hb_cond_timedwait(task->pv->post.frame_completed,task->pv->post.frame_completed_lock,1000);
883 if(*task->pv->job->die)
884 break;
886 task->pv->post.frame_go = 0;
887 hb_unlock(task->pv->post.frame_completed_lock);
889 // this is just a simple fun/test case
890 #if 0
892 int i = 0;
893 char *cur_line;
894 char* luma = task->pv->post.in->plane[0].data;
895 int pitch = task->pv->post.in->plane[0].stride;
896 int h = task->pv->post.in->plane[0].height;
897 int w = task->pv->post.in->plane[0].width;
898 for (i = 0; i < h; i++){
900 cur_line = luma + i * pitch;
901 if(i>h/4 && i < 3*h/4 && i % 5 == 0 )
902 memset(cur_line, 0 , w );
905 #endif
907 if(task->pv->post.in)
909 qsv_yuv420_to_nv12(task->pv->sws_context_to_nv12, task->out, task->pv->post.in);
912 // signal: output is prepared, converted from internal buffer into pipeline
913 hb_lock(task->pv->post_busy.frame_completed_lock);
914 task->pv->post_busy.frame_go = 1;
915 hb_cond_broadcast(task->pv->post_busy.frame_completed);
916 hb_unlock(task->pv->post_busy.frame_completed_lock);
918 unlock_frame(task->processor.alloc,task->in);
919 unlock_frame(task->processor.alloc,task->out);
921 return sts;
924 #endif // USE_QSV