1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: ML 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla code.
18 * The Initial Developer of the Original Code is the Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Chris Double <chris.double@double.co.nz>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
41 #include "nsIDocument.h"
42 #include "nsThreadUtils.h"
43 #include "nsIDOMHTMLMediaElement.h"
44 #include "nsNetUtil.h"
45 #include "nsAudioStream.h"
46 #include "nsChannelReader.h"
47 #include "nsHTMLVideoElement.h"
48 #include "nsIObserver.h"
49 #include "nsIObserverService.h"
50 #include "nsAutoLock.h"
51 #include "nsIRenderingContext.h"
52 #include "gfxContext.h"
53 #include "gfxImageSurface.h"
54 #include "nsPresContext.h"
55 #include "nsOggDecoder.h"
58 The maximum height and width of the video. Used for
59 sanitizing the memory allocation of the RGB buffer
61 #define MAX_VIDEO_WIDTH 2000
62 #define MAX_VIDEO_HEIGHT 2000
64 // The number of entries in oggplay buffer list. This value
65 // is the one used by the oggplay examples.
66 #define OGGPLAY_BUFFER_SIZE 20
68 // The number of frames to read before audio callback is called.
69 // This value is the one used by the oggplay examples.
70 #define OGGPLAY_FRAMES_PER_CALLBACK 2048
72 // Offset into Ogg buffer containing audio information. This value
73 // is the one used by the oggplay examples.
74 #define OGGPLAY_AUDIO_OFFSET 250L
76 // Maximum mumber of milliseconds to pause while buffering video data
77 // on a slow connection. The DATA_WAIT is used to compute the maximum
78 // amount of data to wait for based on the current bytes/second
79 // download rate. The TIME_WAIT is the total maximum time to wait in
80 // case the bytes/second rate slows down.
81 #define MAX_BUFFERING_DATA_WAIT_MS 15000
82 #define MAX_BUFFERING_TIME_WAIT_MS 30000
84 // An Event that uses an nsOggDecoder object. It provides a
85 // way to inform the event that the decoder object is no longer
86 // valid, so queued events can safely ignore it.
87 class nsDecoderEvent
: public nsRunnable
{
89 nsDecoderEvent(nsOggDecoder
* decoder
) :
98 return mLock
!= nsnull
;
101 virtual ~nsDecoderEvent()
104 PR_DestroyLock(mLock
);
111 nsAutoLock
lock(mLock
);
126 nsAutoLock
lock(mLock
);
127 return RunWithLock();
131 virtual nsresult
RunWithLock() = 0;
134 nsOggDecoder
* mDecoder
;
137 // This handles the Ogg decoding loop. It blocks waiting for
138 // ogg data, decodes that data, and then goes back to blocking.
139 // It is synchronised with the nsVideoPresentationEvent internally
140 // by OggPlay so that it doesn't go to far ahead or behind the
141 // display of the video frame data.
142 class nsVideoDecodeEvent
: public nsDecoderEvent
145 nsVideoDecodeEvent(nsOggDecoder
* decoder
) :
146 nsDecoderEvent(decoder
)
151 nsresult
RunWithLock()
153 if (mDecoder
&& mDecoder
->StepDecoding()) {
154 NS_GetCurrentThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
157 LOG(PR_LOG_DEBUG
, ("Decoding thread completed"));
163 // PR_TRUE if we are actively decoding
164 PRPackedBool mDecoding
;
167 class nsVideoPresentationEvent
: public nsDecoderEvent
170 nsVideoPresentationEvent(nsOggDecoder
* decoder
) :
171 nsDecoderEvent(decoder
)
175 // Stop the invalidation timer. When we aren't decoding
176 // video frames we stop the timer since it takes a fair
177 // amount of CPU on some platforms.
178 void StopInvalidating()
181 // Stop the invalidation timer
182 nsCOMPtr
<nsIRunnable
> event
=
183 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, mDecoder
, StopInvalidating
);
186 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
191 nsresult
RunWithLock() {
192 if (mDecoder
&& !mDecoder
->IsPaused() && mDecoder
->StepDisplay()) {
193 NS_GetCurrentThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
196 LOG(PR_LOG_DEBUG
, ("Presentation thread completed"));
203 NS_IMPL_THREADSAFE_ISUPPORTS1(nsOggDecoder
, nsIObserver
)
205 void nsOggDecoder::Pause()
207 if (!mPresentationThread
)
211 nsCOMPtr
<nsIRunnable
> event
=
212 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, DoPause
);
214 mPresentationThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
217 void nsOggDecoder::DoPause()
221 mAudioStream
->Pause();
223 mSystemSyncSeconds
= double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
226 float nsOggDecoder::GetVolume()
230 mAudioStream
->GetVolume(&volume
);
233 volume
= mInitialVolume
;
239 void nsOggDecoder::SetVolume(float volume
)
242 mAudioStream
->SetVolume(volume
);
245 mInitialVolume
= volume
;
249 float nsOggDecoder::GetDuration()
251 // Currently not implemented. Video Spec says to return
257 nsOggDecoder::nsOggDecoder() :
260 mVideoNextFrameTime(0.0),
261 mLoadInProgress(PR_FALSE
),
262 mPlayAfterLoad(PR_FALSE
),
263 mNotifyOnShutdown(PR_FALSE
),
264 mVideoCurrentFrameTime(0.0),
273 mFirstFrameLoaded(PR_FALSE
),
274 mFirstFrameCondVar(nsnull
),
275 mFirstFrameLock(nsnull
),
276 mSystemSyncSeconds(0.0),
277 mResourceLoaded(PR_FALSE
),
278 mMetadataLoaded(PR_FALSE
)
282 PRBool
nsOggDecoder::Init()
284 mFirstFrameLock
= PR_NewLock();
285 mFirstFrameCondVar
= mFirstFrameLock
? PR_NewCondVar(mFirstFrameLock
) : nsnull
;
287 mDecodeEvent
= new nsVideoDecodeEvent(this);
288 mPresentationEvent
= new nsVideoPresentationEvent(this);
290 return mFirstFrameLock
&&
291 mFirstFrameCondVar
&&
292 mDecodeEvent
&& mDecodeEvent
->Init() &&
293 mPresentationEvent
&& mPresentationEvent
->Init() &&
294 nsVideoDecoder::Init();
297 void nsOggDecoder::Shutdown()
300 // Must prepare oggplay for close to stop the decode event from
301 // waiting on oggplay's semaphore. Without this, revoke deadlocks.
303 oggplay_prepare_for_close(mPlayer
);
306 mDecodeEvent
->Revoke();
307 mDecodeEvent
= nsnull
;
309 if (mPresentationEvent
) {
310 mPresentationEvent
->Revoke();
311 mPresentationEvent
= nsnull
;
315 nsVideoDecoder::Shutdown();
318 nsOggDecoder::~nsOggDecoder()
321 if (mFirstFrameCondVar
) {
322 PR_DestroyCondVar(mFirstFrameCondVar
);
323 mFirstFrameCondVar
= nsnull
;
325 if (mFirstFrameLock
) {
326 PR_DestroyLock(mFirstFrameLock
);
327 mFirstFrameLock
= nsnull
;
331 nsIntSize
nsOggDecoder::GetVideoSize(nsIntSize defaultSize
)
333 return (mRGBWidth
== -1 || mRGBHeight
== -1) ? defaultSize
: nsIntSize(mRGBWidth
, mRGBHeight
);
336 double nsOggDecoder::GetVideoFramerate() {
340 PRBool
nsOggDecoder::IsPaused()
345 PRBool
nsOggDecoder::StepDecoding()
347 PRBool stop
= PR_TRUE
;
348 if (mPlayer
&& mDecodeThread
) {
349 OggPlayErrorCode r
= oggplay_step_decoding(mPlayer
);
350 if (r
!= E_OGGPLAY_CONTINUE
&&
351 r
!= E_OGGPLAY_USER_INTERRUPT
&&
352 r
!= E_OGGPLAY_TIMEOUT
) {
354 // Inform the element that we've ended the video
355 nsCOMPtr
<nsIRunnable
> event
=
356 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, PlaybackCompleted
);
359 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
365 if (r
== E_OGGPLAY_CONTINUE
)
372 void nsOggDecoder::BufferData()
374 if (!mPaused
&& mReader
&& mMetadataLoaded
&& !mResourceLoaded
) {
375 double bps
= mReader
->BytesPerSecond();
376 PRUint32 bytes
= static_cast<PRUint32
>((bps
* MAX_BUFFERING_DATA_WAIT_MS
)/1000.0);
378 // Buffer if it looks like we'll starve for data during
379 // MAX_BUFFERING_DATA_WAIT_MS time period.
380 if (mReader
->Available() < bytes
) {
381 PRIntervalTime start
= PR_IntervalNow();
383 nsCOMPtr
<nsIRunnable
> event
=
384 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, BufferingStarted
);
386 // The pause event must be processed before the
387 // buffering loop occurs. Otherwise the loop exits
388 // immediately as it checks to see if the user chose
390 NS_DispatchToMainThread(event
, NS_DISPATCH_SYNC
);
393 // Sleep to allow more data to be downloaded. Note that we are
394 // sleeping the decode thread, not the main thread. We stop
395 // sleeping when the resource is completely loaded, or the user
396 // explicitly chooses to continue playing, or an arbitary time
397 // period has elapsed or we've downloaded enough bytes to
398 // continue playing at the current download rate.
399 while (!mResourceLoaded
&&
401 (PR_IntervalToMilliseconds(PR_IntervalNow() - start
) < MAX_BUFFERING_TIME_WAIT_MS
) &&
402 mReader
->Available() < bytes
) {
404 ("Buffering data until %d bytes available or %d milliseconds",
405 (long)(bytes
- mReader
->Available()),
406 MAX_BUFFERING_TIME_WAIT_MS
- (PR_IntervalToMilliseconds(PR_IntervalNow() - start
))));
408 PR_Sleep(PR_MillisecondsToInterval(1000));
410 bps
= mReader
->BytesPerSecond();
411 bytes
= static_cast<PRUint32
>((bps
* (MAX_BUFFERING_DATA_WAIT_MS
))/1000.0);
415 nsCOMPtr
<nsIRunnable
> event
=
416 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, BufferingStopped
);
418 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
424 nsresult
nsOggDecoder::Load(nsIURI
* aURI
)
430 mReader
= new nsChannelReader();
431 NS_ENSURE_TRUE(mReader
, NS_ERROR_OUT_OF_MEMORY
);
433 rv
= mReader
->Init(this, aURI
);
434 NS_ENSURE_SUCCESS(rv
, rv
);
436 rv
= NS_NewThread(getter_AddRefs(mDecodeThread
));
437 NS_ENSURE_SUCCESS(rv
, rv
);
438 rv
= NS_NewThread(getter_AddRefs(mPresentationThread
));
439 NS_ENSURE_SUCCESS(rv
, rv
);
441 mLoadInProgress
= PR_TRUE
;
442 nsCOMPtr
<nsIRunnable
> event
=
443 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, LoadOggHeaders
);
444 NS_ENSURE_TRUE(event
, NS_ERROR_OUT_OF_MEMORY
);
446 rv
= mDecodeThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
447 NS_ENSURE_SUCCESS(rv
, rv
);
454 nsresult
nsOggDecoder::Play()
457 if (mLoadInProgress
) {
458 mPlayAfterLoad
= PR_TRUE
;
465 StartPlaybackThreads();
468 if (!mNotifyOnShutdown
) {
469 nsCOMPtr
<nsIObserverService
> observerService
=
470 do_GetService("@mozilla.org/observer-service;1");
471 if (observerService
) {
473 NS_SUCCEEDED(observerService
->AddObserver(this,
474 NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
478 NS_WARNING("Could not get an observer service. Video decoding events may not shutdown cleanly.");
485 nsresult
nsOggDecoder::Seek(float time
)
487 return NS_ERROR_NOT_IMPLEMENTED
;
490 nsresult
nsOggDecoder::PlaybackRateChanged()
492 return NS_ERROR_NOT_IMPLEMENTED
;
495 void nsOggDecoder::Stop()
497 mLoadInProgress
= PR_FALSE
;
502 oggplay_prepare_for_close(mPlayer
);
504 mDecodeThread
->Shutdown();
505 mDecodeThread
= nsnull
;
507 if (mPresentationThread
) {
508 if (!mFirstFrameLoaded
) {
509 nsAutoLock
lock(mFirstFrameLock
);
510 mFirstFrameLoaded
= PR_TRUE
;
511 PR_NotifyAllCondVar(mFirstFrameCondVar
);
514 mPresentationThread
->Shutdown();
515 mPresentationThread
= nsnull
;
519 oggplay_close(mPlayer
);
523 mVideoCurrentFrameTime
= 0.0;
525 if (mNotifyOnShutdown
) {
526 nsCOMPtr
<nsIObserverService
> observerService
=
527 do_GetService("@mozilla.org/observer-service;1");
528 if (observerService
) {
529 mNotifyOnShutdown
= PR_FALSE
;
530 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
535 void nsOggDecoder::HandleVideoData(int track_num
, OggPlayVideoData
* video_data
) {
538 oggplay_get_video_y_size(mPlayer
, track_num
, &y_width
, &y_height
);
542 oggplay_get_video_uv_size(mPlayer
, track_num
, &uv_width
, &uv_height
);
544 if (y_width
>= MAX_VIDEO_WIDTH
|| y_height
>= MAX_VIDEO_HEIGHT
)
548 nsAutoLock
lock(mVideoUpdateLock
);
550 SetRGBData(y_width
, y_height
, mFramerate
, nsnull
);
552 // If there is not enough memory to allocate the RGB buffer,
553 // don't display it, but continue without error. When enough
554 // memory is available the display will start working again.
556 OggPlayYUVChannels yuv
;
557 OggPlayRGBChannels rgb
;
559 yuv
.ptry
= video_data
->y
;
560 yuv
.ptru
= video_data
->u
;
561 yuv
.ptrv
= video_data
->v
;
562 yuv
.uv_width
= uv_width
;
563 yuv
.uv_height
= uv_height
;
564 yuv
.y_width
= y_width
;
565 yuv
.y_height
= y_height
;
567 rgb
.ptro
= mRGB
.get();
568 rgb
.rgb_width
= mRGBWidth
;
569 rgb
.rgb_height
= mRGBHeight
;
571 oggplay_yuv2bgr(&yuv
, &rgb
);
576 void nsOggDecoder::HandleAudioData(OggPlayAudioData
* audio_data
, int size
) {
578 // 'size' is number of samples. Multiply by number of channels
579 // to get the actual number of floats being sent.
580 nsresult rv
= mAudioStream
->Write(reinterpret_cast<float*>(audio_data
), size
* mAudioChannels
);
581 if (!NS_SUCCEEDED(rv
)) {
582 LOG(PR_LOG_ERROR
, ("Could not write audio data to pipe"));
587 double nsOggDecoder::GetSyncTime()
590 if (mAudioStream
&& mAudioTrack
!= -1) {
591 mAudioStream
->GetTime(&time
);
594 // No audio stream, or audio track. Sync to system clock.
596 (mSystemSyncSeconds
== 0.0) ?
598 double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0 - mSystemSyncSeconds
;
604 void nsOggDecoder::OpenAudioStream()
606 mAudioStream
= new nsAudioStream();
608 LOG(PR_LOG_ERROR
, ("Could not create audio stream"));
611 mAudioStream
->Init(mAudioChannels
, mAudioRate
);
612 mAudioStream
->SetVolume(mInitialVolume
);
616 void nsOggDecoder::CloseAudioStream()
619 mAudioStream
->Shutdown();
620 mAudioStream
= nsnull
;
624 void nsOggDecoder::LoadOggHeaders()
626 LOG(PR_LOG_DEBUG
, ("Loading Ogg Headers"));
627 mPlayer
= oggplay_open_with_reader((OggPlayReader
*)mReader
);
629 LOG(PR_LOG_DEBUG
, ("There are %d tracks", oggplay_get_num_tracks(mPlayer
)));
631 for (int i
= 0; i
< oggplay_get_num_tracks(mPlayer
); ++i
) {
632 LOG(PR_LOG_DEBUG
, ("Tracks %d: %s", i
, oggplay_get_track_typename(mPlayer
, i
)));
633 if (mVideoTrack
== -1 && oggplay_get_track_type(mPlayer
, i
) == OGGZ_CONTENT_THEORA
) {
634 oggplay_set_callback_num_frames(mPlayer
, i
, 1);
637 oggplay_get_video_fps(mPlayer
, i
, &fpsd
, &fpsn
);
638 mFramerate
= fpsd
== 0 ? 0.0 : double(fpsn
)/double(fpsd
);
639 LOG(PR_LOG_DEBUG
, ("Frame rate: %f", mFramerate
));
641 else if (mAudioTrack
== -1 && oggplay_get_track_type(mPlayer
, i
) == OGGZ_CONTENT_VORBIS
) {
643 oggplay_set_offset(mPlayer
, i
, OGGPLAY_AUDIO_OFFSET
);
644 oggplay_get_audio_samplerate(mPlayer
, i
, &mAudioRate
);
645 oggplay_get_audio_channels(mPlayer
, i
, &mAudioChannels
);
646 LOG(PR_LOG_DEBUG
, ("samplerate: %d, channels: %d", mAudioRate
, mAudioChannels
));
649 if (oggplay_set_track_active(mPlayer
, i
) < 0)
650 LOG(PR_LOG_ERROR
, ("Could not set track %d active", i
));
653 if (mVideoTrack
== -1) {
654 oggplay_set_callback_num_frames(mPlayer
, mAudioTrack
, OGGPLAY_FRAMES_PER_CALLBACK
);
657 oggplay_use_buffer(mPlayer
, OGGPLAY_BUFFER_SIZE
);
659 // Inform the element that we've loaded the Ogg metadata
660 nsCOMPtr
<nsIRunnable
> metadataLoadedEvent
=
661 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, MetadataLoaded
);
663 if (metadataLoadedEvent
) {
664 NS_DispatchToMainThread(metadataLoadedEvent
, NS_DISPATCH_NORMAL
);
667 // Load the first frame of data
668 nsCOMPtr
<nsIRunnable
> firstFrameEvent
=
669 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, LoadFirstFrame
);
671 if (firstFrameEvent
) {
672 NS_GetCurrentThread()->Dispatch(firstFrameEvent
, NS_DISPATCH_NORMAL
);
677 // This is always run in the decoder thread
678 void nsOggDecoder::LoadFirstFrame()
680 if (StepDecoding()) {
681 // Inform the element that we've loaded the first frame of data
682 nsCOMPtr
<nsIRunnable
> frameLoadedEvent
=
683 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, FirstFrameLoaded
);
685 if (frameLoadedEvent
) {
686 NS_DispatchToMainThread(frameLoadedEvent
, NS_DISPATCH_NORMAL
);
688 nsCOMPtr
<nsIRunnable
> displayEvent
=
689 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, DisplayFirstFrame
);
691 mPresentationThread
->Dispatch(displayEvent
, NS_DISPATCH_NORMAL
);
694 // Notify waiting presentation thread that it can start playing
696 nsAutoLock
lock(mFirstFrameLock
);
698 mFirstFrameLoaded
= PR_TRUE
;
699 PR_NotifyAllCondVar(mFirstFrameCondVar
);
701 mDecodeThread
->Dispatch(mDecodeEvent
, NS_DISPATCH_NORMAL
);
705 void nsOggDecoder::StartPresentationThread()
708 nsAutoLock
lock(mFirstFrameLock
);
710 while (!mFirstFrameLoaded
)
711 PR_WaitCondVar(mFirstFrameCondVar
, PR_INTERVAL_NO_TIMEOUT
);
715 mAudioStream
->Resume();
721 mSystemSyncSeconds
= double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
722 mPresentationThread
->Dispatch(mPresentationEvent
, NS_DISPATCH_NORMAL
);
725 float nsOggDecoder::GetCurrentTime()
727 return mVideoCurrentFrameTime
;
730 void nsOggDecoder::GetCurrentURI(nsIURI
** aURI
)
732 NS_IF_ADDREF(*aURI
= mURI
);
736 void nsOggDecoder::DisplayFirstFrame()
738 // Step through the decoded frames, allowing display of the first frame
743 void nsOggDecoder::ProcessTrack(int aTrackNumber
, OggPlayCallbackInfo
* aTrackInfo
)
745 OggPlayDataType type
= oggplay_callback_info_get_type(aTrackInfo
);
746 OggPlayDataHeader
** headers
= oggplay_callback_info_get_headers(aTrackInfo
);
748 case OGGPLAY_INACTIVE
:
753 case OGGPLAY_YUV_VIDEO
:
755 double video_time
= ((double)oggplay_callback_info_get_presentation_time(headers
[0]))/1000.0;
756 mVideoCurrentFrameTime
= video_time
;
758 OggPlayVideoData
* video_data
= oggplay_callback_info_get_video_data(headers
[0]);
759 HandleVideoData(aTrackNumber
, video_data
);
762 case OGGPLAY_FLOATS_AUDIO
:
764 int required
= oggplay_callback_info_get_required(aTrackInfo
);
765 for (int j
= 0; j
< required
; ++j
) {
766 int size
= oggplay_callback_info_get_record_size(headers
[j
]);
767 OggPlayAudioData
* audio_data
= oggplay_callback_info_get_audio_data(headers
[j
]);
768 HandleAudioData(audio_data
, size
);
774 if (oggplay_callback_info_get_required(aTrackInfo
) > 0)
775 LOG(PR_LOG_DEBUG
, ("CMML: %s", oggplay_callback_info_get_text_data(headers
[0])));
783 PRBool
nsOggDecoder::StepDisplay()
785 if (!mPlayer
/* || !mDecodeThread */) {
789 int num_tracks
= oggplay_get_num_tracks(mPlayer
);
790 OggPlayCallbackInfo
** track_info
= oggplay_buffer_retrieve_next(mPlayer
);
793 double audio_time
= GetSyncTime();
794 PRInt32 millis
= PRInt32((mVideoNextFrameTime
-audio_time
) * 1000.0);
795 // LOG(PR_LOG_DEBUG, ("Sleep: %d %f %f", millis, audio_time, mVideoNextFrameTime));
796 mVideoNextFrameTime
+= 1.0/mFramerate
;
799 PR_Sleep(PR_MillisecondsToInterval(millis
));
802 if (mVideoTrack
!= -1 && num_tracks
> mVideoTrack
) {
803 ProcessTrack(mVideoTrack
, track_info
[mVideoTrack
]);
806 if (mAudioTrack
!= -1 && num_tracks
> mAudioTrack
) {
807 ProcessTrack(mAudioTrack
, track_info
[mAudioTrack
]);
810 oggplay_buffer_release(mPlayer
, track_info
);
813 PR_Sleep(PR_MillisecondsToInterval(10));
819 void nsOggDecoder::StartPlaybackThreads()
821 StartInvalidating(mFramerate
);
823 nsCOMPtr
<nsIRunnable
> event
=
824 NS_NEW_RUNNABLE_METHOD(nsOggDecoder
, this, StartPresentationThread
);
826 mPresentationThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
829 void nsOggDecoder::MetadataLoaded()
831 mMetadataLoaded
= PR_TRUE
;
833 mElement
->MetadataLoaded();
836 if (mPlayAfterLoad
) {
837 mPlayAfterLoad
= PR_FALSE
;
838 StartPlaybackThreads();
840 mLoadInProgress
= PR_FALSE
;
843 void nsOggDecoder::FirstFrameLoaded()
845 StartInvalidating(mFramerate
);
847 mElement
->FirstFrameLoaded();
851 void nsOggDecoder::ResourceLoaded()
853 mResourceLoaded
= PR_TRUE
;
855 mElement
->ResourceLoaded();
860 void nsOggDecoder::PlaybackCompleted()
863 mElement
->PlaybackCompleted();
867 NS_IMETHODIMP
nsOggDecoder::Observe(nsISupports
*aSubjet
,
869 const PRUnichar
*someData
)
871 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
878 PRUint32
nsOggDecoder::GetBytesLoaded()
880 return mBytesDownloaded
;
883 PRUint32
nsOggDecoder::GetTotalBytes()
885 // TODO: Need to compute this for ogg files
889 void nsOggDecoder::UpdateBytesDownloaded(PRUint32 aBytes
)
891 mBytesDownloaded
= aBytes
;
894 void nsOggDecoder::BufferingStopped()
897 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME
);
902 void nsOggDecoder::BufferingStarted()
906 mElement
->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE
);