source: trunk/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm

Last change on this file was 846, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 27.5 KB
Line 
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 "quicktimevideoplayer.h"
19#include "mediaobject.h"
20#include "videowidget.h"
21#include "audiodevice.h"
22#include "quicktimestreamreader.h"
23
24#include <QtCore/QCoreApplication>
25#include <QtCore/QEventLoop>
26#include <QtCore/QFileInfo>
27#include <QtCore/QUrl>
28#include <QtOpenGL/QGLContext>
29
30#import <QTKit/QTTrack.h>
31#import <QTKit/QTMedia.h>
32#import <QuartzCore/CIContext.h>
33#import <QuartzCore/CIFilter.h>
34
35#ifdef QUICKTIME_C_API_AVAILABLE
36 #include <QuickTime/QuickTime.h>
37 #undef check // avoid name clash;
38 #include <AGL/agl.h>
39#endif
40
41QT_BEGIN_NAMESPACE
42
43namespace Phonon
44{
45namespace QT7
46{
47
48// Defined in videowidget.cpp:
49QGLWidget *PhononSharedQGLWidget();
50
51QuickTimeVideoPlayer::QuickTimeVideoPlayer() : QObject(0)
52{
53 m_state = NoMedia;
54 m_mediaSource = MediaSource();
55 m_QTMovie = 0;
56 m_streamReader = 0;
57 m_playbackRate = 1.0f;
58 m_masterVolume = 1.0f;
59 m_relativeVolume = 1.0f;
60 m_currentTime = 0;
61 m_mute = false;
62 m_audioEnabled = false;
63 m_hasVideo = false;
64 m_playbackRateSat = false;
65 m_isDrmProtected = false;
66 m_isDrmAuthorized = true;
67 m_primaryRenderingTarget = 0;
68 m_primaryRenderingCIImage = 0;
69 m_QImagePixelBuffer = 0;
70
71#ifdef QUICKTIME_C_API_AVAILABLE
72 OSStatus err = EnterMovies();
73 BACKEND_ASSERT2(err == noErr, "Could not initialize QuickTime", FATAL_ERROR)
74 createVisualContext();
75#endif
76}
77
78QuickTimeVideoPlayer::~QuickTimeVideoPlayer()
79{
80 unsetVideo();
81 [(NSObject*)m_primaryRenderingTarget release];
82 m_primaryRenderingTarget = 0;
83#ifdef QUICKTIME_C_API_AVAILABLE
84 if (m_visualContext)
85 CFRelease(m_visualContext);
86#endif
87}
88
89void QuickTimeVideoPlayer::createVisualContext()
90{
91#ifdef QUICKTIME_C_API_AVAILABLE
92 PhononSharedQGLWidget()->makeCurrent();
93
94 PhononAutoReleasePool pool;
95 CGLContextObj cglContext = CGLGetCurrentContext();
96 NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
97 CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
98 BACKEND_ASSERT2(cglContext, "Could not get current CoreVideo GL context (OpenGL)", FATAL_ERROR)
99 BACKEND_ASSERT2(cglPixelFormat, "Could not get current CoreVideo pixel format (OpenGL)", FATAL_ERROR)
100
101 CFTypeRef keys[] = { kQTVisualContextWorkingColorSpaceKey };
102 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
103 CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault,
104 (const void **)keys,
105 (const void **)&colorSpace, 1,
106 &kCFTypeDictionaryKeyCallBacks,
107 &kCFTypeDictionaryValueCallBacks);
108
109 OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext,
110 cglPixelFormat, textureContextAttributes, &m_visualContext);
111 CFRelease(textureContextAttributes);
112 BACKEND_ASSERT2(err == noErr, "Could not create visual context (OpenGL)", FATAL_ERROR)
113#endif // QUICKTIME_C_API_AVAILABLE
114}
115
116bool QuickTimeVideoPlayer::videoFrameChanged()
117{
118 if (!m_QTMovie || !m_hasVideo)
119 return false;
120
121#ifdef QUICKTIME_C_API_AVAILABLE
122 if (m_primaryRenderingTarget)
123 return true;
124 if (!m_visualContext)
125 return false;
126
127 QTVisualContextTask(m_visualContext);
128 return QTVisualContextIsNewImageAvailable(m_visualContext, 0);
129
130#elif defined(QT_MAC_USE_COCOA)
131 return true;
132
133#else
134 return false;
135#endif
136}
137
138CVOpenGLTextureRef QuickTimeVideoPlayer::currentFrameAsCVTexture()
139{
140#ifdef QUICKTIME_C_API_AVAILABLE
141 if (!m_visualContext)
142 return 0;
143 CVOpenGLTextureRef texture = 0;
144 OSStatus err = QTVisualContextCopyImageForTime(m_visualContext, 0, 0, &texture);
145 BACKEND_ASSERT3(err == noErr, "Could not copy image for time in QuickTime player", FATAL_ERROR, 0)
146 return texture;
147
148#else
149 return 0;
150#endif
151}
152
153QImage QuickTimeVideoPlayer::currentFrameAsQImage()
154{
155#ifdef QUICKTIME_C_API_AVAILABLE
156 QGLContext *prevContext = const_cast<QGLContext *>(QGLContext::currentContext());
157 CVOpenGLTextureRef texture = currentFrameAsCVTexture();
158 GLenum target = CVOpenGLTextureGetTarget(texture);
159 GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2];
160
161 if (!m_QImagePixelBuffer){
162 m_QImagePixelBuffer = new QGLPixelBuffer(videoRect().size(), QGLFormat::defaultFormat(), PhononSharedQGLWidget());
163 m_QImagePixelBuffer->makeCurrent();
164 glEnable(target);
165 glDisable(GL_BLEND);
166 glDisable(GL_CULL_FACE);
167 } else {
168 m_QImagePixelBuffer->makeCurrent();
169 }
170
171 CVOpenGLTextureGetCleanTexCoords(texture, upperLeft, upperRight, lowerRight, lowerLeft);
172 glBindTexture(target, CVOpenGLTextureGetName(texture));
173 glBegin(GL_QUADS);
174 glTexCoord2f(lowerLeft[0], lowerLeft[1]);
175 glVertex2i(-1, 1);
176 glTexCoord2f(lowerRight[0], lowerRight[1]);
177 glVertex2i(1, 1);
178 glTexCoord2f(upperRight[0], upperRight[1]);
179 glVertex2i(1, -1);
180 glTexCoord2f(upperLeft[0], upperLeft[1]);
181 glVertex2i(-1, -1);
182 glEnd();
183
184 QImage image = m_QImagePixelBuffer->toImage();
185 CVOpenGLTextureRelease(texture);
186 // Because of QuickTime, m_QImagePixelBuffer->doneCurrent() will fail.
187 // So we store, and restore, the context our selves:
188 prevContext->makeCurrent();
189 return image;
190#else
191 CIImage *img = (CIImage *)currentFrameAsCIImage();
192 if (!img)
193 return QImage();
194
195 NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img];
196 CGRect bounds = [img extent];
197 QImage qImg([bitmap bitmapData], bounds.size.width, bounds.size.height, QImage::Format_ARGB32);
198 QImage swapped = qImg.rgbSwapped();
199 [bitmap release];
200 [img release];
201 return swapped;
202#endif
203}
204
205void QuickTimeVideoPlayer::setPrimaryRenderingCIImage(void *ciImage)
206{
207 [(CIImage *)m_primaryRenderingCIImage release];
208 m_primaryRenderingCIImage = ciImage;
209 [(CIImage *)m_primaryRenderingCIImage retain];
210}
211
212void QuickTimeVideoPlayer::setPrimaryRenderingTarget(NSObject *target)
213{
214 [(NSObject*)m_primaryRenderingTarget release];
215 m_primaryRenderingTarget = target;
216 [(NSObject*)m_primaryRenderingTarget retain];
217}
218
219void *QuickTimeVideoPlayer::primaryRenderingCIImage()
220{
221 return m_primaryRenderingCIImage;
222}
223
224void *QuickTimeVideoPlayer::currentFrameAsCIImage()
225{
226 if (!m_QTMovie)
227 return 0;
228
229#if defined(QT_MAC_USE_COCOA)
230 if (m_primaryRenderingCIImage){
231 CIImage *img = (CIImage *)m_primaryRenderingCIImage;
232 if (m_brightness || m_contrast || m_saturation){
233 CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"];
234 [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"];
235 [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"];
236 [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"];
237 [colorFilter setValue:img forKey:@"inputImage"];
238 img = [colorFilter valueForKey:@"outputImage"];
239 }
240 if (m_hue){
241 CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"];
242 [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"];
243 [colorFilter setValue:img forKey:@"inputImage"];
244 img = [colorFilter valueForKey:@"outputImage"];
245 }
246 return [img retain];
247 }
248#endif
249
250#ifdef QUICKTIME_C_API_AVAILABLE
251 CVOpenGLTextureRef cvImg = currentFrameAsCVTexture();
252 CIImage *img = [[CIImage alloc] initWithCVImageBuffer:cvImg];
253 CVOpenGLTextureRelease(cvImg);
254 return img;
255#else
256 return 0;
257#endif
258}
259
260GLuint QuickTimeVideoPlayer::currentFrameAsGLTexture()
261{
262 CIImage *img = (CIImage *)currentFrameAsCIImage();
263 if (!img)
264 return 0;
265
266 NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img];
267 GLuint texName = 0;
268 glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]);
269 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
270 glGenTextures(1, &texName);
271 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texName);
272 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
273
274 int samplesPerPixel = [bitmap samplesPerPixel];
275 if (![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)){
276 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0,
277 samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
278 [bitmap pixelsWide], [bitmap pixelsHigh],
279 0, samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
280 GL_UNSIGNED_BYTE, [bitmap bitmapData]);
281 } else {
282 // Handle other bitmap formats.
283 }
284
285 [bitmap release];
286 [img release];
287 return texName;
288}
289
290void QuickTimeVideoPlayer::setMasterVolume(float volume)
291{
292 setVolume(volume, m_relativeVolume);
293}
294
295void QuickTimeVideoPlayer::setRelativeVolume(float volume)
296{
297 setVolume(m_masterVolume, volume);
298}
299
300void QuickTimeVideoPlayer::setVolume(float masterVolume, float relativeVolume)
301{
302 m_masterVolume = masterVolume;
303 m_relativeVolume = relativeVolume;
304 if (!m_QTMovie || !m_audioEnabled || m_mute)
305 return;
306 [m_QTMovie setVolume:(m_masterVolume * m_relativeVolume)];
307}
308
309void QuickTimeVideoPlayer::setMute(bool mute)
310{
311 m_mute = mute;
312 if (!m_QTMovie || m_state != Playing || !m_audioEnabled)
313 return;
314
315 // Work-around bug that happends if you set/unset mute
316 // before movie is playing, and audio is not played
317 // through graph. Then audio is delayed.
318 [m_QTMovie setMuted:mute];
319 [m_QTMovie setVolume:(mute ? 0 : m_masterVolume * m_relativeVolume)];
320}
321
322void QuickTimeVideoPlayer::enableAudio(bool enable)
323{
324 m_audioEnabled = enable;
325 if (!m_QTMovie || m_state != Playing)
326 return;
327
328 // Work-around bug that happends if you set/unset mute
329 // before movie is playing, and audio is not played
330 // through graph. Then audio is delayed.
331 [m_QTMovie setMuted:(!enable || m_mute)];
332 [m_QTMovie setVolume:((!enable || m_mute) ? 0 : m_masterVolume * m_relativeVolume)];
333}
334
335bool QuickTimeVideoPlayer::audioEnabled()
336{
337 return m_audioEnabled;
338}
339
340bool QuickTimeVideoPlayer::setAudioDevice(int id)
341{
342 if (!m_QTMovie)
343 return false;
344
345#ifdef QUICKTIME_C_API_AVAILABLE
346 // The following code will not work for some media codecs that
347 // typically mingle audio/video frames (e.g mpeg).
348 CFStringRef idString = PhononCFString::toCFStringRef(AudioDevice::deviceUID(id));
349 QTAudioContextRef context;
350 QTAudioContextCreateForAudioDevice(kCFAllocatorDefault, idString, 0, &context);
351 OSStatus err = SetMovieAudioContext([m_QTMovie quickTimeMovie], context);
352 CFRelease(context);
353 if (err != noErr)
354 return false;
355 return true;
356#else
357 Q_UNUSED(id);
358 return false;
359#endif
360}
361
362void QuickTimeVideoPlayer::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation)
363{
364 if (!m_QTMovie)
365 return;
366
367 // 0 is default value for the colors
368 // in phonon, so adjust scale:
369 contrast += 1;
370 saturation += 1;
371
372 m_brightness = brightness;
373 m_contrast = contrast;
374 m_hue = hue;
375 m_saturation = saturation;
376
377#ifdef QUICKTIME_C_API_AVAILABLE
378 Float32 value;
379 value = brightness;
380 SetMovieVisualBrightness([m_QTMovie quickTimeMovie], value, 0);
381 value = contrast;
382 SetMovieVisualContrast([m_QTMovie quickTimeMovie], value, 0);
383 value = hue;
384 SetMovieVisualHue([m_QTMovie quickTimeMovie], value, 0);
385 value = saturation;
386 SetMovieVisualSaturation([m_QTMovie quickTimeMovie], value, 0);
387#endif
388}
389
390QRect QuickTimeVideoPlayer::videoRect() const
391{
392 if (!m_QTMovie)
393 return QRect();
394
395 PhononAutoReleasePool pool;
396 NSSize size = [[m_QTMovie attributeForKey:@"QTMovieCurrentSizeAttribute"] sizeValue];
397 return QRect(0, 0, size.width, size.height);
398}
399
400void QuickTimeVideoPlayer::unsetVideo()
401{
402 if (!m_QTMovie)
403 return;
404
405 [m_QTMovie release];
406 m_QTMovie = 0;
407 delete m_streamReader;
408 m_streamReader = 0;
409 m_currentTime = 0;
410 m_state = NoMedia;
411 m_isDrmProtected = false;
412 m_isDrmAuthorized = true;
413 m_mediaSource = MediaSource();
414 [(CIImage *)m_primaryRenderingCIImage release];
415 m_primaryRenderingCIImage = 0;
416 delete m_QImagePixelBuffer;
417 m_QImagePixelBuffer = 0;
418}
419
420QuickTimeVideoPlayer::State QuickTimeVideoPlayer::state() const
421{
422 return m_state;
423}
424
425quint64 QuickTimeVideoPlayer::timeLoaded()
426{
427 if (!m_QTMovie)
428 return 0;
429#ifdef QUICKTIME_C_API_AVAILABLE
430 TimeValue value;
431 GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &value);
432 quint64 loaded = static_cast<quint64>(float(value) / float(GetMovieTimeScale([m_QTMovie quickTimeMovie])) * 1000.0f);
433 return (loaded == INT_MAX) ? 0 : loaded;
434#else
435 return 0;
436#endif
437}
438
439float QuickTimeVideoPlayer::percentageLoaded()
440{
441 if (!m_QTMovie || !isSeekable())
442 return 0;
443#ifdef QUICKTIME_C_API_AVAILABLE
444 TimeValue loaded;
445 GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &loaded);
446 float duration = GetMovieDuration([m_QTMovie quickTimeMovie]);
447 return duration ? float(loaded) / duration : 0;
448#else
449 return 0;
450#endif
451}
452
453void QuickTimeVideoPlayer::waitStatePlayable()
454{
455#if defined(QT_MAC_USE_COCOA)
456 long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue];
457 while (state != QTMovieLoadStateError && state < QTMovieLoadStatePlayable)
458 state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue];
459#elif defined(QUICKTIME_C_API_AVAILABLE)
460 long state = GetMovieLoadState([m_QTMovie quickTimeMovie]);
461 while (state != kMovieLoadStateError && state < kMovieLoadStatePlayable){
462 MoviesTask(0, 0);
463 state = GetMovieLoadState([m_QTMovie quickTimeMovie]);
464 }
465#endif
466}
467
468bool QuickTimeVideoPlayer::movieNotLoaded()
469{
470 if (!m_QTMovie)
471 return true;
472
473#if defined(QT_MAC_USE_COCOA)
474 long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue];
475 return state == QTMovieLoadStateError;
476#elif defined(QUICKTIME_C_API_AVAILABLE)
477 long state = GetMovieLoadState([m_QTMovie quickTimeMovie]);
478 return state == kMovieLoadStateError;
479#endif
480}
481
482void QuickTimeVideoPlayer::setError(NSError *error)
483{
484 if (!error)
485 return;
486 QString desc = QString::fromUtf8([[error localizedDescription] UTF8String]);
487 if (desc == "The file is not a movie file.")
488 desc = QLatin1String("Could not decode media source.");
489 else if (desc == "A necessary data reference could not be resolved."){
490 if (codecExistsAccordingToSuffix(mediaSourcePath()))
491 desc = QLatin1String("Could not locate media source.");
492 else
493 desc = QLatin1String("Could not decode media source.");
494 } else if (desc == "You do not have sufficient permissions for this operation.")
495 desc = QLatin1String("Could not open media source.");
496 SET_ERROR(desc, FATAL_ERROR)
497}
498
499bool QuickTimeVideoPlayer::errorOccured()
500{
501 if (gGetErrorType() != NO_ERROR){
502 return true;
503 } else if (movieNotLoaded()){
504 SET_ERROR("Could not open media source.", FATAL_ERROR)
505 return true;
506 }
507 return false;
508}
509
510bool QuickTimeVideoPlayer::codecExistsAccordingToSuffix(const QString &fileName)
511{
512 PhononAutoReleasePool pool;
513 NSArray *fileTypes = [QTMovie movieFileTypes:QTIncludeAllTypes];
514 for (uint i=0; i<[fileTypes count]; ++i){
515 NSString *type = [fileTypes objectAtIndex:i];
516 QString formattedType = QString::fromUtf8([type UTF8String]);
517 formattedType.remove('\'').remove('.');
518 if (fileName.endsWith(QChar('.') + formattedType, Qt::CaseInsensitive))
519 return true;
520 }
521 return false;
522}
523
524void QuickTimeVideoPlayer::setMediaSource(const MediaSource &mediaSource)
525{
526 PhononAutoReleasePool pool;
527 unsetVideo();
528 m_mediaSource = mediaSource;
529 if (mediaSource.type() == MediaSource::Empty || mediaSource.type() == MediaSource::Invalid){
530 m_state = NoMedia;
531 return;
532 }
533 openMovieFromCurrentMediaSource();
534 if (errorOccured()){
535 unsetVideo();
536 return;
537 }
538
539#ifdef QUICKTIME_C_API_AVAILABLE
540 if (m_visualContext)
541 SetMovieVisualContext([m_QTMovie quickTimeMovie], m_visualContext);
542#endif
543
544 waitStatePlayable();
545 if (errorOccured()){
546 unsetVideo();
547 return;
548 }
549
550 readProtection();
551 preRollMovie();
552 if (errorOccured()){
553 unsetVideo();
554 return;
555 }
556
557 if (!m_playbackRateSat)
558 m_playbackRate = prefferedPlaybackRate();
559 checkIfVideoAwailable();
560 enableAudio(m_audioEnabled);
561 setMute(m_mute);
562 setVolume(m_masterVolume, m_relativeVolume);
563 pause();
564}
565
566void QuickTimeVideoPlayer::openMovieFromCurrentMediaSource()
567{
568 switch (m_mediaSource.type()){
569 case MediaSource::LocalFile:
570 openMovieFromFile();
571 break;
572 case MediaSource::Url:
573 openMovieFromUrl();
574 break;
575 case MediaSource::Disc:
576 CASE_UNSUPPORTED("Could not open media source.", FATAL_ERROR)
577 break;
578 case MediaSource::Stream:
579 openMovieFromStream();
580 break;
581 case MediaSource::Empty:
582 case MediaSource::Invalid:
583 break;
584 }
585}
586
587QString QuickTimeVideoPlayer::mediaSourcePath()
588{
589 switch (m_mediaSource.type()){
590 case MediaSource::LocalFile:{
591 QFileInfo fileInfo(m_mediaSource.fileName());
592 return fileInfo.isSymLink() ? fileInfo.symLinkTarget() : fileInfo.canonicalFilePath();
593 break;}
594 case MediaSource::Url:
595 return m_mediaSource.url().toEncoded();
596 break;
597 default:
598 break;
599 }
600 return QString();
601}
602
603void QuickTimeVideoPlayer::openMovieFromDataRef(QTDataReference *dataRef)
604{
605 PhononAutoReleasePool pool;
606 NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:
607 dataRef, QTMovieDataReferenceAttribute,
608 [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,
609 [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute,
610 [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute,
611 [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute,
612 nil];
613
614 NSError *err = 0;
615 m_QTMovie = [[QTMovie movieWithAttributes:attr error:&err] retain];
616 if (err){
617 [m_QTMovie release];
618 m_QTMovie = 0;
619 setError(err);
620 }
621}
622
623void QuickTimeVideoPlayer::openMovieFromData(QByteArray *data, char *fileType)
624{
625 PhononAutoReleasePool pool;
626 NSString *type = [NSString stringWithUTF8String:fileType];
627 NSData *nsData = [NSData dataWithBytesNoCopy:data->data() length:data->size() freeWhenDone:NO];
628 QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToData:nsData name:type MIMEType:@""];
629 openMovieFromDataRef(dataRef);
630}
631
632void QuickTimeVideoPlayer::openMovieFromDataGuessType(QByteArray *data)
633{
634 // It turns out to be better to just try the standard file types rather
635 // than using e.g [QTMovie movieFileTypes:QTIncludeCommonTypes]. Some
636 // codecs *think* they can decode the stream, and crash...
637#define TryOpenMovieWithCodec(type) gClearError(); \
638 openMovieFromData(data, "."type); \
639 if (m_QTMovie) return;
640
641 TryOpenMovieWithCodec("avi");
642 TryOpenMovieWithCodec("mp4");
643 TryOpenMovieWithCodec("m4p");
644 TryOpenMovieWithCodec("m1s");
645 TryOpenMovieWithCodec("mp3");
646 TryOpenMovieWithCodec("mpeg");
647 TryOpenMovieWithCodec("mov");
648 TryOpenMovieWithCodec("ogg");
649 TryOpenMovieWithCodec("wav");
650 TryOpenMovieWithCodec("wmv");
651#undef TryOpenMovieWithCodec(type)
652}
653
654void QuickTimeVideoPlayer::openMovieFromFile()
655{
656 NSString *nsFilename = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath());
657 QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToFile:nsFilename];
658 openMovieFromDataRef(dataRef);
659}
660
661void QuickTimeVideoPlayer::openMovieFromUrl()
662{
663 PhononAutoReleasePool pool;
664 NSString *urlString = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath());
665 NSURL *url = [NSURL URLWithString: urlString];
666 QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToURL:url];
667 openMovieFromDataRef(dataRef);
668}
669
670void QuickTimeVideoPlayer::openMovieFromStream()
671{
672 m_streamReader = new QuickTimeStreamReader(m_mediaSource);
673 if (!m_streamReader->readAllData())
674 return;
675 openMovieFromDataGuessType(m_streamReader->pointerToData());
676}
677
678MediaSource QuickTimeVideoPlayer::mediaSource() const
679{
680 return m_mediaSource;
681}
682
683QTMovie *QuickTimeVideoPlayer::qtMovie() const
684{
685 return m_QTMovie;
686}
687
688void QuickTimeVideoPlayer::setPlaybackRate(float rate)
689{
690 PhononAutoReleasePool pool;
691 m_playbackRateSat = true;
692 m_playbackRate = rate;
693 if (m_QTMovie)
694 [m_QTMovie setRate:m_playbackRate];
695}
696
697float QuickTimeVideoPlayer::playbackRate() const
698{
699 return m_playbackRate;
700}
701
702quint64 QuickTimeVideoPlayer::currentTime() const
703{
704 if (!m_QTMovie || m_state == Paused)
705 return m_currentTime;
706
707 PhononAutoReleasePool pool;
708 QTTime qtTime = [m_QTMovie currentTime];
709 quint64 t = static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f);
710 const_cast<QuickTimeVideoPlayer *>(this)->m_currentTime = t;
711 return m_currentTime;
712}
713
714long QuickTimeVideoPlayer::timeScale() const
715{
716 if (!m_QTMovie)
717 return 0;
718
719 PhononAutoReleasePool pool;
720 return [[m_QTMovie attributeForKey:@"QTMovieTimeScaleAttribute"] longValue];
721}
722
723QString QuickTimeVideoPlayer::timeToString(quint64 ms)
724{
725 int sec = ms/1000;
726 int min = sec/60;
727 int hour = min/60;
728 return QString(QLatin1String("%1:%2:%3:%4")).arg(hour%60).arg(min%60).arg(sec%60).arg(ms%1000);
729}
730
731QString QuickTimeVideoPlayer::currentTimeString()
732{
733 return timeToString(currentTime());
734}
735
736quint64 QuickTimeVideoPlayer::duration() const
737{
738 if (!m_QTMovie)
739 return 0;
740
741 PhononAutoReleasePool pool;
742 QTTime qtTime = [m_QTMovie duration];
743 return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f);
744}
745
746void QuickTimeVideoPlayer::play()
747{
748 if (!canPlayMedia())
749 return;
750
751 PhononAutoReleasePool pool;
752 m_state = Playing;
753 enableAudio(m_audioEnabled);
754 setMute(m_mute);
755 [m_QTMovie setRate:m_playbackRate];
756}
757
758void QuickTimeVideoPlayer::pause()
759{
760 if (!canPlayMedia())
761 return;
762
763 PhononAutoReleasePool pool;
764 currentTime();
765 m_state = Paused;
766
767 if (isSeekable())
768 [m_QTMovie setRate:0];
769 else // pretend to be paused:
770 [m_QTMovie setMuted:0];
771}
772
773void QuickTimeVideoPlayer::seek(quint64 milliseconds)
774{
775 if (!canPlayMedia() || !isSeekable() || milliseconds == currentTime())
776 return;
777 if (milliseconds > duration())
778 milliseconds = duration();
779
780 PhononAutoReleasePool pool;
781 QTTime newQTTime = [m_QTMovie currentTime];
782 newQTTime.timeValue = (milliseconds / 1000.0f) * newQTTime.timeScale;
783 [m_QTMovie setCurrentTime:newQTTime];
784
785 // The movie might not have been able to seek
786 // to the exact point we told it to. So set
787 // the current time according to what the movie says:
788 newQTTime = [m_QTMovie currentTime];
789 m_currentTime = static_cast<quint64>
790 (float(newQTTime.timeValue) / float(newQTTime.timeScale) * 1000.0f);
791
792 if (m_state == Paused){
793 // We need (for reasons unknown) to task
794 // the movie twize to make sure that
795 // a subsequent call to frameAsCvTexture
796 // returns the correct frame:
797#ifdef QUICKTIME_C_API_AVAILABLE
798 MoviesTask(0, 0);
799 MoviesTask(0, 0);
800#elif defined(QT_MAC_USE_COCOA)
801 qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
802#endif
803 }
804}
805
806bool QuickTimeVideoPlayer::canPlayMedia() const
807{
808 if (!m_QTMovie)
809 return false;
810 return m_isDrmAuthorized;
811}
812
813bool QuickTimeVideoPlayer::isPlaying() const
814{
815 return m_state == Playing;
816}
817
818bool QuickTimeVideoPlayer::isSeekable() const
819{
820 return canPlayMedia() && (duration()-1) != INT_MAX;
821}
822
823float QuickTimeVideoPlayer::prefferedPlaybackRate() const
824{
825 if (!m_QTMovie)
826 return 0;
827
828 PhononAutoReleasePool pool;
829 return [[m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue];
830}
831
832#ifdef QUICKTIME_C_API_AVAILABLE
833void MoviePrePrerollCompleteCallBack(Movie /*theMovie*/, OSErr /*thePrerollErr*/, void * /*userData*/)
834{
835 // QuickTimeVideoPlayer *player = static_cast<QuickTimeVideoPlayer *>(userData);
836}
837#endif
838
839bool QuickTimeVideoPlayer::preRollMovie(qint64 startTime)
840{
841 if (!canPlayMedia())
842 return false;
843
844#ifdef QUICKTIME_C_API_AVAILABLE
845 if (PrePrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate),
846 0 /*MoviePrePrerollCompleteCallBack*/, this) != noErr) // No callback means wait (synch)
847 return false;
848
849 if (PrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate)) != noErr)
850 return false;
851
852 return true;
853#else
854 Q_UNUSED(startTime);
855 return false;
856#endif
857}
858
859bool QuickTimeVideoPlayer::hasAudio() const
860{
861 if (!m_QTMovie)
862 return false;
863
864 PhononAutoReleasePool pool;
865 return [[m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES;
866}
867
868bool QuickTimeVideoPlayer::hasVideo() const
869{
870 return m_hasVideo;
871}
872
873bool QuickTimeVideoPlayer::hasMovie() const
874{
875 return m_QTMovie != 0;
876}
877
878void QuickTimeVideoPlayer::checkIfVideoAwailable()
879{
880 PhononAutoReleasePool pool;
881 m_hasVideo = [[m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES;
882}
883
884bool QuickTimeVideoPlayer::isDrmProtected() const
885{
886 return m_isDrmProtected;
887}
888
889bool QuickTimeVideoPlayer::isDrmAuthorized() const
890{
891 return m_isDrmAuthorized;
892}
893/*
894void QuickTimeVideoPlayer::movieCodecIsMPEG()
895{
896 NSArray *tracks = [m_QTMovie tracks];
897 for (QTTrack *track in tracks)
898 if ([[track media] hasCharacteristic:QTMediaTypeMPEG])
899 return true;
900 return false;
901}
902*/
903
904static void QtGetTrackProtection(QTTrack *track, bool &isDrmProtected, bool &isDrmAuthorized)
905{
906 isDrmProtected = false;
907 isDrmAuthorized = true;
908
909#ifdef QUICKTIME_C_API_AVAILABLE
910 QTMedia *media = [track media];
911 MediaHandler mediaHandler = GetMediaHandler([media quickTimeMedia]);
912 if (mediaHandler){
913 // Regardless, skip message boxes pointing to iTunes regarding DRM:
914 Boolean boolFalse = false;
915 QTSetComponentProperty(mediaHandler,
916 kQTPropertyClass_DRM, kQTDRMPropertyID_InteractWithUser,
917 sizeof(boolFalse), &boolFalse);
918
919 // Check track:
920 Boolean value;
921 OSStatus err = QTGetComponentProperty(mediaHandler,
922 kQTPropertyClass_DRM, kQTDRMPropertyID_IsProtected,
923 sizeof(value), &value, 0);
924 isDrmProtected = (err == noErr) ? bool(value) : false;
925 err = QTGetComponentProperty(mediaHandler,
926 kQTPropertyClass_DRM, kQTDRMPropertyID_IsAuthorized,
927 sizeof(value), &value, 0);
928 isDrmAuthorized = (err == noErr) ? bool(value) : true;
929 }
930#else
931 Q_UNUSED(track);
932#endif // QUICKTIME_C_API_AVAILABLE
933}
934
935void QuickTimeVideoPlayer::readProtection()
936{
937 m_isDrmProtected = false;
938 m_isDrmAuthorized = true;
939
940 NSArray *tracks = [m_QTMovie tracks];
941 for (uint i=0; i<[tracks count]; ++i){
942 QTTrack *track = [tracks objectAtIndex:i];
943 bool isDrmProtected = false;
944 bool isDrmAuthorized = true;
945 QtGetTrackProtection(track, isDrmProtected, isDrmAuthorized);
946 if (isDrmProtected)
947 m_isDrmProtected = true;
948 if (!isDrmAuthorized)
949 m_isDrmAuthorized = false;
950 }
951}
952
953}}
954
955QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.