2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program 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
13 * GNU 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/>.
23 // setting that here because otherwise SampleFormat is defined to AVSampleFormat
24 // which we don't use here
25 #define FF_API_OLD_SAMPLE_FMT 0
32 #include "libavcodec/dxva2.h"
33 #include "../DVDCodecUtils.h"
36 #include "windowing/WindowingFactory.h"
37 #include "../../../VideoRenderers/WinRenderer.h"
38 #include "settings/Settings.h"
39 #include "settings/MediaSettings.h"
40 #include "boost/shared_ptr.hpp"
41 #include "utils/AutoPtrHandle.h"
42 #include "utils/StringUtils.h"
43 #include "settings/AdvancedSettings.h"
44 #include "settings/MediaSettings.h"
45 #include "cores/VideoRenderers/RenderManager.h"
46 #include "win32/WIN32Util.h"
48 #define ALLOW_ADDING_SURFACES 0
51 using namespace AUTOPTR;
54 typedef HRESULT (__stdcall *DXVAHDCreateVideoServicePtr)(IDirect3DDevice9Ex *pD3DDevice, const DXVAHD_CONTENT_DESC *pContentDesc, DXVAHD_DEVICE_USAGE Usage, PDXVAHDSW_Plugin pPlugin, IDXVAHD_Device **ppDevice);
55 static DXVAHDCreateVideoServicePtr g_DXVAHDCreateVideoService;
62 CLog::Log(LOGERROR, __FUNCTION__" - failed executing "#a" at line %d with error %x", __LINE__, res); \
67 #define LOGIFERROR(a) \
72 CLog::Log(LOGERROR, __FUNCTION__" - failed executing "#a" at line %d with error %x", __LINE__, res); \
76 static bool LoadDXVAHD()
78 static CCriticalSection g_section;
79 static HMODULE g_handle;
81 CSingleLock lock(g_section);
84 g_handle = LoadLibraryEx("dxva2.dll", NULL, 0);
90 g_DXVAHDCreateVideoService = (DXVAHDCreateVideoServicePtr)GetProcAddress(g_handle, "DXVAHD_CreateDevice");
91 if(g_DXVAHDCreateVideoService == NULL)
98 static std::string GUIDToString(const GUID& guid)
100 std::string buffer = StringUtils::Format("%08X-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"
101 , guid.Data1, guid.Data2, guid.Data3
102 , guid.Data4[0], guid.Data4[1]
103 , guid.Data4[2], guid.Data4[3], guid.Data4[4]
104 , guid.Data4[5], guid.Data4[6], guid.Data4[7]);
108 CProcessorHD::CProcessorHD()
114 g_Windowing.Register(this);
120 CProcessorHD::~CProcessorHD()
122 g_Windowing.Unregister(this);
126 void CProcessorHD::UnInit()
128 CSingleLock lock(m_section);
130 SAFE_RELEASE(m_pDXVAHD);
133 void CProcessorHD::Close()
135 CSingleLock lock(m_section);
136 SAFE_RELEASE(m_pDXVAVP);
138 for(unsigned i = 0; i < m_frames.size(); i++)
140 SAFE_RELEASE(m_frames[i].context);
141 SAFE_RELEASE(m_frames[i].pSurface);
145 SAFE_RELEASE(m_context);
148 for (unsigned i = 0; i < m_size; i++)
150 SAFE_RELEASE(m_surfaces[i]);
157 bool CProcessorHD::UpdateSize(const DXVA2_VideoDesc& dsc)
162 bool CProcessorHD::PreInit()
166 CLog::Log(LOGWARNING, __FUNCTION__" - DXVAHD not loaded.");
172 CSingleLock lock(m_section);
174 DXVAHD_RATIONAL fps = { 60, 1 };
175 DXVAHD_CONTENT_DESC desc;
176 desc.InputFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
177 desc.InputFrameRate = fps;
178 desc.InputWidth = 640;
179 desc.InputHeight = 480;
180 desc.OutputFrameRate = fps;
181 desc.OutputWidth = 640;
182 desc.OutputHeight = 480;
184 HRESULT cvres = g_DXVAHDCreateVideoService( (IDirect3DDevice9Ex*)g_Windowing.Get3DDevice()
186 , DXVAHD_DEVICE_USAGE_OPTIMAL_QUALITY
192 if(cvres == E_NOINTERFACE)
193 CLog::Log(LOGNOTICE, __FUNCTION__" - The Direct3d device doesn't support DXVA-HD.");
195 CLog::Log(LOGERROR, __FUNCTION__" - failed to create DXVAHD device %x", cvres);
200 CHECK(m_pDXVAHD->GetVideoProcessorDeviceCaps( &m_VPDevCaps ));
202 if (m_VPDevCaps.VideoProcessorCount == 0)
204 CLog::Log(LOGWARNING, __FUNCTION__" - unable to find any video processor. GPU drivers doesn't support DXVA-HD.");
208 // Create the array of video processor caps.
209 DXVAHD_VPCAPS* pVPCaps = new (std::nothrow) DXVAHD_VPCAPS[ m_VPDevCaps.VideoProcessorCount ];
212 CLog::Log(LOGERROR, __FUNCTION__" - unable to create video processor caps array. Out of memory.");
216 HRESULT hr = m_pDXVAHD->GetVideoProcessorCaps( m_VPDevCaps.VideoProcessorCount, pVPCaps );
219 CLog::Log(LOGERROR, __FUNCTION__" - failed get processor caps with error %x.", hr);
228 for (unsigned int i = 0; i < m_VPDevCaps.VideoProcessorCount; i++)
230 if (pVPCaps[i].FutureFrames > m_max_fwd_refs)
232 m_max_fwd_refs = pVPCaps[i].FutureFrames;
235 if (pVPCaps[i].PastFrames > m_max_back_refs)
237 m_max_back_refs = pVPCaps[i].PastFrames;
241 m_size = m_max_back_refs + 1 + m_max_fwd_refs + 2; // refs + 1 display + 2 safety frames
243 // Get the image filtering capabilities.
244 for (long i = 0; i < NUM_FILTERS; i++)
246 if (m_VPDevCaps.FilterCaps & (1 << i))
248 m_pDXVAHD->GetVideoProcessorFilterRange(PROCAMP_FILTERS[i], &m_Filters[i].Range);
249 m_Filters[i].bSupported = true;
253 m_Filters[i].bSupported = false;
257 m_VPCaps = pVPCaps[0];
258 m_device = m_VPCaps.VPGuid;
265 bool CProcessorHD::Open(UINT width, UINT height, unsigned int flags, unsigned int format, unsigned int extended_format)
269 CSingleLock lock(m_section);
279 m_renderFormat = format;
281 if (g_advancedSettings.m_DXVANoDeintProcForProgressive)
283 CLog::Log(LOGNOTICE, __FUNCTION__" - Auto deinterlacing mode workaround activated. Deinterlacing processor will be used only for interlaced frames.");
286 if (format == RENDER_FMT_DXVA)
288 m_format = (D3DFORMAT)extended_format;
292 // Only NV12 software colorspace conversion is implemented for now
293 m_format = (D3DFORMAT)MAKEFOURCC('N','V','1','2');
294 if (!CreateSurfaces())
298 if (!OpenProcessor())
308 bool CProcessorHD::ReInit()
310 return PreInit() && (m_renderFormat == RENDER_FMT_DXVA || CreateSurfaces());
313 bool CProcessorHD::OpenProcessor()
315 // restore the device if it was lost
316 if (!m_pDXVAHD && !ReInit())
321 SAFE_RELEASE(m_pDXVAVP);
323 CLog::Log(LOGDEBUG, __FUNCTION__" - processor selected %s.", GUIDToString(m_device).c_str());
325 CHECK(m_pDXVAHD->CreateVideoProcessor(&m_device, &m_pDXVAVP));
327 DXVAHD_STREAM_STATE_D3DFORMAT_DATA d3dformat = { m_format };
328 LOGIFERROR(m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_D3DFORMAT
329 , sizeof(d3dformat), &d3dformat ));
331 DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE_DATA data =
333 0, // Type: 0=Video, 1=Graphics
334 0, // RGB_Range: 0=Full, 1=Limited
335 m_flags & CONF_FLAGS_YUVCOEF_BT709 ? 1 : 0, // YCbCr_Matrix: 0=BT.601, 1=BT.709
336 m_flags & CONF_FLAGS_YUV_FULLRANGE ? 1 : 0 // YCbCr_xvYCC: 0=Conventional YCbCr, 1=xvYCC
338 LOGIFERROR(m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE
339 , sizeof(data), &data ));
341 DXVAHD_COLOR_YCbCrA bgColor = { 0.0625f, 0.5f, 0.5f, 1.0f }; // black color
342 DXVAHD_COLOR backgroundColor;
343 backgroundColor.YCbCr = bgColor;
344 DXVAHD_BLT_STATE_BACKGROUND_COLOR_DATA backgroundData = { true, backgroundColor }; // {YCbCr, DXVAHD_COLOR}
345 LOGIFERROR(m_pDXVAVP->SetVideoProcessBltState( DXVAHD_BLT_STATE_BACKGROUND_COLOR
346 , sizeof (backgroundData), &backgroundData ));
348 DXVAHD_STREAM_STATE_ALPHA_DATA alpha = { true, 1.0f };
349 LOGIFERROR(m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_ALPHA
350 , sizeof(alpha), &alpha ));
355 bool CProcessorHD::CreateSurfaces()
357 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
358 m_surfaces = (LPDIRECT3DSURFACE9*)calloc(m_size, sizeof(LPDIRECT3DSURFACE9));
359 for (unsigned idx = 0; idx < m_size; idx++)
360 CHECK(pD3DDevice->CreateOffscreenPlainSurface(
361 (m_width + 15) & ~15,
362 (m_height + 15) & ~15,
364 m_VPDevCaps.InputPool,
368 m_context = new CSurfaceContext();
373 REFERENCE_TIME CProcessorHD::Add(DVDVideoPicture* picture)
375 CSingleLock lock(m_section);
377 IDirect3DSurface9* surface = NULL;
378 CSurfaceContext* context = NULL;
380 if (picture->iFlags & DVP_FLAG_DROPPED)
385 switch (picture->format)
387 case RENDER_FMT_DXVA:
389 surface = (IDirect3DSurface9*)picture->data[3];
390 context = picture->context;
394 case RENDER_FMT_YUV420P:
398 CLog::Log(LOGWARNING, __FUNCTION__" - not initialized.");
402 surface = m_surfaces[m_index];
403 m_index = (m_index + 1) % m_size;
407 D3DLOCKED_RECT rectangle;
408 if (FAILED(surface->LockRect(&rectangle, NULL, 0)))
413 // Convert to NV12 - Luma
414 // TODO: Optimize this later using shaders/swscale/etc.
415 uint8_t *s = picture->data[0];
416 uint8_t* bits = (uint8_t*)(rectangle.pBits);
417 for (unsigned y = 0; y < picture->iHeight; y++)
419 memcpy(bits, s, picture->iWidth);
420 s += picture->iLineSize[0];
421 bits += rectangle.Pitch;
424 D3DSURFACE_DESC desc;
425 if (FAILED(surface->GetDesc(&desc)))
430 // Convert to NV12 - Chroma
431 uint8_t *s_u, *s_v, *d_uv;
432 for (unsigned y = 0; y < picture->iHeight/2; y++)
434 s_u = picture->data[1] + (y * picture->iLineSize[1]);
435 s_v = picture->data[2] + (y * picture->iLineSize[2]);
436 d_uv = ((uint8_t*)(rectangle.pBits)) + (desc.Height + y) * rectangle.Pitch;
437 for (unsigned x = 0; x < picture->iWidth/2; x++)
444 if (FAILED(surface->UnlockRect()))
453 CLog::Log(LOGWARNING, __FUNCTION__" - colorspace not supported by processor, skipping frame.");
458 if (!surface || !context)
468 frame.index = m_frame;
469 frame.pSurface = surface;
470 frame.context = context;
471 frame.format = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
473 if (picture->iFlags & DVP_FLAG_INTERLACED)
475 frame.format = picture->iFlags & DVP_FLAG_TOP_FIELD_FIRST
476 ? DXVAHD_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST
477 : DXVAHD_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
480 m_frames.push_back(frame);
482 if (m_frames.size() > m_size)
484 SAFE_RELEASE(m_frames.front().context);
485 SAFE_RELEASE(m_frames.front().pSurface);
487 m_frames.pop_front();
493 bool CProcessorHD::ApplyFilter(DXVAHD_FILTER filter, int value, int min, int max, int def)
495 if (filter > NUM_FILTERS)
499 // Unsupported filter. Ignore.
500 if (!m_Filters[filter].bSupported)
505 DXVAHD_FILTER_RANGE_DATA range = m_Filters[filter].Range;
510 val = range.Default + (range.Maximum - range.Default) * (value - def) / (max - def);
514 val = range.Default + (range.Minimum - range.Default) * (value - def) / (min - def);
521 DXVAHD_STREAM_STATE_FILTER_DATA data = { true, val };
522 DXVAHD_STREAM_STATE state = static_cast<DXVAHD_STREAM_STATE>(DXVAHD_STREAM_STATE_FILTER_BRIGHTNESS + filter);
524 return !FAILED( m_pDXVAVP->SetVideoProcessStreamState( 0, state, sizeof(data), &data ) );
527 bool CProcessorHD::Render(CRect src, CRect dst, IDirect3DSurface9* target, REFERENCE_TIME frame, DWORD flags)
529 CSingleLock lock(m_section);
531 // restore processor if it was lost
532 if(!m_pDXVAVP && !OpenProcessor())
537 EDEINTERLACEMODE deinterlace_mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
538 if (g_advancedSettings.m_DXVANoDeintProcForProgressive)
539 deinterlace_mode = (flags & RENDER_FLAG_FIELD0 || flags & RENDER_FLAG_FIELD1) ? VS_DEINTERLACEMODE_FORCE : VS_DEINTERLACEMODE_OFF;
540 EINTERLACEMETHOD interlace_method = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
542 bool progressive = deinterlace_mode == VS_DEINTERLACEMODE_OFF
543 || ( interlace_method != VS_INTERLACEMETHOD_DXVA_BOB
544 && interlace_method != VS_INTERLACEMETHOD_DXVA_BEST);
546 // minFrame is the first samples to keep. Delete the rest.
547 REFERENCE_TIME minFrame = frame - m_max_back_refs * 2;
549 SFrames::iterator it = m_frames.begin();
550 while (it != m_frames.end())
552 if (it->index < minFrame)
554 SAFE_RELEASE(it->context);
555 SAFE_RELEASE(it->pSurface);
556 it = m_frames.erase(it);
567 D3DSURFACE_DESC desc;
568 CHECK(target->GetDesc(&desc));
569 CRect rectTarget(0, 0, desc.Width, desc.Height);
570 CWIN32Util::CropSource(src, dst, rectTarget);
571 RECT sourceRECT = { src.x1, src.y1, src.x2, src.y2 };
572 RECT dstRECT = { dst.x1, dst.y1, dst.x2, dst.y2 };
574 // MinTime and MaxTime are now the first and last samples to feed the processor.
575 minFrame = frame - m_VPCaps.PastFrames * 2;
576 REFERENCE_TIME maxFrame = frame + m_VPCaps.FutureFrames * 2;
579 DXVAHD_FRAME_FORMAT dxvaFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
581 DXVAHD_STREAM_DATA stream_data = { 0 };
582 stream_data.Enable = TRUE;
583 stream_data.PastFrames = 0;
584 stream_data.FutureFrames = 0;
585 stream_data.ppPastSurfaces = new IDirect3DSurface9*[m_VPCaps.PastFrames];
586 stream_data.ppFutureSurfaces = new IDirect3DSurface9*[m_VPCaps.FutureFrames];
588 for(it = m_frames.begin(); it != m_frames.end(); ++it)
590 if (it->index >= minFrame && it->index <= maxFrame)
592 if (it->index < frame)
594 // frames order should be { .., T-1, T-2, T-3 }
595 stream_data.ppPastSurfaces[(frame - it->index)/2 - 1] = it->pSurface;
596 stream_data.PastFrames++;
598 else if (it->index == frame)
600 stream_data.pInputSurface = it->pSurface;
601 dxvaFrameFormat = (DXVAHD_FRAME_FORMAT) it->format;
604 else if (it->index > frame)
606 // frames order should be { T+1, T+2, T+3, .. }
607 stream_data.ppFutureSurfaces[(it->index - frame)/2 - 1] = it->pSurface;
608 stream_data.FutureFrames++;
613 // no present frame, skip
616 CLog::Log(LOGWARNING, __FUNCTION__" - uncomplete stream data, skipping frame.");
620 // Override the sample format when the processor doesn't need to deinterlace or when deinterlacing is forced and flags are missing.
623 dxvaFrameFormat = DXVAHD_FRAME_FORMAT_PROGRESSIVE;
625 else if (deinterlace_mode == VS_DEINTERLACEMODE_FORCE
626 && dxvaFrameFormat == DXVAHD_FRAME_FORMAT_PROGRESSIVE)
628 dxvaFrameFormat = DXVAHD_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
631 bool frameProgressive = dxvaFrameFormat == DXVAHD_FRAME_FORMAT_PROGRESSIVE;
633 // Progressive or Interlaced video at normal rate.
634 stream_data.InputFrameOrField = frame + (flags & RENDER_FLAG_FIELD1 ? 1 : 0);
635 stream_data.OutputIndex = flags & RENDER_FLAG_FIELD1 && !frameProgressive ? 1 : 0;
637 DXVAHD_STREAM_STATE_FRAME_FORMAT_DATA frame_format = { dxvaFrameFormat };
638 LOGIFERROR( m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_FRAME_FORMAT
639 , sizeof(frame_format), &frame_format ) );
641 DXVAHD_STREAM_STATE_DESTINATION_RECT_DATA dstRect = { true, dstRECT };
642 LOGIFERROR( m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_DESTINATION_RECT
643 , sizeof(dstRect), &dstRect));
645 DXVAHD_STREAM_STATE_SOURCE_RECT_DATA srcRect = { true, sourceRECT };
646 LOGIFERROR( m_pDXVAVP->SetVideoProcessStreamState( 0, DXVAHD_STREAM_STATE_SOURCE_RECT
647 , sizeof(srcRect), &srcRect));
649 ApplyFilter( DXVAHD_FILTER_BRIGHTNESS, CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness
651 ApplyFilter( DXVAHD_FILTER_CONTRAST, CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast
654 unsigned int uiRange = g_Windowing.UseLimitedColor() ? 1 : 0;
655 DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE_DATA colorData =
657 0, // 0 = playback, 1 = video processing
658 uiRange, // 0 = 0-255, 1 = 16-235
659 1, // 0 = BT.601, 1 = BT.709
660 1 // 0 = Conventional YCbCr, 1 = xvYCC
663 LOGIFERROR( m_pDXVAVP->SetVideoProcessBltState( DXVAHD_BLT_STATE_OUTPUT_COLOR_SPACE
664 , sizeof(colorData), &colorData ));
666 DXVAHD_BLT_STATE_TARGET_RECT_DATA targetRect = { true, dstRECT };
667 LOGIFERROR( m_pDXVAVP->SetVideoProcessBltState( DXVAHD_BLT_STATE_TARGET_RECT
668 , sizeof(targetRect), &targetRect ) );
670 HRESULT hr = m_pDXVAVP->VideoProcessBltHD(target, frame, 1, &stream_data);
673 CLog::Log(LOGERROR, __FUNCTION__" - failed executing VideoProcessBltHD with error %x", hr);
676 delete [] stream_data.ppPastSurfaces;
677 delete [] stream_data.ppFutureSurfaces;