Merge pull request #5101 from FernetMenta/ffmpeg-threads
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDCodecs / Video / VAAPI.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "system.h"
21 #ifdef HAVE_LIBVA
22 #include "windowing/WindowingFactory.h"
23 #include "VAAPI.h"
24 #include "DVDVideoCodec.h"
25 #include <boost/scoped_array.hpp>
26 #include <boost/weak_ptr.hpp>
27
28 #define CHECK(a) \
29 do { \
30   VAStatus res = a; \
31   if(res != VA_STATUS_SUCCESS) \
32   { \
33     CLog::Log(LOGERROR, "VAAPI - failed executing "#a" at line %d with error %x:%s", __LINE__, res, vaErrorStr(res)); \
34     return false; \
35   } \
36 } while(0);
37
38 #define WARN(a) \
39 do { \
40   VAStatus res = a; \
41   if(res != VA_STATUS_SUCCESS) \
42     CLog::Log(LOGWARNING, "VAAPI - failed executing "#a" at line %d with error %x:%s", __LINE__, res, vaErrorStr(res)); \
43 } while(0);
44
45 #ifndef VA_SURFACE_ATTRIB_SETTABLE
46 #define vaCreateSurfaces(d, f, w, h, s, ns, a, na) \
47     vaCreateSurfaces(d, w, h, f, ns, s)
48 #endif
49
50 using namespace std;
51 using namespace boost;
52 using namespace VAAPI;
53
54 // settings codecs mapping
55 DVDCodecAvailableType g_vaapi_available[] = {
56   { AV_CODEC_ID_H263, "videoplayer.usevaapimpeg4" },
57   { AV_CODEC_ID_MPEG4, "videoplayer.usevaapimpeg4" },
58   { AV_CODEC_ID_WMV3, "videoplayer.usevaapivc1" },
59   { AV_CODEC_ID_VC1, "videoplayer.usevaapivc1" },
60   { AV_CODEC_ID_MPEG2VIDEO, "videoplayer.usevaapimpeg2" },
61 };
62 const size_t settings_count = sizeof(g_vaapi_available) / sizeof(DVDCodecAvailableType);
63
64 static int compare_version(int major_l, int minor_l, int micro_l, int major_r, int minor_r, int micro_r)
65 {
66   if(major_l < major_r) return -1;
67   if(major_l > major_r) return  1;
68   if(minor_l < minor_r) return -1;
69   if(minor_l > minor_r) return  1;
70   if(micro_l < micro_r) return -1;
71   if(micro_l > micro_r) return  1;
72   return 0;
73 }
74
75 static void RelBufferS(AVCodecContext *avctx, AVFrame *pic)
76 { ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->RelBuffer(avctx, pic); }
77
78 static int GetBufferS(AVCodecContext *avctx, AVFrame *pic) 
79 {  return ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->GetBuffer(avctx, pic); }
80
81 static inline VASurfaceID GetSurfaceID(AVFrame *pic)
82 { return (VASurfaceID)(uintptr_t)pic->data[3]; }
83
84 static CDisplayPtr GetGlobalDisplay()
85 {
86   static weak_ptr<CDisplay> display_global;
87
88   CDisplayPtr display(display_global.lock());
89   if(display)
90   {
91     if(display->lost())
92     {
93       CLog::Log(LOGERROR, "VAAPI - vaapi display is in lost state");
94       display.reset();
95     }    
96     return display;
97   }
98
99   VADisplay disp;
100   disp = vaGetDisplayGLX(g_Windowing.GetDisplay());
101
102   int major_version, minor_version;
103   VAStatus res = vaInitialize(disp, &major_version, &minor_version);
104
105   CLog::Log(LOGDEBUG, "VAAPI - initialize version %d.%d", major_version, minor_version);
106
107   if(res != VA_STATUS_SUCCESS)
108   {
109     CLog::Log(LOGERROR, "VAAPI - unable to initialize display %d - %s", res, vaErrorStr(res));
110     return display;
111   }
112
113   const char* vendor = vaQueryVendorString(disp);
114   CLog::Log(LOGDEBUG, "VAAPI - vendor: %s", vendor);
115
116   bool deinterlace = true;
117   int major, minor, micro;
118   bool support_4k = true;
119   if(sscanf(vendor,  "Intel i965 driver - %d.%d.%d", &major, &minor, &micro) == 3)
120   {
121     /* older version will crash and burn */
122     if(compare_version(major, minor, micro, 1, 0, 17) < 0)
123     {
124       CLog::Log(LOGDEBUG, "VAAPI - deinterlace not support on this intel driver version");
125       deinterlace = false;
126     }
127     // do the same check for 4K decoding: version < 1.2.0 (stable) and 1.0.21 (staging)
128     // cannot decode 4K and will crash the GPU
129     if((compare_version(major, minor, micro, 1, 2, 0) < 0) && (compare_version(major, minor, micro, 1, 0, 21) < 0))
130     {
131       support_4k = false;
132     }
133   }
134
135   display = CDisplayPtr(new CDisplay(disp, deinterlace));
136   display->support_4k(support_4k);
137   display_global = display;
138   return display;
139 }
140
141 CDisplay::~CDisplay()
142 {
143   CLog::Log(LOGDEBUG, "VAAPI - destroying display %p", m_display);
144   WARN(vaTerminate(m_display))
145 }
146
147 CSurface::~CSurface()
148 {
149   CLog::Log(LOGDEBUG, "VAAPI - destroying surface 0x%x", (int)m_id);
150   CSingleLock lock(*m_display);
151   WARN(vaDestroySurfaces(m_display->get(), &m_id, 1))
152 }
153
154 CSurfaceGL::~CSurfaceGL()
155 {
156   CLog::Log(LOGDEBUG, "VAAPI - destroying glx surface %p", m_id);
157   CSingleLock lock(*m_display);
158   WARN(vaDestroySurfaceGLX(m_display->get(), m_id))
159 }
160
161 CDecoder::CDecoder()
162 {
163   m_refs            = 0;
164   m_surfaces_count  = 0;
165   m_config          = 0;
166   m_context         = 0;
167   m_hwaccel         = (vaapi_context*)calloc(1, sizeof(vaapi_context));
168   memset(m_surfaces, 0, sizeof(*m_surfaces));
169 }
170
171 CDecoder::~CDecoder()
172 {
173   Close();
174   free(m_hwaccel);
175 }
176
177 void CDecoder::RelBuffer(AVCodecContext *avctx, AVFrame *pic)
178 {
179   VASurfaceID surface = GetSurfaceID(pic);
180
181   for(std::list<CSurfacePtr>::iterator it = m_surfaces_used.begin(); it != m_surfaces_used.end(); ++it)
182   {    
183     if((*it)->m_id == surface)
184     {
185       m_surfaces_free.push_back(*it);
186       m_surfaces_used.erase(it);
187       break;
188     }
189   }
190   pic->data[0] = NULL;
191   pic->data[1] = NULL;
192   pic->data[2] = NULL;
193   pic->data[3] = NULL;
194 }
195
196 int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic)
197 {
198   VASurfaceID surface = GetSurfaceID(pic);
199   CSurface*   wrapper = NULL;
200   std::list<CSurfacePtr>::iterator it = m_surfaces_free.begin();
201   if(surface)
202   {
203     /* reget call */
204     for(; it != m_surfaces_free.end(); ++it)
205     {
206       if((*it)->m_id == surface)
207       {
208         wrapper = it->get();
209         m_surfaces_used.push_back(*it);
210         m_surfaces_free.erase(it);
211         break;
212       }
213     }
214     if(!wrapper)
215     {
216       CLog::Log(LOGERROR, "VAAPI - unable to find requested surface");
217       return -1; 
218     }
219   }
220   else
221   {
222     // To avoid stutter, we scan the free surface pool (provided by decoder) for surfaces
223     // that are 100% not in use by renderer. The pointers to these surfaces have a use_count of 1.
224     for (; it != m_surfaces_free.end() && it->use_count() > 1; ++it) {}
225
226     // If we have zero free surface from decoder OR all free surfaces are in use by renderer, we allocate a new surface
227     if (it == m_surfaces_free.end())
228     {
229       if (!m_surfaces_free.empty()) CLog::Log(LOGERROR, "VAAPI - renderer still using all freed up surfaces by decoder");
230       CLog::Log(LOGERROR, "VAAPI - unable to find free surface, trying to allocate a new one");
231       if(!EnsureSurfaces(avctx, m_surfaces_count+1) || m_surfaces_free.empty())
232       {
233         CLog::Log(LOGERROR, "VAAPI - unable to find free surface");
234         return -1;
235       }
236       // Set itarator position to the newly allocated surface (end-1)
237       it = m_surfaces_free.end(); --it;
238     }
239     /* getbuffer call */
240     wrapper = it->get();
241     surface = wrapper->m_id;
242     m_surfaces_used.push_back(*it);
243     m_surfaces_free.erase(it);
244   }
245
246   pic->type           = FF_BUFFER_TYPE_USER;
247   pic->data[0]        = (uint8_t*)wrapper;
248   pic->data[1]        = NULL;
249   pic->data[2]        = NULL;
250   pic->data[3]        = (uint8_t*)surface;
251   pic->linesize[0]    = 0;
252   pic->linesize[1]    = 0;
253   pic->linesize[2]    = 0;
254   pic->linesize[3]    = 0;
255   pic->reordered_opaque= avctx->reordered_opaque;
256   return 0;
257 }
258
259 void CDecoder::Close()
260
261   if(m_context)
262     WARN(vaDestroyContext(m_display->get(), m_context))
263   m_context = 0;
264
265   if(m_config)
266     WARN(vaDestroyConfig(m_display->get(), m_config))
267   m_config = 0;
268   
269   m_surfaces_free.clear();
270   m_surfaces_used.clear();
271   m_surfaces_count = 0;
272   m_refs           = 0;
273   memset(m_hwaccel , 0, sizeof(*m_hwaccel));
274   memset(m_surfaces, 0, sizeof(*m_surfaces));
275   m_display.reset();
276   m_holder.surface.reset();
277 }
278
279 bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int surfaces)
280 {
281   // check if user wants to decode this format with VAAPI
282   if (CDVDVideoCodec::IsCodecDisabled(g_vaapi_available, settings_count, avctx->codec_id))
283     return false;
284
285   VAEntrypoint entrypoint = VAEntrypointVLD;
286   VAProfile    profile;
287
288   CLog::Log(LOGDEBUG, "VAAPI - attempting to open codec %d with profile %d at level %d with %d reference frames", avctx->codec_id, avctx->profile, avctx->level, avctx->refs);
289
290   vector<VAProfile> accepted;
291   switch (avctx->codec_id) {
292     case AV_CODEC_ID_MPEG2VIDEO:
293       accepted.push_back(VAProfileMPEG2Main);
294       break;
295     case AV_CODEC_ID_MPEG4:
296     case AV_CODEC_ID_H263:
297       accepted.push_back(VAProfileMPEG4AdvancedSimple);
298       break;
299     case AV_CODEC_ID_H264:
300     {
301 #ifdef FF_PROFILE_H264_BASELINE
302       if  (avctx->profile == FF_PROFILE_H264_BASELINE)
303         accepted.push_back(VAProfileH264Baseline);
304       else
305       {
306         if(avctx->profile == FF_PROFILE_H264_MAIN)
307           accepted.push_back(VAProfileH264Main); 
308 #else
309       {
310         // fallback to high profile if libavcodec is too old to export
311         // profile information; it will likely work
312 #endif
313         // fallback to high profile if main profile is not available
314         accepted.push_back(VAProfileH264High);
315       }
316       break;
317     }
318     case AV_CODEC_ID_WMV3:
319       accepted.push_back(VAProfileVC1Main);
320       break;
321     case AV_CODEC_ID_VC1:
322       accepted.push_back(VAProfileVC1Advanced);
323       break;
324     default:
325       return false;
326   }
327
328   m_display = GetGlobalDisplay();
329   if(!m_display)
330     return false;
331
332   if(!m_display->support_4k() && (avctx->width > 1920 || avctx->height > 1088))
333   {
334     CLog::Log(LOGDEBUG, "VAAPI - frame size (%dx%d) too large - disallowing", avctx->width, avctx->height);
335     return false;
336   }
337
338   int num_display_attrs = 0;
339   scoped_array<VADisplayAttribute> display_attrs(new VADisplayAttribute[vaMaxNumDisplayAttributes(m_display->get())]);
340
341   CHECK(vaQueryDisplayAttributes(m_display->get(), display_attrs.get(), &num_display_attrs))
342
343   for(int i = 0; i < num_display_attrs; i++)
344   {
345       VADisplayAttribute * const display_attr = &display_attrs[i];
346       CLog::Log(LOGDEBUG, "VAAPI - attrib %d (%s/%s) min %d max %d value 0x%x\n"
347               , display_attr->type
348               ,(display_attr->flags & VA_DISPLAY_ATTRIB_GETTABLE) ? "get" : "---"
349               ,(display_attr->flags & VA_DISPLAY_ATTRIB_SETTABLE) ? "set" : "---"
350               , display_attr->min_value
351               , display_attr->max_value
352               , display_attr->value);
353   }
354
355   int num_profiles = 0;
356   scoped_array<VAProfile> profiles(new VAProfile[vaMaxNumProfiles(m_display->get())]);
357   CHECK(vaQueryConfigProfiles(m_display->get(), profiles.get(), &num_profiles))
358
359   for(int i = 0; i < num_profiles; i++)
360     CLog::Log(LOGDEBUG, "VAAPI - profile %d", profiles[i]);
361
362   vector<VAProfile>::iterator selected = find_first_of(accepted.begin()
363                                                      , accepted.end()
364                                                      , profiles.get()
365                                                      , profiles.get()+num_profiles);
366   if(selected == accepted.end())
367   {
368     CLog::Log(LOGDEBUG, "VAAPI - unable to find a suitable profile");
369     return false;
370   }
371
372   profile = *selected;
373
374   VAConfigAttrib attrib;
375   attrib.type = VAConfigAttribRTFormat;
376   CHECK(vaGetConfigAttributes(m_display->get(), profile, entrypoint, &attrib, 1))
377
378   if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
379   {
380     CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", attrib.value);
381     return false;
382   }
383
384   CHECK(vaCreateConfig(m_display->get(), profile, entrypoint, &attrib, 1, &m_hwaccel->config_id))
385   m_config = m_hwaccel->config_id;
386
387   m_renderbuffers_count = surfaces;
388   if (!EnsureContext(avctx))
389     return false;
390
391   m_hwaccel->display     = m_display->get();
392
393   avctx->hwaccel_context = m_hwaccel;
394   avctx->get_buffer      = GetBufferS;
395   avctx->reget_buffer    = GetBufferS;
396   avctx->release_buffer  = RelBufferS;
397   avctx->draw_horiz_band = NULL;
398   avctx->slice_flags     = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
399   return true;
400 }
401
402 bool CDecoder::EnsureContext(AVCodecContext *avctx)
403 {
404   if(avctx->refs && avctx->refs <= m_refs)
405     return true;
406
407   if(m_refs > 0)
408     CLog::Log(LOGWARNING, "VAAPI - reference frame count increasing, reiniting decoder");
409
410   m_refs = avctx->refs;
411   if(m_refs == 0)
412   {
413     if(avctx->codec_id == AV_CODEC_ID_H264)
414       m_refs = 16;
415     else
416       m_refs = 2;
417   }
418   return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + 1);
419 }
420
421 bool CDecoder::EnsureSurfaces(AVCodecContext *avctx, unsigned n_surfaces_count)
422 {
423   CLog::Log(LOGDEBUG, "VAAPI - making sure %d surfaces are allocated for given %d references", n_surfaces_count, avctx->refs);
424
425   if(n_surfaces_count > m_surfaces_max)
426   {
427     CLog::Log(LOGERROR, "VAAPI - Failed to ensure surfaces! Requested %d surfaces. Maximum possible count is %d!", n_surfaces_count, m_surfaces_max);
428     return false;
429   }
430
431   if(n_surfaces_count <= m_surfaces_count)
432     return true;
433
434   const unsigned old_surfaces_count = m_surfaces_count;
435   m_surfaces_count = n_surfaces_count;
436
437   CHECK(vaCreateSurfaces(m_display->get()
438                        , VA_RT_FORMAT_YUV420
439                        , avctx->width
440                        , avctx->height
441                        , &m_surfaces[old_surfaces_count]
442                        , m_surfaces_count - old_surfaces_count
443                        , NULL
444                        , 0))
445
446   for(unsigned i = old_surfaces_count; i < m_surfaces_count; i++)
447     m_surfaces_free.push_back(CSurfacePtr(new CSurface(m_surfaces[i], m_display)));
448
449   //shared_ptr<VASurfaceID const> test = VASurfaceIDPtr(m_surfaces[0], m_display);
450
451   if(m_context)
452     WARN(vaDestroyContext(m_display->get(), m_context))
453   m_context = 0;
454
455   CHECK(vaCreateContext(m_display->get()
456                       , m_config
457                       , avctx->width
458                       , avctx->height
459                       , VA_PROGRESSIVE
460                       , m_surfaces
461                       , m_surfaces_count
462                       , &m_hwaccel->context_id))
463   m_context = m_hwaccel->context_id;
464   return true;
465 }
466
467 int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame)
468 {
469   int status = Check(avctx);
470   if(status)
471     return status;
472
473   if(frame)
474     return VC_BUFFER | VC_PICTURE;
475   else
476     return VC_BUFFER;
477 }
478
479 bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
480 {
481   ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(picture);
482   VASurfaceID surface = GetSurfaceID(frame);
483
484
485   m_holder.surface.reset();
486
487   std::list<CSurfacePtr>::iterator it;
488   for(it = m_surfaces_used.begin(); it != m_surfaces_used.end() && !m_holder.surface; ++it)
489   {    
490     if((*it)->m_id == surface)
491     {
492       m_holder.surface = *it;
493       break;
494     }
495   }
496
497   for(it = m_surfaces_free.begin(); it != m_surfaces_free.end() && !m_holder.surface; ++it)
498   {    
499     if((*it)->m_id == surface)
500     {
501       m_holder.surface = *it;
502       break;
503     }
504   }
505   if(!m_holder.surface)
506   {
507     CLog::Log(LOGERROR, "VAAPI - Unable to find surface");
508     return false;
509   }
510
511   picture->format = RENDER_FMT_VAAPI;
512   picture->vaapi  = &m_holder;
513   return true;
514 }
515
516 int CDecoder::Check(AVCodecContext* avctx)
517 {
518   if (m_display == NULL)
519   {
520     if(!Open(avctx, avctx->pix_fmt))
521     {
522       CLog::Log(LOGERROR, "VAAPI - Unable to recover device after display was closed");
523       Close();
524       return VC_ERROR;
525     }
526   }
527
528   if (m_display->lost())
529   {
530     Close();
531     if(!Open(avctx, avctx->pix_fmt))
532     {
533       CLog::Log(LOGERROR, "VAAPI - Unable to recover device after display was lost");
534       Close();
535       return VC_ERROR;
536     }
537     return VC_FLUSHED;
538   }
539
540   if (!EnsureContext(avctx))
541     return VC_ERROR;
542
543   m_holder.surface.reset();
544   return 0;
545 }
546
547 unsigned CDecoder::GetAllowedReferences()
548 {
549   return m_renderbuffers_count;
550 }
551
552 #endif