import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1206298 - Part 1: Update the expiration time on the fake cache entry object for an intercepted request in the non-e10s case; r=mcmanus (0fa1afb14d)
- Bug 1206298 - Part 2: Add a test to make sure that we respect Cache-Control headers on the synthesized response; r=jdm (c7a05d4415)
- Bug 121442 - Add platform to GMP storage base dir. r=gerald (6f40a18f39)
- Bug 1214967 - Proxy observer service notification across to content process when GMPs are added/removed. r=billm,jwwang (088f770b47)
- Bug 1211812 - Add pref to select GMP to use for unencrypted decoding. r=jwwang Bug 1212670 - Implement GMPDecoderModule::SupportsMimeType() and EMEDecoderModule::SupportsMimeType(). r=jwwang (10acef26cd)
- Bug 1214932 - Remove fragmented-mp4 from media prefs. r=jya (f2935767c1)
- Bug 1214967 - Create a list of GMPs/codecs that can be used for <video> decoding. r=jwwang (3c68f6e08b)
- import some vars and prefs to compile again (5456bb7124)
- Bug 1205083 - Don't enable low latency WMF video decoding as it crashes sometimes. r=jya (353e727811)
- Bug 1101885: P1. Make pref dynamic. r=cpearce (8a3de82e1e)
- Bug 1189776: Store our audio decode time in TimeUnits. r=cpearce (0a01637580)
- Bug 1202296 - Hide the MFTDecoder within MFTManager so that we can recreate it opaquely. r=cpearce (148e4d1923)
- Bug 1211339 - Ensure WMFDecoderModule::SupportsMimeType checks it can create decoders. r=jya (f6e53ff08c)
- Bug 1101885: P2. Don't shutdown WMF framework before releasing decoder. r=cpearce (b373402e74)
- Bug 1101885: P3. Allow decoder creation fallback. r=cpearce (650ef55564)
- Bug 1219300 - Add mutex to protect the |result| because the variable will be access by multiple threads at the same time. r=cpearce (a0f489cbcf)
- Bug 1101885: P4. Enable Intel VP8/VP9 HW decoder by default. r=cpearce (cffbca2905)
- Bug 1101885: P5. Only attempt to use HW VP decoder if DXVA active. r=cpearce (fd0f12c118)
- spaces (f110c8ab3d)
- Bug 1203217 - Add some logging when we detect and skip an ID3v2 tag. r=kinetik (74533f7c1e)
- small changes (1ab8008527)
- Bug 1164453 - Assert that decoder callback is set before using it. r=snorp (79bee3ccbe)
- Bug 1190379 - Use AndroidDecoderModule for VP8/9. r=jya (2c672b6630)
- Bug 1188871: P2. Call DrainComplete should an error occurs while draining. r=snorp (81f5669010)
- Bug 1163667 - [5.1] Ensure empty demuxer sample queue before initiating draining. r=snorp (8a4dd86a28)
- Bug 1204483 - Fix busted audio decoding output on Android r=esawin (b0c7dca1ce)
- Bug 1220491 - clarify ownership relationships for creators of AudioData; r=gerald (cd043d3ef1)
- bug 1162364 detect and abort MF_E_TRANSFORM_STREAM_CHANGE infinite loops r=cpearce (a0f3e17e5f)
- Bug 1214065 - Remove unused arguments from MediaDecoder::Load() and its friends. r=kinetik. (6e610f3aed)
- Bug 1211766 - Remove AbstractMediaDecoder::GetReentrantMonitor(). r=jya. (beb1a22439)
- some cleanup and add back some gestreamer stuff (55c4a19f78)
- Bug 1204434 - Remove check of MediaDecoder::IsMediaSeekable from OggReader::ReadMetadata. r=cpearce. (75bf15a1f5)
- Bug 1209888 - Remove usage of decoder monitor from OggReader. r=jya. (4e92df9c1c)
- Bug 1204882 - Move MediaDecoder::FrameStatistics out of MediaDecoder for easier use in other classes. r=jwwang (7f5d6035be)
- Bug 1209887. Part 1 - add assertions. r=jya. (d5a7057c79)
- Bug 1209887. Part 2 - remove usage of decoder monitor. r=jya. (56a6de8874)
- Bug 1209887. Part 3 - remove unused code. r=jya. (7cd48c424b)
- missing bit of 1208930 (045f09408a)
- Bug 1211327 - Remove unnecessary usage of decoder monitor from MediaDecoderReader and sub-classes. r=jya. (2a15ac759a)
- missing bit 1211766 (221c0a957f)
- Bug 1214519 - Fix the coding style of member initializer lists of MediaDecoder. r=jya. (654636af36)
- Bug 1206977: P12. Properly shutdown all created test decoders. r=cpearce (79cd0ebc83)
- Bug 1206977: P14. Remove obsolete / redundant code. r=cpearce (b5a85ee060)
- Bug 1208348 - Check whether DirectShow can decode MP3 before assuming it will work. r=jya (988030aec7)
- Bug 1209886 - Clean up InstantiateDecoder() in DecoderTraits.cpp. r=kinetik. (822cac0dee)
- Bug 1213176: P1. Remove most MediaFormatReader dependencies on its MediaDecoder parent. r=jwwang (7b5b000408)
- Bug 1214989. Part 1 - add MediaDecoderOwner to the constructors of MediaDecoder and sub-classes. r=gerald. (f2f6df4bf2)
- Bug 1214989. Part 2 - add MediaDecoderOwner to Clone() and overrides. r=gerald. (db9947115d)
- Bug 1214989. Part 3 - remove MediaDecoder::Init() and its callers. r=gerald. (4353925106)
- Bug 1217714 - Remove some unused functions from MediaDecoderReader. r=jya. (ae50a0f881)
- Bug 1194524 - Use channel->ascynOpen2 in dom/media/MediaResource.cpp (r=sicking) (2a28e80f82)
- Bug 1218280. Part 1 - create MediaResourceCallback for MediaResource to send notifications. r=roc. (54c5f58cb0)
- Bug 1218280. Part 2 - remove unused code. r=roc. (161d4e28bb)
- Bug 1219169. Part 1 - Remove AbstractMediaDecoder::OnStateMachineTaskQueue(). r=jya. (1836fb4bbc)
- Bug 1219169. Part 2 - move MediaDecoderStateMachine::OnTaskQueue() to private. r=jya. (873ecac93a)
- Bug 1217692. Part 1 - move members that don't have to be public to protected or private sections. r=jya. (25ea01b514)
- Bug 1217692. Part 2 - fix some styles to keep 80 cols limit. r=jya. (0aabebd4ed)
- Bug 1220558. Part 1 - remove unused arguments from MediaDecoderReader::DispatchNotifyDataArrived() and its callees/callers. r=jya. (0e57bafdb4)
- Bug 1218157: Only ever read from cached data in NotifyDataArrived. r=cpearce (235ef09e63)
- Bug 1220551. Part 1 - fix the parameters passed to mBufferedState->NotifyDataArrived(). r=jya. (61bdc160b1)
- Bug 1220551. Part 2 - remove arguments from NotifyDataArrivedInternal(). r=jya. (edc0c18550)
This commit is contained in:
2022-12-03 11:38:18 +08:00
parent 3fd460890c
commit 6b21bacef6
128 changed files with 1993 additions and 1546 deletions
+5
View File
@@ -919,6 +919,11 @@ pref("dom.ipc.plugins.enabled.x86_64", true);
pref("dom.ipc.plugins.enabled", true);
#endif
// If decoding-via-gmp is turned on for <video>, default to using
// Adobe's GMP for decoding.
pref("media.gmp.decoder.aac", 2);
pref("media.gmp.decoder.h264", 2);
pref("browser.tabs.remote", false);
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+6 -12
View File
@@ -1263,7 +1263,7 @@ nsresult HTMLMediaElement::LoadResource()
if (IsAutoplayEnabled()) {
mJoinLatency.Start();
}
return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
return FinishDecoderSetup(decoder, resource, nullptr);
}
// determine what security checks need to be performed in AsyncOpen2().
@@ -2749,17 +2749,12 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
MediaResource* originalResource = aOriginal->GetResource();
if (!originalResource)
return NS_ERROR_FAILURE;
RefPtr<MediaDecoder> decoder = aOriginal->Clone();
RefPtr<MediaDecoder> decoder = aOriginal->Clone(this);
if (!decoder)
return NS_ERROR_FAILURE;
LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
if (!decoder->Init(this)) {
LOG(LogLevel::Debug, ("%p Failed to init cloned decoder %p", this, decoder.get()));
return NS_ERROR_FAILURE;
}
decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
RefPtr<MediaResource> resource = originalResource->CloneData(decoder);
@@ -2768,7 +2763,7 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
return NS_ERROR_FAILURE;
}
return FinishDecoderSetup(decoder, resource, nullptr, aOriginal);
return FinishDecoderSetup(decoder, resource, nullptr);
}
nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
@@ -2812,14 +2807,13 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
}
return NS_OK;
} else {
return FinishDecoderSetup(decoder, resource, aListener, nullptr);
return FinishDecoderSetup(decoder, resource, aListener);
}
}
nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
MediaResource* aStream,
nsIStreamListener** aListener,
MediaDecoder* aCloneDonor)
nsIStreamListener** aListener)
{
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
@@ -2849,7 +2843,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
// can affect how we feed data to MediaStreams
NotifyDecoderPrincipalChanged();
nsresult rv = aDecoder->Load(aListener, aCloneDonor);
nsresult rv = aDecoder->Load(aListener);
if (NS_FAILED(rv)) {
ShutdownDecoder();
LOG(LogLevel::Debug, ("%p Failed to load for decoder %p", this, aDecoder));
+2 -3
View File
@@ -631,7 +631,7 @@ public:
* A public wrapper for FinishDecoderSetup()
*/
nsresult FinishDecoderSetup(MediaDecoder* aDecoder, MediaResource* aStream) {
return FinishDecoderSetup(aDecoder, aStream, nullptr, nullptr);
return FinishDecoderSetup(aDecoder, aStream, nullptr);
}
// Returns true if the media element is being destroyed. Used in
@@ -793,8 +793,7 @@ protected:
*/
nsresult FinishDecoderSetup(MediaDecoder* aDecoder,
MediaResource* aStream,
nsIStreamListener **aListener,
MediaDecoder* aCloneDonor);
nsIStreamListener **aListener);
/**
* Call this after setting up mLoadingSrc and mDecoder.
+1 -1
View File
@@ -227,7 +227,7 @@ HTMLVideoElement::GetVideoPlaybackQuality()
}
if (mDecoder) {
MediaDecoder::FrameStatistics& stats = mDecoder->GetFrameStatistics();
FrameStatistics& stats = mDecoder->GetFrameStatistics();
totalFrames = stats.GetParsedFrames();
droppedFrames = stats.GetDroppedFrames();
corruptedFrames = 0;
+8
View File
@@ -193,6 +193,7 @@
#include "mozilla/widget/PuppetBidiKeyboard.h"
#include "mozilla/RemoteSpellCheckEngineChild.h"
#include "GMPServiceChild.h"
#include "GMPDecoderModule.h"
#include "gfxPlatform.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
@@ -1622,6 +1623,13 @@ ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId)
return true;
}
bool
ContentChild::RecvNotifyGMPsChanged()
{
GMPDecoderModule::UpdateUsableCodecs();
return true;
}
PCrashReporterChild*
ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
const uint32_t& processType)
+2
View File
@@ -296,6 +296,8 @@ public:
const nsString& aSessionId) override;
virtual bool RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
virtual bool RecvNotifyGMPsChanged() override;
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;
+4
View File
@@ -691,6 +691,7 @@ static const char* sObserverTopics[] = {
"profiler-subprocess-gather",
"profiler-subprocess",
#endif
"gmp-changed",
};
#ifdef MOZ_NUWA_PROCESS
@@ -3247,6 +3248,9 @@ ContentParent::Observe(nsISupports* aSubject,
}
}
#endif
else if (!strcmp(aTopic, "gmp-changed")) {
unused << SendNotifyGMPsChanged();
}
return NS_OK;
}
+5
View File
@@ -673,6 +673,11 @@ child:
*/
async NotifyPresentationReceiverCleanUp(nsString aSessionId);
/**
* Notify the child that the Gecko Media Plugins installed changed.
*/
async NotifyGMPsChanged();
parent:
/**
* Tell the content process some attributes of itself. This is
+2 -2
View File
@@ -13,12 +13,12 @@
namespace mozilla {
MediaDecoder*
ADTSDecoder::Clone()
ADTSDecoder::Clone(MediaDecoderOwner* aOwner)
{
if (!IsEnabled())
return nullptr;
return new ADTSDecoder();
return new ADTSDecoder(aOwner);
}
MediaDecoderStateMachine*
+2 -2
View File
@@ -14,8 +14,8 @@ namespace mozilla {
class ADTSDecoder : public MediaDecoder {
public:
// MediaDecoder interface.
explicit ADTSDecoder() : MediaDecoder() {}
MediaDecoder* Clone() override;
explicit ADTSDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
MediaDecoderStateMachine* CreateStateMachine() override;
// Returns true if the ADTS backend is pref'ed on, and we're running on a
+1 -8
View File
@@ -45,16 +45,10 @@ enum class MediaDecoderEventVisibility : int8_t {
class AbstractMediaDecoder : public nsIObserver
{
public:
// Returns the monitor for other threads to synchronise access to
// state.
virtual ReentrantMonitor& GetReentrantMonitor() = 0;
// A special version of the above for the ogg decoder that is allowed to be
// called cross-thread.
virtual bool IsOggDecoderShutdown() { return false; }
virtual bool OnStateMachineTaskQueue() const = 0;
// Get the current MediaResource being used. Its URI will be returned
// by currentSrc. Returns what was passed to Load(), if Load() has been called.
virtual MediaResource* GetResource() const = 0;
@@ -114,8 +108,7 @@ public:
// Called by the reader's MediaResource as data arrives over the network.
// Must be called on the main thread.
virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset,
bool aThrottleUpdates) = 0;
virtual void NotifyDataArrived(bool aThrottleUpdates) = 0;
// Set by Reader if the current audio track can be offloaded
virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
+3 -3
View File
@@ -41,10 +41,10 @@ public:
while (aFrames > 0) {
uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[samples]);
auto buffer = MakeUnique<AudioDataValue[]>(samples);
// Copy audio data to buffer using caller-provided functor.
uint32_t framesCopied = aCopyFunc(buffer, samples);
uint32_t framesCopied = aCopyFunc(buffer.get(), samples);
NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames");
@@ -57,7 +57,7 @@ public:
aTime,
duration.value(),
framesCopied,
buffer.forget(),
Move(buffer),
aChannels,
aSampleRate));
+1 -1
View File
@@ -50,7 +50,7 @@ private:
return principal.forget();
}
virtual bool CanClone() override { return false; }
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback*) override
{
return nullptr;
}
+28 -26
View File
@@ -575,31 +575,31 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
#ifdef MOZ_FMP4
if (IsMP4SupportedType(aType)) {
decoder = new MP4Decoder();
decoder = new MP4Decoder(aOwner);
return decoder.forget();
}
#endif
if (IsMP3SupportedType(aType)) {
decoder = new MP3Decoder();
if (IsMP3SupportedType(aType)) {
decoder = new MP3Decoder(aOwner);
return decoder.forget();
}
if (IsAACSupportedType(aType)) {
decoder = new ADTSDecoder();
if (IsAACSupportedType(aType)) {
decoder = new ADTSDecoder(aOwner);
return decoder.forget();
}
#ifdef MOZ_RAW
if (IsRawType(aType)) {
decoder = new RawDecoder();
decoder = new RawDecoder(aOwner);
return decoder.forget();
}
#endif
if (IsOggType(aType)) {
decoder = new OggDecoder();
decoder = new OggDecoder(aOwner);
return decoder.forget();
}
#ifdef MOZ_WAVE
if (IsWaveType(aType)) {
decoder = new WaveDecoder();
decoder = new WaveDecoder(aOwner);
return decoder.forget();
}
#endif
@@ -622,10 +622,10 @@ if (IsAACSupportedType(aType)) {
}
#if ANDROID_VERSION >= 18
decoder = MediaDecoder::IsOmxAsyncEnabled()
? static_cast<MediaDecoder*>(new MediaCodecDecoder())
: static_cast<MediaDecoder*>(new MediaOmxDecoder());
? static_cast<MediaDecoder*>(new MediaCodecDecoder(aOwner))
: static_cast<MediaDecoder*>(new MediaOmxDecoder(aOwner));
#else
decoder = new MediaOmxDecoder();
decoder = new MediaOmxDecoder(aOwner);
#endif
return decoder.forget();
}
@@ -634,10 +634,10 @@ if (IsAACSupportedType(aType)) {
if (IsRtspSupportedType(aType)) {
#if ANDROID_VERSION >= 18
decoder = MediaDecoder::IsOmxAsyncEnabled()
? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder())
: static_cast<MediaDecoder*>(new RtspOmxDecoder());
? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder(aOwner))
: static_cast<MediaDecoder*>(new RtspOmxDecoder(aOwner));
#else
decoder = new RtspOmxDecoder();
decoder = new RtspOmxDecoder(aOwner);
#endif
return decoder.forget();
}
@@ -645,31 +645,29 @@ if (IsAACSupportedType(aType)) {
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
decoder = new AndroidMediaDecoder(aType);
decoder = new AndroidMediaDecoder(aOwner, aType);
return decoder.forget();
}
#endif
if (DecoderTraits::IsWebMType(aType)) {
decoder = new WebMDecoder();
decoder = new WebMDecoder(aOwner);
return decoder.forget();
}
#ifdef MOZ_DIRECTSHOW
// Note: DirectShow should come before WMF, so that we prefer DirectShow's
// MP3 support over WMF's.
if (IsDirectShowSupportedType(aType)) {
decoder = new DirectShowDecoder();
decoder = new DirectShowDecoder(aOwner);
return decoder.forget();
}
#endif
#ifdef MOZ_APPLEMEDIA
if (IsAppleMediaSupportedType(aType)) {
decoder = new AppleDecoder();
decoder = new AppleDecoder(aOwner);
return decoder.forget();
}
#endif
NS_ENSURE_TRUE(decoder != nullptr, nullptr);
NS_ENSURE_TRUE(decoder->Init(aOwner), nullptr);
return nullptr;
}
@@ -678,11 +676,7 @@ already_AddRefed<MediaDecoder>
DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<MediaDecoder> decoder(InstantiateDecoder(aType, aOwner));
NS_ENSURE_TRUE(decoder != nullptr, nullptr);
NS_ENSURE_TRUE(decoder->Init(aOwner), nullptr);
return decoder.forget();
return InstantiateDecoder(aType, aOwner);
}
/* static */
@@ -699,12 +693,17 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
decoderReader = new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()));
} else
#endif
if (IsMP3SupportedType(aType)) {
if (IsMP3SupportedType(aType)) {
decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource()));
} else
if (IsAACSupportedType(aType)) {
decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource()));
} else
#ifdef MOZ_GSTREAMER
if (IsGStreamerSupportedType(aType)) {
decoderReader = new GStreamerReader(aDecoder);
} else
#endif
#ifdef MOZ_RAW
if (IsRawType(aType)) {
decoderReader = new RawReader(aDecoder);
@@ -776,6 +775,9 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
!IsB2GSupportOnlyType(aType)) ||
#endif
IsWebMType(aType) ||
#ifdef MOZ_GSTREAMER
IsGStreamerSupportedType(aType) ||
#endif
#ifdef MOZ_ANDROID_OMX
(MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) ||
#endif
+106
View File
@@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef FrameStatistics_h_
#define FrameStatistics_h_
namespace mozilla {
// Frame decoding/painting related performance counters.
// Threadsafe.
class FrameStatistics {
public:
FrameStatistics() :
mReentrantMonitor("FrameStats"),
mParsedFrames(0),
mDecodedFrames(0),
mPresentedFrames(0),
mDroppedFrames(0),
mCorruptFrames(0) {}
// Returns number of frames which have been parsed from the media.
// Can be called on any thread.
uint32_t GetParsedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mParsedFrames;
}
// Returns the number of parsed frames which have been decoded.
// Can be called on any thread.
uint32_t GetDecodedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mDecodedFrames;
}
// Returns the number of decoded frames which have been sent to the rendering
// pipeline for painting ("presented").
// Can be called on any thread.
uint32_t GetPresentedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mPresentedFrames;
}
// Number of frames that have been skipped because they have missed their
// compoisition deadline.
uint32_t GetDroppedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mDroppedFrames + mCorruptFrames;
}
uint32_t GetCorruptedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mCorruptFrames;
}
// Increments the parsed and decoded frame counters by the passed in counts.
// Can be called on any thread.
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) {
if (aParsed == 0 && aDecoded == 0 && aDropped == 0)
return;
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mParsedFrames += aParsed;
mDecodedFrames += aDecoded;
mDroppedFrames += aDropped;
}
// Increments the presented frame counters.
// Can be called on any thread.
void NotifyPresentedFrame() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
++mPresentedFrames;
}
void NotifyCorruptFrame() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
++mCorruptFrames;
}
private:
// ReentrantMonitor to protect access of playback statistics.
ReentrantMonitor mReentrantMonitor;
// Number of frames parsed and demuxed from media.
// Access protected by mReentrantMonitor.
uint32_t mParsedFrames;
// Number of parsed frames which were actually decoded.
// Access protected by mReentrantMonitor.
uint32_t mDecodedFrames;
// Number of decoded frames which were actually sent down the rendering
// pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
uint32_t mPresentedFrames;
uint32_t mDroppedFrames;
uint32_t mCorruptFrames;
};
} // namespace mozilla
#endif
+6 -37
View File
@@ -15,11 +15,11 @@
namespace mozilla {
MediaDecoder*
MP3Decoder::Clone() {
MP3Decoder::Clone(MediaDecoderOwner* aOwner) {
if (!IsEnabled()) {
return nullptr;
}
return new MP3Decoder();
return new MP3Decoder(aOwner);
}
MediaDecoderStateMachine*
@@ -29,43 +29,12 @@ MP3Decoder::CreateStateMachine() {
return new MediaDecoderStateMachine(this, reader);
}
static already_AddRefed<MediaDataDecoder>
CreateTestMP3Decoder(AudioInfo& aConfig)
{
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
RefPtr<MediaDataDecoder> decoder(
platform->CreateDecoder(aConfig, nullptr, nullptr));
return decoder.forget();
}
static bool
CanCreateMP3Decoder()
{
static bool haveCachedResult = false;
static bool result = false;
if (haveCachedResult) {
return result;
}
AudioInfo config;
config.mMimeType = "audio/mpeg";
config.mRate = 48000;
config.mChannels = 2;
config.mBitDepth = 16;
RefPtr<MediaDataDecoder> decoder(CreateTestMP3Decoder(config));
if (decoder) {
result = true;
}
haveCachedResult = true;
return result;
}
/* static */
bool
MP3Decoder::IsEnabled() {
return CanCreateMP3Decoder();
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"));
}
/* static */
@@ -73,7 +42,7 @@ bool MP3Decoder::CanHandleMediaType(const nsACString& aType,
const nsAString& aCodecs)
{
if (aType.EqualsASCII("audio/mp3") || aType.EqualsASCII("audio/mpeg")) {
return CanCreateMP3Decoder() &&
return IsEnabled() &&
(aCodecs.IsEmpty() || aCodecs.EqualsASCII("mp3"));
}
return false;
+2 -1
View File
@@ -13,7 +13,8 @@ namespace mozilla {
class MP3Decoder : public MediaDecoder {
public:
// MediaDecoder interface.
MediaDecoder* Clone() override;
explicit MP3Decoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
MediaDecoderStateMachine* CreateStateMachine() override;
// Returns true if the MP3 backend is preffed on, and we're running on a
+65 -64
View File
@@ -17,13 +17,13 @@
#ifdef PR_LOGGING
PRLogModuleInfo* gMP3DemuxerLog;
#define MP3DEMUXER_LOG(msg, ...) \
#define MP3LOG(msg, ...) \
MOZ_LOG(gMP3DemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
#define MP3DEMUXER_LOGV(msg, ...) \
#define MP3LOGV(msg, ...) \
MOZ_LOG(gMP3DemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
#else
#define MP3DEMUXER_LOG(msg, ...)
#define MP3DEMUXER_LOGV(msg, ...)
#define MP3LOG(msg, ...)
#define MP3LOGV(msg, ...)
#endif
using mozilla::media::TimeUnit;
@@ -50,13 +50,13 @@ MP3Demuxer::InitInternal() {
RefPtr<MP3Demuxer::InitPromise>
MP3Demuxer::Init() {
if (!InitInternal()) {
MP3DEMUXER_LOG("MP3Demuxer::Init() failure: waiting for data");
MP3LOG("MP3Demuxer::Init() failure: waiting for data");
return InitPromise::CreateAndReject(
DemuxerFailureReason::DEMUXER_ERROR, __func__);
}
MP3DEMUXER_LOG("MP3Demuxer::Init() successful");
MP3LOG("MP3Demuxer::Init() successful");
return InitPromise::CreateAndResolve(NS_OK, __func__);
}
@@ -87,14 +87,14 @@ void
MP3Demuxer::NotifyDataArrived() {
// TODO: bug 1169485.
NS_WARNING("Unimplemented function NotifyDataArrived");
MP3DEMUXER_LOGV("NotifyDataArrived()");
MP3LOGV("NotifyDataArrived()");
}
void
MP3Demuxer::NotifyDataRemoved() {
// TODO: bug 1169485.
NS_WARNING("Unimplemented function NotifyDataRemoved");
MP3DEMUXER_LOGV("NotifyDataRemoved()");
MP3LOGV("NotifyDataRemoved()");
}
@@ -127,8 +127,8 @@ MP3TrackDemuxer::Init() {
// Read the first frame to fetch sample rate and other meta data.
RefPtr<MediaRawData> frame(GetNextFrame(FindFirstFrame()));
MP3DEMUXER_LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
StreamLength(), !!frame);
MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
StreamLength(), !!frame);
if (!frame) {
return false;
@@ -147,9 +147,9 @@ MP3TrackDemuxer::Init() {
mInfo->mMimeType = "audio/mpeg";
mInfo->mDuration = Duration().ToMicroseconds();
MP3DEMUXER_LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
mInfo->mDuration);
MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
mInfo->mDuration);
return mSamplesPerSecond && mChannels;
}
@@ -200,6 +200,10 @@ MP3TrackDemuxer::Seek(TimeUnit aTime) {
TimeUnit
MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
const auto& vbr = mParser.VBRInfo();
if (!aTime.ToMicroseconds()) {
// Quick seek to the beginning of the stream.
@@ -226,10 +230,10 @@ MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
TimeUnit
MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) {
MP3DEMUXER_LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset);
MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset);
if (!aTime.ToMicroseconds()) {
return FastSeek(aTime);
@@ -246,29 +250,27 @@ MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) {
MediaByteRange nextRange = FindNextFrame();
while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
nextRange = FindNextFrame();
MP3DEMUXER_LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset, Duration(mFrameIndex + 1));
MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset, Duration(mFrameIndex + 1));
}
MP3DEMUXER_LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset);
MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
mOffset);
return SeekPosition();
}
RefPtr<MP3TrackDemuxer::SamplesPromise>
MP3TrackDemuxer::GetSamples(int32_t aNumSamples) {
MP3DEMUXER_LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
aNumSamples,
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
mSamplesPerSecond, mChannels);
MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d "
"mSamplesPerSecond=%d mChannels=%d",
aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
mSamplesPerFrame, mSamplesPerSecond, mChannels);
if (!aNumSamples) {
return SamplesPromise::CreateAndReject(
@@ -286,14 +288,13 @@ MP3TrackDemuxer::GetSamples(int32_t aNumSamples) {
frames->mSamples.AppendElement(frame);
}
MP3DEMUXER_LOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64
" mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
frames->mSamples.Length(), aNumSamples,
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
mSamplesPerSecond, mChannels);
MP3LOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64
" mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames,
mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
mChannels);
if (frames->mSamples.IsEmpty()) {
return SamplesPromise::CreateAndReject(
@@ -304,7 +305,7 @@ MP3TrackDemuxer::GetSamples(int32_t aNumSamples) {
void
MP3TrackDemuxer::Reset() {
MP3DEMUXER_LOG("Reset()");
MP3LOG("Reset()");
FastSeek(TimeUnit());
mParser.Reset();
@@ -425,12 +426,11 @@ MP3TrackDemuxer::FindNextFrame() {
static const int BUFFER_SIZE = 64;
static const int MAX_SKIPPED_BYTES = 1024 * BUFFER_SIZE;
MP3DEMUXER_LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
mSamplesPerSecond, mChannels);
MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
" mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
mSamplesPerFrame, mSamplesPerSecond, mChannels);
uint8_t buffer[BUFFER_SIZE];
int32_t read = 0;
@@ -488,19 +488,18 @@ MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) {
UpdateState(aRange);
MP3DEMUXER_LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
mSamplesPerSecond, mChannels);
MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
" mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
mSamplesPerFrame, mSamplesPerSecond, mChannels);
return true;
}
already_AddRefed<MediaRawData>
MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
MP3DEMUXER_LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})");
MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})");
if (!aRange.Length()) {
return nullptr;
}
@@ -510,7 +509,7 @@ MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
if (!frameWriter->SetSize(aRange.Length())) {
MP3DEMUXER_LOG("GetNext() Exit failed to allocated media buffer");
MP3LOG("GetNext() Exit failed to allocated media buffer");
return nullptr;
}
@@ -539,12 +538,11 @@ MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
mFirstFrameOffset = frame->mOffset;
}
MP3DEMUXER_LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64
" mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
"mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
mSamplesPerSecond, mChannels);
MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
" mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
" mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
mSamplesPerFrame, mSamplesPerSecond, mChannels);
return frame.forget();
}
@@ -653,7 +651,7 @@ MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) {
aSize = ClampReadSize(aOffset, aSize);
uint32_t read = 0;
MP3DEMUXER_LOGV("MP3TrackDemuxer::Read -> ReadAt(%d)", aSize);
MP3LOGV("MP3TrackDemuxer::Read -> ReadAt(%d)", aSize);
const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
static_cast<uint32_t>(aSize), &read);
NS_ENSURE_SUCCESS(rv, 0);
@@ -753,9 +751,13 @@ FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip) {
if (skipSize > aReader->Remaining()) {
// Skipping across the ID3v2 tag would take us past the end of the buffer, therefore we
// return immediately and let the calling function handle skipping the rest of the tag.
MP3LOGV("ID3v2 tag detected, size=%d, "
"needing to skip %d bytes past the current buffer",
tagSize, tagSize - aReader->Remaining());
*aBytesToSkip = skipSize - aReader->Remaining();
return false;
}
MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
aReader->Read(skipSize);
} else {
// No ID3v2 tag found, rewinding reader in order to search for a MPEG frame header.
@@ -1180,7 +1182,7 @@ static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
static const uint8_t MIN_MAJOR_VER = 2;
static const uint8_t MAX_MAJOR_VER = 4;
}
} // namespace id3_header
uint32_t
ID3Parser::Parse(ByteReader* aReader) {
@@ -1192,7 +1194,6 @@ ID3Parser::Parse(ByteReader* aReader) {
// Header found, return total tag size.
return ID3Header::SIZE + Header().Size() + Header().FooterSize();
}
return 0;
}
+7 -9
View File
@@ -46,7 +46,7 @@ AudioData::EnsureAudioBuffer()
size_t
AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData.get());
if (mAudioBuffer) {
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
}
@@ -61,15 +61,13 @@ AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
{
NS_ENSURE_TRUE(aOther, nullptr);
RefPtr<AudioData> v = new AudioData(aOther->mOffset,
aTimestamp,
aDuration,
aOther->mFrames,
aOther->mAudioData,
aOther->mChannels,
aOther->mRate);
aTimestamp,
aDuration,
aOther->mFrames,
Move(aOther->mAudioData),
aOther->mChannels,
aOther->mRate);
v->mDiscontinuity = aOther->mDiscontinuity;
// Remove aOther's AudioData as it can't be shared across two targets.
aOther->mAudioData.forget();
return v.forget();
}
+4 -3
View File
@@ -13,6 +13,7 @@
#include "nsIMemoryReporter.h"
#include "SharedBuffer.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
namespace mozilla {
@@ -123,13 +124,13 @@ public:
int64_t aTime,
int64_t aDuration,
uint32_t aFrames,
AudioDataValue* aData,
UniquePtr<AudioDataValue[]> aData,
uint32_t aChannels,
uint32_t aRate)
: MediaData(sType, aOffset, aTime, aDuration, aFrames)
, mChannels(aChannels)
, mRate(aRate)
, mAudioData(aData) {}
, mAudioData(Move(aData)) {}
static const Type sType = AUDIO_DATA;
static const char* sTypeName;
@@ -154,7 +155,7 @@ public:
// mChannels channels, each with mFrames frames
RefPtr<SharedBuffer> mAudioBuffer;
// mFrames frames, each with mChannels values
nsAutoArrayPtr<AudioDataValue> mAudioData;
UniquePtr<AudioDataValue[]> mAudioData;
protected:
~AudioData() {}
+122 -96
View File
@@ -138,7 +138,6 @@ void
MediaDecoder::NotifyOwnerActivityChanged()
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mOwner) {
NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
@@ -153,6 +152,8 @@ MediaDecoder::NotifyOwnerActivityChanged()
bool
MediaDecoder::IsHeuristicDormantSupported() const
{
MOZ_ASSERT(NS_IsMainThread());
return
#if defined(MOZ_EME)
// We disallow dormant for encrypted media until bug 1181864 is fixed.
@@ -345,69 +346,69 @@ MediaDecoder::IsInfinite()
return mInfiniteStream;
}
MediaDecoder::MediaDecoder() :
mWatchManager(this, AbstractThread::MainThread()),
mDormantSupported(false),
mLogicalPosition(0.0),
mDuration(std::numeric_limits<double>::quiet_NaN()),
mReentrantMonitor("media.decoder"),
mIgnoreProgressData(false),
mInfiniteStream(false),
mOwner(nullptr),
mPlaybackStatistics(new MediaChannelStatistics()),
mPinnedForSeek(false),
mShuttingDown(false),
mPausedForPlaybackRateNull(false),
mMinimizePreroll(false),
mMediaTracksConstructed(false),
mFiredMetadataLoaded(false),
mIsDormant(false),
mWasEndedWhenEnteredDormant(false),
mIsHeuristicDormantSupported(
Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)),
mHeuristicDormantTimeout(
Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS)),
mIsHeuristicDormant(false),
mStateMachineIsShutdown(AbstractThread::MainThread(), true,
"MediaDecoder::mStateMachineIsShutdown (Mirror)"),
mBuffered(AbstractThread::MainThread(), TimeIntervals(),
"MediaDecoder::mBuffered (Mirror)"),
mNextFrameStatus(AbstractThread::MainThread(),
MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
"MediaDecoder::mNextFrameStatus (Mirror)"),
mCurrentPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mCurrentPosition (Mirror)"),
mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mStateMachineDuration (Mirror)"),
mPlaybackPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mPlaybackPosition (Mirror)"),
mVolume(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mVolume (Canonical)"),
mPlaybackRate(AbstractThread::MainThread(), 1.0,
"MediaDecoder::mPlaybackRate (Canonical)"),
mPreservesPitch(AbstractThread::MainThread(), true,
"MediaDecoder::mPreservesPitch (Canonical)"),
mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mEstimatedDuration (Canonical)"),
mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
"MediaDecoder::mExplicitDuration (Canonical)"),
mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
"MediaDecoder::mPlayState (Canonical)"),
mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
"MediaDecoder::mNextState (Canonical)"),
mLogicallySeeking(AbstractThread::MainThread(), false,
"MediaDecoder::mLogicallySeeking (Canonical)"),
mSameOriginMedia(AbstractThread::MainThread(), false,
"MediaDecoder::mSameOriginMedia (Canonical)"),
mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mPlaybackBytesPerSecond (Canonical)"),
mPlaybackRateReliable(AbstractThread::MainThread(), true,
"MediaDecoder::mPlaybackRateReliable (Canonical)"),
mDecoderPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mDecoderPosition (Canonical)"),
mMediaSeekable(AbstractThread::MainThread(), true,
"MediaDecoder::mMediaSeekable (Canonical)")
MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
: mWatchManager(this, AbstractThread::MainThread())
, mDormantSupported(false)
, mLogicalPosition(0.0)
, mDuration(std::numeric_limits<double>::quiet_NaN())
, mIgnoreProgressData(false)
, mInfiniteStream(false)
, mOwner(aOwner)
, mVideoFrameContainer(aOwner->GetVideoFrameContainer())
, mPlaybackStatistics(new MediaChannelStatistics())
, mPinnedForSeek(false)
, mShuttingDown(false)
, mPausedForPlaybackRateNull(false)
, mMinimizePreroll(false)
, mMediaTracksConstructed(false)
, mFiredMetadataLoaded(false)
, mIsDormant(false)
, mWasEndedWhenEnteredDormant(false)
, mIsHeuristicDormantSupported(
Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
, mHeuristicDormantTimeout(
Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS))
, mIsHeuristicDormant(false)
, mStateMachineIsShutdown(AbstractThread::MainThread(), true,
"MediaDecoder::mStateMachineIsShutdown (Mirror)")
, mBuffered(AbstractThread::MainThread(), TimeIntervals(),
"MediaDecoder::mBuffered (Mirror)")
, mNextFrameStatus(AbstractThread::MainThread(),
MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
"MediaDecoder::mNextFrameStatus (Mirror)")
, mCurrentPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mCurrentPosition (Mirror)")
, mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mStateMachineDuration (Mirror)")
, mPlaybackPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mPlaybackPosition (Mirror)")
, mVolume(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mVolume (Canonical)")
, mPlaybackRate(AbstractThread::MainThread(), 1.0,
"MediaDecoder::mPlaybackRate (Canonical)")
, mPreservesPitch(AbstractThread::MainThread(), true,
"MediaDecoder::mPreservesPitch (Canonical)")
, mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mEstimatedDuration (Canonical)")
, mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
"MediaDecoder::mExplicitDuration (Canonical)")
, mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
"MediaDecoder::mPlayState (Canonical)")
, mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
"MediaDecoder::mNextState (Canonical)")
, mLogicallySeeking(AbstractThread::MainThread(), false,
"MediaDecoder::mLogicallySeeking (Canonical)")
, mSameOriginMedia(AbstractThread::MainThread(), false,
"MediaDecoder::mSameOriginMedia (Canonical)")
, mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mPlaybackBytesPerSecond (Canonical)")
, mPlaybackRateReliable(AbstractThread::MainThread(), true,
"MediaDecoder::mPlaybackRateReliable (Canonical)")
, mDecoderPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mDecoderPosition (Canonical)")
, mMediaSeekable(AbstractThread::MainThread(), true,
"MediaDecoder::mMediaSeekable (Canonical)")
{
MOZ_COUNT_CTOR(MediaDecoder);
MOZ_ASSERT(NS_IsMainThread());
@@ -436,16 +437,8 @@ MediaDecoder::MediaDecoder() :
// mIgnoreProgressData
mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::SeekingChanged);
}
bool
MediaDecoder::Init(MediaDecoderOwner* aOwner)
{
MOZ_ASSERT(NS_IsMainThread());
mOwner = aOwner;
mVideoFrameContainer = aOwner->GetVideoFrameContainer();
MediaShutdownManager::Instance().Register(this);
return true;
}
void
@@ -500,8 +493,7 @@ MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
}
nsresult
MediaDecoder::Load(nsIStreamListener** aStreamListener,
MediaDecoder* aCloneDonor)
MediaDecoder::Load(nsIStreamListener** aStreamListener)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mResource, "Can't load without a MediaResource");
@@ -512,18 +504,16 @@ MediaDecoder::Load(nsIStreamListener** aStreamListener,
SetStateMachine(CreateStateMachine());
NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
return InitializeStateMachine(aCloneDonor);
return InitializeStateMachine();
}
nsresult
MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
MediaDecoder::InitializeStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
nsresult rv = mDecoderStateMachine->Init(
cloneDonor ? cloneDonor->mDecoderStateMachine.get() : nullptr);
nsresult rv = mDecoderStateMachine->Init();
NS_ENSURE_SUCCESS(rv, rv);
// If some parameters got set before the state machine got created,
@@ -730,6 +720,50 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
NotifySuspendedStatusChanged();
}
nsresult
MediaDecoder::FinishDecoderSetup(MediaResource* aResource)
{
MOZ_ASSERT(NS_IsMainThread());
HTMLMediaElement* element = mOwner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
element->FinishDecoderSetup(this, aResource);
return NS_OK;
}
void
MediaDecoder::NotifyNetworkError()
{
NetworkError();
}
void
MediaDecoder::NotifyDecodeError()
{
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &MediaDecoder::DecodeError);
AbstractThread::MainThread()->Dispatch(r.forget());
}
void
MediaDecoder::NotifyDataEnded(nsresult aStatus)
{
RefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->NotifyDownloadEnded(aStatus);
if (NS_SUCCEEDED(aStatus)) {
HTMLMediaElement* element = self->mOwner->GetMediaElement();
if (element) {
element->DownloadSuspended();
}
// NotifySuspendedStatusChanged will tell the element that download
// has been suspended "by the cache", which is true since we never
// download anything. The element can then transition to HAVE_ENOUGH_DATA.
self->NotifySuspendedStatusChanged();
}
});
AbstractThread::MainThread()->Dispatch(r.forget());
}
void
MediaDecoder::ResetConnectionState()
{
@@ -943,6 +977,13 @@ MediaDecoder::NotifyPrincipalChanged()
void
MediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
{
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArgs<int64_t, int64_t>(
this, &MediaDecoder::NotifyBytesConsumed, aBytes, aOffset);
AbstractThread::MainThread()->Dispatch(r.forget());
return;
}
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown || mIgnoreProgressData) {
@@ -968,8 +1009,6 @@ MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
bool fireEnded = false;
bool seekWasAborted = false;
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// An additional seek was requested while the current seek was
// in operation.
UnpinForSeek();
@@ -1200,12 +1239,6 @@ MediaDecoder::SetLoadInBackground(bool aLoadInBackground)
}
}
bool
MediaDecoder::OnStateMachineTaskQueue() const
{
return mDecoderStateMachine->OnTaskQueue();
}
void
MediaDecoder::SetPlaybackRate(double aPlaybackRate)
{
@@ -1256,11 +1289,6 @@ MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
}
}
ReentrantMonitor&
MediaDecoder::GetReentrantMonitor() {
return mReentrantMonitor;
}
ImageContainer*
MediaDecoder::GetImageContainer()
{
@@ -1317,13 +1345,11 @@ void MediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
}
void
MediaDecoder::NotifyDataArrived(uint32_t aLength,
int64_t aOffset,
bool aThrottleUpdates) {
MediaDecoder::NotifyDataArrived(bool aThrottleUpdates) {
MOZ_ASSERT(NS_IsMainThread());
if (mDecoderStateMachine) {
mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
mDecoderStateMachine->DispatchNotifyDataArrived(aThrottleUpdates);
}
// ReadyState computation depends on MediaDecoder::CanPlayThrough, which
+21 -124
View File
@@ -200,10 +200,12 @@ destroying the MediaDecoder object.
#include "nsITimer.h"
#include "AbstractMediaDecoder.h"
#include "FrameStatistics.h"
#include "MediaDecoderOwner.h"
#include "MediaEventSource.h"
#include "MediaMetadataManager.h"
#include "MediaResource.h"
#include "MediaResourceCallback.h"
#include "MediaStatistics.h"
#include "MediaStreamGraph.h"
#include "TimeUnits.h"
@@ -267,7 +269,7 @@ struct SeekTarget {
MediaDecoderEventVisibility mEventVisibility;
};
class MediaDecoder : public AbstractMediaDecoder
class MediaDecoder : public AbstractMediaDecoder, public MediaResourceCallback
{
public:
struct SeekResolveValue {
@@ -294,24 +296,18 @@ public:
// Must be called exactly once, on the main thread, during startup.
static void InitStatics();
MediaDecoder();
explicit MediaDecoder(MediaDecoderOwner* aOwner);
// Reset the decoder and notify the media element that
// server connection is closed.
virtual void ResetConnectionState();
virtual void ResetConnectionState() override;
// Create a new decoder of the same type as this one.
// Subclasses must implement this.
virtual MediaDecoder* Clone() = 0;
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) = 0;
// Create a new state machine to run this decoder.
// Subclasses must implement this.
virtual MediaDecoderStateMachine* CreateStateMachine() = 0;
// Call on the main thread only.
// Perform any initialization required for the decoder.
// Return true on successful initialisation, false
// on failure.
virtual bool Init(MediaDecoderOwner* aOwner);
// Cleanup internal data structures. Must be called on the main
// thread by the owning object before that object disposes of this object.
virtual void Shutdown();
@@ -319,8 +315,7 @@ public:
// Start downloading the media. Decode the downloaded data up to the
// point of the first frame of data.
// This is called at most once per decoder, after Init().
virtual nsresult Load(nsIStreamListener** aListener,
MediaDecoder* aCloneDonor);
virtual nsresult Load(nsIStreamListener** aListener);
// Called in |Load| to open mResource.
nsresult OpenResource(nsIStreamListener** aStreamListener);
@@ -359,7 +354,7 @@ public:
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType);
// Initialize state machine and schedule it.
nsresult InitializeStateMachine(MediaDecoder* aCloneDonor);
nsresult InitializeStateMachine();
// Start playback of a video. 'Load' must have previously been
// called.
@@ -412,7 +407,7 @@ public:
//
// When the media stream ends, we can know the duration, thus the stream is
// no longer considered to be infinite.
virtual void SetInfinite(bool aInfinite);
virtual void SetInfinite(bool aInfinite) override;
// Return true if the stream is infinite (see SetInfinite).
virtual bool IsInfinite();
@@ -421,11 +416,11 @@ public:
// If MediaResource::IsSuspendedByCache returns true, then the decoder
// should stop buffering or otherwise waiting for download progress and
// start consuming data, if possible, because the cache is full.
virtual void NotifySuspendedStatusChanged();
virtual void NotifySuspendedStatusChanged() override;
// Called by MediaResource when some data has been received.
// Call on the main thread only.
virtual void NotifyBytesDownloaded();
virtual void NotifyBytesDownloaded() override;
// Called by nsChannelToPipeListener or MediaResource when the
// download has ended. Called on the main thread only. aStatus is
@@ -434,12 +429,11 @@ public:
// Called as data arrives on the stream and is read into the cache. Called
// on the main thread only.
virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset,
bool aThrottleUpdates) override;
virtual void NotifyDataArrived(bool aThrottleUpdates) override;
// Called by MediaResource when the principal of the resource has
// changed. Called on main thread only.
virtual void NotifyPrincipalChanged();
virtual void NotifyPrincipalChanged() override;
// Called by the MediaResource to keep track of the number of bytes read
// from the resource. Called on the main by an event runner dispatched
@@ -511,17 +505,11 @@ public:
void SetLoadInBackground(bool aLoadInBackground);
// Returns a weak reference to the media decoder owner.
MediaDecoderOwner* GetMediaOwner() const;
bool OnStateMachineTaskQueue() const override;
MediaDecoderOwner* GetMediaOwner() const override;
MediaDecoderStateMachine* GetStateMachine() const;
void SetStateMachine(MediaDecoderStateMachine* aStateMachine);
// Returns the monitor for other threads to synchronise access to
// state.
ReentrantMonitor& GetReentrantMonitor() override;
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
virtual media::TimeIntervals GetBuffered();
@@ -733,99 +721,6 @@ private:
// at any time.
MediaStatistics GetStatistics();
// Frame decoding/painting related performance counters.
// Threadsafe.
class FrameStatistics {
public:
FrameStatistics() :
mReentrantMonitor("MediaDecoder::FrameStats"),
mParsedFrames(0),
mDecodedFrames(0),
mPresentedFrames(0),
mDroppedFrames(0),
mCorruptFrames(0) {}
// Returns number of frames which have been parsed from the media.
// Can be called on any thread.
uint32_t GetParsedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mParsedFrames;
}
// Returns the number of parsed frames which have been decoded.
// Can be called on any thread.
uint32_t GetDecodedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mDecodedFrames;
}
// Returns the number of decoded frames which have been sent to the rendering
// pipeline for painting ("presented").
// Can be called on any thread.
uint32_t GetPresentedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mPresentedFrames;
}
// Number of frames that have been skipped because they have missed their
// compoisition deadline.
uint32_t GetDroppedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mDroppedFrames + mCorruptFrames;
}
uint32_t GetCorruptedFrames() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mCorruptFrames;
}
// Increments the parsed and decoded frame counters by the passed in counts.
// Can be called on any thread.
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) {
if (aParsed == 0 && aDecoded == 0 && aDropped == 0)
return;
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mParsedFrames += aParsed;
mDecodedFrames += aDecoded;
mDroppedFrames += aDropped;
}
// Increments the presented frame counters.
// Can be called on any thread.
void NotifyPresentedFrame() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
++mPresentedFrames;
}
void NotifyCorruptFrame() {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
++mCorruptFrames;
}
private:
// ReentrantMonitor to protect access of playback statistics.
ReentrantMonitor mReentrantMonitor;
// Number of frames parsed and demuxed from media.
// Access protected by mReentrantMonitor.
uint32_t mParsedFrames;
// Number of parsed frames which were actually decoded.
// Access protected by mReentrantMonitor.
uint32_t mDecodedFrames;
// Number of decoded frames which were actually sent down the rendering
// pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
uint32_t mPresentedFrames;
uint32_t mDroppedFrames;
uint32_t mCorruptFrames;
};
// Return the frame decode/paint related statistics.
FrameStatistics& GetFrameStatistics() { return mFrameStats; }
@@ -928,11 +823,6 @@ private:
// Explicitly private to force access via accessors.
RefPtr<MediaDecoderStateMachine> mDecoderStateMachine;
// |ReentrantMonitor| for detecting when the video play state changes. A call
// to |Wait| on this monitor will block the thread until the next state
// change. Explicitly private for force access via GetReentrantMonitor.
ReentrantMonitor mReentrantMonitor;
protected:
virtual void CallSeek(const SeekTarget& aTarget);
@@ -1149,6 +1039,13 @@ public:
AbstractCanonical<bool>* CanonicalMediaSeekable() {
return &mMediaSeekable;
}
private:
/* MediaResourceCallback functions */
virtual nsresult FinishDecoderSetup(MediaResource* aResource) override;
virtual void NotifyNetworkError() override;
virtual void NotifyDecodeError() override;
virtual void NotifyDataEnded(nsresult aStatus) override;
};
} // namespace mozilla
+5 -17
View File
@@ -90,6 +90,9 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
void
MediaDecoderReader::InitializationTask()
{
if (!mDecoder) {
return;
}
if (mDecoder->CanonicalDurationOrNull()) {
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
}
@@ -101,7 +104,6 @@ MediaDecoderReader::InitializationTask()
MediaDecoderReader::~MediaDecoderReader()
{
MOZ_ASSERT(mShutdown);
MOZ_ASSERT(!mDecoder);
ResetDecode();
MOZ_COUNT_DTOR(MediaDecoderReader);
}
@@ -182,22 +184,11 @@ MediaDecoderReader::UpdateBuffered()
}
void
MediaDecoderReader::ThrottledNotifyDataArrived(const Interval<int64_t>& aInterval)
MediaDecoderReader::ThrottledNotifyDataArrived()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
if (mThrottledInterval.isNothing()) {
mThrottledInterval.emplace(aInterval);
} else if (mThrottledInterval.ref().Contains(aInterval)) {
return;
} else if (!mThrottledInterval.ref().Contiguous(aInterval)) {
DoThrottledNotify();
mThrottledInterval.emplace(aInterval);
} else {
mThrottledInterval = Some(mThrottledInterval.ref().Span(aInterval));
}
// If it's been long enough since our last update, do it.
if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) {
DoThrottledNotify();
@@ -226,9 +217,7 @@ MediaDecoderReader::DoThrottledNotify()
MOZ_ASSERT(OnTaskQueue());
mLastThrottledNotify = TimeStamp::Now();
mThrottledNotify.DisconnectIfExists();
Interval<int64_t> interval = mThrottledInterval.ref();
mThrottledInterval.reset();
NotifyDataArrived(interval);
NotifyDataArrived();
}
media::TimeIntervals
@@ -253,7 +242,6 @@ MediaDecoderReader::AsyncReadMetadata()
typedef ReadMetadataFailureReason Reason;
MOZ_ASSERT(OnTaskQueue());
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
// Attempt to read the metadata.
+115 -105
View File
@@ -58,6 +58,11 @@ enum class ReadMetadataFailureReason : int8_t
// Unless otherwise specified, methods and fields of this class can only
// be accessed on the decode task queue.
class MediaDecoderReader {
friend class ReRequestVideoWithSkipTask;
friend class ReRequestAudioTask;
static const bool IsExclusive = true;
public:
enum NotDecodedReason {
END_OF_STREAM,
@@ -66,16 +71,20 @@ public:
CANCELED
};
typedef MozPromise<RefPtr<MetadataHolder>, ReadMetadataFailureReason, /* IsExclusive = */ true> MetadataPromise;
typedef MozPromise<RefPtr<MediaData>, NotDecodedReason, /* IsExclusive = */ true> AudioDataPromise;
typedef MozPromise<RefPtr<MediaData>, NotDecodedReason, /* IsExclusive = */ true> VideoDataPromise;
typedef MozPromise<int64_t, nsresult, /* IsExclusive = */ true> SeekPromise;
using MetadataPromise =
MozPromise<RefPtr<MetadataHolder>, ReadMetadataFailureReason, IsExclusive>;
using AudioDataPromise =
MozPromise<RefPtr<MediaData>, NotDecodedReason, IsExclusive>;
using VideoDataPromise =
MozPromise<RefPtr<MediaData>, NotDecodedReason, IsExclusive>;
using SeekPromise = MozPromise<int64_t, nsresult, IsExclusive>;
// Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
// But in the current architecture it's only ever used exclusively (by MDSM),
// so we mark it that way to verify our assumptions. If you have a use-case
// for multiple WaitForData consumers, feel free to flip the exclusivity here.
typedef MozPromise<MediaData::Type, WaitForDataRejectValue, /* IsExclusive = */ true> WaitForDataPromise;
using WaitForDataPromise =
MozPromise<MediaData::Type, WaitForDataRejectValue, IsExclusive>;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
@@ -83,12 +92,6 @@ public:
// destroyed.
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
// Does any spinup that needs to happen on this task queue. This runs on a
// different thread than Init, and there should not be ordering dependencies
// between the two (even though in practice, Init will always run first right
// now thanks to the tail dispatcher).
void InitializationTask();
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
virtual nsresult Init() { return NS_OK; }
@@ -98,7 +101,7 @@ public:
virtual bool IsWaitingOnCDMResource() { return false; }
// Release media resources they should be released in dormant state
// The reader can be made usable again by calling ReadMetadata().
virtual void ReleaseMediaResources() {};
virtual void ReleaseMediaResources() {}
// Breaks reference-counted cycles. Called during shutdown.
// WARNING: If you override this, you must call the base implementation
// in your override.
@@ -110,7 +113,7 @@ public:
// thread.
virtual RefPtr<ShutdownPromise> Shutdown();
virtual bool OnTaskQueue()
virtual bool OnTaskQueue() const
{
return OwnerThread()->IsCurrentThreadIn();
}
@@ -146,39 +149,29 @@ public:
virtual RefPtr<VideoDataPromise>
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
friend class ReRequestVideoWithSkipTask;
friend class ReRequestAudioTask;
// By default, the state machine polls the reader once per second when it's
// in buffering mode. Some readers support a promise-based mechanism by which
// they notify the state machine when the data arrives.
virtual bool IsWaitForDataSupported() { return false; }
virtual RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
virtual bool HasAudio() = 0;
virtual bool HasVideo() = 0;
virtual RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType)
{
MOZ_CRASH();
}
// The default implementation of AsyncReadMetadata is implemented in terms of
// synchronous ReadMetadata() calls. Implementations may also
// override AsyncReadMetadata to create a more proper async implementation.
virtual RefPtr<MetadataPromise> AsyncReadMetadata();
// Read header data for all bitstreams in the file. Fills aInfo with
// the data required to present the media, and optionally fills *aTags
// with tag metadata from the file.
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) { MOZ_CRASH(); }
// Fills aInfo with the latest cached data required to present the media,
// ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) { };
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) {}
// Moves the decode head to aTime microseconds. aEndTime denotes the end
// time of the media in usecs. This is only needed for OggReader, and should
// probably be removed somehow.
virtual RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) = 0;
virtual RefPtr<SeekPromise> Seek(int64_t aTime, int64_t aEndTime) = 0;
// Called to move the reader into idle state. When the reader is
// created it is assumed to be active (i.e. not idle). When the media
@@ -190,7 +183,7 @@ public:
// Note: DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek should
// activate the decoder if necessary. The state machine only needs to know
// when to call SetIdle().
virtual void SetIdle() { }
virtual void SetIdle() {}
// Tell the reader that the data decoded are not for direct playback, so it
// can accept more files, in particular those which have more channels than
@@ -200,27 +193,6 @@ public:
mIgnoreAudioOutputFormat = true;
}
// Populates aBuffered with the time ranges which are buffered. This may only
// be called on the decode task queue, and should only be used internally by
// UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
// else.
//
// This base implementation in MediaDecoderReader estimates the time ranges
// buffered by interpolating the cached byte ranges with the duration
// of the media. Reader subclasses should override this method if they
// can quickly calculate the buffered ranges more accurately.
//
// The primary advantage of this implementation in the reader base class
// is that it's a fast approximation, which does not perform any I/O.
//
// The OggReader relies on this base implementation not performing I/O,
// since in FirefoxOS we can't do I/O on the main thread, where this is
// called.
virtual media::TimeIntervals GetBuffered();
// Recomputes mBuffered.
virtual void UpdateBuffered();
// MediaSourceReader opts out of the start-time-guessing mechanism.
virtual bool ForceZeroStartTime() const { return false; }
@@ -241,53 +213,39 @@ public:
virtual size_t SizeOfVideoQueueInFrames();
virtual size_t SizeOfAudioQueueInFrames();
protected:
friend class TrackBuffer;
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { }
void NotifyDataArrived(const media::Interval<int64_t>& aInfo)
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
NotifyDataArrivedInternal(aInfo.Length(), aInfo.mStart);
UpdateBuffered();
}
// Invokes NotifyDataArrived while throttling the calls to occur at most every mThrottleDuration ms.
void ThrottledNotifyDataArrived(const media::Interval<int64_t>& aInterval);
void DoThrottledNotify();
public:
// In situations where these notifications come from stochastic network
// activity, we can save significant recomputation by throttling the delivery
// of these updates to the reader implementation. We don't want to do this
// throttling when the update comes from MSE code, since that code needs the
// updates to be observable immediately, and is generally less
// trigger-happy with notifications anyway.
void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
void DispatchNotifyDataArrived(bool aThrottleUpdates)
{
RefPtr<nsRunnable> r =
NS_NewRunnableMethodWithArg<media::Interval<int64_t>>(this, aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived
: &MediaDecoderReader::NotifyDataArrived,
media::Interval<int64_t>(aOffset, aOffset + aLength));
OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
RefPtr<nsRunnable> r = NS_NewRunnableMethod(
this,
aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived :
&MediaDecoderReader::NotifyDataArrived);
OwnerThread()->Dispatch(
r.forget(), AbstractThread::DontAssertDispatchSuccess);
}
// Notify the reader that data from the resource was evicted (MediaSource only)
virtual void NotifyDataRemoved() {}
void NotifyDataArrived()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
NotifyDataArrivedInternal();
UpdateBuffered();
}
virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
// Returns a pointer to the decoder.
AbstractMediaDecoder* GetDecoder() {
return mDecoder;
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
{
return &mBuffered;
}
RefPtr<VideoDataPromise> DecodeToFirstVideoData();
MediaInfo GetMediaInfo() { return mInfo; }
// Indicates if the media is seekable.
// ReadMetada should be called before calling this method.
virtual bool IsMediaSeekable() = 0;
@@ -306,7 +264,8 @@ public:
OwnerThread()->Dispatch(r.forget());
}
TaskQueue* OwnerThread() {
TaskQueue* OwnerThread() const
{
return mTaskQueue;
}
@@ -322,30 +281,41 @@ public:
virtual void DisableHardwareAcceleration() {}
TimedMetadataEventSource& TimedMetadataEvent() {
TimedMetadataEventSource& TimedMetadataEvent()
{
return mTimedMetadataEvent;
}
protected:
virtual ~MediaDecoderReader();
// Overrides of this function should decodes an unspecified amount of
// audio data, enqueuing the audio data in mAudioQueue. Returns true
// when there's more audio to decode, false if the audio is finished,
// end of file has been reached, or an un-recoverable read error has
// occured. This function blocks until the decode is complete.
virtual bool DecodeAudioData() {
return false;
// Populates aBuffered with the time ranges which are buffered. This may only
// be called on the decode task queue, and should only be used internally by
// UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
// else.
//
// This base implementation in MediaDecoderReader estimates the time ranges
// buffered by interpolating the cached byte ranges with the duration
// of the media. Reader subclasses should override this method if they
// can quickly calculate the buffered ranges more accurately.
//
// The primary advantage of this implementation in the reader base class
// is that it's a fast approximation, which does not perform any I/O.
//
// The OggReader relies on this base implementation not performing I/O,
// since in FirefoxOS we can't do I/O on the main thread, where this is
// called.
virtual media::TimeIntervals GetBuffered();
RefPtr<VideoDataPromise> DecodeToFirstVideoData();
bool HaveStartTime()
{
MOZ_ASSERT(OnTaskQueue());
return mStartTime.isSome();
}
// Overrides of this function should read and decodes one video frame.
// Packets with a timestamp less than aTimeThreshold will be decoded
// (unless they're not keyframes and aKeyframeSkip is true), but will
// not be added to the queue. This function blocks until the decode
// is complete.
virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) {
return false;
}
int64_t StartTime() { MOZ_ASSERT(HaveStartTime()); return mStartTime.ref(); }
// Queue of audio frames. This queue is threadsafe, and is accessed from
// the audio, decoder, state machine, and main threads.
@@ -375,9 +345,6 @@ protected:
// Buffered range.
Canonical<media::TimeIntervals> mBuffered;
public:
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() { return &mBuffered; }
protected:
// Stores presentation info required for playback.
MediaInfo mInfo;
@@ -407,8 +374,6 @@ protected:
// things such that all GetBuffered calls go through the MDSM, which would
// offset the range accordingly.
Maybe<int64_t> mStartTime;
bool HaveStartTime() { MOZ_ASSERT(OnTaskQueue()); return mStartTime.isSome(); }
int64_t StartTime() { MOZ_ASSERT(HaveStartTime()); return mStartTime.ref(); }
// This is a quick-and-dirty way for DecodeAudioData implementations to
// communicate the presence of a decoding error to RequestAudioData. We should
@@ -421,6 +386,51 @@ protected:
TimedMetadataEventProducer mTimedMetadataEvent;
private:
// Does any spinup that needs to happen on this task queue. This runs on a
// different thread than Init, and there should not be ordering dependencies
// between the two (even though in practice, Init will always run first right
// now thanks to the tail dispatcher).
void InitializationTask();
// Read header data for all bitstreams in the file. Fills aInfo with
// the data required to present the media, and optionally fills *aTags
// with tag metadata from the file.
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
{
MOZ_CRASH();
}
// Recomputes mBuffered.
virtual void UpdateBuffered();
virtual void NotifyDataArrivedInternal() {}
// Invokes NotifyDataArrived while throttling the calls to occur
// at most every mThrottleDuration ms.
void ThrottledNotifyDataArrived();
void DoThrottledNotify();
// Overrides of this function should decodes an unspecified amount of
// audio data, enqueuing the audio data in mAudioQueue. Returns true
// when there's more audio to decode, false if the audio is finished,
// end of file has been reached, or an un-recoverable read error has
// occured. This function blocks until the decode is complete.
virtual bool DecodeAudioData()
{
return false;
}
// Overrides of this function should read and decodes one video frame.
// Packets with a timestamp less than aTimeThreshold will be decoded
// (unless they're not keyframes and aKeyframeSkip is true), but will
// not be added to the queue. This function blocks until the decode
// is complete.
virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold)
{
return false;
}
// Promises used only for the base-class (sync->async adapter) implementation
// of Request{Audio,Video}Data.
MozPromiseHolder<AudioDataPromise> mBaseAudioPromise;
+10 -10
View File
@@ -1020,7 +1020,7 @@ bool MediaDecoderStateMachine::IsPlaying() const
return mMediaSink->IsPlaying();
}
nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
nsresult MediaDecoderStateMachine::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = mReader->Init();
@@ -2432,7 +2432,7 @@ MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
// Update corrupt-frames statistics
if (aData->mImage && !aData->mImage->IsValid()) {
MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
frameStats.NotifyCorruptFrame();
// If more than 10% of the last 30 frames have been corrupted, then try disabling
// hardware acceleration. We use 10 as the corrupt value because RollingMean<>
@@ -2565,7 +2565,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
VideoQueue().PushFront(currentFrame);
if (framesRemoved > 0) {
mVideoFrameEndTime = currentFrame->GetEndTime();
MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
frameStats.NotifyPresentedFrame();
}
}
@@ -2682,7 +2682,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
}
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
uint32_t channels = audio->mChannels;
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
auto audioData = MakeUnique<AudioDataValue[]>(frames * channels);
memcpy(audioData.get(),
audio->mAudioData.get() + (framesToPrune.value() * channels),
frames * channels * sizeof(AudioDataValue));
@@ -2691,12 +2691,12 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
return NS_ERROR_FAILURE;
}
RefPtr<AudioData> data(new AudioData(audio->mOffset,
mCurrentSeek.mTarget.mTime,
duration.value(),
frames,
audioData.forget(),
channels,
audio->mRate));
mCurrentSeek.mTarget.mTime,
duration.value(),
frames,
Move(audioData),
channels,
audio->mRate));
PushFront(data, MediaData::AUDIO_DATA);
return NS_OK;
+7 -7
View File
@@ -134,7 +134,7 @@ public:
MediaDecoderReader* aReader,
bool aRealTime = false);
nsresult Init(MediaDecoderStateMachine* aCloneDonor);
nsresult Init();
// Enumeration for the valid decoding states
enum State {
@@ -169,9 +169,9 @@ public:
OwnerThread()->Dispatch(runnable.forget());
}
void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
void DispatchNotifyDataArrived(bool aThrottleUpdates)
{
mReader->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
mReader->DispatchNotifyDataArrived(aThrottleUpdates);
}
// Notifies the state machine that should minimize the number of samples
@@ -233,10 +233,6 @@ public:
// Immutable after construction - may be called on any thread.
bool IsRealTime() const { return mRealTime; }
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
bool OnTaskQueue() const;
size_t SizeOfVideoQueue() {
if (mReader) {
return mReader->SizeOfVideoQueueInBytes();
@@ -252,6 +248,10 @@ public:
}
private:
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
bool OnTaskQueue() const;
// Initialization that needs to happen on the task queue. This is the first
// task that gets run on the task queue, and is dispatched from the MDSM
// constructor immediately after the task queue is created.
+31 -31
View File
@@ -8,7 +8,6 @@
#include "mozilla/Preferences.h"
#include "nsPrintfCString.h"
#include "nsSize.h"
#include "ImageContainer.h"
#include "Layers.h"
#include "MediaData.h"
#include "MediaInfo.h"
@@ -63,19 +62,22 @@ TrackTypeToStr(TrackInfo::TrackType aTrack)
#endif
MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
MediaDataDemuxer* aDemuxer)
MediaDataDemuxer* aDemuxer,
VideoFrameContainer* aVideoFrameContainer,
layers::LayersBackend aLayersBackend)
: MediaDecoderReader(aDecoder)
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2))
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2))
, mDemuxer(aDemuxer)
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mSeekable(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mHardwareAccelerationDisabled(false)
, mVideoFrameContainer(aVideoFrameContainer)
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
@@ -150,6 +152,9 @@ MediaFormatReader::InitLayersBackendType()
// Extract the layer manager backend type so that platform decoders
// can determine whether it's worthwhile using hardware accelerated
// video decoding.
if (!mDecoder) {
return;
}
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (!owner) {
NS_WARNING("MediaFormatReader without a decoder owner, can't get HWAccel");
@@ -230,7 +235,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
// To decode, we need valid video and a place to put it.
bool videoActive = !!mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack) &&
mDecoder->GetImageContainer();
GetImageContainer();
if (videoActive) {
// We currently only handle the first video track.
@@ -256,7 +261,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
mIsEncrypted = crypto && crypto->IsEncrypted();
if (crypto && crypto->IsEncrypted()) {
if (mDecoder && crypto && crypto->IsEncrypted()) {
mInfo.mCrypto = *crypto;
}
@@ -339,7 +344,7 @@ MediaFormatReader::EnsureDecodersCreated()
mVideo.mCallback,
mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE :
mLayersBackendType,
mDecoder->GetImageContainer());
GetImageContainer());
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
}
@@ -1226,7 +1231,9 @@ MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping succeeded, skipped %u frames", aSkipped);
mSkipRequest.Complete();
mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
if (mDecoder) {
mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
}
MOZ_ASSERT(!mVideo.mError); // We have flushed the decoder, no frame could
// have been decoded (and as such errored)
ScheduleUpdate(TrackInfo::kVideoTrack);
@@ -1238,7 +1245,9 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
mSkipRequest.Complete();
mDecoder->NotifyDecodedFrames(aFailure.mSkipped, 0, aFailure.mSkipped);
if (mDecoder) {
mDecoder->NotifyDecodedFrames(aFailure.mSkipped, 0, aFailure.mSkipped);
}
MOZ_ASSERT(mVideo.HasPromise());
switch (aFailure.mFailure) {
case DemuxerFailureReason::END_OF_STREAM:
@@ -1420,10 +1429,8 @@ void MediaFormatReader::ReleaseMediaResources()
{
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
VideoFrameContainer* container =
mDecoder ? mDecoder->GetVideoFrameContainer() : nullptr;
if (container) {
container->ClearCurrentFrame();
if (mVideoFrameContainer) {
mVideoFrameContainer->ClearCurrentFrame();
}
if (mVideo.mDecoder) {
mVideo.mDecoder->Shutdown();
@@ -1438,21 +1445,17 @@ MediaFormatReader::VideoIsHardwareAccelerated() const
}
void
MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
MediaFormatReader::NotifyDemuxer()
{
MOZ_ASSERT(OnTaskQueue());
LOGV("aLength=%u, aOffset=%lld", aLength, aOffset);
if (mShutdown || !mDemuxer ||
(!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
return;
}
if (aLength || aOffset) {
mDemuxer->NotifyDataArrived();
} else {
mDemuxer->NotifyDataRemoved();
}
mDemuxer->NotifyDataArrived();
if (!mInitDone) {
return;
}
@@ -1467,20 +1470,10 @@ MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
}
void
MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
MediaFormatReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(aLength);
NotifyDemuxer(aLength, aOffset);
}
void
MediaFormatReader::NotifyDataRemoved()
{
MOZ_ASSERT(OnTaskQueue());
NotifyDemuxer(0, 0);
NotifyDemuxer();
}
bool
@@ -1489,4 +1482,11 @@ MediaFormatReader::ForceZeroStartTime() const
return !mDemuxer->ShouldComputeStartTime();
}
layers::ImageContainer*
MediaFormatReader::GetImageContainer()
{
return mVideoFrameContainer
? mVideoFrameContainer->GetImageContainer() : nullptr;
}
} // namespace mozilla
+12 -15
View File
@@ -23,7 +23,10 @@ class MediaFormatReader final : public MediaDecoderReader
typedef media::Interval<int64_t> ByteInterval;
public:
MediaFormatReader(AbstractMediaDecoder* aDecoder, MediaDataDemuxer* aDemuxer);
MediaFormatReader(AbstractMediaDecoder* aDecoder,
MediaDataDemuxer* aDemuxer,
VideoFrameContainer* aVideoFrameContainer = nullptr,
layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE);
virtual ~MediaFormatReader();
@@ -37,16 +40,6 @@ public:
RefPtr<AudioDataPromise> RequestAudioData() override;
bool HasVideo() override
{
return mVideo.mTrackDemuxer;
}
bool HasAudio() override
{
return mAudio.mTrackDemuxer;
}
RefPtr<MetadataPromise> AsyncReadMetadata() override;
void ReadUpdatedMetadata(MediaInfo* aInfo) override;
@@ -60,10 +53,9 @@ public:
}
protected:
void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
public:
void NotifyDataRemoved() override;
void NotifyDataArrivedInternal() override;
public:
media::TimeIntervals GetBuffered() override;
bool ForceZeroStartTime() const override;
@@ -92,12 +84,14 @@ public:
}
private:
bool HasVideo() { return mVideo.mTrackDemuxer; }
bool HasAudio() { return mAudio.mTrackDemuxer; }
bool InitDemuxer();
// Notify the demuxer that new data has been received.
// The next queued task calling GetBuffered() is guaranteed to have up to date
// buffered ranges.
void NotifyDemuxer(uint32_t aLength, int64_t aOffset);
void NotifyDemuxer();
void ReturnOutput(MediaData* aData, TrackType aTrack);
bool EnsureDecodersCreated();
@@ -415,6 +409,9 @@ private:
// Pending decoders initialization.
MozPromiseRequestHolder<MediaDataDecoder::InitPromise::AllPromiseType> mDecodersInitRequest;
RefPtr<VideoFrameContainer> mVideoFrameContainer;
layers::ImageContainer* GetImageContainer();
#if defined(READER_DORMANT_HEURISTIC)
const bool mDormantEnabled;
#endif
+56 -156
View File
@@ -7,11 +7,11 @@
#include "mozilla/DebugOnly.h"
#include "MediaResource.h"
#include "MediaResourceCallback.h"
#include "RtspMediaResource.h"
#include "mozilla/Mutex.h"
#include "nsDebug.h"
#include "MediaDecoder.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsIFile.h"
@@ -64,11 +64,11 @@ NS_IMPL_ADDREF(MediaResource)
NS_IMPL_RELEASE_WITH_DESTROY(MediaResource, Destroy())
NS_IMPL_QUERY_INTERFACE0(MediaResource)
ChannelMediaResource::ChannelMediaResource(MediaDecoder* aDecoder,
ChannelMediaResource::ChannelMediaResource(MediaResourceCallback* aCallback,
nsIChannel* aChannel,
nsIURI* aURI,
const nsACString& aContentType)
: BaseMediaResource(aDecoder, aChannel, aURI, aContentType),
: BaseMediaResource(aCallback, aChannel, aURI, aContentType),
mOffset(0),
mReopenOnError(false),
mIgnoreClose(false),
@@ -160,7 +160,7 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
{
NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
@@ -182,7 +182,7 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
// If the request was cancelled by nsCORSListenerProxy due to failing
// the CORS security check, send an error through to the media element.
if (status == NS_ERROR_DOM_BAD_URI) {
mDecoder->NetworkError();
mCallback->NotifyNetworkError();
return NS_ERROR_DOM_BAD_URI;
}
}
@@ -210,7 +210,7 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
// work here.
mCacheStream.NotifyDataEnded(status);
} else {
mDecoder->NetworkError();
mCallback->NotifyNetworkError();
}
// This disconnects our listener so we don't get any more data. We
@@ -248,8 +248,8 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
// Content-Range header text should be parse-able.
CMLOG("Error processing \'Content-Range' for "
"HTTP_PARTIAL_RESPONSE_CODE: rv[%x] channel[%p] decoder[%p]",
rv, hc.get(), mDecoder);
mDecoder->NetworkError();
rv, hc.get(), mCallback);
mCallback->NotifyNetworkError();
CloseChannel();
return NS_OK;
}
@@ -306,7 +306,7 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
dataIsBounded = true;
}
mDecoder->SetInfinite(!dataIsBounded);
mCallback->SetInfinite(!dataIsBounded);
}
mCacheStream.SetTransportSeekable(seekable);
@@ -376,7 +376,7 @@ ChannelMediaResource::ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
}
CMLOG("Received bytes [%lld] to [%lld] of [%lld] for decoder[%p]",
aRangeStart, aRangeEnd, aRangeTotal, mDecoder);
aRangeStart, aRangeEnd, aRangeTotal, mCallback);
return NS_OK;
}
@@ -454,14 +454,13 @@ ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream,
{
CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
closure->mResource->mDecoder->NotifyDataArrived(aCount, closure->mResource->mOffset,
/* aThrottleUpdates = */ true);
closure->mResource->mCallback->NotifyDataArrived(/* aThrottleUpdates = */ true);
// Keep track of where we're up to.
RESOURCE_LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add "
"[%d] bytes for decoder[%p]",
closure->mResource, closure->mResource->mOffset, aCount,
closure->mResource->mDecoder);
closure->mResource->mCallback);
closure->mResource->mOffset += aCount;
closure->mResource->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
@@ -561,37 +560,16 @@ nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStreamListener> listener = mListener.get();
// Ensure that if we're loading cross domain, that the server is sending
// an authorizing Access-Control header.
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
if (element->ShouldCheckAllowOrigin()) {
RefPtr<nsCORSListenerProxy> crossSiteListener =
new nsCORSListenerProxy(mListener,
element->NodePrincipal(),
false);
NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
rv = crossSiteListener->Init(mChannel, DataURIHandling::Allow);
NS_ENSURE_SUCCESS(rv, rv);
listener = crossSiteListener;
} else {
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(element->NodePrincipal(),
mURI,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = SetupChannelHeaders();
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->AsyncOpen(listener, nullptr);
rv = mChannel->AsyncOpen2(mListener);
NS_ENSURE_SUCCESS(rv, rv);
// Tell the media element that we are fetching data from a channel.
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
element->DownloadResumed(true);
}
@@ -624,7 +602,7 @@ nsresult ChannelMediaResource::SetupChannelHeaders()
// Send Accept header for video and audio types only (Bug 489071)
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
@@ -658,13 +636,13 @@ bool ChannelMediaResource::CanClone()
return mCacheStream.IsAvailableForSharing();
}
already_AddRefed<MediaResource> ChannelMediaResource::CloneData(MediaDecoder* aDecoder)
already_AddRefed<MediaResource> ChannelMediaResource::CloneData(MediaResourceCallback* aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
NS_ASSERTION(mCacheStream.IsAvailableForSharing(), "Stream can't be cloned");
RefPtr<ChannelMediaResource> resource =
new ChannelMediaResource(aDecoder,
new ChannelMediaResource(aCallback,
nullptr,
mURI,
GetContentType());
@@ -773,7 +751,7 @@ void ChannelMediaResource::Suspend(bool aCloseImmediately)
{
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
if (!owner) {
// Shutting down; do nothing.
return;
@@ -806,7 +784,7 @@ void ChannelMediaResource::Resume()
{
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
if (!owner) {
// Shutting down; do nothing.
return;
@@ -855,7 +833,7 @@ ChannelMediaResource::RecreateChannel()
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
(mLoadInBackground ? nsIRequest::LOAD_BACKGROUND : 0);
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
if (!owner) {
// The decoder is being shut down, so don't bother opening a new channel
return NS_OK;
@@ -868,14 +846,9 @@ ChannelMediaResource::RecreateChannel()
nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
if (nsContentUtils::ChannelShouldInheritPrincipal(element->NodePrincipal(),
mURI,
false, // aInheritForAboutBlank
false // aForceInherit
)) {
securityFlags = nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
}
nsSecurityFlags securityFlags = element->ShouldCheckAllowOrigin()
? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
MOZ_ASSERT(element->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
nsContentPolicyType contentPolicyType = element->IsHTMLElement(nsGkAtoms::audio) ?
@@ -910,7 +883,7 @@ void
ChannelMediaResource::DoNotifyDataReceived()
{
mDataReceivedEvent.Revoke();
mDecoder->NotifyBytesDownloaded();
mCallback->NotifyBytesDownloaded();
}
void
@@ -928,41 +901,11 @@ ChannelMediaResource::CacheClientNotifyDataReceived()
NS_DispatchToMainThread(mDataReceivedEvent.get());
}
class DataEnded : public nsRunnable {
public:
DataEnded(MediaDecoder* aDecoder, nsresult aStatus) :
mDecoder(aDecoder), mStatus(aStatus) {}
NS_IMETHOD Run() {
mDecoder->NotifyDownloadEnded(mStatus);
if (NS_SUCCEEDED(mStatus)) {
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
if (owner) {
dom::HTMLMediaElement* element = owner->GetMediaElement();
if (element) {
element->DownloadSuspended();
}
}
// NotifySuspendedStatusChanged will tell the element that download
// has been suspended "by the cache", which is true since we never download
// anything. The element can then transition to HAVE_ENOUGH_DATA.
mDecoder->NotifySuspendedStatusChanged();
}
return NS_OK;
}
private:
RefPtr<MediaDecoder> mDecoder;
nsresult mStatus;
};
void
ChannelMediaResource::CacheClientNotifyDataEnded(nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
// NOTE: this can be called with the media cache lock held, so don't
// block or do anything which might try to acquire a lock!
nsCOMPtr<nsIRunnable> event = new DataEnded(mDecoder, aStatus);
NS_DispatchToCurrentThread(event);
mCallback->NotifyDataEnded(aStatus);
}
void
@@ -970,14 +913,14 @@ ChannelMediaResource::CacheClientNotifyPrincipalChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
mDecoder->NotifyPrincipalChanged();
mCallback->NotifyPrincipalChanged();
}
void
ChannelMediaResource::CacheClientNotifySuspendedStatusChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
mDecoder->NotifySuspendedStatusChanged();
mCallback->NotifySuspendedStatusChanged();
}
nsresult
@@ -986,7 +929,7 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
CMLOG("CacheClientSeek requested for aOffset [%lld] for decoder [%p]",
aOffset, mDecoder);
aOffset, mCallback);
CloseChannel();
@@ -1196,11 +1139,11 @@ ChannelSuspendAgent::IsSuspended()
class FileMediaResource : public BaseMediaResource
{
public:
FileMediaResource(MediaDecoder* aDecoder,
FileMediaResource(MediaResourceCallback* aCallback,
nsIChannel* aChannel,
nsIURI* aURI,
const nsACString& aContentType) :
BaseMediaResource(aDecoder, aChannel, aURI, aContentType),
BaseMediaResource(aCallback, aChannel, aURI, aContentType),
mSize(-1),
mLock("FileMediaResource.mLock"),
mSizeInitialized(false)
@@ -1217,7 +1160,7 @@ public:
virtual void Resume() override {}
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
virtual bool CanClone() override;
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override;
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aCallback) override;
virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
// These methods are called off the main thread.
@@ -1331,8 +1274,7 @@ void FileMediaResource::EnsureSizeInitialized()
nsresult res = mInput->Available(&size);
if (NS_SUCCEEDED(res) && size <= INT64_MAX) {
mSize = (int64_t)size;
nsCOMPtr<nsIRunnable> event = new DataEnded(mDecoder, NS_OK);
NS_DispatchToMainThread(event);
mCallback->NotifyDataEnded(NS_OK);
}
}
@@ -1372,20 +1314,16 @@ nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput));
}
} else {
// Ensure that we never load a local file from some page on a
// web server.
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(element->NodePrincipal(),
mURI,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->Open(getter_AddRefs(mInput));
#ifdef DEBUG
{
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
MOZ_ASSERT((loadInfo->GetSecurityMode() &
nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) == 0,
"can not enforce CORS when calling Open2()");
}
#endif
rv = mChannel->Open2(getter_AddRefs(mInput));
}
NS_ENSURE_SUCCESS(rv, rv);
@@ -1432,11 +1370,11 @@ bool FileMediaResource::CanClone()
return true;
}
already_AddRefed<MediaResource> FileMediaResource::CloneData(MediaDecoder* aDecoder)
already_AddRefed<MediaResource> FileMediaResource::CloneData(MediaResourceCallback* aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
if (!owner) {
// The decoder is being shut down, so we can't clone
return nullptr;
@@ -1449,14 +1387,9 @@ already_AddRefed<MediaResource> FileMediaResource::CloneData(MediaDecoder* aDeco
nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
NS_ENSURE_TRUE(loadGroup, nullptr);
nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
if (nsContentUtils::ChannelShouldInheritPrincipal(element->NodePrincipal(),
mURI,
false, // aInheritForAboutBlank
false // aForceInherit
)) {
securityFlags = nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
}
nsSecurityFlags securityFlags = element->ShouldCheckAllowOrigin()
? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
MOZ_ASSERT(element->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
nsContentPolicyType contentPolicyType = element->IsHTMLElement(nsGkAtoms::audio) ?
@@ -1474,7 +1407,7 @@ already_AddRefed<MediaResource> FileMediaResource::CloneData(MediaDecoder* aDeco
if (NS_FAILED(rv))
return nullptr;
RefPtr<MediaResource> resource(new FileMediaResource(aDecoder, channel, mURI, GetContentType()));
RefPtr<MediaResource> resource(new FileMediaResource(aCallback, channel, mURI, GetContentType()));
return resource.forget();
}
@@ -1595,7 +1528,7 @@ int64_t FileMediaResource::Tell()
}
already_AddRefed<MediaResource>
MediaResource::Create(MediaDecoder* aDecoder, nsIChannel* aChannel)
MediaResource::Create(MediaResourceCallback* aCallback, nsIChannel* aChannel)
{
NS_ASSERTION(NS_IsMainThread(),
"MediaResource::Open called on non-main thread");
@@ -1613,11 +1546,11 @@ MediaResource::Create(MediaDecoder* aDecoder, nsIChannel* aChannel)
nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
RefPtr<MediaResource> resource;
if (fc || IsBlobURI(uri)) {
resource = new FileMediaResource(aDecoder, aChannel, uri, contentType);
resource = new FileMediaResource(aCallback, aChannel, uri, contentType);
} else if (IsRtspURI(uri)) {
resource = new RtspMediaResource(aDecoder, aChannel, uri, contentType);
resource = new RtspMediaResource(aCallback, aChannel, uri, contentType);
} else {
resource = new ChannelMediaResource(aDecoder, aChannel, uri, contentType);
resource = new ChannelMediaResource(aCallback, aChannel, uri, contentType);
}
return resource.forget();
}
@@ -1632,7 +1565,7 @@ void BaseMediaResource::SetLoadInBackground(bool aLoadInBackground) {
return;
}
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
if (!owner) {
NS_WARNING("Null owner in MediaResource::SetLoadInBackground()");
return;
@@ -1685,45 +1618,12 @@ void BaseMediaResource::ModifyLoadFlags(nsLoadFlags aFlags)
}
}
class DispatchBytesConsumedEvent : public nsRunnable {
public:
DispatchBytesConsumedEvent(MediaDecoder* aDecoder,
int64_t aNumBytes,
int64_t aOffset)
: mDecoder(aDecoder),
mNumBytes(aNumBytes),
mOffset(aOffset)
{
MOZ_COUNT_CTOR(DispatchBytesConsumedEvent);
}
protected:
~DispatchBytesConsumedEvent()
{
MOZ_COUNT_DTOR(DispatchBytesConsumedEvent);
}
public:
NS_IMETHOD Run() {
mDecoder->NotifyBytesConsumed(mNumBytes, mOffset);
// Drop ref to decoder on main thread, just in case this reference
// ends up being the last owning reference somehow.
mDecoder = nullptr;
return NS_OK;
}
RefPtr<MediaDecoder> mDecoder;
int64_t mNumBytes;
int64_t mOffset;
};
void BaseMediaResource::DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset)
{
if (aNumBytes <= 0) {
return;
}
RefPtr<nsIRunnable> event(new DispatchBytesConsumedEvent(mDecoder, aNumBytes, aOffset));
NS_DispatchToMainThread(event);
mCallback->NotifyBytesConsumed(aNumBytes, aOffset);
}
nsresult
+9 -9
View File
@@ -43,7 +43,7 @@ class nsIPrincipal;
namespace mozilla {
class MediaDecoder;
class MediaResourceCallback;
class MediaChannelStatistics;
/**
@@ -270,7 +270,7 @@ public:
// Create a new stream of the same type that refers to the same URI
// with a new channel. Any cached data associated with the original
// stream should be accessible in the new stream too.
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) = 0;
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aCallback) = 0;
// Set statistics to be recorded to the object passed in.
virtual void RecordStatisticsTo(MediaChannelStatistics *aStatistics) { }
@@ -387,7 +387,7 @@ public:
* Create a resource, reading data from the channel. Call on main thread only.
* The caller must follow up by calling resource->Open().
*/
static already_AddRefed<MediaResource> Create(MediaDecoder* aDecoder, nsIChannel* aChannel);
static already_AddRefed<MediaResource> Create(MediaResourceCallback* aCallback, nsIChannel* aChannel);
/**
* Open the stream. This creates a stream listener and returns it in
@@ -460,7 +460,7 @@ public:
// - mChannel
// - mURI (possibly owned, looks like just a ref from mChannel)
// Not owned:
// - mDecoder
// - mCallback
size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
size += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
@@ -480,11 +480,11 @@ public:
}
protected:
BaseMediaResource(MediaDecoder* aDecoder,
BaseMediaResource(MediaResourceCallback* aCallback,
nsIChannel* aChannel,
nsIURI* aURI,
const nsACString& aContentType) :
mDecoder(aDecoder),
mCallback(aCallback),
mChannel(aChannel),
mURI(aURI),
mContentType(aContentType),
@@ -516,7 +516,7 @@ protected:
// This is not an nsCOMPointer to prevent a circular reference
// between the decoder to the media stream object. The stream never
// outlives the lifetime of the decoder.
MediaDecoder* mDecoder;
MediaResourceCallback* mCallback;
// Channel used to download the media data. Must be accessed
// from the main thread only.
@@ -593,7 +593,7 @@ private:
class ChannelMediaResource : public BaseMediaResource
{
public:
ChannelMediaResource(MediaDecoder* aDecoder,
ChannelMediaResource(MediaResourceCallback* aDecoder,
nsIChannel* aChannel,
nsIURI* aURI,
const nsACString& aContentType);
@@ -643,7 +643,7 @@ public:
// Return true if the stream has been closed.
bool IsClosed() const { return mCacheStream.IsClosed(); }
virtual bool CanClone() override;
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override;
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aDecoder) override;
// Set statistics to be recorded to the object passed in. If not called,
// |ChannelMediaResource| will create it's own statistics objects in |Open|.
void RecordStatisticsTo(MediaChannelStatistics *aStatistics) override {
+75
View File
@@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef MediaResourceCallback_h_
#define MediaResourceCallback_h_
#include "nsError.h"
namespace mozilla {
class MediaResource;
class MediaDecoderOwner;
/**
* A callback used by MediaResource (sub-classes like FileMediaResource,
* RtspMediaResource, and ChannelMediaResource) to notify various events.
* Currently this is implemented by MediaDecoder only.
*
* Since this class has no pure virtual function, it is convenient to write
* gtests for the readers without using a mock MediaResource when you don't
* care about the events notified by the MediaResource.
*/
class MediaResourceCallback {
public:
// Returns a weak reference to the media decoder owner.
virtual MediaDecoderOwner* GetMediaOwner() const { return nullptr; }
// Notify is duration is known to this MediaResource.
virtual void SetInfinite(bool aInfinite) {}
// Notify if seeking is supported by this MediaResource.
virtual void SetMediaSeekable(bool aMediaSeekable) {}
// Notify that server connection is closed.
virtual void ResetConnectionState() {}
// Used by RtspMediaResource which has an unusual sequence
// to setup the decoder.
virtual nsresult FinishDecoderSetup(MediaResource* aResource) {
return NS_OK;
}
// Notify that a network error is encountered.
virtual void NotifyNetworkError() {}
// Notify that decoding has failed.
virtual void NotifyDecodeError() {}
// Notify that data arrives on the stream and is read into the cache.
virtual void NotifyDataArrived(bool aThrottleUpdates) {}
// Notify that MediaResource has received some data.
virtual void NotifyBytesDownloaded() {}
// Notify download is ended.
// NOTE: this can be called with the media cache lock held, so don't
// block or do anything which might try to acquire a lock!
virtual void NotifyDataEnded(nsresult aStatus) {}
// Notify that the principal of MediaResource has changed.
virtual void NotifyPrincipalChanged() {}
// Notify that the "cache suspended" status of MediaResource changes.
virtual void NotifySuspendedStatusChanged() {}
// Notify the number of bytes read from the resource.
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) {}
};
} // namespace mozilla
#endif //MediaResourceCallback_h_
+26 -32
View File
@@ -483,9 +483,9 @@ RtspTrackBuffer::PlayoutDelayTimerCallback(nsITimer *aTimer,
//-----------------------------------------------------------------------------
// RtspMediaResource
//-----------------------------------------------------------------------------
RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder,
RtspMediaResource::RtspMediaResource(MediaResourceCallback* aCallback,
nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType)
: BaseMediaResource(aDecoder, aChannel, aURI, aContentType)
: BaseMediaResource(aCallback, aChannel, aURI, aContentType)
, mIsConnected(false)
, mIsLiveStream(false)
, mHasTimestamp(true)
@@ -532,8 +532,8 @@ void RtspMediaResource::NotifySuspend(bool aIsSuspend)
RTSPMLOG("NotifySuspend %d",aIsSuspend);
mIsSuspend = aIsSuspend;
if (mDecoder) {
mDecoder->NotifySuspendedStatusChanged();
if (mCallback) {
mCallback->NotifySuspendedStatusChanged();
}
}
@@ -683,9 +683,7 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
// video, we give up moving forward.
if (!IsVideoEnabled() && IsVideo(tracks, meta)) {
// Give up, report error to media element.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
NS_DispatchToMainThread(event);
mCallback->NotifyDecodeError();
return NS_ERROR_FAILURE;
}
uint64_t durationUs = 0;
@@ -712,7 +710,7 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
mTrackBuffer[i]->Start();
}
if (!mDecoder) {
if (!mCallback) {
return NS_ERROR_FAILURE;
}
@@ -720,34 +718,30 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
if (durationUs) {
// Not live stream.
mIsLiveStream = false;
mDecoder->SetInfinite(false);
mCallback->SetInfinite(false);
} else {
// Live stream.
// Check the preference "media.realtime_decoder.enabled".
if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) {
// Give up, report error to media element.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
NS_DispatchToMainThread(event);
mCallback->NotifyDecodeError();
return NS_ERROR_FAILURE;
} else {
mIsLiveStream = true;
bool seekable = false;
mDecoder->SetInfinite(true);
mDecoder->SetMediaSeekable(seekable);
mCallback->SetInfinite(true);
mCallback->SetMediaSeekable(seekable);
}
}
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
// Fires an initial progress event.
owner->DownloadProgressed();
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
nsresult rv = mCallback->FinishDecoderSetup(this);
NS_ENSURE_SUCCESS(rv, rv);
element->FinishDecoderSetup(mDecoder, this);
mIsConnected = true;
return NS_OK;
}
@@ -761,7 +755,7 @@ RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
mTrackBuffer[i]->Reset();
}
if (mDecoder) {
if (mCallback) {
if (aReason == NS_ERROR_NOT_INITIALIZED ||
aReason == NS_ERROR_CONNECTION_REFUSED ||
aReason == NS_ERROR_NOT_CONNECTED ||
@@ -769,11 +763,11 @@ RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
// Report error code to Decoder.
RTSPMLOG("Error in OnDisconnected 0x%x", aReason);
mIsConnected = false;
mDecoder->NetworkError();
mCallback->NotifyNetworkError();
} else {
// Resetting the decoder and media element when the connection
// between RTSP client and server goes down.
mDecoder->ResetConnectionState();
mCallback->ResetConnectionState();
}
}
@@ -791,18 +785,18 @@ void RtspMediaResource::Suspend(bool aCloseImmediately)
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
mIsSuspend = true;
if (NS_WARN_IF(!mDecoder)) {
if (NS_WARN_IF(!mCallback)) {
return;
}
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE_VOID(owner);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE_VOID(element);
mMediaStreamController->Suspend();
element->DownloadSuspended();
mDecoder->NotifySuspendedStatusChanged();
mCallback->NotifySuspendedStatusChanged();
}
void RtspMediaResource::Resume()
@@ -810,11 +804,11 @@ void RtspMediaResource::Resume()
NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
mIsSuspend = false;
if (NS_WARN_IF(!mDecoder)) {
if (NS_WARN_IF(!mCallback)) {
return;
}
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE_VOID(owner);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE_VOID(element);
@@ -823,7 +817,7 @@ void RtspMediaResource::Resume()
element->DownloadResumed();
}
mMediaStreamController->Resume();
mDecoder->NotifySuspendedStatusChanged();
mCallback->NotifySuspendedStatusChanged();
}
nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener)
@@ -835,11 +829,11 @@ nsresult RtspMediaResource::Close()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
mMediaStreamController->Stop();
// Since mDecoder is not an nsCOMPtr in BaseMediaResource, we have to
// Since mCallback is not an nsCOMPtr in BaseMediaResource, we have to
// explicitly set it as null pointer in order to prevent misuse from this
// object (RtspMediaResource).
if (mDecoder) {
mDecoder = nullptr;
if (mCallback) {
mCallback = nullptr;
}
return NS_OK;
}
@@ -861,7 +855,7 @@ nsresult RtspMediaResource::SeekTime(int64_t aOffset)
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
aOffset, mDecoder);
aOffset, mCallback);
// Clear buffer and raise the frametype flag.
for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY);
+2 -2
View File
@@ -71,7 +71,7 @@ class RtspTrackBuffer;
class RtspMediaResource : public BaseMediaResource
{
public:
RtspMediaResource(MediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI,
RtspMediaResource(MediaResourceCallback* aCallback, nsIChannel* aChannel, nsIURI* aURI,
const nsACString& aContentType);
virtual ~RtspMediaResource();
@@ -176,7 +176,7 @@ public:
virtual bool CanClone() override {
return false;
}
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback*)
override {
return nullptr;
}
+3 -1
View File
@@ -10,7 +10,9 @@
namespace mozilla {
AndroidMediaDecoder::AndroidMediaDecoder(const nsACString& aType) : mType(aType)
AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner,
const nsACString& aType)
: MediaDecoder(aOwner), mType(aType)
{
}
+4 -2
View File
@@ -15,14 +15,16 @@ class AndroidMediaDecoder : public MediaDecoder
{
nsCString mType;
public:
AndroidMediaDecoder(const nsACString& aType);
AndroidMediaDecoder(MediaDecoderOwner* aOwner, const nsACString& aType);
const nsresult GetContentType(nsACString& aType) const {
aType = mType;
return NS_OK;
}
virtual MediaDecoder* Clone() { return new AndroidMediaDecoder(mType); }
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
return new AndroidMediaDecoder(aOwner, mType);
}
virtual MediaDecoderStateMachine* CreateStateMachine();
};
-10
View File
@@ -48,16 +48,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold);
virtual bool HasAudio()
{
return mHasAudio;
}
virtual bool HasVideo()
{
return mHasVideo;
}
virtual bool IsMediaSeekable()
{
// not used
+4 -4
View File
@@ -9,15 +9,15 @@
namespace mozilla {
AppleDecoder::AppleDecoder()
: MediaDecoder()
AppleDecoder::AppleDecoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
{
}
MediaDecoder *
AppleDecoder::Clone()
AppleDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new AppleDecoder();
return new AppleDecoder(aOwner);
}
MediaDecoderStateMachine *
+2 -2
View File
@@ -12,9 +12,9 @@ namespace mozilla {
class AppleDecoder : public MediaDecoder
{
public:
AppleDecoder();
explicit AppleDecoder(MediaDecoderOwner* aOwner);
virtual MediaDecoder* Clone() override;
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
virtual MediaDecoderStateMachine* CreateStateMachine() override;
};
+25 -27
View File
@@ -208,7 +208,7 @@ AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
do {
// Decompressed audio buffer
nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
auto decoded = MakeUnique<uint8_t[]>(decodedSize);
AudioBufferList decBuffer;
decBuffer.mNumberBuffers = 1;
@@ -246,9 +246,11 @@ AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
LOGD("pushed audio at time %lfs; duration %lfs\n",
(double)time / USECS_PER_S, (double)duration / USECS_PER_S);
auto samples = UniquePtr<AudioDataValue[]>(reinterpret_cast<AudioDataValue*>
(decoded.release()));
AudioData *audio = new AudioData(mResource.Tell(),
time, duration, numFrames,
reinterpret_cast<AudioDataValue *>(decoded.forget()),
Move(samples),
mAudioChannels, mAudioSampleRate);
mAudioQueue.Push(audio);
@@ -303,21 +305,6 @@ AppleMP3Reader::DecodeVideoFrame(bool &aKeyframeSkip,
return false;
}
bool
AppleMP3Reader::HasAudio()
{
MOZ_ASSERT(OnTaskQueue());
return mStreamReady;
}
bool
AppleMP3Reader::HasVideo()
{
MOZ_ASSERT(OnTaskQueue());
return false;
}
bool
AppleMP3Reader::IsMediaSeekable()
{
@@ -513,30 +500,41 @@ AppleMP3Reader::Seek(int64_t aTime, int64_t aEndTime)
}
void
AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
AppleMP3Reader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
if (!mMP3FrameParser.NeedsData()) {
return;
}
IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
AutoPinned<MediaResource> resource(mResource.GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
mResource.MediaReadAt(interval.mStart, interval.Length());
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
}
uint64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
LOGD("Updating media duration to %lluus\n", duration);
MOZ_ASSERT(mDecoder);
mDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
}
uint64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
LOGD("Updating media duration to %lluus\n", duration);
MOZ_ASSERT(mDecoder);
mDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
}
}
+1 -5
View File
@@ -28,9 +28,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
virtual bool HasAudio() override;
virtual bool HasVideo() override;
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override;
@@ -47,8 +44,7 @@ public:
UInt32 *aFlags);
protected:
virtual void NotifyDataArrivedInternal(uint32_t aLength,
int64_t aOffset) override;
virtual void NotifyDataArrivedInternal() override;
public:
virtual bool IsMediaSeekable() override;
+4 -2
View File
@@ -45,10 +45,12 @@ DirectShowDecoder::GetSupportedCodecs(const nsACString& aType,
bool
DirectShowDecoder::IsEnabled()
{
return Preferences::GetBool("media.directshow.enabled");
return CanDecodeMP3UsingDirectShow() &&
Preferences::GetBool("media.directshow.enabled");
}
DirectShowDecoder::DirectShowDecoder()
DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
{
MOZ_COUNT_CTOR(DirectShowDecoder);
}
+3 -3
View File
@@ -16,14 +16,14 @@ class DirectShowDecoder : public MediaDecoder
{
public:
DirectShowDecoder();
explicit DirectShowDecoder(MediaDecoderOwner* aOwner);
virtual ~DirectShowDecoder();
MediaDecoder* Clone() override {
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
if (!IsEnabled()) {
return nullptr;
}
return new DirectShowDecoder();
return new DirectShowDecoder(aOwner);
}
MediaDecoderStateMachine* CreateStateMachine() override;
+20 -24
View File
@@ -341,20 +341,6 @@ DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip,
return false;
}
bool
DirectShowReader::HasAudio()
{
MOZ_ASSERT(OnTaskQueue());
return true;
}
bool
DirectShowReader::HasVideo()
{
MOZ_ASSERT(OnTaskQueue());
return false;
}
RefPtr<MediaDecoderReader::SeekPromise>
DirectShowReader::Seek(int64_t aTargetUs, int64_t aEndTime)
{
@@ -394,29 +380,39 @@ DirectShowReader::SeekInternal(int64_t aTargetUs)
}
void
DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
DirectShowReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
if (!mMP3FrameParser.NeedsData()) {
return;
}
IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
AutoPinned<MediaResource> resource(mDecoder->GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
MOZ_ASSERT(mDecoder);
mDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
}
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
MOZ_ASSERT(mDecoder);
mDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
}
}
+1 -5
View File
@@ -47,9 +47,6 @@ public:
bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
bool HasAudio() override;
bool HasVideo() override;
nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override;
@@ -57,8 +54,7 @@ public:
Seek(int64_t aTime, int64_t aEndTime) override;
protected:
void NotifyDataArrivedInternal(uint32_t aLength,
int64_t aOffset) override;
void NotifyDataArrivedInternal() override;
public:
bool IsMediaSeekable() override;
+48 -3
View File
@@ -208,10 +208,8 @@ CreateAndAddFilter(IGraphBuilder* aGraph,
}
HRESULT
AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
IBaseFilter **aOutFilter)
CreateMP3DMOWrapperFilter(IBaseFilter **aOutFilter)
{
NS_ENSURE_TRUE(aGraph, E_POINTER);
NS_ENSURE_TRUE(aOutFilter, E_POINTER);
HRESULT hr;
@@ -245,6 +243,24 @@ AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
return hr;
}
filter.forget(aOutFilter);
return S_OK;
}
HRESULT
AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
IBaseFilter **aOutFilter)
{
NS_ENSURE_TRUE(aGraph, E_POINTER);
NS_ENSURE_TRUE(aOutFilter, E_POINTER);
HRESULT hr;
// Create the wrapper filter.
RefPtr<IBaseFilter> filter;
hr = CreateMP3DMOWrapperFilter(getter_AddRefs(filter));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Add the wrapper filter to graph.
hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO");
if (FAILED(hr)) {
@@ -257,6 +273,35 @@ AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
return S_OK;
}
bool
CanDecodeMP3UsingDirectShow()
{
RefPtr<IBaseFilter> filter;
// Can we create the MP3 demuxer filter?
if (FAILED(CoCreateInstance(CLSID_MPEG1Splitter,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
getter_AddRefs(filter)))) {
return false;
}
// Can we create either the WinXP MP3 decoder filter or the MP3 DMO decoder?
if (FAILED(CoCreateInstance(CLSID_MPEG_LAYER_3_DECODER_FILTER,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
getter_AddRefs(filter))) &&
FAILED(CreateMP3DMOWrapperFilter(getter_AddRefs(filter)))) {
return false;
}
// Else, we can create all of the components we need. Assume
// DirectShow is going to work...
return true;
}
// Match a pin by pin direction and connection state.
HRESULT
MatchUnconnectedPin(IPin* aPin,
+5
View File
@@ -115,6 +115,11 @@ RefTimeToSeconds(const REFERENCE_TIME aRefTime)
const char*
GetDirectShowGuidName(const GUID& aGuid);
// Returns true if we can instantiate an MP3 demuxer and decoder filters.
// Use this to detect whether MP3 support is installed.
bool
CanDecodeMP3UsingDirectShow();
} // namespace mozilla
#endif
+7 -70
View File
@@ -38,7 +38,8 @@ namespace mozilla {
#undef MP4_READER_DORMANT_HEURISTIC
#endif
MP4Decoder::MP4Decoder()
MP4Decoder::MP4Decoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
{
#if defined(MP4_READER_DORMANT_HEURISTIC)
mDormantSupported = Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false);
@@ -47,7 +48,10 @@ MP4Decoder::MP4Decoder()
MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
{
MediaDecoderReader* reader = new MediaFormatReader(this, new MP4Demuxer(GetResource()));
MediaDecoderReader* reader =
new MediaFormatReader(this,
new MP4Demuxer(GetResource()),
GetVideoFrameContainer());
return new MediaDecoderStateMachine(this, reader);
}
@@ -224,74 +228,7 @@ MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aFail
return false;
}
bool result = decoder->IsHardwareAccelerated(aFailureReason);
return result;
}
/* static */ bool
MP4Decoder::CanCreateH264Decoder()
{
static bool haveCachedResult = false;
static bool result = false;
if (haveCachedResult) {
return result;
}
VideoInfo config;
RefPtr<MediaDataDecoder> decoder(
CreateTestH264Decoder(layers::LayersBackend::LAYERS_BASIC, config));
if (decoder) {
decoder->Shutdown();
result = true;
}
haveCachedResult = true;
return result;
}
static already_AddRefed<MediaDataDecoder>
CreateTestAACDecoder(AudioInfo& aConfig)
{
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
RefPtr<MediaDataDecoder> decoder(
platform->CreateDecoder(aConfig, nullptr, nullptr));
return decoder.forget();
}
// bipbop.mp4's extradata/config...
static const uint8_t sTestAACExtraData[] = {
0x03, 0x80, 0x80, 0x80, 0x22, 0x00, 0x02, 0x00, 0x04, 0x80,
0x80, 0x80, 0x14, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x51, 0x00, 0x00, 0x11, 0x51, 0x05, 0x80, 0x80, 0x80,
0x02, 0x13, 0x90, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02
};
static const uint8_t sTestAACConfig[] = { 0x13, 0x90 };
/* static */ bool
MP4Decoder::CanCreateAACDecoder()
{
static bool haveCachedResult = false;
static bool result = false;
if (haveCachedResult) {
return result;
}
AudioInfo config;
config.mMimeType = "audio/mp4a-latm";
config.mRate = 22050;
config.mChannels = 2;
config.mBitDepth = 16;
config.mProfile = 2;
config.mExtendedProfile = 2;
config.mCodecSpecificConfig->AppendElements(sTestAACConfig,
MOZ_ARRAY_LENGTH(sTestAACConfig));
config.mExtraData->AppendElements(sTestAACExtraData,
MOZ_ARRAY_LENGTH(sTestAACExtraData));
RefPtr<MediaDataDecoder> decoder(CreateTestAACDecoder(config));
if (decoder) {
result = true;
}
haveCachedResult = true;
decoder->Shutdown();
return result;
}
+3 -5
View File
@@ -14,13 +14,13 @@ namespace mozilla {
class MP4Decoder : public MediaDecoder
{
public:
MP4Decoder();
explicit MP4Decoder(MediaDecoderOwner* aOwner);
virtual MediaDecoder* Clone() override {
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
if (!IsEnabled()) {
return nullptr;
}
return new MP4Decoder();
return new MP4Decoder(aOwner);
}
virtual MediaDecoderStateMachine* CreateStateMachine() override;
@@ -38,8 +38,6 @@ public:
static bool IsEnabled();
static bool IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aReason);
static bool CanCreateAACDecoder();
static bool CanCreateH264Decoder();
};
} // namespace mozilla
-7
View File
@@ -53,13 +53,6 @@ extern PRLogModuleInfo* GetGMPLog();
namespace gmp {
static bool
FileExists(nsIFile* aFile)
{
bool exists = false;
return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
}
GMPChild::GMPChild()
: mAsyncShutdown(nullptr)
, mGMPMessageLoop(MessageLoop::current())
+150 -21
View File
@@ -38,6 +38,8 @@
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#endif
#include "nsIXULRuntime.h"
#include "GMPDecoderModule.h"
#include <limits>
namespace mozilla {
@@ -141,6 +143,96 @@ GeckoMediaPluginServiceParent::Init()
return GetThread(getter_AddRefs(thread));
}
already_AddRefed<nsIFile>
CloneAndAppend(nsIFile* aFile, const nsAString& aDir)
{
nsCOMPtr<nsIFile> f;
nsresult rv = aFile->Clone(getter_AddRefs(f));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
rv = f->Append(aDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return f.forget();
}
static void
MoveAndOverwrite(nsIFile* aOldStorageDir,
nsIFile* aNewStorageDir,
const nsAString& aSubDir)
{
nsresult rv;
nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldStorageDir, aSubDir));
if (NS_WARN_IF(!srcDir)) {
return;
}
if (!FileExists(srcDir)) {
// No sub-directory to be migrated.
return;
}
nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewStorageDir, aSubDir));
if (FileExists(dstDir)) {
// We must have migrated before already, and then ran an old version
// of Gecko again which created storage at the old location. Overwrite
// the previously migrated storage.
rv = dstDir->Remove(true);
if (NS_WARN_IF(NS_FAILED(rv))) {
// MoveTo will fail.
return;
}
}
rv = srcDir->MoveTo(aNewStorageDir, EmptyString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
static void
MigratePreGecko42StorageDir(nsIFile* aOldStorageDir,
nsIFile* aNewStorageDir)
{
MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("id"));
MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("storage"));
}
static nsresult
GMPPlatformString(nsAString& aOutPlatform)
{
// Append the OS and arch so that we don't reuse the storage if the profile is
// copied or used under a different bit-ness, or copied to another platform.
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
if (!runtime) {
return NS_ERROR_FAILURE;
}
nsAutoCString OS;
nsresult rv = runtime->GetOS(OS);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCString arch;
rv = runtime->GetXPCOMABI(arch);
if (NS_FAILED(rv)) {
return rv;
}
nsCString platform;
platform.Append(OS);
platform.AppendLiteral("_");
platform.Append(arch);
aOutPlatform = NS_ConvertUTF8toUTF16(platform);
return NS_OK;
}
nsresult
GeckoMediaPluginServiceParent::InitStorage()
@@ -174,6 +266,33 @@ GeckoMediaPluginServiceParent::InitStorage()
return rv;
}
nsCOMPtr<nsIFile> gmpDirWithoutPlatform;
rv = mStorageBaseDir->Clone(getter_AddRefs(gmpDirWithoutPlatform));
if (NS_FAILED(rv)) {
return rv;
}
nsAutoString platform;
rv = GMPPlatformString(platform);
if (NS_FAILED(rv)) {
return rv;
}
rv = mStorageBaseDir->Append(platform);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
return rv;
}
// Prior to 42, GMP storage was stored in $profile/gmp/. After 42, it's
// stored in $profile/gmp/$platform/. So we must migrate any old records
// from the old location to the new location, for forwards compatibility.
MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir);
return GeckoMediaPluginService::Init();
}
@@ -540,6 +659,27 @@ GeckoMediaPluginServiceParent::LoadFromEnvironment()
mScannedPluginOnDisk = true;
}
class NotifyObserversTask final : public nsRunnable {
public:
explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
: mTopic(aTopic)
, mData(aData)
{}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
if (obsService) {
obsService->NotifyObservers(nullptr, mTopic, mData.get());
}
return NS_OK;
}
private:
~NotifyObserversTask() {}
const char* mTopic;
const nsString mData;
};
NS_IMETHODIMP
GeckoMediaPluginServiceParent::PathRunnable::Run()
{
@@ -550,6 +690,16 @@ GeckoMediaPluginServiceParent::PathRunnable::Run()
mOperation == REMOVE_AND_DELETE_FROM_DISK,
mDefer);
}
#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
// For e10s, we must fire a notification so that all ContentParents notify
// their children to update the codecs that the GMPDecoderModule can use.
NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL);
// For non-e10s, and for decoding in the chrome process, must update GMP
// PDM's codecs list directly.
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
GMPDecoderModule::UpdateUsableCodecs();
}));
#endif
return NS_OK;
}
@@ -759,27 +909,6 @@ GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
return gmp.get();
}
class NotifyObserversTask final : public nsRunnable {
public:
explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
: mTopic(aTopic)
, mData(aData)
{}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
if (obsService) {
obsService->NotifyObservers(nullptr, mTopic, mData.get());
}
return NS_OK;
}
private:
~NotifyObserversTask() {}
const char* mTopic;
const nsString mData;
};
void
GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
{
+7
View File
@@ -64,4 +64,11 @@ ToBase64(const nsTArray<uint8_t>& aBytes)
return base64;
}
bool
FileExists(nsIFile* aFile)
{
bool exists = false;
return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
}
} // namespace mozilla
+3
View File
@@ -37,6 +37,9 @@ SplitAt(const char* aDelims,
nsCString
ToBase64(const nsTArray<uint8_t>& aBytes);
bool
FileExists(nsIFile* aFile);
} // namespace mozilla
#endif
+4 -1
View File
@@ -15,7 +15,10 @@ namespace mozilla {
class GStreamerDecoder : public MediaDecoder
{
public:
virtual MediaDecoder* Clone() { return new GStreamerDecoder(); }
explicit GStreamerDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
return new GStreamerDecoder(aOwner);
}
virtual MediaDecoderStateMachine* CreateStateMachine();
static bool CanHandleMediaType(const nsACString& aMIMEType, const nsAString* aCodecs);
};
+20 -11
View File
@@ -1275,8 +1275,7 @@ GStreamerReader::AutoplugSortCb(GstElement* aElement, GstPad* aPad,
* If this is an MP3 stream, pass any new data we get to the MP3 frame parser
* for duration estimation.
*/
void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
int64_t aOffset)
void GStreamerReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
if (HasVideo()) {
@@ -1286,22 +1285,32 @@ void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
return;
}
IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
AutoPinned<MediaResource> resource(mResource.GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
mResource.MediaReadAt(interval.mStart, interval.Length());
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration && mUseParserDuration) {
MOZ_ASSERT(mDecoder);
mLastParserDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
}
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration && mUseParserDuration) {
MOZ_ASSERT(mDecoder);
mLastParserDuration = duration;
mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
}
}
+3 -12
View File
@@ -53,24 +53,15 @@ public:
virtual media::TimeIntervals GetBuffered() override;
protected:
virtual void NotifyDataArrivedInternal(uint32_t aLength,
int64_t aOffset) override;
virtual void NotifyDataArrivedInternal() override;
public:
virtual bool HasAudio() override {
return mInfo.HasAudio();
}
virtual bool HasVideo() override {
return mInfo.HasVideo();
}
layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() { return mInfo.HasAudio(); }
bool HasVideo() { return mInfo.HasVideo(); }
void ReadAndPushData(guint aLength);
RefPtr<layers::PlanarYCbCrImage> GetImageFromBuffer(GstBuffer* aBuffer);
void CopyIntoImageBuffer(GstBuffer *aBuffer, GstBuffer** aOutBuffer, RefPtr<layers::PlanarYCbCrImage> &image);
+1 -1
View File
@@ -25,7 +25,7 @@ public:
return nullptr;
}
virtual bool CanClone() override { return false; }
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback*)
override
{
return nullptr;
+1 -1
View File
@@ -469,7 +469,7 @@ DecodedAudioDataSink::PlayFromAudioQueue()
SINK_LOG_V("playing %u frames of audio at time %lld",
audio->mFrames, audio->mTime);
if (audio->mRate == mInfo.mRate && audio->mChannels == mInfo.mChannels) {
mAudioStream->Write(audio->mAudioData, audio->mFrames);
mAudioStream->Write(audio->mAudioData.get(), audio->mFrames);
} else {
SINK_LOG_V("mismatched sample format mInfo=[%uHz/%u channels] audio=[%uHz/%u channels]",
mInfo.mRate, mInfo.mChannels, audio->mRate, audio->mChannels);
+7 -6
View File
@@ -28,15 +28,15 @@ using namespace mozilla::media;
namespace mozilla {
MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
: mMediaSource(nullptr)
: MediaDecoder(aElement)
, mMediaSource(nullptr)
, mEnded(false)
{
SetExplicitDuration(UnspecifiedNaN<double>());
Init(aElement);
}
MediaDecoder*
MediaSourceDecoder::Clone()
MediaSourceDecoder::Clone(MediaDecoderOwner* aOwner)
{
// TODO: Sort out cloning.
return nullptr;
@@ -47,12 +47,13 @@ MediaSourceDecoder::CreateStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
mDemuxer = new MediaSourceDemuxer();
RefPtr<MediaFormatReader> reader = new MediaFormatReader(this, mDemuxer);
RefPtr<MediaFormatReader> reader =
new MediaFormatReader(this, mDemuxer, GetVideoFrameContainer());
return new MediaDecoderStateMachine(this, reader);
}
nsresult
MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
MediaSourceDecoder::Load(nsIStreamListener**)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!GetStateMachine());
@@ -62,7 +63,7 @@ MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
return NS_ERROR_FAILURE;
}
nsresult rv = GetStateMachine()->Init(nullptr);
nsresult rv = GetStateMachine()->Init();
NS_ENSURE_SUCCESS(rv, rv);
SetStateMachineParameters();
+2 -2
View File
@@ -36,9 +36,9 @@ class MediaSourceDecoder : public MediaDecoder
public:
explicit MediaSourceDecoder(dom::HTMLMediaElement* aElement);
virtual MediaDecoder* Clone() override;
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
virtual MediaDecoderStateMachine* CreateStateMachine() override;
virtual nsresult Load(nsIStreamListener**, MediaDecoder*) override;
virtual nsresult Load(nsIStreamListener**) override;
virtual media::TimeIntervals GetSeekable() override;
media::TimeIntervals GetBuffered() override;
+1 -1
View File
@@ -32,7 +32,7 @@ public:
virtual void Suspend(bool aCloseImmediately) override { UNIMPLEMENTED(); }
virtual void Resume() override { UNIMPLEMENTED(); }
virtual bool CanClone() override { UNIMPLEMENTED(); return false; }
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override { UNIMPLEMENTED(); return nullptr; }
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback*) override { UNIMPLEMENTED(); return nullptr; }
virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override { UNIMPLEMENTED(); }
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
+2 -2
View File
@@ -270,7 +270,7 @@ SourceBuffer::Ended()
mContentManager->Ended();
// We want the MediaSourceReader to refresh its buffered range as it may
// have been modified (end lined up).
mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false);
mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
}
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
@@ -433,7 +433,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
// Tell our parent decoder that we have received new data.
// The information provided do not matter much so long as it is monotonically
// increasing.
mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false);
mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
// Send progress event.
mMediaSource->GetDecoder()->NotifyBytesDownloaded();
}
+1 -1
View File
@@ -43,7 +43,7 @@ public:
virtual void Suspend(bool aCloseImmediately) override { UNIMPLEMENTED(); }
virtual void Resume() override { UNIMPLEMENTED(); }
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override { UNIMPLEMENTED(); return nullptr; }
virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override { UNIMPLEMENTED(); return nullptr; }
virtual already_AddRefed<MediaResource> CloneData(MediaResourceCallback*) override { UNIMPLEMENTED(); return nullptr; }
virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override { UNIMPLEMENTED(); }
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
+2
View File
@@ -110,6 +110,7 @@ EXPORTS += [
'EncodedBufferCache.h',
'FileBlockCache.h',
'FlushableTaskQueue.h',
'FrameStatistics.h',
'Intervals.h',
'Latency.h',
'MediaCache.h',
@@ -126,6 +127,7 @@ EXPORTS += [
'MediaQueue.h',
'MediaRecorder.h',
'MediaResource.h',
'MediaResourceCallback.h',
'MediaSegment.h',
'MediaStatistics.h',
'MediaStreamGraph.h',
+5 -4
View File
@@ -13,16 +13,17 @@ namespace mozilla {
class OggDecoder : public MediaDecoder
{
public:
OggDecoder()
: mShutdownBitMonitor("mShutdownBitMonitor")
explicit OggDecoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
, mShutdownBitMonitor("mShutdownBitMonitor")
, mShutdownBit(false)
{}
virtual MediaDecoder* Clone() override {
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
if (!IsOggEnabled()) {
return nullptr;
}
return new OggDecoder();
return new OggDecoder(aOwner);
}
virtual MediaDecoderStateMachine* CreateStateMachine() override;
+14 -20
View File
@@ -470,22 +470,16 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
SetupMediaTracksInfo(serials);
if (HasAudio() || HasVideo()) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsOggDecoderShutdown() &&
mResource.GetLength() >= 0 && mDecoder->IsMediaSeekable())
{
if (mInfo.mMetadataDuration.isNothing() &&
!mDecoder->IsOggDecoderShutdown() &&
mResource.GetLength() >= 0) {
// We didn't get a duration from the index or a Content-Duration header.
// Seek to the end of file to find the end time.
int64_t length = mResource.GetLength();
NS_ASSERTION(length > 0, "Must have a content length to get end time");
int64_t endTime = 0;
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
endTime = RangeEndTime(length);
}
int64_t endTime = RangeEndTime(length);
if (endTime != -1) {
mInfo.mUnadjustedMetadataEndTime.emplace(TimeUnit::FromMicroseconds(endTime));
LOG(LogLevel::Debug, ("Got Ogg duration from seeking to end %lld", endTime));
@@ -526,7 +520,7 @@ nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) {
ogg_int64_t endFrame = aPacket->granulepos;
while ((frames = vorbis_synthesis_pcmout(&mVorbisState->mDsp, &pcm)) > 0) {
mVorbisState->ValidateVorbisPacketSamples(aPacket, frames);
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
for (uint32_t j = 0; j < channels; ++j) {
VorbisPCMValue* channel = pcm[j];
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
@@ -545,7 +539,7 @@ nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) {
startTime,
duration,
frames,
buffer.forget(),
Move(buffer),
channels,
mVorbisState->mInfo.rate));
@@ -575,17 +569,17 @@ nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
if (frames < 120 || frames > 5760)
return NS_ERROR_FAILURE;
uint32_t channels = mOpusState->mChannels;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
// Decode to the appropriate sample type.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
int ret = opus_multistream_decode_float(mOpusState->mDecoder,
aPacket->packet, aPacket->bytes,
buffer, frames, false);
buffer.get(), frames, false);
#else
int ret = opus_multistream_decode(mOpusState->mDecoder,
aPacket->packet, aPacket->bytes,
buffer, frames, false);
buffer.get(), frames, false);
#endif
if (ret < 0)
return NS_ERROR_FAILURE;
@@ -615,13 +609,13 @@ nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
}
int32_t keepFrames = frames - skipFrames;
int samples = keepFrames * channels;
nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
auto trimBuffer = MakeUnique<AudioDataValue[]>(samples);
for (int i = 0; i < samples; i++)
trimBuffer[i] = buffer[skipFrames*channels + i];
startFrame = endFrame - keepFrames;
frames = keepFrames;
buffer = trimBuffer;
buffer = Move(trimBuffer);
mOpusState->mSkip -= skipFrames;
LOG(LogLevel::Debug, ("Opus decoder skipping %d frames", skipFrames));
@@ -662,7 +656,7 @@ nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
startTime,
endTime - startTime,
frames,
buffer.forget(),
Move(buffer),
channels,
mOpusState->mRate));
@@ -1027,7 +1021,7 @@ struct nsAutoOggSyncState {
int64_t OggReader::RangeEndTime(int64_t aEndOffset)
{
MOZ_ASSERT(OnTaskQueue() || mDecoder->OnStateMachineTaskQueue());
MOZ_ASSERT(OnTaskQueue());
int64_t position = mResource.Tell();
int64_t endTime = RangeEndTime(0, aEndOffset, false);
@@ -1945,7 +1939,7 @@ media::TimeIntervals OggReader::GetBuffered()
VideoData* OggReader::FindStartTime(int64_t& aOutStartTime)
{
MOZ_ASSERT(OnTaskQueue() || mDecoder->OnStateMachineTaskQueue());
MOZ_ASSERT(OnTaskQueue());
// Extract the start times of the bitstreams in order to calculate
// the duration.
+9 -9
View File
@@ -60,15 +60,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
virtual bool HasAudio() override {
return (mVorbisState != 0 && mVorbisState->mActive) ||
(mOpusState != 0 && mOpusState->mActive);
}
virtual bool HasVideo() override {
return mTheoraState != 0 && mTheoraState->mActive;
}
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override;
virtual RefPtr<SeekPromise>
@@ -78,6 +69,15 @@ public:
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() {
return (mVorbisState != 0 && mVorbisState->mActive) ||
(mOpusState != 0 && mOpusState->mActive);
}
bool HasVideo() {
return mTheoraState != 0 && mTheoraState->mActive;
}
// TODO: DEPRECATED. This uses synchronous decoding.
// Stores the presentation time of the first frame we'd be able to play if
// we started playback at the current position. Returns the first video
+2 -2
View File
@@ -12,9 +12,9 @@
namespace mozilla {
MediaDecoder*
MediaCodecDecoder::Clone()
MediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new MediaCodecDecoder();
return new MediaCodecDecoder(aOwner);
}
MediaOmxCommonReader*
+3 -1
View File
@@ -15,8 +15,10 @@ namespace mozilla {
class MediaCodecDecoder : public MediaOmxCommonDecoder
{
public:
explicit MediaCodecDecoder(MediaDecoderOwner* aOwner)
: MediaOmxCommonDecoder(aOwner) {}
virtual MediaDecoder* Clone();
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
virtual MediaOmxCommonReader* CreateReader();
+14 -4
View File
@@ -517,13 +517,23 @@ MediaCodecReader::HasVideo()
}
void
MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
int64_t aOffset)
MediaCodecReader::NotifyDataArrivedInternal()
{
IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
AutoPinned<MediaResource> resource(mDecoder->GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
resource->MediaReadAt(interval.mStart, interval.Length());
MonitorAutoLock monLock(mParserMonitor);
if (mNextParserPosition == mParsedDataLength &&
mNextParserPosition >= interval.mStart &&
+3 -4
View File
@@ -72,7 +72,7 @@ protected:
// Used to retrieve some special information that can only be retrieved after
// all contents have been continuously parsed. (ex. total duration of some
// variable-bit-rate MP3 files.)
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
virtual void NotifyDataArrivedInternal() override;
public:
// Flush the TaskQueue, flush MediaCodec and raise the mDiscontinuity.
@@ -86,9 +86,6 @@ public:
// Disptach a DecodeAduioDataTask to decode video data.
virtual RefPtr<AudioDataPromise> RequestAudioData() override;
virtual bool HasAudio();
virtual bool HasVideo();
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
// Moves the decode head to aTime microseconds. aStartTime and aEndTime
@@ -181,6 +178,8 @@ protected:
MozPromiseHolder<MediaResourcePromise> mMediaResourcePromise;
private:
virtual bool HasAudio() override;
virtual bool HasVideo() override;
// An intermediary class that can be managed by android::sp<T>.
// Redirect codecReserved() and codecCanceled() to MediaCodecReader.
+2 -2
View File
@@ -23,8 +23,8 @@ namespace mozilla {
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
MediaOmxCommonDecoder::MediaOmxCommonDecoder()
: MediaDecoder()
MediaOmxCommonDecoder::MediaOmxCommonDecoder(MediaDecoderOwner* aOwner)
: MediaDecoder(aOwner)
, mReader(nullptr)
, mCanOffloadAudio(false)
, mFallbackToStateMachine(false)
+1 -1
View File
@@ -21,7 +21,7 @@ class MediaOmxCommonReader;
class MediaOmxCommonDecoder : public MediaDecoder
{
public:
MediaOmxCommonDecoder();
explicit MediaOmxCommonDecoder(MediaDecoderOwner* aOwner);
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility) override;
+4
View File
@@ -49,6 +49,10 @@ protected:
android::MediaStreamSource* mStreamSource;
// Get value from the preferece, if true, we stop the audio offload.
bool IsMonoAudioEnabled();
private:
virtual bool HasAudio() = 0;
virtual bool HasVideo() = 0;
};
} // namespace mozilla
+2 -2
View File
@@ -13,9 +13,9 @@ using namespace android;
namespace mozilla {
MediaDecoder*
MediaOmxDecoder::Clone()
MediaOmxDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new MediaOmxDecoder();
return new MediaOmxDecoder(aOwner);
}
MediaOmxCommonReader*
+3 -1
View File
@@ -13,7 +13,9 @@ namespace mozilla {
class MediaOmxDecoder : public MediaOmxCommonDecoder
{
public:
virtual MediaDecoder* Clone();
explicit MediaOmxDecoder(MediaDecoderOwner* aOwner)
: MediaOmxCommonDecoder(aOwner) {}
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
virtual MediaOmxCommonReader* CreateReader();
virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader);
};
+20 -10
View File
@@ -96,7 +96,7 @@ private:
while (mLength) {
uint32_t length = std::min<uint64_t>(mLength, UINT32_MAX);
mOmxReader->NotifyDataArrived(Interval<int64_t>(mOffset, mOffset + length));
mOmxReader->NotifyDataArrived();
mLength -= length;
mOffset += length;
}
@@ -455,7 +455,7 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
return true;
}
void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
void MediaOmxReader::NotifyDataArrivedInternal()
{
MOZ_ASSERT(OnTaskQueue());
RefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
@@ -469,21 +469,31 @@ void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset
return;
}
IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
AutoPinned<MediaResource> resource(mDecoder->GetResource());
nsTArray<MediaByteRange> byteRanges;
nsresult rv = resource->GetCachedRanges(byteRanges);
if (NS_FAILED(rv)) {
return;
}
IntervalSet<int64_t> intervals;
for (auto& range : byteRanges) {
intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
}
for (const auto& interval : intervals) {
RefPtr<MediaByteBuffer> bytes =
mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
resource->MediaReadAt(interval.mStart, interval.Length());
NS_ENSURE_TRUE_VOID(bytes);
mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
if (!mMP3FrameParser.IsMP3()) {
return;
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration) {
mLastParserDuration = duration;
decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
}
}
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration) {
mLastParserDuration = duration;
decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
}
}
+4 -11
View File
@@ -71,7 +71,7 @@ public:
~MediaOmxReader();
protected:
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
virtual void NotifyDataArrivedInternal() override;
public:
virtual nsresult ResetDecode()
@@ -85,16 +85,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold);
virtual bool HasAudio()
{
return mHasAudio;
}
virtual bool HasVideo()
{
return mHasVideo;
}
virtual void ReleaseMediaResources();
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
@@ -118,6 +108,9 @@ private:
class ProcessCachedDataTask;
class NotifyDataArrivedRunnable;
virtual bool HasAudio() override { return mHasAudio; }
virtual bool HasVideo() override { return mHasVideo; }
bool IsShutdown() {
MutexAutoLock lock(mShutdownMutex);
return mIsShutdown;
+2 -2
View File
@@ -13,9 +13,9 @@
namespace mozilla {
MediaDecoder*
RtspMediaCodecDecoder::Clone()
RtspMediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new RtspMediaCodecDecoder();
return new RtspMediaCodecDecoder(aOwner);
}
MediaOmxCommonReader*
+4 -1
View File
@@ -14,7 +14,10 @@ namespace mozilla {
class RtspMediaCodecDecoder final : public MediaOmxCommonDecoder
{
public:
virtual MediaDecoder* Clone() override;
explicit RtspMediaCodecDecoder(MediaDecoderOwner* aOwner)
: MediaOmxCommonDecoder(aOwner) {}
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
virtual MediaOmxCommonReader* CreateReader() override;
+2 -2
View File
@@ -11,9 +11,9 @@
namespace mozilla {
MediaDecoder* RtspOmxDecoder::Clone()
MediaDecoder* RtspOmxDecoder::Clone(MediaDecoderOwner* aOwner)
{
return new RtspOmxDecoder();
return new RtspOmxDecoder(aOwner);
}
MediaDecoderStateMachine*
+2 -3
View File
@@ -21,8 +21,7 @@ namespace mozilla {
class RtspOmxDecoder : public MediaDecoder
{
public:
RtspOmxDecoder()
: MediaDecoder() {
explicit RtspOmxDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {
MOZ_COUNT_CTOR(RtspOmxDecoder);
}
@@ -30,7 +29,7 @@ public:
MOZ_COUNT_DTOR(RtspOmxDecoder);
}
virtual MediaDecoder* Clone() override final;
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override final;
virtual MediaDecoderStateMachine* CreateStateMachine() override final;
virtual void ChangeState(PlayState aState) override final;
};
+71 -18
View File
@@ -45,10 +45,21 @@ extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
bool PDMFactory::sUseBlankDecoder = false;
#ifdef MOZ_GONK_MEDIACODEC
bool PDMFactory::sGonkDecoderEnabled = false;
#endif
#ifdef MOZ_WIDGET_ANDROID
bool PDMFactory::sAndroidMCDecoderEnabled = false;
bool PDMFactory::sAndroidMCDecoderPreferred = false;
#endif
bool PDMFactory::sGMPDecoderEnabled = false;
#ifdef MOZ_FFMPEG
bool PDMFactory::sFFmpegDecoderEnabled = false;
#endif
#ifdef XP_WIN
bool PDMFactory::sWMFDecoderEnabled = false;
#endif
bool PDMFactory::sEnableFuzzingWrapper = false;
uint32_t PDMFactory::sVideoOutputMinimumInterval_ms = 0;
bool PDMFactory::sDontDelayInputExhausted = false;
@@ -65,20 +76,28 @@ PDMFactory::Init()
alreadyInitialized = true;
Preferences::AddBoolVarCache(&sUseBlankDecoder,
"media.fragmented-mp4.use-blank-decoder");
"media.use-blank-decoder");
#ifdef MOZ_GONK_MEDIACODEC
Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
"media.fragmented-mp4.gonk.enabled", false);
"media.gonk.enabled", false);
#endif
#ifdef MOZ_WIDGET_ANDROID
Preferences::AddBoolVarCache(&sAndroidMCDecoderEnabled,
"media.fragmented-mp4.android-media-codec.enabled", false);
"media.android-media-codec.enabled", false);
Preferences::AddBoolVarCache(&sAndroidMCDecoderPreferred,
"media.fragmented-mp4.android-media-codec.preferred", false);
"media.android-media-codec.preferred", false);
#endif
Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
"media.fragmented-mp4.gmp.enabled", false);
"media.gmp.decoder.enabled", false);
#ifdef MOZ_FFMPEG
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
"media.ffmpeg.enabled", false);
#endif
#ifdef XP_WIN
Preferences::AddBoolVarCache(&sWMFDecoderEnabled,
"media.wmf.enabled", false);
#endif
Preferences::AddBoolVarCache(&sEnableFuzzingWrapper,
"media.decoder.fuzzing.enabled", false);
@@ -96,6 +115,7 @@ PDMFactory::Init()
#ifdef MOZ_FFMPEG
FFmpegRuntimeLinker::Link();
#endif
GMPDecoderModule::Init();
}
PDMFactory::PDMFactory()
@@ -114,18 +134,51 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer)
{
RefPtr<PlatformDecoderModule> current = (mEMEPDM && aConfig.mCrypto.mValid)
? mEMEPDM : GetDecoder(aConfig.mMimeType);
bool isEncrypted = mEMEPDM && aConfig.mCrypto.mValid;
if (!current) {
return nullptr;
if (isEncrypted) {
return CreateDecoderWithPDM(mEMEPDM,
aConfig,
aTaskQueue,
aCallback,
aLayersBackend,
aImageContainer);
}
for (auto& current : mCurrentPDMs) {
if (!current->SupportsMimeType(aConfig.mMimeType)) {
continue;
}
RefPtr<MediaDataDecoder> m =
CreateDecoderWithPDM(current,
aConfig,
aTaskQueue,
aCallback,
aLayersBackend,
aImageContainer);
if (m) {
return m.forget();
}
}
NS_WARNING("Unable to create a decoder, no platform found.");
return nullptr;
}
already_AddRefed<MediaDataDecoder>
PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer)
{
MOZ_ASSERT(aPDM);
RefPtr<MediaDataDecoder> m;
if (aConfig.GetAsAudioInfo()) {
m = current->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
aTaskQueue,
aCallback);
m = aPDM->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
aTaskQueue,
aCallback);
return m.forget();
}
@@ -145,7 +198,7 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
if (H264Converter::IsH264(aConfig)) {
RefPtr<H264Converter> h
= new H264Converter(current,
= new H264Converter(aPDM,
*aConfig.GetAsVideoInfo(),
aLayersBackend,
aImageContainer,
@@ -159,11 +212,11 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
m = h.forget();
}
} else {
m = current->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
aLayersBackend,
aImageContainer,
aTaskQueue,
callback);
m = aPDM->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
aLayersBackend,
aImageContainer,
aTaskQueue,
callback);
}
if (callbackWrapper && m) {
+18 -1
View File
@@ -53,13 +53,30 @@ private:
bool StartupPDM(PlatformDecoderModule* aPDM);
// Returns the first PDM in our list supporting the mimetype.
already_AddRefed<PlatformDecoderModule> GetDecoder(const nsACString& aMimeType);
already_AddRefed<MediaDataDecoder>
CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer);
// Caches pref media.fragmented-mp4.use-blank-decoder
// PDM pref caches...
static bool sUseBlankDecoder;
#ifdef MOZ_GONK_MEDIACODEC
static bool sGonkDecoderEnabled;
#endif
#ifdef MOZ_WIDGET_ANDROID
static bool sAndroidMCDecoderPreferred;
static bool sAndroidMCDecoderEnabled;
#endif
static bool sGMPDecoderEnabled;
#ifdef MOZ_FFMPEG
static bool sFFmpegDecoderEnabled;
#endif
#ifdef XP_WIN
static bool sWMFDecoderEnabled;
#endif
static bool sEnableFuzzingWrapper;
static uint32_t sVideoOutputMinimumInterval_ms;
static bool sDontDelayInputExhausted;
@@ -183,7 +183,7 @@ public:
frames.value() > (UINT32_MAX / mChannelCount)) {
return nullptr;
}
AudioDataValue* samples = new AudioDataValue[frames.value() * mChannelCount];
auto samples = MakeUnique<AudioDataValue[]>(frames.value() * mChannelCount);
// Fill the sound buffer with an A4 tone.
static const float pi = 3.14159265f;
static const float noteHz = 440.0f;
@@ -198,7 +198,7 @@ public:
aDTS.ToMicroseconds(),
aDuration.ToMicroseconds(),
uint32_t(frames.value()),
samples,
Move(samples),
mChannelCount,
mSampleRate);
}
+4 -4
View File
@@ -168,17 +168,17 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
return -1;
}
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
// Decode to the appropriate sample type.
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
int ret = opus_multistream_decode_float(mOpusDecoder,
aSample->Data(), aSample->Size(),
buffer, frames, false);
buffer.get(), frames, false);
#else
int ret = opus_multistream_decode(mOpusDecoder,
aSample->Data(), aSample->Size(),
buffer, frames, false);
buffer.get(), frames, false);
#endif
if (ret < 0) {
return -1;
@@ -264,7 +264,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
time.value(),
duration.value(),
frames,
buffer.forget(),
Move(buffer),
mOpusParser->mChannels,
mOpusParser->mRate));
mFrames += frames;
@@ -189,7 +189,7 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
}
while (frames > 0) {
uint32_t channels = mVorbisDsp.vi->channels;
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
auto buffer = MakeUnique<AudioDataValue[]>(frames*channels);
for (uint32_t j = 0; j < channels; ++j) {
VorbisPCMValue* channel = pcm[j];
for (uint32_t i = 0; i < uint32_t(frames); ++i) {
@@ -220,7 +220,7 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
time.value(),
duration.value(),
frames,
buffer.forget(),
Move(buffer),
mVorbisDsp.vi->channels,
mVorbisDsp.vi->rate));
mFrames += aTotalFrames;
@@ -7,6 +7,7 @@
#include "GMPAudioDecoder.h"
#include "nsServiceManagerUtils.h"
#include "MediaInfo.h"
#include "GMPDecoderModule.h"
namespace mozilla {
@@ -36,7 +37,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
size_t numFrames = aPCM.Length() / aChannels;
MOZ_ASSERT((aPCM.Length() % aChannels) == 0);
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[aPCM.Length()]);
auto audioData = MakeUnique<AudioDataValue[]>(aPCM.Length());
for (size_t i = 0; i < aPCM.Length(); ++i) {
audioData[i] = AudioSampleToFloat(aPCM[i]);
@@ -71,12 +72,12 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
}
RefPtr<AudioData> audio(new AudioData(mLastStreamOffset,
timestamp.value(),
duration.value(),
numFrames,
audioData.forget(),
aChannels,
aRate));
timestamp.value(),
duration.value(),
numFrames,
Move(audioData),
aChannels,
aRate));
#ifdef LOG_SAMPLE_DECODE
LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
@@ -126,6 +127,11 @@ void
GMPAudioDecoder::InitTags(nsTArray<nsCString>& aTags)
{
aTags.AppendElement(NS_LITERAL_CSTRING("aac"));
const Maybe<nsCString> gmp(
GMPDecoderModule::PreferredGMP(NS_LITERAL_CSTRING("audio/mp4a-latm")));
if (gmp.isSome()) {
aTags.AppendElement(gmp.value());
}
}
nsCString
@@ -10,6 +10,13 @@
#include "MediaDataDecoderProxy.h"
#include "mozIGeckoMediaPluginService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticMutex.h"
#include "gmp-audio-decode.h"
#include "gmp-video-decode.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif
namespace mozilla {
@@ -86,4 +93,144 @@ GMPDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
}
}
static bool
HasGMPFor(const nsACString& aAPI,
const nsACString& aCodec,
const nsACString& aGMP)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef XP_WIN
// gmp-clearkey uses WMF for decoding, so if we're using clearkey we must
// verify that WMF works before continuing.
if (aGMP.EqualsLiteral("org.w3.clearkey")) {
RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
if (aCodec.EqualsLiteral("aac") &&
!pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))) {
return false;
}
if (aCodec.EqualsLiteral("h264") &&
!pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"))) {
return false;
}
}
#endif
nsTArray<nsCString> tags;
tags.AppendElement(aCodec);
tags.AppendElement(aGMP);
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
bool hasPlugin = false;
if (NS_FAILED(mps->HasPluginForAPI(aAPI, &tags, &hasPlugin))) {
return false;
}
return hasPlugin;
}
StaticMutex sGMPCodecsMutex;
struct GMPCodecs {
const char* mKeySystem;
bool mHasAAC;
bool mHasH264;
};
static GMPCodecs sGMPCodecs[] = {
{ "org.w3.clearkey", false, false },
{ "com.adobe.primetime", false, false },
};
void
GMPDecoderModule::UpdateUsableCodecs()
{
MOZ_ASSERT(NS_IsMainThread());
StaticMutexAutoLock lock(sGMPCodecsMutex);
for (GMPCodecs& gmp : sGMPCodecs) {
gmp.mHasAAC = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"),
nsDependentCString(gmp.mKeySystem));
gmp.mHasH264 = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
NS_LITERAL_CSTRING("h264"),
nsDependentCString(gmp.mKeySystem));
}
}
static uint32_t sPreferredAacGmp = 0;
static uint32_t sPreferredH264Gmp = 0;
/* static */
void
GMPDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread());
// GMPService::HasPluginForAPI is main thread only, so to implement
// SupportsMimeType() we build a table of the codecs which each whitelisted
// GMP has and update it when any GMPs are removed or added at runtime.
UpdateUsableCodecs();
Preferences::AddUintVarCache(&sPreferredAacGmp,
"media.gmp.decoder.aac", 0);
Preferences::AddUintVarCache(&sPreferredH264Gmp,
"media.gmp.decoder.h264", 0);
}
/* static */
const Maybe<nsCString>
GMPDecoderModule::PreferredGMP(const nsACString& aMimeType)
{
Maybe<nsCString> rv;
if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
switch (sPreferredAacGmp) {
case 1: rv.emplace(NS_LITERAL_CSTRING("org.w3.clearkey")); break;
case 2: rv.emplace(NS_LITERAL_CSTRING("com.adobe.primetime")); break;
default: break;
}
}
if (aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4")) {
switch (sPreferredH264Gmp) {
case 1: rv.emplace(NS_LITERAL_CSTRING("org.w3.clearkey")); break;
case 2: rv.emplace(NS_LITERAL_CSTRING("com.adobe.primetime")); break;
default: break;
}
}
return rv;
}
/* static */
bool
GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
const Maybe<nsCString>& aGMP)
{
const bool isAAC = aMimeType.EqualsLiteral("audio/mp4a-latm");
const bool isH264 = aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4");
StaticMutexAutoLock lock(sGMPCodecsMutex);
for (GMPCodecs& gmp : sGMPCodecs) {
if (isAAC && gmp.mHasAAC &&
(aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
return true;
}
if (isH264 && gmp.mHasH264 &&
(aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
return true;
}
}
return false;
}
bool
GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType)
{
return SupportsMimeType(aMimeType, PreferredGMP(aMimeType));
}
} // namespace mozilla
@@ -8,6 +8,7 @@
#define GMPDecoderModule_h_
#include "PlatformDecoderModule.h"
#include "mozilla/Maybe.h"
namespace mozilla {
@@ -35,12 +36,18 @@ public:
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
bool
SupportsMimeType(const nsACString& aMimeType) override
{
// TODO properly.
return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
aMimeType.EqualsLiteral("video/avc");
}
SupportsMimeType(const nsACString& aMimeType) override;
// Main thread only.
static void Init();
static const Maybe<nsCString> PreferredGMP(const nsACString& aMimeType);
static bool SupportsMimeType(const nsACString& aMimeType,
const Maybe<nsCString>& aGMP);
// Main thread only.
static void UpdateUsableCodecs();
};
} // namespace mozilla
@@ -9,6 +9,7 @@
#include "mozilla/Endian.h"
#include "prsystem.h"
#include "MediaData.h"
#include "GMPDecoderModule.h"
namespace mozilla {
@@ -107,6 +108,11 @@ void
GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
{
aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
const Maybe<nsCString> gmp(
GMPDecoderModule::PreferredGMP(NS_LITERAL_CSTRING("video/avc")));
if (gmp.isSome()) {
aTags.AppendElement(gmp.value());
}
}
nsCString
@@ -20,7 +20,6 @@
#include "nsPromiseFlatString.h"
#include <jni.h>
#include <string.h>
using namespace mozilla;
using namespace mozilla::gl;
@@ -28,10 +27,28 @@ using namespace mozilla::widget::sdk;
namespace mozilla {
#define INVOKE_CALLBACK(Func, ...) \
if (mCallback) { \
mCallback->Func(__VA_ARGS__); \
} else { \
NS_WARNING("callback not set"); \
}
static const char* TranslateMimeType(const nsACString& aMimeType)
{
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
return "video/x-vnd.on2.vp8";
} else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
return "video/x-vnd.on2.vp9";
}
return PromiseFlatCString(aMimeType).get();
}
static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType)
{
MediaCodec::LocalRef codec;
NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(PromiseFlatCString(aMimeType).get(), &codec), nullptr);
NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(TranslateMimeType(aMimeType),
&codec), nullptr);
return codec;
}
@@ -170,7 +187,7 @@ public:
gfx::IntRect(0, 0,
mConfig.mDisplay.width,
mConfig.mDisplay.height));
mCallback->Output(v);
INVOKE_CALLBACK(Output, v);
return NS_OK;
}
@@ -229,23 +246,31 @@ public:
int32_t size;
NS_ENSURE_SUCCESS(rv = aInfo->Size(&size), rv);
const int32_t numFrames = (size / numChannels) / 2;
AudioDataValue* audio = new AudioDataValue[size];
PodCopy(audio, static_cast<AudioDataValue*>(aBuffer), size);
int32_t offset;
NS_ENSURE_SUCCESS(rv = aInfo->Offset(&offset), rv);
#ifdef MOZ_SAMPLE_TYPE_S16
int32_t numSamples = size / 2;
#else
#error We only support 16-bit integer PCM
#endif
const int32_t numFrames = numSamples / numChannels;
auto audio = MakeUnique<AudioDataValue[]>(numSamples);
uint8_t* bufferStart = static_cast<uint8_t*>(aBuffer) + offset;
PodCopy(audio.get(), reinterpret_cast<AudioDataValue*>(bufferStart), numSamples);
int64_t presentationTimeUs;
NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv);
RefPtr<AudioData> data = new AudioData(offset, presentationTimeUs,
aDuration.ToMicroseconds(),
numFrames,
audio,
numChannels,
sampleRate);
mCallback->Output(data);
RefPtr<AudioData> data = new AudioData(0, presentationTimeUs,
aDuration.ToMicroseconds(),
numFrames,
Move(audio),
numChannels,
sampleRate);
INVOKE_CALLBACK(Output, data);
return NS_OK;
}
};
@@ -274,7 +299,7 @@ AndroidDecoderModule::CreateVideoDecoder(
MediaFormat::LocalRef format;
NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
aConfig.mMimeType,
TranslateMimeType(aConfig.mMimeType),
aConfig.mDisplay.width,
aConfig.mDisplay.height,
&format), nullptr);
@@ -357,7 +382,7 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
{
mDecoder = CreateDecoder(mMimeType);
if (!mDecoder) {
mCallback->Error();
INVOKE_CALLBACK(Error);
return NS_ERROR_FAILURE;
}
@@ -380,7 +405,11 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
#define HANDLE_DECODER_ERROR() \
if (NS_FAILED(res)) { \
NS_WARNING("exiting decoder loop due to exception"); \
mCallback->Error(); \
if (mDraining) { \
INVOKE_CALLBACK(DrainComplete); \
mDraining = false; \
} \
INVOKE_CALLBACK(Error); \
break; \
}
@@ -426,7 +455,7 @@ void MediaCodecDataDecoder::DecoderLoop()
while (!mStopping && !mDraining && !mFlushing && mQueue.empty()) {
if (mQueue.empty()) {
// We could be waiting here forever if we don't signal that we need more input
mCallback->InputExhausted();
INVOKE_CALLBACK(InputExhausted);
}
lock.Wait();
}
@@ -444,14 +473,14 @@ void MediaCodecDataDecoder::DecoderLoop()
continue;
}
if (mDraining && !sample && !waitingEOF) {
draining = true;
}
// We're not stopping or draining, so try to get a sample
if (!mQueue.empty()) {
sample = mQueue.front();
}
if (mDraining && !sample && !waitingEOF) {
draining = true;
}
}
if (draining && !waitingEOF) {
@@ -523,7 +552,7 @@ void MediaCodecDataDecoder::DecoderLoop()
HANDLE_DECODER_ERROR();
} else if (outputStatus < 0) {
NS_WARNING("unknown error from decoder!");
mCallback->Error();
INVOKE_CALLBACK(Error);
// Don't break here just in case it's recoverable. If it's not, others stuff will fail later and
// we'll bail out.
@@ -543,7 +572,7 @@ void MediaCodecDataDecoder::DecoderLoop()
mMonitor.Notify();
mMonitor.Unlock();
mCallback->DrainComplete();
INVOKE_CALLBACK(DrainComplete);
}
mDecoder->ReleaseOutputBuffer(outputStatus, false);
+7 -7
View File
@@ -273,15 +273,15 @@ AppleATDecoder::DecodeSample(MediaRawData* aSample)
duration.ToSeconds());
#endif
nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
auto data = MakeUnique<AudioDataValue[]>(outputData.Length());
PodCopy(data.get(), &outputData[0], outputData.Length());
RefPtr<AudioData> audio = new AudioData(aSample->mOffset,
aSample->mTime,
duration.ToMicroseconds(),
numFrames,
data.forget(),
channels,
rate);
aSample->mTime,
duration.ToMicroseconds(),
numFrames,
Move(data),
channels,
rate);
mCallback->Output(audio);
return NS_OK;
}
@@ -60,22 +60,21 @@ FFmpegAudioDecoder<LIBAV_VER>::InitCodecContext()
(major == 53) ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
}
static AudioDataValue*
static UniquePtr<AudioDataValue[]>
CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
{
MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
nsAutoArrayPtr<AudioDataValue> audio(
new AudioDataValue[aNumChannels * aNumAFrames]);
auto audio = MakeUnique<AudioDataValue[]>(aNumChannels * aNumAFrames);
if (aFrame->format == AV_SAMPLE_FMT_FLT) {
// Audio data already packed. No need to do anything other than copy it
// into a buffer we own.
memcpy(audio, aFrame->data[0],
memcpy(audio.get(), aFrame->data[0],
aNumChannels * aNumAFrames * sizeof(AudioDataValue));
} else if (aFrame->format == AV_SAMPLE_FMT_FLTP) {
// Planar audio data. Pack it into something we can understand.
AudioDataValue* tmp = audio;
AudioDataValue* tmp = audio.get();
AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data);
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
@@ -84,7 +83,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
}
} else if (aFrame->format == AV_SAMPLE_FMT_S16) {
// Audio data already packed. Need to convert from S16 to 32 bits Float
AudioDataValue* tmp = audio;
AudioDataValue* tmp = audio.get();
int16_t* data = reinterpret_cast<int16_t**>(aFrame->data)[0];
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
@@ -94,7 +93,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
} else if (aFrame->format == AV_SAMPLE_FMT_S16P) {
// Planar audio data. Convert it from S16 to 32 bits float
// and pack it into something we can understand.
AudioDataValue* tmp = audio;
AudioDataValue* tmp = audio.get();
int16_t** data = reinterpret_cast<int16_t**>(aFrame->data);
for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
for (uint32_t channel = 0; channel < aNumChannels; channel++) {
@@ -103,7 +102,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
}
}
return audio.forget();
return audio;
}
void
@@ -140,8 +139,8 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
uint32_t numChannels = mCodecContext->channels;
uint32_t samplingRate = mCodecContext->sample_rate;
nsAutoArrayPtr<AudioDataValue> audio(
CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples));
UniquePtr<AudioDataValue[]> audio =
CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples);
media::TimeUnit duration =
FramesToTimeUnit(mFrame->nb_samples, samplingRate);
@@ -152,12 +151,12 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
}
RefPtr<AudioData> data = new AudioData(samplePosition,
pts.ToMicroseconds(),
duration.ToMicroseconds(),
mFrame->nb_samples,
audio.forget(),
numChannels,
samplingRate);
pts.ToMicroseconds(),
duration.ToMicroseconds(),
mFrame->nb_samples,
Move(audio),
numChannels,
samplingRate);
mCallback->Output(data);
pts += duration;
if (!pts.IsValid()) {
+1 -1
View File
@@ -218,7 +218,7 @@ MFTDecoder::Output(RefPtr<IMFSample>* aOutput)
}
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
// Type change, probably geometric aperature change.
// Type change, probably geometric aperture change.
// Reconfigure decoder output type, so that GetOutputMediaType()
// returns the new type, and return the error code to caller.
// This is an expected failure, so don't warn on encountering it.
+26 -22
View File
@@ -68,7 +68,6 @@ WMFAudioMFTManager::WMFAudioMFTManager(
const AudioInfo& aConfig)
: mAudioChannels(aConfig.mChannels)
, mAudioRate(aConfig.mRate)
, mAudioFrameOffset(0)
, mAudioFrameSum(0)
, mMustRecaptureAudioPosition(true)
{
@@ -114,63 +113,63 @@ WMFAudioMFTManager::GetMediaSubtypeGUID()
};
}
already_AddRefed<MFTDecoder>
bool
WMFAudioMFTManager::Init()
{
NS_ENSURE_TRUE(mStreamType != Unknown, nullptr);
NS_ENSURE_TRUE(mStreamType != Unknown, false);
RefPtr<MFTDecoder> decoder(new MFTDecoder());
HRESULT hr = decoder->Create(GetMFTGUID());
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
// Setup input/output media types
RefPtr<IMFMediaType> inputType;
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = inputType->SetGUID(MF_MT_SUBTYPE, GetMediaSubtypeGUID());
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = inputType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, mAudioRate);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = inputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mAudioChannels);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
if (mStreamType == AAC) {
hr = inputType->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0x0); // Raw AAC packet
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = inputType->SetBlob(MF_MT_USER_DATA,
mUserData.Elements(),
mUserData.Length());
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
}
RefPtr<IMFMediaType> outputType;
hr = wmf::MFCreateMediaType(getter_AddRefs(outputType));
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = outputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = outputType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
hr = decoder->SetMediaTypes(inputType, outputType);
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mDecoder = decoder;
return decoder.forget();
return true;
}
HRESULT
@@ -206,6 +205,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
aOutData = nullptr;
RefPtr<IMFSample> sample;
HRESULT hr;
int typeChangeCount = 0;
while (true) {
hr = mDecoder->Output(&sample);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
@@ -214,6 +214,11 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
hr = UpdateOutputType();
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Catch infinite loops, but some decoders perform at least 2 stream
// changes on consecutive calls, so be permissive.
// 100 is arbitrarily > 2.
NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE);
++typeChangeCount;
continue;
}
break;
@@ -261,8 +266,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
LONGLONG timestampHns = 0;
hr = sample->GetSampleTime(&timestampHns);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
mAudioTimeOffset = media::TimeUnit::FromMicroseconds(timestampHns / 10);
mMustRecaptureAudioPosition = false;
}
// We can assume PCM 16 output.
@@ -276,7 +280,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
return S_OK;
}
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[numSamples]);
auto audioData = MakeUnique<AudioDataValue[]>(numSamples);
int16_t* pcm = (int16_t*)data;
for (int32_t i = 0; i < numSamples; ++i) {
@@ -286,7 +290,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
buffer->Unlock();
media::TimeUnit timestamp =
FramesToTimeUnit(mAudioFrameOffset + mAudioFrameSum, mAudioRate);
mAudioTimeOffset + FramesToTimeUnit(mAudioFrameSum, mAudioRate);
NS_ENSURE_TRUE(timestamp.IsValid(), E_FAIL);
mAudioFrameSum += numFrames;
@@ -298,7 +302,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
timestamp.ToMicroseconds(),
duration.ToMicroseconds(),
numFrames,
audioData.forget(),
Move(audioData),
mAudioChannels,
mAudioRate);
+7 -7
View File
@@ -12,6 +12,9 @@
#include "mozilla/RefPtr.h"
#include "WMFMediaDataDecoder.h"
extern const GUID CLSID_WebmMfVp8Dec;
extern const GUID CLSID_WebmMfVp9Dec;
namespace mozilla {
class WMFAudioMFTManager : public MFTManager {
@@ -19,7 +22,7 @@ public:
WMFAudioMFTManager(const AudioInfo& aConfig);
~WMFAudioMFTManager();
virtual already_AddRefed<MFTDecoder> Init() override;
bool Init();
HRESULT Input(MediaRawData* aSample) override;
@@ -39,16 +42,13 @@ private:
HRESULT UpdateOutputType();
// IMFTransform wrapper that performs the decoding.
RefPtr<MFTDecoder> mDecoder;
uint32_t mAudioChannels;
uint32_t mAudioRate;
nsTArray<BYTE> mUserData;
// The offset, in audio frames, at which playback started since the
// The offset, at which playback started since the
// last discontinuity.
int64_t mAudioFrameOffset;
media::TimeUnit mAudioTimeOffset;
// The number of audio frames that we've played since the last
// discontinuity.
int64_t mAudioFrameSum;
@@ -63,7 +63,7 @@ private:
const GUID& GetMFTGUID();
const GUID& GetMediaSubtypeGUID();
// True if we need to re-initialize mAudioFrameOffset and mAudioFrameSum
// True if we need to re-initialize mAudioTimeOffset and mAudioFrameSum
// from the next audio packet we decode. This happens after a seek, since
// WMF doesn't mark a stream as having a discontinuity after a seek(0).
bool mMustRecaptureAudioPosition;
+74 -57
View File
@@ -19,15 +19,17 @@
#include "nsIGfxInfo.h"
#include "GfxDriverInfo.h"
#include "gfxWindowsPlatform.h"
#include "nsServiceManagerUtils.h" // for do_GetService
#include "MediaInfo.h"
#include "prsystem.h"
#include "mozilla/Maybe.h"
#include "mozilla/StaticMutex.h"
namespace mozilla {
static bool sDXVAEnabled = false;
static int sNumDecoderThreads = -1;
static bool sIsIntelDecoderEnabled = false;
static bool sLowLatencyMFTEnabled = false;
WMFDecoderModule::WMFDecoderModule()
: mWMFInitialized(false)
@@ -67,7 +69,9 @@ WMFDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
sDXVAEnabled = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
sIsIntelDecoderEnabled = Preferences::GetBool("media.webm.intel_decoder.enabled", false);
Preferences::AddBoolVarCache(&sIsIntelDecoderEnabled,
"media.webm.intel_decoder.enabled");
sLowLatencyMFTEnabled = Preferences::GetBool("media.wmf.low-latency.enabled", false);
SetNumOfDecoderThreads();
}
@@ -78,6 +82,13 @@ WMFDecoderModule::GetNumDecoderThreads()
return sNumDecoderThreads;
}
/* static */
bool
WMFDecoderModule::LowLatencyMFTEnabled()
{
return sLowLatencyMFTEnabled;
}
nsresult
WMFDecoderModule::Startup()
{
@@ -98,14 +109,12 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
aImageContainer,
sDXVAEnabled));
RefPtr<MFTDecoder> mft = manager->Init();
if (!mft) {
if (!manager->Init()) {
return nullptr;
}
RefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(manager.forget(), mft, aVideoTaskQueue, aCallback);
new WMFMediaDataDecoder(manager.forget(), aVideoTaskQueue, aCallback);
return decoder.forget();
}
@@ -116,78 +125,86 @@ WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
MediaDataDecoderCallback* aCallback)
{
nsAutoPtr<WMFAudioMFTManager> manager(new WMFAudioMFTManager(aConfig));
RefPtr<MFTDecoder> mft = manager->Init();
if (!mft) {
if (!manager->Init()) {
return nullptr;
}
RefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(manager.forget(), mft, aAudioTaskQueue, aCallback);
new WMFMediaDataDecoder(manager.forget(), aAudioTaskQueue, aCallback);
return decoder.forget();
}
static bool
CanCreateMFTDecoder(const GUID& aGuid)
{
if (FAILED(wmf::MFStartup())) {
return false;
}
bool hasdecoder = false;
{
RefPtr<MFTDecoder> decoder(new MFTDecoder());
hasdecoder = SUCCEEDED(decoder->Create(aGuid));
}
wmf::MFShutdown();
return hasdecoder;
}
template<const GUID& aGuid>
static bool
CanCreateWMFDecoder()
{
static StaticMutex sMutex;
StaticMutexAutoLock lock(sMutex);
static Maybe<bool> result;
if (result.isNothing()) {
result.emplace(CanCreateMFTDecoder(aGuid));
}
return result.value();
}
bool
WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType)
{
return aMimeType.EqualsLiteral("video/mp4") ||
aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("audio/mp4a-latm") ||
aMimeType.EqualsLiteral("audio/mpeg") ||
(sIsIntelDecoderEnabled &&
(aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
aMimeType.EqualsLiteral("video/webm; codecs=vp9")));
if ((aMimeType.EqualsLiteral("audio/mp4a-latm") ||
aMimeType.EqualsLiteral("audio/mp4")) &&
CanCreateWMFDecoder<CLSID_CMSAACDecMFT>()) {
return true;
}
if ((aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4")) &&
CanCreateWMFDecoder<CLSID_CMSH264DecoderMFT>()) {
return true;
}
if (aMimeType.EqualsLiteral("audio/mpeg") &&
CanCreateWMFDecoder<CLSID_CMP3DecMediaObject>()) {
return true;
}
if (sIsIntelDecoderEnabled && sDXVAEnabled) {
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8") &&
CanCreateWMFDecoder<CLSID_WebmMfVp8Dec>()) {
return true;
}
if (aMimeType.EqualsLiteral("video/webm; codecs=vp9") &&
CanCreateWMFDecoder<CLSID_WebmMfVp9Dec>()) {
return true;
}
}
// Some unsupported codec.
return false;
}
PlatformDecoderModule::ConversionRequired
WMFDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
{
if (aConfig.IsVideo() &&
(aConfig.mMimeType.EqualsLiteral("video/avc") ||
aConfig.mMimeType.EqualsLiteral("video/mp4"))) {
(aConfig.mMimeType.EqualsLiteral("video/avc") ||
aConfig.mMimeType.EqualsLiteral("video/mp4"))) {
return kNeedAnnexB;
} else {
return kNeedNone;
}
}
static bool
ClassesRootRegKeyExists(const nsAString& aRegKeyPath)
{
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
aRegKeyPath,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
return false;
}
regKey->Close();
return true;
}
/* static */ bool
WMFDecoderModule::HasH264()
{
// CLSID_CMSH264DecoderMFT
return ClassesRootRegKeyExists(
NS_LITERAL_STRING("CLSID\\{32D186A7-218F-4C75-8876-DD77273A8999}"));
}
/* static */ bool
WMFDecoderModule::HasAAC()
{
// CLSID_CMSAACDecMFT
return ClassesRootRegKeyExists(
NS_LITERAL_STRING("CLSID\\{62CE7E72-4C71-4D20-B15D-452831A87D9D}"));
}
} // namespace mozilla
+1 -7
View File
@@ -36,18 +36,12 @@ public:
ConversionRequired
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
// Accessors that report whether we have the required MFTs available
// on the system to play various codecs. Windows Vista doesn't have the
// H.264/AAC decoders if the "Platform Update Supplement for Windows Vista"
// is not installed.
static bool HasAAC();
static bool HasH264();
// Called on main thread.
static void Init();
// Called from any thread, must call init first
static int GetNumDecoderThreads();
static bool LowLatencyMFTEnabled();
private:
bool mWMFInitialized;
};
@@ -17,12 +17,10 @@ extern PRLogModuleInfo* GetPDMLog();
namespace mozilla {
WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
MFTDecoder* aDecoder,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mDecoder(aDecoder)
, mMFTManager(aMFTManager)
, mMonitor("WMFMediaDataDecoder")
, mIsFlushing(false)
@@ -38,10 +36,7 @@ RefPtr<MediaDataDecoder::InitPromise>
WMFMediaDataDecoder::Init()
{
MOZ_ASSERT(!mIsShutDown);
return mDecoder ?
InitPromise::CreateAndResolve(mMFTManager->GetType(), __func__) :
InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
return InitPromise::CreateAndResolve(mMFTManager->GetType(), __func__);
}
nsresult
@@ -70,7 +65,6 @@ WMFMediaDataDecoder::ProcessShutdown()
//SendTelemetry(S_OK);
}
}
mDecoder = nullptr;
}
// Inserts data into the decoder's pipeline.
@@ -143,8 +137,8 @@ WMFMediaDataDecoder::ProcessOutput()
void
WMFMediaDataDecoder::ProcessFlush()
{
if (mDecoder) {
mDecoder->Flush();
if (mMFTManager) {
mMFTManager->Flush();
}
MonitorAutoLock mon(mMonitor);
mIsFlushing = false;
@@ -176,11 +170,9 @@ WMFMediaDataDecoder::ProcessDrain()
MonitorAutoLock mon(mMonitor);
isFlushing = mIsFlushing;
}
if (!isFlushing && mDecoder) {
if (!isFlushing && mMFTManager) {
// Order the decoder to drain...
if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
NS_WARNING("Failed to send DRAIN command to MFT");
}
mMFTManager->Drain();
// Then extract all available output.
ProcessOutput();
}
+12 -6
View File
@@ -22,10 +22,6 @@ class MFTManager {
public:
virtual ~MFTManager() {}
// Creates an initializs the MFTDecoder.
// Returns nullptr on failure.
virtual already_AddRefed<MFTDecoder> Init() = 0;
// Submit a compressed sample for decoding.
// This should forward to the MFTDecoder after performing
// any required sample formatting.
@@ -40,6 +36,15 @@ public:
virtual HRESULT Output(int64_t aStreamOffset,
RefPtr<MediaData>& aOutput) = 0;
void Flush() { mDecoder->Flush(); }
void Drain()
{
if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
NS_WARNING("Failed to send DRAIN command to MFT");
}
}
// Destroys all resources.
virtual void Shutdown() = 0;
@@ -47,6 +52,9 @@ public:
virtual TrackInfo::TrackType GetType() = 0;
protected:
// IMFTransform wrapper that performs the decoding.
RefPtr<MFTDecoder> mDecoder;
};
// Decodes audio and video using Windows Media Foundation. Samples are decoded
@@ -57,7 +65,6 @@ public:
class WMFMediaDataDecoder : public MediaDataDecoder {
public:
WMFMediaDataDecoder(MFTManager* aOutputSource,
MFTDecoder* aDecoder,
FlushableTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback);
~WMFMediaDataDecoder();
@@ -97,7 +104,6 @@ private:
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
RefPtr<MFTDecoder> mDecoder;
nsAutoPtr<MFTManager> mMFTManager;
// The last offset into the media resource that was passed into Input().

Some files were not shown because too many files have changed in this diff Show More