1/*
2 * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include "config.h"
26
27#include "QTMovieVisualContext.h"
28
29#include "QTMovieTask.h"
30#include <CVBase.h>
31#include <CVHostTime.h>
32#include <ImageCompression.h>
33#include <Movies.h>
34#include <windows.h>
35
36struct QTCVTimeStamp {
37    CVTimeStamp t;
38};
39
40class QTMovieVisualContextPriv {
41public:
42    QTMovieVisualContextPriv(QTMovieVisualContext* parent, QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType);
43    ~QTMovieVisualContextPriv();
44
45    bool isImageAvailableForTime(const QTCVTimeStamp*) const;
46    QTPixelBuffer imageForTime(const QTCVTimeStamp*);
47    void task();
48
49    QTVisualContextRef visualContextRef();
50
51    void setMovie(PassRefPtr<QTMovie>);
52    QTMovie* movie() const;
53
54    static void imageAvailableCallback(QTVisualContextRef visualContext, const CVTimeStamp *timeStamp, void *refCon);
55
56private:
57    QTMovieVisualContext* m_parent;
58    QTMovieVisualContextClient* m_client;
59    QTVisualContextRef m_visualContext;
60    RefPtr<QTMovie> m_movie;
61
62};
63
64static CFDictionaryRef createPixelBufferOptionsDictionary(QTPixelBuffer::Type contextType)
65{
66    const void* key = kQTVisualContextPixelBufferAttributesKey;
67    const void* value = QTPixelBuffer::createPixelBufferAttributesDictionary(contextType);
68    CFDictionaryRef pixelBufferOptions = CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
69    CFRelease(value);
70    return pixelBufferOptions;
71}
72
73static CFDictionaryRef pixelBufferCreationOptions(QTPixelBuffer::Type contextType)
74{
75    if (contextType == QTPixelBuffer::ConfigureForCAImageQueue) {
76        static CFDictionaryRef imageQueueOptions = createPixelBufferOptionsDictionary(contextType);
77        return imageQueueOptions;
78    }
79
80    ASSERT(contextType == QTPixelBuffer::ConfigureForCGImage);
81    static CFDictionaryRef cgImageOptions = createPixelBufferOptionsDictionary(contextType);
82    return cgImageOptions;
83}
84
85QTMovieVisualContextPriv::QTMovieVisualContextPriv(QTMovieVisualContext* parent, QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType)
86        : m_parent(parent)
87        , m_client(client)
88        , m_visualContext(0)
89{
90    typedef OSStatus ( __cdecl *pfnQTPixelBufferContextCreate)(CFAllocatorRef, CFDictionaryRef, QTVisualContextRef*);
91    static pfnQTPixelBufferContextCreate pPixelBufferContextCreate = 0;
92    if (!pPixelBufferContextCreate) {
93        HMODULE qtmlDLL = ::LoadLibraryW(L"QTMLClient.dll");
94        if (!qtmlDLL)
95            return;
96        pPixelBufferContextCreate = reinterpret_cast<pfnQTPixelBufferContextCreate>(GetProcAddress(qtmlDLL, "QTPixelBufferContextCreate"));
97        if (!pPixelBufferContextCreate)
98            return;
99    }
100
101    OSStatus status = pPixelBufferContextCreate(kCFAllocatorDefault, pixelBufferCreationOptions(contextType), &m_visualContext);
102    if (status == noErr && m_visualContext)
103        QTVisualContextSetImageAvailableCallback(m_visualContext, &QTMovieVisualContextPriv::imageAvailableCallback, static_cast<void*>(this));
104}
105
106QTMovieVisualContextPriv::~QTMovieVisualContextPriv()
107{
108    if (m_visualContext)
109        QTVisualContextSetImageAvailableCallback(m_visualContext, 0, 0);
110}
111
112bool QTMovieVisualContextPriv::isImageAvailableForTime(const QTCVTimeStamp* timeStamp) const
113{
114    if (!m_visualContext)
115        return false;
116
117    return QTVisualContextIsNewImageAvailable(m_visualContext, reinterpret_cast<const CVTimeStamp*>(timeStamp));
118}
119
120QTPixelBuffer QTMovieVisualContextPriv::imageForTime(const QTCVTimeStamp* timeStamp)
121{
122    QTPixelBuffer pixelBuffer;
123    if (m_visualContext) {
124        CVImageBufferRef newImage = 0;
125        OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, kCFAllocatorDefault, reinterpret_cast<const CVTimeStamp*>(timeStamp), &newImage);
126        if (status == noErr)
127            pixelBuffer.adopt(newImage);
128    }
129    return pixelBuffer;
130}
131
132void QTMovieVisualContextPriv::task()
133{
134    if (m_visualContext)
135        QTVisualContextTask(m_visualContext);
136}
137
138QTVisualContextRef QTMovieVisualContextPriv::visualContextRef()
139{
140    return m_visualContext;
141}
142
143void QTMovieVisualContextPriv::setMovie(PassRefPtr<QTMovie> movie)
144{
145    if (movie == m_movie)
146        return;
147
148    if (m_movie) {
149        SetMovieVisualContext(m_movie->getMovieHandle(), 0);
150        m_movie = 0;
151    }
152
153    if (movie)
154        OSStatus status = SetMovieVisualContext(movie->getMovieHandle(), m_visualContext);
155
156    m_movie = movie;
157}
158
159QTMovie* QTMovieVisualContextPriv::movie() const
160{
161    return m_movie.get();
162}
163
164void QTMovieVisualContextPriv::imageAvailableCallback(QTVisualContextRef visualContext, const CVTimeStamp *timeStamp, void *refCon)
165{
166    if (!refCon)
167        return;
168
169    QTMovieVisualContextPriv* vc = static_cast<QTMovieVisualContextPriv*>(refCon);
170    if (!vc->m_client)
171        return;
172
173    vc->m_client->imageAvailableForTime(reinterpret_cast<const QTCVTimeStamp*>(timeStamp));
174}
175
176PassRefPtr<QTMovieVisualContext> QTMovieVisualContext::create(QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType)
177{
178    return adoptRef(new QTMovieVisualContext(client, contextType));
179}
180
181QTMovieVisualContext::QTMovieVisualContext(QTMovieVisualContextClient* client, QTPixelBuffer::Type contextType)
182    : m_private(new QTMovieVisualContextPriv(this, client, contextType))
183{
184}
185
186QTMovieVisualContext::~QTMovieVisualContext()
187{
188}
189
190bool QTMovieVisualContext::isImageAvailableForTime(const QTCVTimeStamp* timeStamp) const
191{
192    return m_private->isImageAvailableForTime(timeStamp);
193}
194
195QTPixelBuffer QTMovieVisualContext::imageForTime(const QTCVTimeStamp* timeStamp)
196{
197    return m_private->imageForTime(timeStamp);
198}
199
200void QTMovieVisualContext::task()
201{
202    m_private->task();
203}
204
205QTVisualContextRef QTMovieVisualContext::visualContextRef()
206{
207    return m_private->visualContextRef();
208}
209
210void QTMovieVisualContext::setMovie(PassRefPtr<QTMovie> movie)
211{
212    m_private->setMovie(movie);
213}
214
215QTMovie* QTMovieVisualContext::movie() const
216{
217    return m_private->movie();
218}
219
220double QTMovieVisualContext::currentHostTime()
221{
222    return CVGetCurrentHostTime() / CVGetHostClockFrequency();
223}
224