• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008, 2009 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 
26 #include "config.h"
27 
28 #if ENABLE(VIDEO)
29 #include "MediaPlayerPrivateQuickTimeWin.h"
30 
31 #include "GraphicsContext.h"
32 #include "KURL.h"
33 #include "QTMovieWin.h"
34 #include "ScrollView.h"
35 #include "StringHash.h"
36 #include <wtf/HashSet.h>
37 #include <wtf/MathExtras.h>
38 #include <wtf/StdLibExtras.h>
39 
40 #if DRAW_FRAME_RATE
41 #include "Font.h"
42 #include "FrameView.h"
43 #include "Frame.h"
44 #include "Document.h"
45 #include "RenderObject.h"
46 #include "RenderStyle.h"
47 #include "Windows.h"
48 #endif
49 
50 using namespace std;
51 
52 namespace WebCore {
53 
create(MediaPlayer * player)54 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
55 {
56     return new MediaPlayerPrivate(player);
57 }
58 
registerMediaEngine(MediaEngineRegistrar registrar)59 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
60 {
61     if (isAvailable())
62         registrar(create, getSupportedTypes, supportsType);
63 }
64 
MediaPlayerPrivate(MediaPlayer * player)65 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
66     : m_player(player)
67     , m_seekTo(-1)
68     , m_endTime(numeric_limits<float>::infinity())
69     , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired)
70     , m_networkState(MediaPlayer::Empty)
71     , m_readyState(MediaPlayer::HaveNothing)
72     , m_enabledTrackCount(0)
73     , m_totalTrackCount(0)
74     , m_hasUnsupportedTracks(false)
75     , m_startedPlaying(false)
76     , m_isStreaming(false)
77 #if DRAW_FRAME_RATE
78     , m_frameCountWhilePlaying(0)
79     , m_timeStartedPlaying(0)
80     , m_timeStoppedPlaying(0)
81 #endif
82 {
83 }
84 
~MediaPlayerPrivate()85 MediaPlayerPrivate::~MediaPlayerPrivate()
86 {
87 }
88 
load(const String & url)89 void MediaPlayerPrivate::load(const String& url)
90 {
91     if (!QTMovieWin::initializeQuickTime()) {
92         // FIXME: is this the right error to return?
93         m_networkState = MediaPlayer::DecodeError;
94         m_player->networkStateChanged();
95         return;
96     }
97 
98     if (m_networkState != MediaPlayer::Loading) {
99         m_networkState = MediaPlayer::Loading;
100         m_player->networkStateChanged();
101     }
102     if (m_readyState != MediaPlayer::HaveNothing) {
103         m_readyState = MediaPlayer::HaveNothing;
104         m_player->readyStateChanged();
105     }
106     cancelSeek();
107 
108     m_qtMovie.set(new QTMovieWin(this));
109     m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch());
110     m_qtMovie->setVolume(m_player->volume());
111     m_qtMovie->setVisible(m_player->visible());
112 }
113 
play()114 void MediaPlayerPrivate::play()
115 {
116     if (!m_qtMovie)
117         return;
118     m_startedPlaying = true;
119 #if DRAW_FRAME_RATE
120     m_frameCountWhilePlaying = 0;
121 #endif
122 
123     m_qtMovie->play();
124 }
125 
pause()126 void MediaPlayerPrivate::pause()
127 {
128     if (!m_qtMovie)
129         return;
130     m_startedPlaying = false;
131 #if DRAW_FRAME_RATE
132     m_timeStoppedPlaying = GetTickCount();
133 #endif
134     m_qtMovie->pause();
135 }
136 
duration() const137 float MediaPlayerPrivate::duration() const
138 {
139     if (!m_qtMovie)
140         return 0;
141     return m_qtMovie->duration();
142 }
143 
currentTime() const144 float MediaPlayerPrivate::currentTime() const
145 {
146     if (!m_qtMovie)
147         return 0;
148     return min(m_qtMovie->currentTime(), m_endTime);
149 }
150 
seek(float time)151 void MediaPlayerPrivate::seek(float time)
152 {
153     cancelSeek();
154 
155     if (!m_qtMovie)
156         return;
157 
158     if (time > duration())
159         time = duration();
160 
161     m_seekTo = time;
162     if (maxTimeLoaded() >= m_seekTo)
163         doSeek();
164     else
165         m_seekTimer.start(0, 0.5f);
166 }
167 
doSeek()168 void MediaPlayerPrivate::doSeek()
169 {
170     float oldRate = m_qtMovie->rate();
171     if (oldRate)
172         m_qtMovie->setRate(0);
173     m_qtMovie->setCurrentTime(m_seekTo);
174     float timeAfterSeek = currentTime();
175     // restore playback only if not at end, othewise QTMovie will loop
176     if (oldRate && timeAfterSeek < duration() && timeAfterSeek < m_endTime)
177         m_qtMovie->setRate(oldRate);
178     cancelSeek();
179 }
180 
cancelSeek()181 void MediaPlayerPrivate::cancelSeek()
182 {
183     m_seekTo = -1;
184     m_seekTimer.stop();
185 }
186 
seekTimerFired(Timer<MediaPlayerPrivate> *)187 void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*)
188 {
189     if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
190         cancelSeek();
191         updateStates();
192         m_player->timeChanged();
193         return;
194     }
195 
196     if (maxTimeLoaded() >= m_seekTo)
197         doSeek();
198     else {
199         MediaPlayer::NetworkState state = networkState();
200         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
201             cancelSeek();
202             updateStates();
203             m_player->timeChanged();
204         }
205     }
206 }
207 
setEndTime(float time)208 void MediaPlayerPrivate::setEndTime(float time)
209 {
210     m_endTime = time;
211 }
212 
paused() const213 bool MediaPlayerPrivate::paused() const
214 {
215     if (!m_qtMovie)
216         return true;
217     return m_qtMovie->rate() == 0.0f;
218 }
219 
seeking() const220 bool MediaPlayerPrivate::seeking() const
221 {
222     if (!m_qtMovie)
223         return false;
224     return m_seekTo >= 0;
225 }
226 
naturalSize() const227 IntSize MediaPlayerPrivate::naturalSize() const
228 {
229     if (!m_qtMovie)
230         return IntSize();
231     int width;
232     int height;
233     m_qtMovie->getNaturalSize(width, height);
234     return IntSize(width, height);
235 }
236 
hasVideo() const237 bool MediaPlayerPrivate::hasVideo() const
238 {
239     if (!m_qtMovie)
240         return false;
241     return m_qtMovie->hasVideo();
242 }
243 
setVolume(float volume)244 void MediaPlayerPrivate::setVolume(float volume)
245 {
246     if (!m_qtMovie)
247         return;
248     m_qtMovie->setVolume(volume);
249 }
250 
setRate(float rate)251 void MediaPlayerPrivate::setRate(float rate)
252 {
253     if (!m_qtMovie)
254         return;
255     m_qtMovie->setRate(rate);
256 }
257 
setPreservesPitch(bool preservesPitch)258 void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch)
259 {
260     if (!m_qtMovie)
261         return;
262     m_qtMovie->setPreservesPitch(preservesPitch);
263 }
264 
dataRate() const265 int MediaPlayerPrivate::dataRate() const
266 {
267     // This is not used at the moment
268     return 0;
269 }
270 
maxTimeBuffered() const271 float MediaPlayerPrivate::maxTimeBuffered() const
272 {
273     // rtsp streams are not buffered
274     return m_isStreaming ? 0 : maxTimeLoaded();
275 }
276 
maxTimeSeekable() const277 float MediaPlayerPrivate::maxTimeSeekable() const
278 {
279     // infinite duration means live stream
280     return !isfinite(duration()) ? 0 : maxTimeLoaded();
281 }
282 
maxTimeLoaded() const283 float MediaPlayerPrivate::maxTimeLoaded() const
284 {
285     if (!m_qtMovie)
286         return 0;
287     return m_qtMovie->maxTimeLoaded();
288 }
289 
bytesLoaded() const290 unsigned MediaPlayerPrivate::bytesLoaded() const
291 {
292     if (!m_qtMovie)
293         return 0;
294     float dur = duration();
295     float maxTime = maxTimeLoaded();
296     if (!dur)
297         return 0;
298     return totalBytes() * maxTime / dur;
299 }
300 
totalBytesKnown() const301 bool MediaPlayerPrivate::totalBytesKnown() const
302 {
303     return totalBytes() > 0;
304 }
305 
totalBytes() const306 unsigned MediaPlayerPrivate::totalBytes() const
307 {
308     if (!m_qtMovie)
309         return 0;
310     return m_qtMovie->dataSize();
311 }
312 
cancelLoad()313 void MediaPlayerPrivate::cancelLoad()
314 {
315     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
316         return;
317 
318     // Cancel the load by destroying the movie.
319     m_qtMovie.clear();
320 
321     updateStates();
322 }
323 
updateStates()324 void MediaPlayerPrivate::updateStates()
325 {
326     MediaPlayer::NetworkState oldNetworkState = m_networkState;
327     MediaPlayer::ReadyState oldReadyState = m_readyState;
328 
329     long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError;
330 
331     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
332         m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
333         if (m_player->inMediaDocument()) {
334             if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
335                 // This is a type of media that we do not handle directly with a <video>
336                 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the
337                 // MediaPlayerClient that we won't support it.
338                 sawUnsupportedTracks();
339                 return;
340             }
341         } else if (!m_enabledTrackCount)
342             loadState = QTMovieLoadStateError;
343     }
344 
345     // "Loaded" is reserved for fully buffered movies, never the case when streaming
346     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
347         m_networkState = MediaPlayer::Loaded;
348         m_readyState = MediaPlayer::HaveEnoughData;
349     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
350         m_readyState = MediaPlayer::HaveEnoughData;
351     } else if (loadState >= QTMovieLoadStatePlayable) {
352         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
353         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
354     } else if (loadState >= QTMovieLoadStateLoaded) {
355         m_readyState = MediaPlayer::HaveMetadata;
356     } else if (loadState > QTMovieLoadStateError) {
357         m_networkState = MediaPlayer::Loading;
358         m_readyState = MediaPlayer::HaveNothing;
359     } else {
360         if (m_player->inMediaDocument()) {
361             // Something went wrong in the loading of media within a standalone file.
362             // This can occur with chained ref movies that eventually resolve to a
363             // file we don't support.
364             sawUnsupportedTracks();
365             return;
366         }
367 
368         float loaded = maxTimeLoaded();
369         if (!loaded)
370             m_readyState = MediaPlayer::HaveNothing;
371 
372         if (!m_enabledTrackCount)
373             m_networkState = MediaPlayer::FormatError;
374         else {
375             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
376             if (loaded > 0)
377                 m_networkState = MediaPlayer::DecodeError;
378             else
379                 m_readyState = MediaPlayer::HaveNothing;
380         }
381     }
382 
383     if (seeking())
384         m_readyState = MediaPlayer::HaveNothing;
385 
386     if (m_networkState != oldNetworkState)
387         m_player->networkStateChanged();
388     if (m_readyState != oldReadyState)
389         m_player->readyStateChanged();
390 }
391 
sawUnsupportedTracks()392 void MediaPlayerPrivate::sawUnsupportedTracks()
393 {
394     m_qtMovie->setDisabled(true);
395     m_hasUnsupportedTracks = true;
396     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
397 }
398 
didEnd()399 void MediaPlayerPrivate::didEnd()
400 {
401     if (m_hasUnsupportedTracks)
402         return;
403 
404     m_startedPlaying = false;
405 #if DRAW_FRAME_RATE
406     m_timeStoppedPlaying = GetTickCount();
407 #endif
408     updateStates();
409     m_player->timeChanged();
410 }
411 
setSize(const IntSize & size)412 void MediaPlayerPrivate::setSize(const IntSize& size)
413 {
414     if (m_hasUnsupportedTracks || !m_qtMovie)
415         return;
416     m_qtMovie->setSize(size.width(), size.height());
417 }
418 
setVisible(bool b)419 void MediaPlayerPrivate::setVisible(bool b)
420 {
421     if (m_hasUnsupportedTracks || !m_qtMovie)
422         return;
423     m_qtMovie->setVisible(b);
424 }
425 
paint(GraphicsContext * p,const IntRect & r)426 void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r)
427 {
428     if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks)
429         return;
430 
431     bool usingTempBitmap = false;
432     OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
433     HDC hdc = p->getWindowsContext(r);
434     if (!hdc) {
435         // The graphics context doesn't have an associated HDC so create a temporary
436         // bitmap where QTMovieWin can draw the frame and we can copy it.
437         usingTempBitmap = true;
438         bitmap.set(p->createWindowsBitmap(r.size()));
439         hdc = bitmap->hdc();
440 
441         // FIXME: is this necessary??
442         XFORM xform;
443         xform.eM11 = 1.0f;
444         xform.eM12 = 0.0f;
445         xform.eM21 = 0.0f;
446         xform.eM22 = 1.0f;
447         xform.eDx = -r.x();
448         xform.eDy = -r.y();
449         SetWorldTransform(hdc, &xform);
450     }
451 
452     m_qtMovie->paint(hdc, r.x(), r.y());
453     if (usingTempBitmap)
454         p->drawWindowsBitmap(bitmap.get(), r.topLeft());
455     else
456         p->releaseWindowsContext(hdc, r);
457 
458 #if DRAW_FRAME_RATE
459     if (m_frameCountWhilePlaying > 10) {
460         Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL;
461         Document* document = frame ? frame->document() : NULL;
462         RenderObject* renderer = document ? document->renderer() : NULL;
463         RenderStyle* styleToUse = renderer ? renderer->style() : NULL;
464         if (styleToUse) {
465             double frameRate = (m_frameCountWhilePlaying - 1) / (0.001 * ( m_startedPlaying ? (GetTickCount() - m_timeStartedPlaying) :
466                 (m_timeStoppedPlaying - m_timeStartedPlaying) ));
467             String text = String::format("%1.2f", frameRate);
468             TextRun textRun(text.characters(), text.length());
469             const Color color(255, 0, 0);
470             p->save();
471             p->translate(r.x(), r.y() + r.height());
472             p->setFont(styleToUse->font());
473             p->setStrokeColor(color);
474             p->setStrokeStyle(SolidStroke);
475             p->setStrokeThickness(1.0f);
476             p->setFillColor(color);
477             p->drawText(textRun, IntPoint(2, -3));
478             p->restore();
479         }
480     }
481 #endif
482 }
483 
mimeTypeCache()484 static HashSet<String> mimeTypeCache()
485 {
486     DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
487     static bool typeListInitialized = false;
488 
489     if (!typeListInitialized) {
490         unsigned count = QTMovieWin::countSupportedTypes();
491         for (unsigned n = 0; n < count; n++) {
492             const UChar* character;
493             unsigned len;
494             QTMovieWin::getSupportedType(n, character, len);
495             if (len)
496                 typeCache.add(String(character, len));
497         }
498 
499         typeListInitialized = true;
500     }
501 
502     return typeCache;
503 }
504 
getSupportedTypes(HashSet<String> & types)505 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
506 {
507     types = mimeTypeCache();
508 }
509 
isAvailable()510 bool MediaPlayerPrivate::isAvailable()
511 {
512     return QTMovieWin::initializeQuickTime();
513 }
514 
supportsType(const String & type,const String & codecs)515 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
516 {
517     // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
518     //  extended MIME type
519     return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
520 }
521 
movieEnded(QTMovieWin * movie)522 void MediaPlayerPrivate::movieEnded(QTMovieWin* movie)
523 {
524     if (m_hasUnsupportedTracks)
525         return;
526 
527     ASSERT(m_qtMovie.get() == movie);
528     didEnd();
529 }
530 
movieLoadStateChanged(QTMovieWin * movie)531 void MediaPlayerPrivate::movieLoadStateChanged(QTMovieWin* movie)
532 {
533     if (m_hasUnsupportedTracks)
534         return;
535 
536     ASSERT(m_qtMovie.get() == movie);
537     updateStates();
538 }
539 
movieTimeChanged(QTMovieWin * movie)540 void MediaPlayerPrivate::movieTimeChanged(QTMovieWin* movie)
541 {
542     if (m_hasUnsupportedTracks)
543         return;
544 
545     ASSERT(m_qtMovie.get() == movie);
546     updateStates();
547     m_player->timeChanged();
548 }
549 
movieNewImageAvailable(QTMovieWin * movie)550 void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie)
551 {
552     if (m_hasUnsupportedTracks)
553         return;
554 
555     ASSERT(m_qtMovie.get() == movie);
556 #if DRAW_FRAME_RATE
557     if (m_startedPlaying) {
558         m_frameCountWhilePlaying++;
559         // to eliminate preroll costs from our calculation,
560         // our frame rate calculation excludes the first frame drawn after playback starts
561         if (1==m_frameCountWhilePlaying)
562             m_timeStartedPlaying = GetTickCount();
563     }
564 #endif
565     m_player->repaint();
566 }
567 
hasSingleSecurityOrigin() const568 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
569 {
570     // We tell quicktime to disallow resources that come from different origins
571     // so we all media is single origin.
572     return true;
573 }
574 
575 }
576 
577 #endif
578 
579