gecko/content/media/nsMediaDecoder.cpp
Ehsan Akhgari f739744839 Bug 810177 - Part 4: Make nsMediaDecoder not depend on nsHTMLMediaElement; r=cpearce
The basic idea in this patch is to create an MediaDecoderOwner
interface which nsHTMLMediaElement would implement, and put everything
needed by nsMediaDeocder on that interface.  In addition to that,
there are a number of other cleanup patches which enables us to
eliminate many of the nsHTMLMediaElement.h #includes in the media code.
2012-11-09 00:52:53 -05:00

295 lines
7.4 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaDecoderOwner.h"
#include "MediaResource.h"
#include "mozilla/Preferences.h"
#include "nsError.h"
using namespace mozilla;
// Number of milliseconds between progress events as defined by spec
static const uint32_t PROGRESS_MS = 350;
// Number of milliseconds of no data before a stall event is fired as defined by spec
static const uint32_t STALL_MS = 3000;
// Number of estimated seconds worth of data we need to have buffered
// ahead of the current playback position before we allow the media decoder
// to report that it can play through the entire media without the decode
// catching up with the download. Having this margin make the
// nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
// fluctuating bitrates.
static const int64_t CAN_PLAY_THROUGH_MARGIN = 10;
nsMediaDecoder::nsMediaDecoder() :
mOwner(nullptr),
mFrameBufferLength(0),
mPinnedForSeek(false),
mShuttingDown(false)
{
MOZ_COUNT_CTOR(nsMediaDecoder);
MediaMemoryReporter::AddMediaDecoder(this);
}
nsMediaDecoder::~nsMediaDecoder()
{
MOZ_COUNT_DTOR(nsMediaDecoder);
MediaMemoryReporter::RemoveMediaDecoder(this);
}
bool nsMediaDecoder::Init(MediaDecoderOwner* aOwner)
{
mOwner = aOwner;
mVideoFrameContainer = aOwner->GetVideoFrameContainer();
return true;
}
void nsMediaDecoder::Shutdown()
{
StopProgress();
mOwner = nullptr;
}
MediaDecoderOwner* nsMediaDecoder::GetMediaOwner() const
{
return mOwner;
}
nsresult nsMediaDecoder::RequestFrameBufferLength(uint32_t aLength)
{
if (aLength < FRAMEBUFFER_LENGTH_MIN || aLength > FRAMEBUFFER_LENGTH_MAX) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
mFrameBufferLength = aLength;
return NS_OK;
}
static void ProgressCallback(nsITimer* aTimer, void* aClosure)
{
nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
decoder->Progress(true);
}
void nsMediaDecoder::Progress(bool aTimer)
{
if (!mOwner)
return;
TimeStamp now = TimeStamp::Now();
if (!aTimer) {
mDataTime = now;
}
// If PROGRESS_MS has passed since the last progress event fired and more
// data has arrived since then, fire another progress event.
if ((mProgressTime.IsNull() ||
now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
!mDataTime.IsNull() &&
now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
mProgressTime = now;
}
if (!mDataTime.IsNull() &&
now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
mOwner->DownloadStalled();
// Null it out
mDataTime = TimeStamp();
}
}
nsresult nsMediaDecoder::StartProgress()
{
if (mProgressTimer)
return NS_OK;
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
return mProgressTimer->InitWithFuncCallback(ProgressCallback,
this,
PROGRESS_MS,
nsITimer::TYPE_REPEATING_SLACK);
}
nsresult nsMediaDecoder::StopProgress()
{
if (!mProgressTimer)
return NS_OK;
nsresult rv = mProgressTimer->Cancel();
mProgressTimer = nullptr;
return rv;
}
void nsMediaDecoder::FireTimeUpdate()
{
if (!mOwner)
return;
mOwner->FireTimeUpdate(true);
}
void nsMediaDecoder::PinForSeek()
{
MediaResource* resource = GetResource();
if (!resource || mPinnedForSeek) {
return;
}
mPinnedForSeek = true;
resource->Pin();
}
void nsMediaDecoder::UnpinForSeek()
{
MediaResource* resource = GetResource();
if (!resource || !mPinnedForSeek) {
return;
}
mPinnedForSeek = false;
resource->Unpin();
}
bool nsMediaDecoder::CanPlayThrough()
{
Statistics stats = GetStatistics();
if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
return false;
}
int64_t bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
int64_t bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
double timeToDownload = bytesToDownload / stats.mDownloadRate;
double timeToPlay = bytesToPlayback / stats.mPlaybackRate;
if (timeToDownload > timeToPlay) {
// Estimated time to download is greater than the estimated time to play.
// We probably can't play through without having to stop to buffer.
return false;
}
// Estimated time to download is less than the estimated time to play.
// We can probably play through without having to buffer, but ensure that
// we've got a reasonable amount of data buffered after the current
// playback position, so that if the bitrate of the media fluctuates, or if
// our download rate or decode rate estimation is otherwise inaccurate,
// we don't suddenly discover that we need to buffer. This is particularly
// required near the start of the media, when not much data is downloaded.
int64_t readAheadMargin =
static_cast<int64_t>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN);
return stats.mTotalBytes == stats.mDownloadPosition ||
stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
}
#ifdef MOZ_RAW
bool
nsMediaDecoder::IsRawEnabled()
{
return Preferences::GetBool("media.raw.enabled");
}
#endif
#ifdef MOZ_OGG
bool
nsMediaDecoder::IsOpusEnabled()
{
#ifdef MOZ_OPUS
return Preferences::GetBool("media.opus.enabled");
#else
return false;
#endif
}
bool
nsMediaDecoder::IsOggEnabled()
{
return Preferences::GetBool("media.ogg.enabled");
}
#endif
#ifdef MOZ_WAVE
bool
nsMediaDecoder::IsWaveEnabled()
{
return Preferences::GetBool("media.wave.enabled");
}
#endif
#ifdef MOZ_WEBM
bool
nsMediaDecoder::IsWebMEnabled()
{
return Preferences::GetBool("media.webm.enabled");
}
#endif
#ifdef MOZ_GSTREAMER
bool
nsMediaDecoder::IsGStreamerEnabled()
{
return Preferences::GetBool("media.gstreamer.enabled");
}
#endif
#ifdef MOZ_WIDGET_GONK
bool
nsMediaDecoder::IsOmxEnabled()
{
return Preferences::GetBool("media.omx.enabled", false);
}
#endif
#ifdef MOZ_MEDIA_PLUGINS
bool
nsMediaDecoder::IsMediaPluginsEnabled()
{
return Preferences::GetBool("media.plugins.enabled");
}
#endif
#ifdef MOZ_DASH
bool
nsMediaDecoder::IsDASHEnabled()
{
return Preferences::GetBool("media.dash.enabled");
}
#endif
namespace mozilla {
MediaMemoryReporter* MediaMemoryReporter::sUniqueInstance;
NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedVideoMemory,
"explicit/media/decoded-video",
KIND_HEAP,
UNITS_BYTES,
MediaMemoryReporter::GetDecodedVideoMemory,
"Memory used by decoded video frames.")
NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedAudioMemory,
"explicit/media/decoded-audio",
KIND_HEAP,
UNITS_BYTES,
MediaMemoryReporter::GetDecodedAudioMemory,
"Memory used by decoded audio chunks.")
MediaMemoryReporter::MediaMemoryReporter()
: mMediaDecodedVideoMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedVideoMemory))
, mMediaDecodedAudioMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedAudioMemory))
{
NS_RegisterMemoryReporter(mMediaDecodedVideoMemory);
NS_RegisterMemoryReporter(mMediaDecodedAudioMemory);
}
MediaMemoryReporter::~MediaMemoryReporter()
{
NS_UnregisterMemoryReporter(mMediaDecodedVideoMemory);
NS_UnregisterMemoryReporter(mMediaDecodedAudioMemory);
}
} // namespace mozilla