On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / content / media / video / src / nsOggDecoder.cpp
bloba3e94d8e780c3901b7f210232c469cd7e5d8d09e
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
14 * License.
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.
22 * Contributor(s):
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 ***** */
38 #include "prlog.h"
39 #include "prmem.h"
40 #include "nsIFrame.h"
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"
57 /*
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 {
88 public:
89 nsDecoderEvent(nsOggDecoder* decoder) :
90 mLock(nsnull),
91 mDecoder(decoder)
95 PRBool Init()
97 mLock = PR_NewLock();
98 return mLock != nsnull;
101 virtual ~nsDecoderEvent()
103 if (mLock) {
104 PR_DestroyLock(mLock);
105 mLock = nsnull;
109 void Revoke()
111 nsAutoLock lock(mLock);
112 mDecoder = nsnull;
115 void Lock()
117 PR_Lock(mLock);
120 void Unlock()
122 PR_Unlock(mLock);
125 NS_IMETHOD Run() {
126 nsAutoLock lock(mLock);
127 return RunWithLock();
130 protected:
131 virtual nsresult RunWithLock() = 0;
133 PRLock* mLock;
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
144 public:
145 nsVideoDecodeEvent(nsOggDecoder* decoder) :
146 nsDecoderEvent(decoder)
150 protected:
151 nsresult RunWithLock()
153 if (mDecoder && mDecoder->StepDecoding()) {
154 NS_GetCurrentThread()->Dispatch(this, NS_DISPATCH_NORMAL);
156 else {
157 LOG(PR_LOG_DEBUG, ("Decoding thread completed"));
159 return NS_OK;
162 private:
163 // PR_TRUE if we are actively decoding
164 PRPackedBool mDecoding;
167 class nsVideoPresentationEvent : public nsDecoderEvent
169 public:
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()
180 if (mDecoder) {
181 // Stop the invalidation timer
182 nsCOMPtr<nsIRunnable> event =
183 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, StopInvalidating);
185 if (event) {
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);
195 else {
196 LOG(PR_LOG_DEBUG, ("Presentation thread completed"));
197 StopInvalidating();
199 return NS_OK;
203 NS_IMPL_THREADSAFE_ISUPPORTS1(nsOggDecoder, nsIObserver)
205 void nsOggDecoder::Pause()
207 if (!mPresentationThread)
208 return;
210 mPaused = PR_TRUE;
211 nsCOMPtr<nsIRunnable> event =
212 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, DoPause);
213 if (event)
214 mPresentationThread->Dispatch(event, NS_DISPATCH_NORMAL);
217 void nsOggDecoder::DoPause()
219 mPaused = PR_TRUE;
220 if (mAudioStream) {
221 mAudioStream->Pause();
223 mSystemSyncSeconds = double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
226 float nsOggDecoder::GetVolume()
228 float volume = 0.0;
229 if (mAudioStream) {
230 mAudioStream->GetVolume(&volume);
232 else {
233 volume = mInitialVolume;
236 return volume;
239 void nsOggDecoder::SetVolume(float volume)
241 if (mAudioStream) {
242 mAudioStream->SetVolume(volume);
244 else {
245 mInitialVolume = volume;
249 float nsOggDecoder::GetDuration()
251 // Currently not implemented. Video Spec says to return
252 // NaN if unknown.
253 // TODO: return NaN
254 return 0.0;
257 nsOggDecoder::nsOggDecoder() :
258 nsVideoDecoder(),
259 mBytesDownloaded(0),
260 mVideoNextFrameTime(0.0),
261 mLoadInProgress(PR_FALSE),
262 mPlayAfterLoad(PR_FALSE),
263 mNotifyOnShutdown(PR_FALSE),
264 mVideoCurrentFrameTime(0.0),
265 mInitialVolume(1.0),
266 mAudioRate(0),
267 mAudioChannels(0),
268 mAudioTrack(-1),
269 mVideoTrack(-1),
270 mPlayer(0),
271 mReader(0),
272 mPaused(PR_TRUE),
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()
299 if (mDecodeEvent) {
300 // Must prepare oggplay for close to stop the decode event from
301 // waiting on oggplay's semaphore. Without this, revoke deadlocks.
302 if (mPlayer) {
303 oggplay_prepare_for_close(mPlayer);
306 mDecodeEvent->Revoke();
307 mDecodeEvent = nsnull;
309 if (mPresentationEvent) {
310 mPresentationEvent->Revoke();
311 mPresentationEvent = nsnull;
314 Stop();
315 nsVideoDecoder::Shutdown();
318 nsOggDecoder::~nsOggDecoder()
320 Shutdown();
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() {
337 return mFramerate;
340 PRBool nsOggDecoder::IsPaused()
342 return mPaused;
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) {
353 stop = PR_TRUE;
354 // Inform the element that we've ended the video
355 nsCOMPtr<nsIRunnable> event =
356 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, PlaybackCompleted);
358 if (event) {
359 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
362 else {
363 stop = PR_FALSE;
365 if (r == E_OGGPLAY_CONTINUE)
366 BufferData();
369 return !stop;
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();
382 if (mElement) {
383 nsCOMPtr<nsIRunnable> event =
384 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, BufferingStarted);
385 if (event)
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
389 // to unpause.
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 &&
400 mPaused &&
401 (PR_IntervalToMilliseconds(PR_IntervalNow() - start) < MAX_BUFFERING_TIME_WAIT_MS) &&
402 mReader->Available() < bytes) {
403 LOG(PR_LOG_DEBUG,
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);
414 if (mElement) {
415 nsCOMPtr<nsIRunnable> event =
416 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, BufferingStopped);
417 if (event)
418 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
424 nsresult nsOggDecoder::Load(nsIURI* aURI)
426 nsresult rv;
427 Stop();
428 mURI = 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);
449 StartProgress();
451 return NS_OK;
454 nsresult nsOggDecoder::Play()
456 mPaused = PR_FALSE;
457 if (mLoadInProgress) {
458 mPlayAfterLoad = PR_TRUE;
459 return NS_OK;
461 else if (!mPlayer) {
462 Load(mURI);
464 else {
465 StartPlaybackThreads();
468 if (!mNotifyOnShutdown) {
469 nsCOMPtr<nsIObserverService> observerService =
470 do_GetService("@mozilla.org/observer-service;1");
471 if (observerService) {
472 mNotifyOnShutdown =
473 NS_SUCCEEDED(observerService->AddObserver(this,
474 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
475 PR_FALSE));
477 else {
478 NS_WARNING("Could not get an observer service. Video decoding events may not shutdown cleanly.");
482 return NS_OK;
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;
498 StopInvalidating();
499 StopProgress();
500 if (mDecodeThread) {
501 if (mPlayer) {
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;
517 CloseAudioStream();
518 if (mPlayer){
519 oggplay_close(mPlayer);
520 mPlayer = nsnull;
522 mPaused = PR_TRUE;
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) {
536 int y_width;
537 int y_height;
538 oggplay_get_video_y_size(mPlayer, track_num, &y_width, &y_height);
540 int uv_width;
541 int uv_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)
545 return;
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.
555 if (mRGB) {
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) {
577 if (mAudioStream) {
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()
589 double time = 0.0;
590 if (mAudioStream && mAudioTrack != -1) {
591 mAudioStream->GetTime(&time);
593 else {
594 // No audio stream, or audio track. Sync to system clock.
595 time =
596 (mSystemSyncSeconds == 0.0) ?
597 0.0 :
598 double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0 - mSystemSyncSeconds;
601 return time;
604 void nsOggDecoder::OpenAudioStream()
606 mAudioStream = new nsAudioStream();
607 if (!mAudioStream) {
608 LOG(PR_LOG_ERROR, ("Could not create audio stream"));
610 else {
611 mAudioStream->Init(mAudioChannels, mAudioRate);
612 mAudioStream->SetVolume(mInitialVolume);
616 void nsOggDecoder::CloseAudioStream()
618 if (mAudioStream) {
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);
628 if (mPlayer) {
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);
635 mVideoTrack = i;
636 int fpsd, fpsn;
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) {
642 mAudioTrack = i;
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);
690 if (displayEvent) {
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);
714 if (mAudioStream) {
715 mAudioStream->Resume();
717 else {
718 OpenAudioStream();
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
739 // of video.
740 StepDisplay();
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);
747 switch(type) {
748 case OGGPLAY_INACTIVE:
750 break;
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);
761 break;
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);
770 break;
772 case OGGPLAY_CMML:
774 if (oggplay_callback_info_get_required(aTrackInfo) > 0)
775 LOG(PR_LOG_DEBUG, ("CMML: %s", oggplay_callback_info_get_text_data(headers[0])));
776 break;
778 default:
779 break;
783 PRBool nsOggDecoder::StepDisplay()
785 if (!mPlayer/* || !mDecodeThread */) {
786 return PR_FALSE;
789 int num_tracks = oggplay_get_num_tracks(mPlayer);
790 OggPlayCallbackInfo ** track_info = oggplay_buffer_retrieve_next(mPlayer);
792 if (track_info) {
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;
798 if (millis > 0) {
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);
812 else {
813 PR_Sleep(PR_MillisecondsToInterval(10));
816 return PR_TRUE;
819 void nsOggDecoder::StartPlaybackThreads()
821 StartInvalidating(mFramerate);
823 nsCOMPtr<nsIRunnable> event =
824 NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, StartPresentationThread);
825 if (event)
826 mPresentationThread->Dispatch(event, NS_DISPATCH_NORMAL);
829 void nsOggDecoder::MetadataLoaded()
831 mMetadataLoaded = PR_TRUE;
832 if (mElement) {
833 mElement->MetadataLoaded();
836 if (mPlayAfterLoad) {
837 mPlayAfterLoad = PR_FALSE;
838 StartPlaybackThreads();
840 mLoadInProgress = PR_FALSE;
843 void nsOggDecoder::FirstFrameLoaded()
845 StartInvalidating(mFramerate);
846 if (mElement) {
847 mElement->FirstFrameLoaded();
851 void nsOggDecoder::ResourceLoaded()
853 mResourceLoaded = PR_TRUE;
854 if (mElement) {
855 mElement->ResourceLoaded();
857 StopProgress();
860 void nsOggDecoder::PlaybackCompleted()
862 if (mElement) {
863 mElement->PlaybackCompleted();
867 NS_IMETHODIMP nsOggDecoder::Observe(nsISupports *aSubjet,
868 const char *aTopic,
869 const PRUnichar *someData)
871 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
872 Shutdown();
875 return NS_OK;
878 PRUint32 nsOggDecoder::GetBytesLoaded()
880 return mBytesDownloaded;
883 PRUint32 nsOggDecoder::GetTotalBytes()
885 // TODO: Need to compute this for ogg files
886 return 0;
889 void nsOggDecoder::UpdateBytesDownloaded(PRUint32 aBytes)
891 mBytesDownloaded = aBytes;
894 void nsOggDecoder::BufferingStopped()
896 if (mElement) {
897 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME);
898 mElement->Play();
902 void nsOggDecoder::BufferingStarted()
904 if (mElement) {
905 mElement->Pause();
906 mElement->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE);