|
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 #include "videoframe.h" |
|
19 #include "quicktimevideoplayer.h" |
|
20 #import <QuartzCore/CIFilter.h> |
|
21 #import <QuartzCore/CIContext.h> |
|
22 |
|
23 //#define CACHE_CV_TEXTURE |
|
24 |
|
25 QT_BEGIN_NAMESPACE |
|
26 |
|
27 namespace Phonon |
|
28 { |
|
29 namespace QT7 |
|
30 { |
|
31 |
|
32 VideoFrame::VideoFrame() |
|
33 { |
|
34 initMembers(); |
|
35 } |
|
36 |
|
37 VideoFrame::VideoFrame(QuickTimeVideoPlayer *videoPlayer) |
|
38 { |
|
39 initMembers(); |
|
40 m_videoPlayer = videoPlayer; |
|
41 } |
|
42 |
|
43 VideoFrame::VideoFrame(const VideoFrame& frame) |
|
44 { |
|
45 copyMembers(frame); |
|
46 retain(); |
|
47 } |
|
48 |
|
49 void VideoFrame::operator=(const VideoFrame& frame) |
|
50 { |
|
51 if (this == &frame) |
|
52 return; |
|
53 |
|
54 release(); |
|
55 copyMembers(frame); |
|
56 retain(); |
|
57 } |
|
58 |
|
59 void VideoFrame::initMembers() |
|
60 { |
|
61 m_cachedCVTextureRef = 0; |
|
62 m_cachedCIImage = 0; |
|
63 m_cachedNSBitmap = 0; |
|
64 m_videoPlayer = 0; |
|
65 m_brightness = 0; |
|
66 m_contrast = 0; |
|
67 m_hue = 0; |
|
68 m_saturation = 0; |
|
69 m_opacity = 1; |
|
70 m_backgroundFrame = 0; |
|
71 } |
|
72 |
|
73 void VideoFrame::copyMembers(const VideoFrame& frame) |
|
74 { |
|
75 #ifdef CACHE_CV_TEXTURE |
|
76 m_cachedCVTextureRef = frame.m_cachedCVTextureRef; |
|
77 #endif |
|
78 m_cachedCIImage = frame.m_cachedCIImage; |
|
79 m_cachedQImage = frame.m_cachedQImage; |
|
80 m_cachedNSBitmap = frame.m_cachedNSBitmap; |
|
81 m_videoPlayer = frame.m_videoPlayer; |
|
82 m_brightness = frame.m_brightness; |
|
83 m_contrast = frame.m_contrast; |
|
84 m_hue = frame.m_hue; |
|
85 m_saturation = frame.m_saturation; |
|
86 m_opacity = frame.m_opacity; |
|
87 m_backgroundFrame = frame.m_backgroundFrame; |
|
88 } |
|
89 |
|
90 VideoFrame::~VideoFrame() |
|
91 { |
|
92 release(); |
|
93 } |
|
94 |
|
95 QuickTimeVideoPlayer *VideoFrame::videoPlayer() |
|
96 { |
|
97 return m_videoPlayer; |
|
98 } |
|
99 |
|
100 void VideoFrame::setBackgroundFrame(const VideoFrame &frame) |
|
101 { |
|
102 m_backgroundFrame = new VideoFrame(frame); |
|
103 } |
|
104 |
|
105 QRect VideoFrame::frameRect() const |
|
106 { |
|
107 return m_videoPlayer->videoRect(); |
|
108 } |
|
109 |
|
110 CVOpenGLTextureRef VideoFrame::cachedCVTexture() const |
|
111 { |
|
112 #ifdef CACHE_CV_TEXTURE |
|
113 if (!m_cachedCVTextureRef && m_videoPlayer){ |
|
114 m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); |
|
115 (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = m_videoPlayer->currentFrameAsCVTexture(); |
|
116 CVOpenGLTextureRetain((const_cast<VideoFrame *>(this))->m_cachedCVTextureRef); |
|
117 } |
|
118 return m_cachedCVTextureRef; |
|
119 #else |
|
120 if (m_videoPlayer){ |
|
121 m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); |
|
122 return m_videoPlayer->currentFrameAsCVTexture(); |
|
123 } |
|
124 return 0; |
|
125 #endif |
|
126 } |
|
127 |
|
128 void *VideoFrame::cachedCIImage() const |
|
129 { |
|
130 if (!m_cachedCIImage && m_videoPlayer){ |
|
131 m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); |
|
132 (const_cast<VideoFrame *>(this))->m_cachedCIImage = m_videoPlayer->currentFrameAsCIImage(); |
|
133 } |
|
134 return m_cachedCIImage; |
|
135 } |
|
136 |
|
137 GLuint VideoFrame::glTextureRef() const |
|
138 { |
|
139 return CVOpenGLTextureGetName(cachedCVTexture()); |
|
140 } |
|
141 |
|
142 void VideoFrame::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) |
|
143 { |
|
144 if (m_backgroundFrame) |
|
145 m_backgroundFrame->setColors(brightness, contrast, hue, saturation); |
|
146 if (m_brightness == brightness |
|
147 && m_contrast == contrast |
|
148 && m_hue == hue |
|
149 && m_saturation == saturation) |
|
150 return; |
|
151 |
|
152 m_brightness = brightness; |
|
153 m_contrast = contrast; |
|
154 m_hue = hue; |
|
155 m_saturation = saturation; |
|
156 |
|
157 invalidateImage(); |
|
158 } |
|
159 |
|
160 CGRect VideoFrame::QRectToCGRect(const QRect & qrect) |
|
161 { |
|
162 CGRect cgrect; |
|
163 cgrect.origin.x = qrect.x(); |
|
164 cgrect.origin.y = qrect.y() + qrect.height(); |
|
165 cgrect.size.width = qrect.width(); |
|
166 cgrect.size.height = -qrect.height(); |
|
167 return cgrect; |
|
168 } |
|
169 |
|
170 bool VideoFrame::hasColorAdjustments() |
|
171 { |
|
172 return (m_brightness || m_contrast || m_saturation || m_hue); |
|
173 } |
|
174 |
|
175 void VideoFrame::setBaseOpacity(qreal opacity) |
|
176 { |
|
177 m_opacity = opacity; |
|
178 } |
|
179 |
|
180 void VideoFrame::drawQImage(QPainter *p, const QRect &rect) const |
|
181 { |
|
182 if (!m_videoPlayer) |
|
183 return; |
|
184 #ifdef QUICKTIME_C_API_AVAILABLE |
|
185 if (m_cachedQImage.isNull()){ |
|
186 m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); |
|
187 (const_cast<VideoFrame *>(this))->m_cachedQImage = m_videoPlayer->currentFrameAsQImage(); |
|
188 } |
|
189 #else |
|
190 // Since cocoa-64 doesn't give us OpenGL textures directly, the process of converting |
|
191 // CIImges into QImages takes time. We could still call m_videoPlayer->currentFrameAsQImage(), |
|
192 // but because of bitmap memory management issues, and the fact that we need to swap red and |
|
193 // blue, we can optimize the process a bit here since we are going to draw immidiatly: |
|
194 CIImage *img = (CIImage*)cachedCIImage(); |
|
195 if (!img) |
|
196 return; |
|
197 |
|
198 if (!m_cachedNSBitmap){ |
|
199 (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = |
|
200 [[NSBitmapImageRep alloc] initWithCIImage:img]; |
|
201 CGRect bounds = [img extent]; |
|
202 int w = bounds.size.width; |
|
203 int h = bounds.size.height; |
|
204 (const_cast<VideoFrame *>(this))->m_cachedQImage = |
|
205 QImage([m_cachedNSBitmap bitmapData], w, h, QImage::Format_ARGB32); |
|
206 // Swap red and blue (same as QImage::rgbSwapped, but without copy) |
|
207 for (int i=0; i<h; ++i) { |
|
208 uint *p = (uint*) m_cachedQImage.scanLine(i); |
|
209 uint *end = p + w; |
|
210 while (p < end) { |
|
211 *p = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); |
|
212 p++; |
|
213 } |
|
214 } |
|
215 } |
|
216 #endif |
|
217 p->drawImage(rect, m_cachedQImage); |
|
218 } |
|
219 |
|
220 void VideoFrame::drawCIImage(const QRect &rect, float opacity) const |
|
221 { |
|
222 drawCIImage(QRectToCGRect(rect), opacity); |
|
223 } |
|
224 |
|
225 void VideoFrame::drawCIImage(const CGRect &rect, float opacity) const |
|
226 { |
|
227 Q_UNUSED(opacity); |
|
228 CIImage *img = (CIImage *) cachedCIImage(); |
|
229 if (!img) |
|
230 return; |
|
231 |
|
232 CIContext* ciContext = [[NSGraphicsContext currentContext] CIContext]; |
|
233 [ciContext drawImage:img inRect:rect fromRect:[img extent]]; |
|
234 } |
|
235 |
|
236 void VideoFrame::drawCVTexture(const QRect &rect, float opacity) const |
|
237 { |
|
238 if (!m_videoPlayer) |
|
239 return; |
|
240 if (m_backgroundFrame) |
|
241 m_backgroundFrame->drawCVTexture(rect, opacity); |
|
242 |
|
243 CVOpenGLTextureRef texRef = cachedCVTexture(); |
|
244 if (!texRef) |
|
245 return; |
|
246 |
|
247 glPushMatrix(); |
|
248 glDisable(GL_CULL_FACE); |
|
249 GLenum target = CVOpenGLTextureGetTarget(texRef); |
|
250 glEnable(target); |
|
251 |
|
252 opacity *= m_opacity; |
|
253 if (opacity < 1){ |
|
254 glEnable(GL_BLEND); |
|
255 glColor4f(1, 1, 1, opacity); |
|
256 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
257 } else { |
|
258 glColor3f(1, 1, 1); |
|
259 glDisable(GL_BLEND); |
|
260 } |
|
261 |
|
262 glBindTexture(target, CVOpenGLTextureGetName(texRef)); |
|
263 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
|
264 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
265 GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; |
|
266 CVOpenGLTextureGetCleanTexCoords(texRef, lowerLeft, lowerRight, upperRight, upperLeft); |
|
267 |
|
268 glBegin(GL_QUADS); |
|
269 glTexCoord2f(lowerLeft[0], lowerLeft[1]); |
|
270 glVertex2i(rect.topLeft().x(), rect.topLeft().y()); |
|
271 glTexCoord2f(lowerRight[0], lowerRight[1]); |
|
272 glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); |
|
273 glTexCoord2f(upperRight[0], upperRight[1]); |
|
274 glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); |
|
275 glTexCoord2f(upperLeft[0], upperLeft[1]); |
|
276 glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); |
|
277 glEnd(); |
|
278 glPopMatrix(); |
|
279 } |
|
280 |
|
281 void VideoFrame::drawGLTexture(const QRect &rect, float opacity) const |
|
282 { |
|
283 if (!m_videoPlayer) |
|
284 return; |
|
285 if (m_backgroundFrame) |
|
286 m_backgroundFrame->drawGLTexture(rect, opacity); |
|
287 |
|
288 GLuint texture = m_videoPlayer->currentFrameAsGLTexture(); |
|
289 if (!texture) |
|
290 return; |
|
291 |
|
292 glPushMatrix(); |
|
293 glDisable(GL_CULL_FACE); |
|
294 glEnable(GL_TEXTURE_RECTANGLE_EXT); |
|
295 |
|
296 opacity *= m_opacity; |
|
297 if (opacity < 1){ |
|
298 glEnable(GL_BLEND); |
|
299 glColor4f(1, 1, 1, opacity); |
|
300 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
301 } else { |
|
302 glColor3f(1, 1, 1); |
|
303 glDisable(GL_BLEND); |
|
304 } |
|
305 |
|
306 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture); |
|
307 glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
|
308 glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
309 |
|
310 QRect videoRect = m_videoPlayer->videoRect(); |
|
311 GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; |
|
312 lowerLeft[0] = 0; |
|
313 lowerLeft[1] = videoRect.height(); |
|
314 lowerRight[0] = videoRect.width(); |
|
315 lowerRight[1] = videoRect.height(); |
|
316 upperRight[0] = videoRect.width(); |
|
317 upperRight[1] = 0; |
|
318 upperLeft[0] = 0; |
|
319 upperLeft[1] = 0; |
|
320 |
|
321 glBegin(GL_QUADS); |
|
322 glTexCoord2f(lowerLeft[0], lowerLeft[1]); |
|
323 glVertex2i(rect.topLeft().x(), rect.topLeft().y()); |
|
324 glTexCoord2f(lowerRight[0], lowerRight[1]); |
|
325 glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); |
|
326 glTexCoord2f(upperRight[0], upperRight[1]); |
|
327 glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); |
|
328 glTexCoord2f(upperLeft[0], upperLeft[1]); |
|
329 glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); |
|
330 glEnd(); |
|
331 glPopMatrix(); |
|
332 |
|
333 |
|
334 // FOR NOW. FREE THE TEXTURE: |
|
335 glDeleteTextures(1, &texture); |
|
336 } |
|
337 |
|
338 bool VideoFrame::isEmpty() |
|
339 { |
|
340 return (m_videoPlayer == 0); |
|
341 } |
|
342 |
|
343 void VideoFrame::invalidateImage() const |
|
344 { |
|
345 #ifdef CACHE_CV_TEXTURE |
|
346 if (m_cachedCVTextureRef){ |
|
347 CVOpenGLTextureRelease(m_cachedCVTextureRef); |
|
348 (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; |
|
349 } |
|
350 #endif |
|
351 if (m_cachedCIImage){ |
|
352 [(CIImage *) m_cachedCIImage release]; |
|
353 (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; |
|
354 } |
|
355 if (m_cachedNSBitmap){ |
|
356 [m_cachedNSBitmap release]; |
|
357 (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; |
|
358 } |
|
359 (const_cast<VideoFrame *>(this))-> m_cachedQImage = QImage(); |
|
360 } |
|
361 |
|
362 void VideoFrame::retain() const |
|
363 { |
|
364 #ifdef CACHE_CV_TEXTURE |
|
365 if (m_cachedCVTextureRef) |
|
366 CVOpenGLTextureRetain(m_cachedCVTextureRef); |
|
367 #endif |
|
368 if (m_cachedCIImage) |
|
369 [(CIImage *) m_cachedCIImage retain]; |
|
370 if (m_backgroundFrame) |
|
371 m_backgroundFrame->retain(); |
|
372 if (m_cachedNSBitmap) |
|
373 [m_cachedNSBitmap retain]; |
|
374 } |
|
375 |
|
376 void VideoFrame::release() const |
|
377 { |
|
378 #ifdef CACHE_CV_TEXTURE |
|
379 if (m_cachedCVTextureRef){ |
|
380 CVOpenGLTextureRelease(m_cachedCVTextureRef); |
|
381 (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; |
|
382 } |
|
383 #endif |
|
384 if (m_cachedCIImage) |
|
385 [(CIImage *) m_cachedCIImage release]; |
|
386 if (m_backgroundFrame) |
|
387 m_backgroundFrame->release(); |
|
388 if (m_cachedNSBitmap) |
|
389 [m_cachedNSBitmap release]; |
|
390 |
|
391 (const_cast<VideoFrame *>(this))->m_backgroundFrame = 0; |
|
392 (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; |
|
393 (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; |
|
394 } |
|
395 |
|
396 }} //namespace Phonon::QT7 |
|
397 |
|
398 QT_END_NAMESPACE |