mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
AndroidMediaDecoder::AndroidMediaDecoder(const nsACString& aType) : mType(aType)
|
||||
AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner,
|
||||
const nsACString& aType)
|
||||
: MediaDecoder(aOwner), mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -37,6 +37,9 @@ SplitAt(const char* aDelims,
|
||||
nsCString
|
||||
ToBase64(const nsTArray<uint8_t>& aBytes);
|
||||
|
||||
bool
|
||||
FileExists(nsIFile* aFile);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
MediaCodecDecoder::Clone()
|
||||
MediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new MediaCodecDecoder();
|
||||
return new MediaCodecDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,9 +13,9 @@ using namespace android;
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
MediaOmxDecoder::Clone()
|
||||
MediaOmxDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new MediaOmxDecoder();
|
||||
return new MediaOmxDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
RtspMediaCodecDecoder::Clone()
|
||||
RtspMediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new RtspMediaCodecDecoder();
|
||||
return new RtspMediaCodecDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder* RtspOmxDecoder::Clone()
|
||||
MediaDecoder* RtspOmxDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new RtspOmxDecoder();
|
||||
return new RtspOmxDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(×tampHns);
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user