qtmobility/plugins/multimedia/qt7/qt7movierenderer.mm
changeset 1 2b40d63a9c3d
child 4 90517678cc4f
equal deleted inserted replaced
0:cfcbf08528c4 1:2b40d63a9c3d
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #import <QTKit/QTKit.h>
       
    43 
       
    44 #include "qt7backend.h"
       
    45 
       
    46 #include "qt7playercontrol.h"
       
    47 #include "qt7movierenderer.h"
       
    48 #include "qt7playersession.h"
       
    49 #include "qcvdisplaylink.h"
       
    50 #include <QtCore/qdebug.h>
       
    51 #include <QtCore/qcoreapplication.h>
       
    52 
       
    53 #include <QGLWidget>
       
    54 
       
    55 #include <QtMultimedia/qabstractvideobuffer.h>
       
    56 #include <QtMultimedia/qabstractvideosurface.h>
       
    57 #include <QtMultimedia/qvideosurfaceformat.h>
       
    58 
       
    59 QTM_USE_NAMESPACE
       
    60 
       
    61 class CVGLTextureVideoBuffer : public QAbstractVideoBuffer
       
    62 {
       
    63 public:
       
    64     CVGLTextureVideoBuffer(CVOpenGLTextureRef buffer)
       
    65         : QAbstractVideoBuffer(NoHandle)
       
    66         , m_buffer(buffer)
       
    67         , m_mode(NotMapped)
       
    68     {
       
    69         CVOpenGLTextureRetain(m_buffer);
       
    70     }
       
    71 
       
    72     virtual ~CVGLTextureVideoBuffer()
       
    73     {
       
    74         CVOpenGLTextureRelease(m_buffer);
       
    75     }
       
    76 
       
    77     QVariant handle() const
       
    78     {
       
    79         GLuint id = CVOpenGLTextureGetName(m_buffer);
       
    80         return QVariant(int(id));
       
    81     }
       
    82 
       
    83     HandleType handleType() const
       
    84     {
       
    85         return GLTextureHandle;
       
    86     }
       
    87 
       
    88     MapMode mapMode() const { return m_mode; }
       
    89 
       
    90     uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
       
    91     {
       
    92         if (numBytes)
       
    93             *numBytes = 0;
       
    94 
       
    95         if (bytesPerLine)
       
    96             *bytesPerLine = 0;
       
    97 
       
    98         m_mode = mode;
       
    99         return 0;
       
   100     }
       
   101 
       
   102     void unmap() { m_mode = NotMapped; }
       
   103 
       
   104 private:
       
   105     CVOpenGLTextureRef m_buffer;
       
   106     MapMode m_mode;
       
   107 };
       
   108 
       
   109 
       
   110 class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer
       
   111 {
       
   112 public:
       
   113     CVPixelBufferVideoBuffer(CVPixelBufferRef buffer)
       
   114         : QAbstractVideoBuffer(NoHandle)
       
   115         , m_buffer(buffer)
       
   116         , m_mode(NotMapped)
       
   117     {
       
   118         CVPixelBufferRetain(m_buffer);
       
   119     }
       
   120 
       
   121     virtual ~CVPixelBufferVideoBuffer()
       
   122     {
       
   123         CVPixelBufferRelease(m_buffer);
       
   124     }
       
   125 
       
   126     MapMode mapMode() const { return m_mode; }
       
   127 
       
   128     uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
       
   129     {
       
   130         if (mode != NotMapped && m_mode == NotMapped) {
       
   131             CVPixelBufferLockBaseAddress(m_buffer, 0);
       
   132 
       
   133             if (numBytes)
       
   134                 *numBytes = CVPixelBufferGetDataSize(m_buffer);
       
   135 
       
   136             if (bytesPerLine)
       
   137                 *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer);
       
   138 
       
   139             m_mode = mode;
       
   140 
       
   141             return (uchar*)CVPixelBufferGetBaseAddress(m_buffer);
       
   142         } else {
       
   143             return 0;
       
   144         }
       
   145     }
       
   146 
       
   147     void unmap()
       
   148     {
       
   149         if (m_mode != NotMapped) {
       
   150             m_mode = NotMapped;
       
   151             CVPixelBufferUnlockBaseAddress(m_buffer, 0);
       
   152         }
       
   153     }
       
   154 
       
   155 private:
       
   156     CVPixelBufferRef m_buffer;
       
   157     MapMode m_mode;
       
   158 };
       
   159 
       
   160 
       
   161 
       
   162 QT7MovieRenderer::QT7MovieRenderer(QObject *parent)
       
   163    :QT7VideoRendererControl(parent),
       
   164     m_movie(0),
       
   165 #ifdef QUICKTIME_C_API_AVAILABLE
       
   166     m_visualContext(0),
       
   167     m_usingGLContext(false),
       
   168     m_currentGLContext(0),
       
   169 #endif
       
   170     m_surface(0)
       
   171 {
       
   172     qDebug() << "QT7MovieRenderer";
       
   173 
       
   174     m_displayLink = new QCvDisplayLink(this);
       
   175     connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
       
   176 }
       
   177 
       
   178 
       
   179 bool QT7MovieRenderer::createGLVisualContext()
       
   180 {
       
   181 #ifdef QUICKTIME_C_API_AVAILABLE
       
   182     AutoReleasePool pool;
       
   183     CGLContextObj cglContext = CGLGetCurrentContext();
       
   184     NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
       
   185     CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
       
   186 
       
   187     OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext,
       
   188                                                 cglPixelFormat, NULL, &m_visualContext);
       
   189     if (err != noErr)
       
   190         qWarning() << "Could not create visual context (OpenGL)";
       
   191 
       
   192     return (err == noErr);
       
   193 #endif // QUICKTIME_C_API_AVAILABLE
       
   194 
       
   195     return false;
       
   196 }
       
   197 
       
   198 #ifdef QUICKTIME_C_API_AVAILABLE
       
   199 static bool DictionarySetValue(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
       
   200 {
       
   201     CFNumberRef  number    = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
       
   202 
       
   203     if (number) {
       
   204         CFDictionarySetValue( dict, key, number );
       
   205         CFRelease( number );
       
   206         return true;
       
   207     }
       
   208     return false;
       
   209 }
       
   210 #endif // QUICKTIME_C_API_AVAILABLE
       
   211 
       
   212 bool QT7MovieRenderer::createPixelBufferVisualContext()
       
   213 {
       
   214 #ifdef QUICKTIME_C_API_AVAILABLE
       
   215     if (m_visualContext) {
       
   216         QTVisualContextRelease(m_visualContext);
       
   217         m_visualContext = 0;
       
   218     }
       
   219 
       
   220     m_pixelBufferContextGeometry = m_nativeSize;
       
   221 
       
   222     CFMutableDictionaryRef  pixelBufferOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
       
   223                                                                            &kCFTypeDictionaryKeyCallBacks,
       
   224                                                                            &kCFTypeDictionaryValueCallBacks);
       
   225     //DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32ARGBPixelFormat );
       
   226     DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat );
       
   227     DictionarySetValue(pixelBufferOptions, kCVPixelBufferWidthKey, m_nativeSize.width() );
       
   228     DictionarySetValue(pixelBufferOptions, kCVPixelBufferHeightKey, m_nativeSize.height() );
       
   229     DictionarySetValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, 16);
       
   230     //CFDictionarySetValue(pixelBufferOptions, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
       
   231 
       
   232     CFMutableDictionaryRef  visualContextOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
       
   233                                                                              &kCFTypeDictionaryKeyCallBacks,
       
   234                                                                              &kCFTypeDictionaryValueCallBacks);
       
   235     CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
       
   236     CFDictionarySetValue(visualContextOptions, kQTVisualContextWorkingColorSpaceKey, CGColorSpaceCreateDeviceRGB());
       
   237 
       
   238     OSStatus err = QTPixelBufferContextCreate(kCFAllocatorDefault,
       
   239                                                visualContextOptions,
       
   240                                                &m_visualContext);
       
   241     CFRelease(pixelBufferOptions);
       
   242     CFRelease(visualContextOptions);
       
   243 
       
   244     if (err != noErr) {
       
   245         qWarning() << "Could not create visual context (PixelBuffer)";
       
   246         return false;
       
   247     }
       
   248 
       
   249     return true;
       
   250 #endif // QUICKTIME_C_API_AVAILABLE
       
   251 
       
   252     return false;
       
   253 }
       
   254 
       
   255 
       
   256 QT7MovieRenderer::~QT7MovieRenderer()
       
   257 {
       
   258     m_displayLink->stop();
       
   259 }
       
   260 
       
   261 void QT7MovieRenderer::setupVideoOutput()
       
   262 {
       
   263     AutoReleasePool pool;
       
   264 
       
   265     qDebug() << "QT7MovieRenderer::setupVideoOutput" << m_movie;
       
   266 
       
   267     if (m_movie == 0 || m_surface == 0) {
       
   268         m_displayLink->stop();
       
   269         return;
       
   270     }
       
   271 
       
   272     NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieCurrentSizeAttribute"] sizeValue];
       
   273     m_nativeSize = QSize(size.width, size.height);
       
   274 
       
   275 #ifdef QUICKTIME_C_API_AVAILABLE
       
   276     bool usedGLContext = m_usingGLContext;
       
   277 
       
   278     if (!m_nativeSize.isEmpty()) {
       
   279 
       
   280         bool glSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty();
       
   281 
       
   282         //Try rendering using opengl textures first:
       
   283         if (glSupported) {
       
   284             QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle);
       
   285 
       
   286             if (m_surface->isActive())
       
   287                 m_surface->stop();
       
   288 
       
   289             qDebug() << "Starting the surface with format" << format;
       
   290             if (!m_surface->start(format)) {
       
   291                 qDebug() << "failed to start video surface" << m_surface->error();
       
   292                 glSupported = false;
       
   293             } else {
       
   294                 m_usingGLContext = true;
       
   295             }
       
   296 
       
   297         }
       
   298 
       
   299         if (!glSupported) {
       
   300             m_usingGLContext = false;
       
   301             QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32);
       
   302 
       
   303             if (m_surface->isActive() && m_surface->surfaceFormat() != format) {
       
   304                 qDebug() << "Surface format was changed, stop the surface.";
       
   305                 m_surface->stop();
       
   306             }
       
   307 
       
   308             if (!m_surface->isActive()) {
       
   309                 qDebug() << "Starting the surface with format" << format;
       
   310                 if (!m_surface->start(format))
       
   311                     qDebug() << "failed to start video surface" << m_surface->error();
       
   312             }
       
   313         }
       
   314     }
       
   315 
       
   316 
       
   317     if (m_visualContext) {
       
   318         //check if the visual context still can be reused
       
   319         if (usedGLContext != m_usingGLContext ||
       
   320             (m_usingGLContext && (m_currentGLContext != QGLContext::currentContext())) ||
       
   321             (!m_usingGLContext && (m_pixelBufferContextGeometry != m_nativeSize))) {
       
   322             QTVisualContextRelease(m_visualContext);
       
   323             m_visualContext = 0;
       
   324         }
       
   325     }
       
   326 
       
   327     if (!m_visualContext) {
       
   328         if (m_usingGLContext) {
       
   329             qDebug() << "Building OpenGL visual context";
       
   330             m_currentGLContext = QGLContext::currentContext();
       
   331             if (!createGLVisualContext()) {
       
   332                 qWarning() << "QT7MovieRenderer: failed to create visual context";
       
   333                 return;
       
   334             }
       
   335         } else {
       
   336             qDebug() << "Building Pixel Buffer visual context";
       
   337             if (!createPixelBufferVisualContext()) {
       
   338                 qWarning() << "QT7MovieRenderer: failed to create visual context";
       
   339                 return;
       
   340             }
       
   341         }
       
   342     }
       
   343 
       
   344     // targets a Movie to render into a visual context
       
   345     SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext);
       
   346 
       
   347 
       
   348 #endif
       
   349 
       
   350     m_displayLink->start();
       
   351 }
       
   352 
       
   353 void QT7MovieRenderer::setEnabled(bool)
       
   354 {
       
   355 }
       
   356 
       
   357 void QT7MovieRenderer::setMovie(void *movie)
       
   358 {
       
   359     qDebug() << "QT7MovieRenderer::setMovie" << movie;
       
   360 
       
   361     if (m_movie == movie)
       
   362         return;
       
   363 
       
   364     QMutexLocker locker(&m_mutex);
       
   365 
       
   366 #ifdef QUICKTIME_C_API_AVAILABLE
       
   367     //ensure the old movie doesn't hold the visual context, otherwise it can't be reused
       
   368     if (m_movie && m_visualContext)
       
   369         SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], 0);
       
   370 #endif
       
   371 
       
   372     m_movie = movie;
       
   373     setupVideoOutput();
       
   374 }
       
   375 
       
   376 QAbstractVideoSurface *QT7MovieRenderer::surface() const
       
   377 {
       
   378     return m_surface;
       
   379 }
       
   380 
       
   381 void QT7MovieRenderer::setSurface(QAbstractVideoSurface *surface)
       
   382 {
       
   383     qDebug() << "Set video surface" << surface;
       
   384 
       
   385     if (surface == m_surface)
       
   386         return;
       
   387 
       
   388     QMutexLocker locker(&m_mutex);
       
   389 
       
   390     if (m_surface && m_surface->isActive())
       
   391         m_surface->stop();
       
   392 
       
   393     m_surface = surface;
       
   394     setupVideoOutput();
       
   395 }
       
   396 
       
   397 
       
   398 QSize QT7MovieRenderer::nativeSize() const
       
   399 {
       
   400     return m_nativeSize;
       
   401 }
       
   402 
       
   403 void QT7MovieRenderer::updateVideoFrame(const CVTimeStamp &ts)
       
   404 {
       
   405 #ifdef QUICKTIME_C_API_AVAILABLE
       
   406 
       
   407     QMutexLocker locker(&m_mutex);
       
   408 
       
   409     if (m_surface && m_surface->isActive() &&
       
   410         m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) {
       
   411 
       
   412         CVImageBufferRef imageBuffer = NULL;
       
   413 
       
   414         OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, &imageBuffer);
       
   415 
       
   416         if (status == noErr && imageBuffer) {
       
   417             //qDebug() << "render video frame";
       
   418             QAbstractVideoBuffer *buffer = 0;
       
   419 
       
   420             if (m_usingGLContext) {
       
   421                 buffer = new CVGLTextureVideoBuffer((CVOpenGLTextureRef)imageBuffer);
       
   422                 CVOpenGLTextureRelease((CVOpenGLTextureRef)imageBuffer);
       
   423                 //qDebug() << "render GL video frame" << buffer->handle();
       
   424             } else {
       
   425                 buffer = new CVPixelBufferVideoBuffer((CVPixelBufferRef)imageBuffer);
       
   426                 CVPixelBufferRelease((CVPixelBufferRef)imageBuffer);
       
   427             }
       
   428 
       
   429             QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_RGB32);            
       
   430             m_surface->present(frame);
       
   431             QTVisualContextTask(m_visualContext);
       
   432         }
       
   433     }
       
   434 #else
       
   435     Q_UNUSED(ts);
       
   436 #endif
       
   437 }
       
   438 
       
   439 #include "moc_qt7movierenderer.cpp"