/* This file is part of the KDE project. Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 or 3 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "videorenderer_evr.h" #include "qevr9.h" #ifndef QT_NO_PHONON_VIDEO #include #include QT_BEGIN_NAMESPACE namespace Phonon { namespace DS9 { //we have to define them here because not all compilers/sdk have them static const GUID MR_VIDEO_RENDER_SERVICE = {0x1092a86c, 0xab1a, 0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} }; static const GUID MR_VIDEO_MIXER_SERVICE = { 0x73cd2fc, 0x6cf4, 0x40b7, {0x88, 0x59, 0xe8, 0x95, 0x52, 0xc8, 0x41, 0xf8} }; static const IID IID_IMFVideoDisplayControl = {0xa490b1e4, 0xab84, 0x4d31, {0xa1, 0xb2, 0x18, 0x1e, 0x03, 0xb1, 0x07, 0x7a} }; static const IID IID_IMFVideoMixerControl = {0xA5C6C53F, 0xC202, 0x4aa5, {0x96, 0x95, 0x17, 0x5B, 0xA8, 0xC5, 0x08, 0xA5} }; static const IID IID_IMFVideoProcessor = {0x6AB0000C, 0xFECE, 0x4d1f, {0xA2, 0xAC, 0xA9, 0x57, 0x35, 0x30, 0x65, 0x6E} }; static const IID IID_IMFGetService = {0xFA993888, 0x4383, 0x415A, {0xA9, 0x30, 0xDD, 0x47, 0x2A, 0x8C, 0xF6, 0xF7} }; static const GUID CLSID_EnhancedVideoRenderer = {0xfa10746c, 0x9b63, 0x4b6c, {0xbc, 0x49, 0xfc, 0x30, 0xe, 0xa5, 0xf2, 0x56} }; template ComPointer getService(const Filter &filter, REFGUID guidService, REFIID riid) { //normally we should use IID_IMFGetService but this introduces another dependency //so here we simply define our own IId with the same value ComPointer ret; ComPointer getService(filter, IID_IMFGetService); if (getService) { getService->GetService(guidService, riid, reinterpret_cast(ret.pparam())); } return ret; } VideoRendererEVR::~VideoRendererEVR() { } bool VideoRendererEVR::isNative() const { return true; } VideoRendererEVR::VideoRendererEVR(QWidget *target) : m_target(target) { if (QSysInfo::WindowsVersion < QSysInfo::WV_VISTA) return; m_filter = Filter(CLSID_EnhancedVideoRenderer, IID_IBaseFilter); if (!m_filter) { return; } ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); if (!filterControl || FAILED(filterControl->SetVideoWindow(reinterpret_cast(target->winId()))) || FAILED(filterControl->SetAspectRatioMode(MFVideoARMode_None)) || // We're in control of the size !getService(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoMixerControl) || !getService(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoProcessor)) { m_filter = Filter(); //will release the interface } } QImage VideoRendererEVR::snapshot() const { // This will always capture black areas where no video is drawn, if any are present. // Due to the hack in notifyResize() ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); if (filterControl) { BITMAPINFOHEADER bmi; BYTE *buffer = 0; DWORD bufferSize; LONGLONG timeStamp; bmi.biSize = sizeof(BITMAPINFOHEADER); HRESULT hr = filterControl->GetCurrentImage(&bmi, &buffer, &bufferSize, &timeStamp); if (SUCCEEDED(hr)) { const int w = qAbs(bmi.biWidth), h = qAbs(bmi.biHeight); // Create image and copy data into image. QImage ret(w, h, QImage::Format_RGB32); if (!ret.isNull()) { uchar *data = buffer; const int bytes_per_line = w * sizeof(QRgb); for (int y = h - 1; y >= 0; --y) { qMemCopy(ret.scanLine(y), //destination data, //source bytes_per_line); data += bytes_per_line; } } ::CoTaskMemFree(buffer); return ret; } } return QImage(); } QSize VideoRendererEVR::videoSize() const { SIZE nativeSize; SIZE aspectRatioSize; ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); filterControl->GetNativeVideoSize(&nativeSize, &aspectRatioSize); return QSize(nativeSize.cx, nativeSize.cy); } void VideoRendererEVR::repaintCurrentFrame(QWidget *target, const QRect &rect) { // repaint the video ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); // All failed results can be safely ignored filterControl->RepaintVideo(); } void VideoRendererEVR::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode) { if (!isActive()) { RECT dummyRect = { 0, 0, 0, 0}; ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); filterControl->SetVideoPosition(0, &dummyRect); return; } const QSize vsize = videoSize(); internalNotifyResize(size, vsize, aspectRatio, scaleMode); RECT dstRectWin = { 0, 0, size.width(), size.height()}; // Resize the Stream output rect instead of the destination rect. // Hacky workaround for flicker in the areas outside of the destination rect // This way these areas don't exist MFVideoNormalizedRect streamOutputRect = { float(m_dstX) / float(size.width()), float(m_dstY) / float(size.height()), float(m_dstWidth + m_dstX) / float(size.width()), float(m_dstHeight + m_dstY) / float(size.height())}; ComPointer filterMixer = getService(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoMixerControl); ComPointer filterControl = getService(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); filterMixer->SetStreamOutputRect(0, &streamOutputRect); filterControl->SetVideoPosition(0, &dstRectWin); } void VideoRendererEVR::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation) { InputPin sink = BackendNode::pins(m_filter, PINDIR_INPUT).first(); OutputPin source; if (FAILED(sink->ConnectedTo(source.pparam()))) { return; //it must be connected to work } // Get the "Video Processor" (used for brightness/contrast/saturation/hue) ComPointer processor = getService(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoProcessor); Q_ASSERT(processor); DXVA2_ValueRange contrastRange; DXVA2_ValueRange brightnessRange; DXVA2_ValueRange saturationRange; DXVA2_ValueRange hueRange; if (FAILED(processor->GetProcAmpRange(DXVA2_ProcAmp_Contrast, &contrastRange))) return; if (FAILED(processor->GetProcAmpRange(DXVA2_ProcAmp_Brightness, &brightnessRange))) return; if (FAILED(processor->GetProcAmpRange(DXVA2_ProcAmp_Saturation, &saturationRange))) return; if (FAILED(processor->GetProcAmpRange(DXVA2_ProcAmp_Hue, &hueRange))) return; DXVA2_ProcAmpValues values; values.Contrast = DXVA2FloatToFixed(((contrast < 0 ? DXVA2FixedToFloat(contrastRange.MinValue) : DXVA2FixedToFloat(contrastRange.MaxValue)) - DXVA2FixedToFloat(contrastRange.DefaultValue)) * qAbs(contrast) + DXVA2FixedToFloat(contrastRange.DefaultValue)); values.Brightness = DXVA2FloatToFixed(((brightness < 0 ? DXVA2FixedToFloat(brightnessRange.MinValue) : DXVA2FixedToFloat(brightnessRange.MaxValue)) - DXVA2FixedToFloat(brightnessRange.DefaultValue)) * qAbs(brightness) + DXVA2FixedToFloat(brightnessRange.DefaultValue)); values.Saturation = DXVA2FloatToFixed(((saturation < 0 ? DXVA2FixedToFloat(saturationRange.MinValue) : DXVA2FixedToFloat(saturationRange.MaxValue)) - DXVA2FixedToFloat(saturationRange.DefaultValue)) * qAbs(saturation) + DXVA2FixedToFloat(saturationRange.DefaultValue)); values.Hue = DXVA2FloatToFixed(((hue < 0 ? DXVA2FixedToFloat(hueRange.MinValue) : DXVA2FixedToFloat(hueRange.MaxValue)) - DXVA2FixedToFloat(hueRange.DefaultValue)) * qAbs(hue) + DXVA2FixedToFloat(hueRange.DefaultValue)); //finally set the settings processor->SetProcAmpValues(DXVA2_ProcAmp_Contrast | DXVA2_ProcAmp_Brightness | DXVA2_ProcAmp_Saturation | DXVA2_ProcAmp_Hue, &values); } } } QT_END_NAMESPACE #endif //QT_NO_PHONON_VIDEO