2 * Copyright (C) 2005-2013 Team XBMC
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.
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.
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/>.
22 #include "windowing/WindowingFactory.h"
24 #include "DVDVideoCodec.h"
25 #include <boost/scoped_array.hpp>
26 #include <boost/weak_ptr.hpp>
31 if(res != VA_STATUS_SUCCESS) \
33 CLog::Log(LOGERROR, "VAAPI - failed executing "#a" at line %d with error %x:%s", __LINE__, res, vaErrorStr(res)); \
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)); \
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)
51 using namespace boost;
52 using namespace VAAPI;
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" },
62 const size_t settings_count = sizeof(g_vaapi_available) / sizeof(DVDCodecAvailableType);
64 static int compare_version(int major_l, int minor_l, int micro_l, int major_r, int minor_r, int micro_r)
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;
75 static void RelBufferS(AVCodecContext *avctx, AVFrame *pic)
76 { ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->RelBuffer(avctx, pic); }
78 static int GetBufferS(AVCodecContext *avctx, AVFrame *pic)
79 { return ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->GetBuffer(avctx, pic); }
81 static inline VASurfaceID GetSurfaceID(AVFrame *pic)
82 { return (VASurfaceID)(uintptr_t)pic->data[3]; }
84 static CDisplayPtr GetGlobalDisplay()
86 static weak_ptr<CDisplay> display_global;
88 CDisplayPtr display(display_global.lock());
93 CLog::Log(LOGERROR, "VAAPI - vaapi display is in lost state");
100 disp = vaGetDisplayGLX(g_Windowing.GetDisplay());
102 int major_version, minor_version;
103 VAStatus res = vaInitialize(disp, &major_version, &minor_version);
105 CLog::Log(LOGDEBUG, "VAAPI - initialize version %d.%d", major_version, minor_version);
107 if(res != VA_STATUS_SUCCESS)
109 CLog::Log(LOGERROR, "VAAPI - unable to initialize display %d - %s", res, vaErrorStr(res));
113 const char* vendor = vaQueryVendorString(disp);
114 CLog::Log(LOGDEBUG, "VAAPI - vendor: %s", vendor);
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, µ) == 3)
121 /* older version will crash and burn */
122 if(compare_version(major, minor, micro, 1, 0, 17) < 0)
124 CLog::Log(LOGDEBUG, "VAAPI - deinterlace not support on this intel driver version");
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))
135 display = CDisplayPtr(new CDisplay(disp, deinterlace));
136 display->support_4k(support_4k);
137 display_global = display;
141 CDisplay::~CDisplay()
143 CLog::Log(LOGDEBUG, "VAAPI - destroying display %p", m_display);
144 WARN(vaTerminate(m_display))
147 CSurface::~CSurface()
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))
154 CSurfaceGL::~CSurfaceGL()
156 CLog::Log(LOGDEBUG, "VAAPI - destroying glx surface %p", m_id);
157 CSingleLock lock(*m_display);
158 WARN(vaDestroySurfaceGLX(m_display->get(), m_id))
164 m_surfaces_count = 0;
167 m_hwaccel = (vaapi_context*)calloc(1, sizeof(vaapi_context));
168 memset(m_surfaces, 0, sizeof(*m_surfaces));
171 CDecoder::~CDecoder()
177 void CDecoder::RelBuffer(AVCodecContext *avctx, AVFrame *pic)
179 VASurfaceID surface = GetSurfaceID(pic);
181 for(std::list<CSurfacePtr>::iterator it = m_surfaces_used.begin(); it != m_surfaces_used.end(); ++it)
183 if((*it)->m_id == surface)
185 m_surfaces_free.push_back(*it);
186 m_surfaces_used.erase(it);
196 int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic)
198 VASurfaceID surface = GetSurfaceID(pic);
199 CSurface* wrapper = NULL;
200 std::list<CSurfacePtr>::iterator it = m_surfaces_free.begin();
204 for(; it != m_surfaces_free.end(); ++it)
206 if((*it)->m_id == surface)
209 m_surfaces_used.push_back(*it);
210 m_surfaces_free.erase(it);
216 CLog::Log(LOGERROR, "VAAPI - unable to find requested surface");
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) {}
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())
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())
233 CLog::Log(LOGERROR, "VAAPI - unable to find free surface");
236 // Set itarator position to the newly allocated surface (end-1)
237 it = m_surfaces_free.end(); --it;
241 surface = wrapper->m_id;
242 m_surfaces_used.push_back(*it);
243 m_surfaces_free.erase(it);
246 pic->type = FF_BUFFER_TYPE_USER;
247 pic->data[0] = (uint8_t*)wrapper;
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;
259 void CDecoder::Close()
262 WARN(vaDestroyContext(m_display->get(), m_context))
266 WARN(vaDestroyConfig(m_display->get(), m_config))
269 m_surfaces_free.clear();
270 m_surfaces_used.clear();
271 m_surfaces_count = 0;
273 memset(m_hwaccel , 0, sizeof(*m_hwaccel));
274 memset(m_surfaces, 0, sizeof(*m_surfaces));
276 m_holder.surface.reset();
279 bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int surfaces)
281 // check if user wants to decode this format with VAAPI
282 if (CDVDVideoCodec::IsCodecDisabled(g_vaapi_available, settings_count, avctx->codec_id))
285 VAEntrypoint entrypoint = VAEntrypointVLD;
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);
290 vector<VAProfile> accepted;
291 switch (avctx->codec_id) {
292 case AV_CODEC_ID_MPEG2VIDEO:
293 accepted.push_back(VAProfileMPEG2Main);
295 case AV_CODEC_ID_MPEG4:
296 case AV_CODEC_ID_H263:
297 accepted.push_back(VAProfileMPEG4AdvancedSimple);
299 case AV_CODEC_ID_H264:
301 #ifdef FF_PROFILE_H264_BASELINE
302 if (avctx->profile == FF_PROFILE_H264_BASELINE)
303 accepted.push_back(VAProfileH264Baseline);
306 if(avctx->profile == FF_PROFILE_H264_MAIN)
307 accepted.push_back(VAProfileH264Main);
310 // fallback to high profile if libavcodec is too old to export
311 // profile information; it will likely work
313 // fallback to high profile if main profile is not available
314 accepted.push_back(VAProfileH264High);
318 case AV_CODEC_ID_WMV3:
319 accepted.push_back(VAProfileVC1Main);
321 case AV_CODEC_ID_VC1:
322 accepted.push_back(VAProfileVC1Advanced);
328 m_display = GetGlobalDisplay();
332 if(!m_display->support_4k() && (avctx->width > 1920 || avctx->height > 1088))
334 CLog::Log(LOGDEBUG, "VAAPI - frame size (%dx%d) too large - disallowing", avctx->width, avctx->height);
338 int num_display_attrs = 0;
339 scoped_array<VADisplayAttribute> display_attrs(new VADisplayAttribute[vaMaxNumDisplayAttributes(m_display->get())]);
341 CHECK(vaQueryDisplayAttributes(m_display->get(), display_attrs.get(), &num_display_attrs))
343 for(int i = 0; i < num_display_attrs; i++)
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"
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);
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))
359 for(int i = 0; i < num_profiles; i++)
360 CLog::Log(LOGDEBUG, "VAAPI - profile %d", profiles[i]);
362 vector<VAProfile>::iterator selected = find_first_of(accepted.begin()
365 , profiles.get()+num_profiles);
366 if(selected == accepted.end())
368 CLog::Log(LOGDEBUG, "VAAPI - unable to find a suitable profile");
374 VAConfigAttrib attrib;
375 attrib.type = VAConfigAttribRTFormat;
376 CHECK(vaGetConfigAttributes(m_display->get(), profile, entrypoint, &attrib, 1))
378 if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
380 CLog::Log(LOGERROR, "VAAPI - invalid yuv format %x", attrib.value);
384 CHECK(vaCreateConfig(m_display->get(), profile, entrypoint, &attrib, 1, &m_hwaccel->config_id))
385 m_config = m_hwaccel->config_id;
387 m_renderbuffers_count = surfaces;
388 if (!EnsureContext(avctx))
391 m_hwaccel->display = m_display->get();
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;
402 bool CDecoder::EnsureContext(AVCodecContext *avctx)
404 if(avctx->refs && avctx->refs <= m_refs)
408 CLog::Log(LOGWARNING, "VAAPI - reference frame count increasing, reiniting decoder");
410 m_refs = avctx->refs;
413 if(avctx->codec_id == AV_CODEC_ID_H264)
418 return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + 1);
421 bool CDecoder::EnsureSurfaces(AVCodecContext *avctx, unsigned n_surfaces_count)
423 CLog::Log(LOGDEBUG, "VAAPI - making sure %d surfaces are allocated for given %d references", n_surfaces_count, avctx->refs);
425 if(n_surfaces_count > m_surfaces_max)
427 CLog::Log(LOGERROR, "VAAPI - Failed to ensure surfaces! Requested %d surfaces. Maximum possible count is %d!", n_surfaces_count, m_surfaces_max);
431 if(n_surfaces_count <= m_surfaces_count)
434 const unsigned old_surfaces_count = m_surfaces_count;
435 m_surfaces_count = n_surfaces_count;
437 CHECK(vaCreateSurfaces(m_display->get()
438 , VA_RT_FORMAT_YUV420
441 , &m_surfaces[old_surfaces_count]
442 , m_surfaces_count - old_surfaces_count
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)));
449 //shared_ptr<VASurfaceID const> test = VASurfaceIDPtr(m_surfaces[0], m_display);
452 WARN(vaDestroyContext(m_display->get(), m_context))
455 CHECK(vaCreateContext(m_display->get()
462 , &m_hwaccel->context_id))
463 m_context = m_hwaccel->context_id;
467 int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame)
469 int status = Check(avctx);
474 return VC_BUFFER | VC_PICTURE;
479 bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture)
481 ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(picture);
482 VASurfaceID surface = GetSurfaceID(frame);
485 m_holder.surface.reset();
487 std::list<CSurfacePtr>::iterator it;
488 for(it = m_surfaces_used.begin(); it != m_surfaces_used.end() && !m_holder.surface; ++it)
490 if((*it)->m_id == surface)
492 m_holder.surface = *it;
497 for(it = m_surfaces_free.begin(); it != m_surfaces_free.end() && !m_holder.surface; ++it)
499 if((*it)->m_id == surface)
501 m_holder.surface = *it;
505 if(!m_holder.surface)
507 CLog::Log(LOGERROR, "VAAPI - Unable to find surface");
511 picture->format = RENDER_FMT_VAAPI;
512 picture->vaapi = &m_holder;
516 int CDecoder::Check(AVCodecContext* avctx)
518 if (m_display == NULL)
520 if(!Open(avctx, avctx->pix_fmt))
522 CLog::Log(LOGERROR, "VAAPI - Unable to recover device after display was closed");
528 if (m_display->lost())
531 if(!Open(avctx, avctx->pix_fmt))
533 CLog::Log(LOGERROR, "VAAPI - Unable to recover device after display was lost");
540 if (!EnsureContext(avctx))
543 m_holder.surface.reset();
547 unsigned CDecoder::GetAllowedReferences()
549 return m_renderbuffers_count;