|
1 /* This file is part of the KDE project. |
|
2 |
|
3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 |
|
5 This library is free software: you can redistribute it and/or modify |
|
6 it under the terms of the GNU Lesser General Public License as published by |
|
7 the Free Software Foundation, either version 2.1 or 3 of the License. |
|
8 |
|
9 This library is distributed in the hope that it will be useful, |
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 GNU Lesser General Public License for more details. |
|
13 |
|
14 You should have received a copy of the GNU Lesser General Public License |
|
15 along with this library. If not, see <http://www.gnu.org/licenses/>. |
|
16 */ |
|
17 |
|
18 |
|
19 #include "videorenderer_vmr9.h" |
|
20 |
|
21 #ifndef QT_NO_PHONON_VIDEO |
|
22 |
|
23 #include <QtGui/QWidget> |
|
24 #include <QtGui/QPainter> |
|
25 #include <QtCore/QTimerEvent> |
|
26 |
|
27 #ifndef Q_OS_WINCE |
|
28 #include <d3d9.h> |
|
29 #include <vmr9.h> |
|
30 #else |
|
31 #include <uuids.h> |
|
32 #endif |
|
33 |
|
34 QT_BEGIN_NAMESPACE |
|
35 |
|
36 |
|
37 namespace Phonon |
|
38 { |
|
39 namespace DS9 |
|
40 { |
|
41 VideoRendererVMR9::~VideoRendererVMR9() |
|
42 { |
|
43 } |
|
44 |
|
45 bool VideoRendererVMR9::isNative() const |
|
46 { |
|
47 return true; |
|
48 } |
|
49 |
|
50 |
|
51 #ifdef Q_OS_WINCE |
|
52 VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target) |
|
53 { |
|
54 m_target->setAttribute(Qt::WA_PaintOnScreen, true); |
|
55 m_filter = Filter(CLSID_VideoRenderer, IID_IBaseFilter); |
|
56 } |
|
57 |
|
58 QSize VideoRendererVMR9::videoSize() const |
|
59 { |
|
60 LONG w = 0, |
|
61 h = 0; |
|
62 ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); |
|
63 if (basic) { |
|
64 basic->GetVideoSize( &w, &h); |
|
65 } |
|
66 return QSize(w, h); |
|
67 } |
|
68 |
|
69 void VideoRendererVMR9::repaintCurrentFrame(QWidget * /*target*/, const QRect & /*rect*/) |
|
70 { |
|
71 //nothing to do here: the renderer paints everything |
|
72 } |
|
73 |
|
74 void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio, |
|
75 Phonon::VideoWidget::ScaleMode scaleMode) |
|
76 { |
|
77 if (!isActive()) { |
|
78 ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); |
|
79 if (basic) { |
|
80 basic->SetDestinationPosition(0, 0, 0, 0); |
|
81 } |
|
82 return; |
|
83 } |
|
84 |
|
85 ComPointer<IVideoWindow> video(m_filter, IID_IVideoWindow); |
|
86 |
|
87 OAHWND owner; |
|
88 HRESULT hr = video->get_Owner(&owner); |
|
89 if (FAILED(hr)) { |
|
90 return; |
|
91 } |
|
92 |
|
93 const OAHWND newOwner = reinterpret_cast<OAHWND>(m_target->winId()); |
|
94 if (owner != newOwner) { |
|
95 video->put_Owner(newOwner); |
|
96 video->put_MessageDrain(newOwner); |
|
97 video->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); |
|
98 } |
|
99 |
|
100 //make sure the widget takes the whole size of the parent |
|
101 video->SetWindowPosition(0, 0, size.width(), size.height()); |
|
102 |
|
103 const QSize vsize = videoSize(); |
|
104 internalNotifyResize(size, vsize, aspectRatio, scaleMode); |
|
105 |
|
106 ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); |
|
107 if (basic) { |
|
108 basic->SetDestinationPosition(m_dstX, m_dstY, m_dstWidth, m_dstHeight); |
|
109 } |
|
110 } |
|
111 |
|
112 void VideoRendererVMR9::applyMixerSettings(qreal /*brightness*/, qreal /*contrast*/, qreal /*m_hue*/, qreal /*saturation*/) |
|
113 { |
|
114 //this can't be supported for WinCE |
|
115 } |
|
116 |
|
117 QImage VideoRendererVMR9::snapshot() const |
|
118 { |
|
119 ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); |
|
120 if (basic) { |
|
121 LONG bufferSize = 0; |
|
122 //1st we get the buffer size |
|
123 basic->GetCurrentImage(&bufferSize, 0); |
|
124 |
|
125 QByteArray buffer; |
|
126 buffer.resize(bufferSize); |
|
127 HRESULT hr = basic->GetCurrentImage(&bufferSize, reinterpret_cast<long*>(buffer.data())); |
|
128 |
|
129 if (SUCCEEDED(hr)) { |
|
130 |
|
131 const BITMAPINFOHEADER *bmi = reinterpret_cast<const BITMAPINFOHEADER*>(buffer.constData()); |
|
132 |
|
133 const int w = qAbs(bmi->biWidth), |
|
134 h = qAbs(bmi->biHeight); |
|
135 |
|
136 // Create image and copy data into image. |
|
137 QImage ret(w, h, QImage::Format_RGB32); |
|
138 |
|
139 if (!ret.isNull()) { |
|
140 const char *data = buffer.constData() + bmi->biSize; |
|
141 const int bytes_per_line = w * sizeof(QRgb); |
|
142 for (int y = h - 1; y >= 0; --y) { |
|
143 qMemCopy(ret.scanLine(y), //destination |
|
144 data, //source |
|
145 bytes_per_line); |
|
146 data += bytes_per_line; |
|
147 } |
|
148 } |
|
149 return ret; |
|
150 } |
|
151 } |
|
152 return QImage(); |
|
153 } |
|
154 |
|
155 #else |
|
156 VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target) |
|
157 { |
|
158 m_filter = Filter(CLSID_VideoMixingRenderer9, IID_IBaseFilter); |
|
159 if (!m_filter) { |
|
160 qWarning("the video widget could not be initialized correctly"); |
|
161 return; |
|
162 } |
|
163 |
|
164 ComPointer<IVMRFilterConfig9> config(m_filter, IID_IVMRFilterConfig9); |
|
165 Q_ASSERT(config); |
|
166 HRESULT hr = config->SetRenderingMode(VMR9Mode_Windowless); |
|
167 Q_ASSERT(SUCCEEDED(hr)); |
|
168 hr = config->SetNumberOfStreams(1); //for now we limit it to 1 input stream |
|
169 Q_ASSERT(SUCCEEDED(hr)); |
|
170 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
171 windowlessControl->SetVideoClippingWindow(reinterpret_cast<HWND>(target->winId())); |
|
172 windowlessControl->SetAspectRatioMode(VMR9ARMode_None); //we're in control of the size |
|
173 } |
|
174 |
|
175 QImage VideoRendererVMR9::snapshot() const |
|
176 { |
|
177 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
178 if (windowlessControl) { |
|
179 BYTE *buffer = 0; |
|
180 HRESULT hr = windowlessControl->GetCurrentImage(&buffer); |
|
181 if (SUCCEEDED(hr)) { |
|
182 |
|
183 const BITMAPINFOHEADER *bmi = reinterpret_cast<BITMAPINFOHEADER*>(buffer); |
|
184 const int w = qAbs(bmi->biWidth), |
|
185 h = qAbs(bmi->biHeight); |
|
186 |
|
187 // Create image and copy data into image. |
|
188 QImage ret(w, h, QImage::Format_RGB32); |
|
189 |
|
190 if (!ret.isNull()) { |
|
191 uchar *data = buffer + bmi->biSize; |
|
192 const int bytes_per_line = w * sizeof(QRgb); |
|
193 for (int y = h - 1; y >= 0; --y) { |
|
194 qMemCopy(ret.scanLine(y), //destination |
|
195 data, //source |
|
196 bytes_per_line); |
|
197 data += bytes_per_line; |
|
198 } |
|
199 } |
|
200 ::CoTaskMemFree(buffer); |
|
201 return ret; |
|
202 } |
|
203 } |
|
204 return QImage(); |
|
205 } |
|
206 |
|
207 QSize VideoRendererVMR9::videoSize() const |
|
208 { |
|
209 LONG w = 0, |
|
210 h = 0; |
|
211 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
212 if (windowlessControl) { |
|
213 windowlessControl->GetNativeVideoSize( &w, &h, 0, 0); |
|
214 } |
|
215 return QSize(w, h); |
|
216 } |
|
217 |
|
218 void VideoRendererVMR9::repaintCurrentFrame(QWidget *target, const QRect &rect) |
|
219 { |
|
220 HDC hDC = target->getDC(); |
|
221 // repaint the video |
|
222 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
223 |
|
224 HRESULT hr = windowlessControl ? windowlessControl->RepaintVideo(target->winId(), hDC) : E_POINTER; |
|
225 if (FAILED(hr) || m_dstY > 0 || m_dstX > 0) { |
|
226 const QColor c = target->palette().color(target->backgroundRole()); |
|
227 COLORREF color = RGB(c.red(), c.green(), c.blue()); |
|
228 HPEN hPen = ::CreatePen(PS_SOLID, 1, color); |
|
229 HBRUSH hBrush = ::CreateSolidBrush(color); |
|
230 ::SelectObject(hDC, hPen); |
|
231 ::SelectObject(hDC, hBrush); |
|
232 // repaint the video |
|
233 if (FAILED(hr)) { |
|
234 //black background : we use the Win32 API to avoid the ghost effect of the backing store |
|
235 ::Rectangle(hDC, 0, 0, target->width(), target->height()); |
|
236 } else { |
|
237 if (m_dstY > 0) { |
|
238 ::Rectangle(hDC, 0, 0, target->width(), m_dstY); //top |
|
239 ::Rectangle(hDC, 0, target->height() - m_dstY, target->width(), target->height()); //bottom |
|
240 } |
|
241 if (m_dstX > 0) { |
|
242 ::Rectangle(hDC, 0, m_dstY, m_dstX, m_dstHeight + m_dstY); //left |
|
243 ::Rectangle(hDC, m_dstWidth + m_dstX, m_dstY, target->width(), m_dstHeight + m_dstY); //right |
|
244 } |
|
245 } |
|
246 ::DeleteObject(hPen); |
|
247 ::DeleteObject(hBrush); |
|
248 } |
|
249 target->releaseDC(hDC); |
|
250 |
|
251 } |
|
252 |
|
253 void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio, |
|
254 Phonon::VideoWidget::ScaleMode scaleMode) |
|
255 { |
|
256 if (!isActive()) { |
|
257 RECT dummyRect = { 0, 0, 0, 0}; |
|
258 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
259 windowlessControl->SetVideoPosition(&dummyRect, &dummyRect); |
|
260 return; |
|
261 } |
|
262 |
|
263 |
|
264 const QSize vsize = videoSize(); |
|
265 internalNotifyResize(size, vsize, aspectRatio, scaleMode); |
|
266 |
|
267 RECT dstRectWin = { m_dstX, m_dstY, m_dstWidth + m_dstX, m_dstHeight + m_dstY}; |
|
268 RECT srcRectWin = { 0, 0, vsize.width(), vsize.height()}; |
|
269 |
|
270 ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); |
|
271 if (windowlessControl) { |
|
272 windowlessControl->SetVideoPosition(&srcRectWin, &dstRectWin); |
|
273 } |
|
274 } |
|
275 |
|
276 void VideoRendererVMR9::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation) |
|
277 { |
|
278 InputPin sink = BackendNode::pins(m_filter, PINDIR_INPUT).first(); |
|
279 OutputPin source; |
|
280 if (FAILED(sink->ConnectedTo(source.pparam()))) { |
|
281 return; //it must be connected to work |
|
282 } |
|
283 |
|
284 //get the mixer (used for brightness/contrast/saturation/hue) |
|
285 ComPointer<IVMRMixerControl9> mixer(m_filter, IID_IVMRMixerControl9); |
|
286 Q_ASSERT(mixer); |
|
287 |
|
288 VMR9ProcAmpControl ctrl; |
|
289 ctrl.dwSize = sizeof(ctrl); |
|
290 ctrl.dwFlags = ProcAmpControl9_Contrast | ProcAmpControl9_Brightness | ProcAmpControl9_Saturation | ProcAmpControl9_Hue; |
|
291 VMR9ProcAmpControlRange range; |
|
292 range.dwSize = sizeof(range); |
|
293 |
|
294 range.dwProperty = ProcAmpControl9_Contrast; |
|
295 HRESULT hr = mixer->GetProcAmpControlRange(0, &range); |
|
296 if (FAILED(hr)) { |
|
297 return; |
|
298 } |
|
299 ctrl.Contrast = ((contrast < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(contrast) + range.DefaultValue; |
|
300 |
|
301 //brightness |
|
302 range.dwProperty = ProcAmpControl9_Brightness; |
|
303 hr = mixer->GetProcAmpControlRange(0, &range); |
|
304 if (FAILED(hr)) { |
|
305 return; |
|
306 } |
|
307 ctrl.Brightness = ((brightness < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(brightness) + range.DefaultValue; |
|
308 |
|
309 //saturation |
|
310 range.dwProperty = ProcAmpControl9_Saturation; |
|
311 hr = mixer->GetProcAmpControlRange(0, &range); |
|
312 if (FAILED(hr)) { |
|
313 return; |
|
314 } |
|
315 ctrl.Saturation = ((saturation < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(saturation) + range.DefaultValue; |
|
316 |
|
317 //hue |
|
318 range.dwProperty = ProcAmpControl9_Hue; |
|
319 hr = mixer->GetProcAmpControlRange(0, &range); |
|
320 if (FAILED(hr)) { |
|
321 return; |
|
322 } |
|
323 ctrl.Hue = ((hue < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(hue) + range.DefaultValue; |
|
324 |
|
325 //finally set the settings |
|
326 mixer->SetProcAmpControl(0, &ctrl); |
|
327 } |
|
328 #endif |
|
329 } |
|
330 } |
|
331 |
|
332 QT_END_NAMESPACE |
|
333 |
|
334 #endif //QT_NO_PHONON_VIDEO |