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