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

- Bug 1144107 - Part 4: Prevent assert when hitting EOS. r=kentuckyfriedtakahe (4e66e9d148)
- Bug 1226842: Error rather than asserting when encountering error in sample table. r=gerald (4bbe6cd7f2)
- Bug 1189992: Don't assume the last chunk always contains the sample we're looking for. r=gerald (4cf3a3f63a)
- Bug 1225703 - Update in-tree libcubeb. r=padenot (49ce53e3c8)
- Bug 1203449 - Remove OutputStreamListener from DecodedStream.cpp. r=roc. (24421a02e1)
- Bug 1207915 - Apply the fix of bug 1052206 to DecodedStream. r=roc. (c73a3aecc6)
- Bug 1218311 - Port the fix of bug 1193614 to VideoSink. r=cpearce. (cbbe3dc1ab)
- Bug 1230338 - Record video frames dropped by the compositor, or while flushing during skip-to-keyframe. r=jya (edf53548d3)
- Bug 1230882. Part 1 - destroy DecodedStreamData properly when dispatch fails. r=roc. (adb3c05671)
- Bug 1230004. Part 1 - cache data in MDSM so it won't need to ask MediaDecoder. r=cpearce. (728c6b3e82)
- Bug 1219163. Part 1 - Remove unused functions from AbstractMediaDecoder. r=jya. (f1dba3d50c)
- Bug 1219163. Part 2 - Move some functions that are never called from the interface of AbstractMediaDecoder down the class hierarchy. r=jya. (c0fc72790f)
- Bug 1226569. Part 1 - Use MediaEventSource to publish MetadataLoaded and FirstFrameLoaded events. r=jya. (76195567a5)
- Bug 1226569. Part 2 - assert functions that should never be called after shutdown. r=jya. (672df604e5)
- Bug 1227797 - Use MediaEventSource to publish playback events for MDSM. r=jya. (4a3fda2f34)
- Bug 1228923 - Merge some MediaEventSource for MDSM. r=jya. (31929f5777)
- Bug 1228939 - 1. add mSeekable to MediaInfo. 2. use MediaEventSource to notify the decoder when the media is not seekable. 3. remove unused code. r=jya. (38fb6d1099)
- Bug 1230004. Part 2 - have MDSM::BeginShutdown return a promise and remove MDSM::mDecoder. r=cpearce. (0f45558e15)
- add some disabled EME stuff (c4fa7e65be)
- Bug 873438 - Implement IAccessible2_2::accessibleWithCaret, r=yzen (9fb71ce7f3)
- Bug 1159872 - Make IAccessible2::Get_States once again return S_OK when it detects a defunct accessible. r=surkov (91ad6d1798)
- bug 606080 - on windows give accessibles a unique 32 bit id r=surkov (848c16f16c)
- Bug 1220897 - fix IAccessible2::get_accessibleWithCaret, r=tbsaunde (492790b4e1)
- Bug 1225298 - Use GCHashMap for UniqueIdMap, r=terrence (ee896fb807)
- Bug 1227567 - Optimise module namespace imports in Ion where we have type information r=shu (19fdf97bc3)
- Bug 1230337 - Fix TypedArrayObject::isNeutered to stop calling typed arrays using inline storage neutered. (r=Waldo) (2838830319)
- Bug 1055472 - Part 1: Factor out GetPrototypeFromConstructor and use it in existing object creation paths. (r=Waldo) (a26c188064)
- Bug 1055472 - Part 2: Make the Function constructor properly subclassable. (r=Waldo) (e2ff4b48d4)
- Bug 1055472 - Part 2b: Make Function.prototype.bind play nicely with subclassed functions. (r=Waldo, shoutouts for good flyby by evilpie) (59b347ced4)
- Bug 1055472 - Part 3: Make the Object constructor properly subclassable. (r=Waldo) (aa23b6c005)
- Bug 1055472 - Part 4: Make the Boolean constructor properly subclassable. (r=Waldo) (6ff68c5198)
- Bug 1055472 - Part 5: Make the various Error constructors properly subclassable. (r=Waldo) (7e36f26bcd)
- Bug 1055472 - Part 6: Make the Number constructor properly subclassable. (r=Waldo) (5b081bb54c)
- Bug 1055472 - Part 7: Make the Date constructor properly subclassable. (r=Waldo) (a841523521)
- Bug 1055472 - Part 8 prelim: Rename InitializeRegExp to RegExpObject::initFromAtom for readability. (r=Waldo) (053ce20768)
- Bug 1055472 - Part 8: Make the RegExp constructor properly subclassable. (r=Waldo) (a522ecca25)
- Bug 1055472 - Part 9: Make the Map constructor properly subclassable. (r=Waldo) (a29bb1408a)
- Bug 1055472 - Part 10: Make the Set constructor properly subclassable. (r=Waldo) (82befdc508)
- Bug 1055472 - Part 11: Make the WeakMap constructor properly subclassable. (r=Waldo) (0ea50757d4)
- Bug 1055472 - Part 12: Mae the WeakSet constructor properly subclassable. (r=Waldo) (6ba67d4fe6)
- Bug 1055472 - Part 13: Make the ArrayBuffer constructor properly subclassable. (r=Waldo) (7811dadf92)
- Bug 1055472 - Part 14: Make the various TypedArray constructors properly subclassable. (r=Waldo, r=bhackett) (6bc10f8ec1)
- Bug 1055472 - Part 15: Make the DataView constructor properly subclassable. (r=jorendorff, r=bhackett) (b2b6aef1f6)
- Bug 1055472 - Part 16: Make the String constructor properly subclassable. (r=Waldo) (6bed2e45bc)
- Bug 1055472 - Part 17: Make the Array constructor properly subclassable. (r=jorendorff, r=bhackett, r=terrence) (d301fe9814)
- Bug 1055472 - Part 18: Incorportate arai's test into the subclassing suite. (r=me) (cfbc97738a)
- Bug 1226416 - Expose a method to get a node's set of immediately dominated nodes in the dominator tree; r=bz,sfink (8226985fb6)
- Bug 1199422 - Stop pattern matching class-constructors in String#replace and Array#sort. r=efaust (58b56ad143)
- Bug 1206308 - include nsprpub and zlib for Windows, plus minor enhancements to make-source-package.sh, r=sstangl (e933f3a27a)
- Bug 1229729 - Make make-source-package.sh work on OS X. r=sfink (d55f9bc552)
- Bug 1221747 - Make JS_vsnprintf() return a value that indicates failure if we reach the buffer limit r=nbp (1bc82ccafd)
- Bug 1227385 - Properly propagate $DIST from top-level after bug 1224490. r=mshal (ee4fc81981)
- Bug 1200304 - Move stumbling code from gonkgps to MozStumbler.cpp. r=jdm (89aea7211c)
- Bug 1207266 - turn off b2g stumbler if no RIL. r=jdm (ae42aeb5a2)
- Bug 1228947 - Replace mfbt/Constants.h with math.h. r=roc (d56e2bbe2f)
- Bug 1176261 - Discard gps with impossibly tiny accuracy. r=jaliu (93a4114ac7)
- Bug 772750 - Get mobile connection technology from MobileConnectionService. r=garvank,echen (742d68becb)
- Bug 1168068 - GonkGPSGeolocationProvider should use mRilDataServiceId in SetReferenceLocation. r=garvank (b07ee26cc4)
- Bug 1227385 - Avoid make variable references in VISIBILITY_FLAGS and STL_FLAGS. r=mshal (e3e7fc9d99)
- Bug 1225298 - Use GCHashSet for atoms table, r=terrence (4bce42238c)
- Bug 1225298 - Use GCHashSet for InnerViewTable, r=terrence (e8da10443c)
- Bug 1203297 - Fix one last bit of unified bustage; r=bbouvier (7395aca8da)
- Bug 1228340: Rename JitOptions into DefaultJitOptions and get rid of the js_ prefix for the instance; r=h4writer (19777f9a49)
- Bug 1227190 part 1. change PrepareScriptEnvironmentAndInvoke to return void, not bool, to make it clearer that it reports exceptions for you. r=jorendorff (7525efb3ad)
- Bug 1227190 part 2. Change PrepareScriptEnvironmentAndInvoke to take a JSContext*, not a JSRuntime*. r=jorendorff (01b69ccab1)
- Bug 1227190 part 3. Make debugger error reporting play nice with the embedding taking ownership of error reporting. r=jorendorff (008195b9a3)
- Bug 1221600 - Tweak comments about standard internal methods in jsobj.h, jsfriendapi.h. r=efaust. (c1a4b0e2bf)
- Bug 1223372 - Handle ToWindowProxyIfWindow returning a dead wrapper in PrepareForWrapping. r=bholley (0b3dd8084b)
- Bug 1197095 - ensure that ForOfIterator does not pass arguments to next calls; r=evilpie (29acf9b12d)
- Bug 1197094 - ForOfIterator calls ToObject on iterable; r=evilpie (533dd11111)
- Bug 1216379 - Throw less cryptic error message when using a non-iterable in a for...of loop. r=jorendorff (ae7d4c3734)
- Bug 1230010 - OdinMonkey: MIPS: Make immediate patching shared. r=luke (97b5650d3a)
- Bug 1131759 - Atomicize SharedScriptData::marked. (r=terrence) (bafe64c080)
- Bug 1229579 - Make class members properly XDR-able. (r=billm) (d95743694f)
- Bug 1227642: Make data a ScopedJSFreePtr in js::detail::CopyScript to ensure it doesn't leak; r=jonco (db8bcafc09)
- Bug 1227287 - Fix minor bug with f.arguments and lazy arguments. r=luke (9c1cc5aaad)
- Bug 1228947 - mingw fixup. (3216b8df21)
- Bug 1231163 - Don't assume the RNG's been initialized by a prior call to Math.random, when a call to Math.random is being inlined. (A method can be inlined once its identity has been guarded against, but mere identity can be established without the method having been called.) r=jwalden (d7e2235e63)
- crash stuff (12dfaea226)
- Bug 1218027 - "--with-system-icu fails: js/src/jsapi.cpp:69: error: visibility does not match previous declaration". r=mh+mozilla (5929f4280c)
- Bug 1229740 - add <array> to the list of wrapped system headers; r=glandium (990a0a22ab)
- Bug 1192312 - Use MediaCodecSource for camera recording since gonkL r=aosmond (f68121e622)
- Bug 1210293 - Remove legacy check from the XPIDL HTMLInputElement::SetUserInput. r=bz (f70fbfd8ea)
- Bug 1220323 - Enable FasterMake backend by default for all apps. r=gps (7421f68979)
- Fixup for b2g bustage from bug 1164921. r=me (6207a28039)
- Bug 1209391 - Remove build/unix/uniq.py. r=mshal (d757f44938)
- Bug 1192233 - delete check for sys/cdefs.h from configure; r=mshal (ca2709a478)
- Bug 1191816 - don't check for sys/int_types.h in configure; r=mshal (3f77884338)
- Bug 1182565 - Disable sandboxing on Linux Thread Sanitizer builds. r=kang (5dcd521ca1)
- missing bits of Bug 1157768 - Build files and config headers for libav fft (5ab19c5158)
- Bug 1149279 - Remove unused build config variable NSS_NO_LIBPKIX. r=gps (0abbcd89c3)
- Bug 1161238 - Remove --disable-logging. r=gps (b896b8f77d)
- Bug 1167201 - Make absence of gconf developer packages a fatal error rather than disabling gconf automatically in the build system. This is necessary because if gconf is disabled, many tests don't work because they use gconf to detect linux. r=glandium (4def45dc4f)
- bug 1220037 pack and unpack Nyquist for MOZ_LIBAV_FFT r=padenot (23e4e277ff)
- bug 1220041 remove unnecessary temp buffer and copy with libav FFT r=padenot (20df464d7b)
- bug 1220041 scale when copying for inverse FFT to avoid iterating twice r=padenot (d9c11db7e2)
- bug 1223520 avoid leaking NaNs to and from the otherwise unused imaginary frequency components r=padenot (0ff120a534)
- bug 1188704 remove now unused PerformInverseFFT() variation r=rillian (b82b4d459a)
- bug 1188704 add accessor functions for setting frequency components for inverse FFT r=rillian (c8357b6268)
- style (3b9fc23a34)
- Bug 1173016 - Properly refcount the inner PeriodicWave object. r=karlt (634e09bb79)
- bug 1187785 reverse tableInterpolationFactor to make it consistent with documentation r=rillian (928fa29f6c)
- bug 1188704 redefine halfSize as fftSize / 2 r=rillian (1cf36a13e3)
- bug 1188704 trim unnecessary extra basic waveform coeffient r=rillian (c33983684a)
- bug 1188704 simplify culling of partials r=rillian (484932e54e)
- bug 1188704 combine scaling with copying r=rillian (7e5d3f9c7d)
- bug 1188704 limit number of Fourier coefficients used to halfSize earlier r=rillian (abba4f31bb)
- bug 1188704 use existing FFTBlock arrays instead of allocating and copying and scaling r=rillian (74b58f3bc3)
- warnings (cf6c0840e8)
- Bug 1171436 - support at least 8192 elements for PeriodicWave r=padenot (7ddeb3ec3f)
- bug 1221831 remove unused Reverb::reset() r=padenot (913849471f)
- bug 1221831 remove unused Reverb::latencyFrames() r=padenot (767744e87c)
- bug 1221830 use WEBAUDIO_BLOCK_SIZE constant in Reverb methods r=padenot (38dc6b0a92)
- bug 1221831 use initial input buffer offset to control when convolver stages perform their FFT r=padenot (4f594ded7a)
- bug 1221831 remove now-unnecessary m_preDelayBuffer r=padenot (af5b36efdc)
- bug 1221830 use WEBAUDIO_BLOCK_SIZE slice size for background convolver thread r=padenot (0d6dbe1daa)
- bug 1221830 double maximum realtime convolver stage size to 2048 r=padenot (1cf7698f2d)
- bug 1221830 use WEBAUDIO_BLOCK_SIZE constant in FFTConvolver r=padenot (955e902db8)
- bug 1221833 reduce FFTConvolver latency by one block r=padenot (024b13396a)
- bug 1221833 replace initial direct convolution stage with fft r=padenot (70319e951b)
- bug 1221833 remove first two half-block-size convolver stages r=padenot (2709910848)
- bug 1221833 remove now-unused direct convolver r=padenot (d0cfb712af)
- bug 1221836 return output pointer from FFTConvolver::process() to save a buffer copy r=padenot (4bf6c5e6c8)
- Bug 1196608 - Link liblgpllibs against mozglue. r=mshal (3212491e68)
- missing bit of 1157768 (3d6844b56e)
- bug 1224102 reduce the size of the final FFT convolution stage if possible r=padenot (f1fbd7f04d)
- Bug 1192587 - Build media/libav in unified mode. r=qDot (77dd2810e3)
- Bug 1228230 - Rely more on top-level configure auto-detection. r=qdot (8add5f4014)
- Bug 1261414 - Don't build libav with sysctl on Unix (it's not used anyway). r=glandium (195c3ddba4)
- Bug 1222405 - (part 1) Store PeriodicWave components for later use r=padenot (a993b3303d)
- Bug 1222405 - (part 2) Build band limited tables after fundamental frequency is known r=padenot (d350f2e201)
- Bug 1222405 - (part 3) Build band limited tables lazily r=padenot (0dc6f013fc)
This commit is contained in:
2023-05-09 11:34:56 +08:00
parent 967126c55b
commit 9855d648fe
255 changed files with 5079 additions and 2547 deletions
+25 -3
View File
@@ -18,6 +18,7 @@
#include "nsIAccessibleTypes.h"
#include "mozilla/a11y/PDocAccessible.h"
#include "Relation.h"
#include "nsAccessibilityService.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
@@ -364,7 +365,7 @@ ia2Accessible::get_states(AccessibleStates* aStates)
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
if (acc->IsDefunct()) {
*aStates = IA2_STATE_DEFUNCT;
return CO_E_OBJNOTCONNECTED;
return S_OK;
}
uint64_t state;
@@ -505,7 +506,7 @@ ia2Accessible::get_uniqueID(long* aUniqueID)
return E_INVALIDARG;
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
*aUniqueID = - reinterpret_cast<intptr_t>(acc->UniqueID());
*aUniqueID = AccessibleWrap::GetChildIDFor(acc);
return S_OK;
A11Y_TRYBLOCK_END
@@ -661,7 +662,28 @@ ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible,
*aAccessible = nullptr;
*aCaretOffset = -1;
return E_NOTIMPL;
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
int32_t caretOffset = -1;
Accessible* accWithCaret = SelectionMgr()->AccessibleWithCaret(&caretOffset);
if (acc->Document() != accWithCaret->Document())
return S_FALSE;
Accessible* child = accWithCaret;
while (!child->IsDoc() && child != acc)
child = child->Parent();
if (child != acc)
return S_FALSE;
*aAccessible = static_cast<IAccessible2*>(
static_cast<AccessibleWrap*>(accWithCaret));
(*aAccessible)->AddRef();
*aCaretOffset = caretOffset;
return S_OK;
A11Y_TRYBLOCK_END
}
+72 -4
View File
@@ -56,16 +56,48 @@ const uint32_t USE_ROLE_STRING = 0;
static gAccessibles = 0;
#endif
#ifdef _WIN64
IDSet AccessibleWrap::sIDGen;
static const uint32_t kNoID = 0;
#endif
static const int32_t kIEnumVariantDisconnected = -1;
////////////////////////////////////////////////////////////////////////////////
// AccessibleWrap
////////////////////////////////////////////////////////////////////////////////
AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
Accessible(aContent, aDoc)
#ifdef _WIN64
, mID(kNoID)
#endif
{
}
AccessibleWrap::~AccessibleWrap()
{
#ifdef _WIN64
if (mID != kNoID)
sIDGen.ReleaseID(mID);
#endif
}
ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
void
AccessibleWrap::Shutdown()
{
#ifdef _WIN64
if (mID != kNoID)
static_cast<DocAccessibleWrap*>(mDoc)->RemoveID(mID);
#endif
Accessible::Shutdown();
}
//-----------------------------------------------------
// IUnknown interface methods - see iunknown.h for documentation
//-----------------------------------------------------
@@ -1255,10 +1287,23 @@ AccessibleWrap::GetChildIDFor(Accessible* aAccessible)
// so that the 3rd party application can call back and get the IAccessible
// the event occurred on.
// Yes, this means we're only compatibible with 32 bit
// MSAA is only available for 32 bit windows, so it's okay
// XXX: bug 606080
return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0;
#ifdef _WIN64
if (!aAccessible || !aAccessible->Document())
return 0;
uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID;
if (*id != kNoID)
return *id;
*id = sIDGen.GetID();
DocAccessibleWrap* doc =
static_cast<DocAccessibleWrap*>(aAccessible->Document());
doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
return *id;
#else
return - reinterpret_cast<intptr_t>(aAccessible);
#endif
}
HWND
@@ -1308,6 +1353,25 @@ AccessibleWrap::NativeAccessible(Accessible* aAccessible)
return static_cast<IDispatch*>(msaaAccessible);
}
#ifdef _WIN64
static Accessible*
GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID)
{
Accessible* child = static_cast<DocAccessibleWrap*>(aDoc)->GetAccessibleByID(aID);
if (child)
return child;
uint32_t childDocCount = aDoc->ChildDocumentCount();
for (uint32_t i = 0; i < childDocCount; i++) {
child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID);
if (child)
return child;
}
return nullptr;
}
#endif
Accessible*
AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
{
@@ -1343,7 +1407,11 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
DocAccessible* document = Document();
Accessible* child =
#ifdef _WIN64
GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal));
#else
document->GetAccessibleByUniqueIDInSubtree(uniqueID);
#endif
// If it is a document then just return an accessible.
if (IsDoc())
+11 -3
View File
@@ -15,6 +15,7 @@
#include "ia2AccessibleHyperlink.h"
#include "ia2AccessibleValue.h"
#include "mozilla/a11y/ProxyAccessible.h"
#include "mozilla/a11y/IDSet.h"
#ifdef __GNUC__
// Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings
@@ -33,8 +34,7 @@ class AccessibleWrap : public Accessible,
public ia2AccessibleValue
{
public: // construction, destruction
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
Accessible(aContent, aDoc) { }
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
@@ -152,6 +152,7 @@ public: // construction, destruction
// Accessible
virtual nsresult HandleAccEvent(AccEvent* aEvent);
virtual void Shutdown() override;
// Helper methods
static int32_t GetChildIDFor(Accessible* aAccessible);
@@ -176,7 +177,11 @@ public: // construction, destruction
static IDispatch* NativeAccessible(Accessible* aAccessible);
protected:
virtual ~AccessibleWrap() { }
virtual ~AccessibleWrap();
#ifdef _WIN64
uint32_t mID;
#endif
/**
* Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it.
@@ -185,6 +190,9 @@ protected:
static ITypeInfo* gTypeInfo;
#ifdef _WIN64
static IDSet sIDGen;
#endif
enum navRelations {
NAVRELATION_CONTROLLED_BY = 0x1000,
@@ -34,12 +34,30 @@ public:
// DocAccessible
virtual void* GetNativeWindow() const;
/**
* Manage the mapping from id to Accessible.
*/
#ifdef _WIN64
void AddID(uint32_t aID, AccessibleWrap* aAcc)
{ mIDToAccessibleMap.Put(aID, aAcc); }
void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
AccessibleWrap* GetAccessibleByID(uint32_t aID) const
{ return mIDToAccessibleMap.Get(aID); }
#endif
protected:
// DocAccessible
virtual void DoInitialUpdate();
protected:
void* mHWND;
/*
* This provides a mapping from 32 bit id to accessible objects.
*/
#ifdef _WIN64
nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
#endif
};
} // namespace a11y
+126
View File
@@ -0,0 +1,126 @@
/* -*- 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/. */
/**
* A class to generate unique IDs in the range [ - 2^31, 0 )
*/
#ifndef MOZILLA_A11Y_IDSet_h_
#define MOZILLA_A11Y_IDSet_h_
#include "mozilla/Attributes.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/SplayTree.h"
namespace mozilla {
namespace a11y {
/**
* On windows an accessible's id must be a negative 32 bit integer. It is
* important to support recycling arbitrary IDs because accessibles can be
* created and destroyed at any time in the life of a page. IDSet provides 2
* operations: generate an ID in the range [ - 2^31, 0 ), and release an ID so
* it can be allocated again. Allocated ID are tracked by a sparse bitmap
* implemented with a splay tree. Nodes in the tree are keyed by the upper N
* bits of the bitwise negation of the ID, and the node contains a bitmap
* tracking the allocation of 2^(32 - N) IDs.
*/
class IDSet
{
public:
MOZ_CONSTEXPR IDSet() : mBitSet(), mIdx(0) {}
/**
* Return a new unique id.
*/
uint32_t GetID()
{
uint32_t idx = mIdx;
while (true) {
BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx));
if (elt->mBitvec[0] != UINT64_MAX) {
uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]);
elt->mBitvec[0] |= (1ull << i);
mIdx = idx;
return ~(elt->mIdx * bitsPerElt + i);
}
if (elt->mBitvec[1] != UINT64_MAX) {
uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]);
elt->mBitvec[1] |= (1ull << i);
mIdx = idx;
return ~(elt->mIdx * bitsPerElt + bitsPerWord + i);
}
idx++;
if (idx > sMaxIdx) {
idx = 0;
}
if (idx == mIdx) {
MOZ_CRASH("used up all the available ids");
}
}
}
/**
* Free a no longer required id so it may be allocated again.
*/
void ReleaseID(uint32_t aID)
{
aID = ~aID;
MOZ_ASSERT(aID < static_cast<uint32_t>(INT32_MAX));
uint32_t idx = aID / bitsPerElt;
mIdx = idx;
BitSetElt* elt = mBitSet.find(BitSetElt(idx));
MOZ_ASSERT(elt);
uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord;
elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord));
if (elt->mBitvec[0] == 0 && elt->mBitvec[1] == 0) {
delete mBitSet.remove(*elt);
}
}
private:
static const unsigned int wordsPerElt = 2;
static const unsigned int bitsPerWord = 64;
static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord;
static const uint32_t sMaxIdx = INT32_MAX / bitsPerElt;
struct BitSetElt : mozilla::SplayTreeNode<BitSetElt>
{
explicit BitSetElt(uint32_t aIdx) :
mIdx(aIdx)
{ mBitvec[0] = mBitvec[1] = 0; }
uint64_t mBitvec[wordsPerElt];
uint32_t mIdx;
static int compare(const BitSetElt& a, const BitSetElt& b)
{
if (a.mIdx == b.mIdx) {
return 0;
}
if (a.mIdx < b.mIdx) {
return -1;
}
return 1;
}
};
SplayTree<BitSetElt, BitSetElt> mBitSet;
uint32_t mIdx;
};
}
}
#endif
+1
View File
@@ -12,6 +12,7 @@ EXPORTS.mozilla.a11y += [
'AccessibleWrap.h',
'Compatibility.h',
'HyperTextAccessibleWrap.h',
'IDSet.h',
]
UNIFIED_SOURCES += [
-1
View File
@@ -23,7 +23,6 @@ MOZ_SERVICES_METRICS=1
MOZ_WEBSMS_BACKEND=1
MOZ_NO_SMART_CARDS=1
MOZ_APP_STATIC_INI=1
NSS_NO_LIBPKIX=1
NSS_DISABLE_DBM=1
MOZ_NO_EV_CERTS=1
MOZ_DISABLE_EXPORT_JS=1
-10
View File
@@ -1,10 +0,0 @@
#! /usr/bin/env python
# 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/.
'''Prints the given arguments in sorted order with duplicates removed.'''
import sys
print(' '.join(sorted(set(sys.argv[1:]))))
+5 -1
View File
@@ -10,5 +10,9 @@
#
# Any library added here should also be reflected in the about:license page.
SharedLibrary('lgpllibs')
GeckoSharedLibrary('lgpllibs', linkage=None)
SHARED_LIBRARY_NAME = 'lgpllibs'
if CONFIG['MOZ_LIBAV_FFT']:
DIRS += ['/media/libav']
DEFFILE = SRCDIR + '/lgpllibs.def'
+3
View File
@@ -183,6 +183,7 @@ app/Message.h
app/MessageRunner.h
arpa/inet.h
arpa/nameser.h
array
asm/page.h
asm/sigcontext.h
asm/signal.h
@@ -752,6 +753,7 @@ media/stagefright/foundation/hexdump.h
media/stagefright/MediaBuffer.h
media/stagefright/MediaBufferGroup.h
media/stagefright/MediaCodec.h
media/stagefright/MediaCodecSource.h
media/stagefright/MediaDefs.h
media/stagefright/MediaErrors.h
media/stagefright/MediaExtractor.h
@@ -1352,6 +1354,7 @@ xlocale.h
#ifdef MOZ_SHARED_ICU
unicode/locid.h
unicode/numsys.h
unicode/timezone.h
unicode/ucal.h
unicode/uchar.h
unicode/uclean.h
+145 -93
View File
@@ -338,6 +338,7 @@ if test -n "$gonkdir" ; then
AC_DEFINE(HAVE_PTHREADS)
MOZ_CHROME_FILE_FORMAT=omni
direct_nspr_config=1
android_cxx_stl=mozstlport
else
MOZ_ANDROID_NDK
@@ -522,6 +523,7 @@ case "$target" in
AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
AC_DEFINE(_USE_MATH_DEFINES) # Otherwise MSVC's math.h doesn't #define M_PI.
if test "$_CC_MAJOR_VERSION" = "18" -a "$_CC_BUILD_VERSION" -ge "30723"; then
_CC_SUITE=12
@@ -652,7 +654,7 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
fi
if test "$WRAP_STL_INCLUDES" = "1"; then
STL_FLAGS='-I$(DIST)/stl_wrappers'
STL_FLAGS="-I${DIST}/stl_wrappers"
fi
CFLAGS="$CFLAGS -D_HAS_EXCEPTIONS=0"
CXXFLAGS="$CXXFLAGS -D_HAS_EXCEPTIONS=0"
@@ -2530,7 +2532,7 @@ if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then
VISIBILITY_FLAGS='-fvisibility=hidden -fvisibility-inlines-hidden'
;;
*)
VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(MOZILLA_DIR)/config/gcc_hidden.h'
VISIBILITY_FLAGS="-I${DIST}/system_wrappers -include ${_topsrcdir}/config/gcc_hidden.h"
WRAP_SYSTEM_INCLUDES=1
;;
esac
@@ -2545,13 +2547,13 @@ case "${OS_TARGET}" in
WINNT|Darwin|Android)
;;
*)
STL_FLAGS='-I$(DIST)/stl_wrappers'
STL_FLAGS="-I${DIST}/stl_wrappers"
WRAP_STL_INCLUDES=1
;;
esac
AC_SUBST(WRAP_SYSTEM_INCLUDES)
AC_SUBST(VISIBILITY_FLAGS)
AC_SUBST_LIST(VISIBILITY_FLAGS)
dnl Checks for header files.
dnl ========================================================
@@ -2638,14 +2640,6 @@ if test "x$enable_dtrace" = "xyes"; then
fi
AC_SUBST(HAVE_DTRACE)
case $target in
*-aix4.3*|*-aix5*)
;;
*)
MOZ_CHECK_HEADERS(sys/cdefs.h)
;;
esac
MOZ_LINUX_PERF_EVENT
dnl Checks for libraries.
@@ -3649,7 +3643,7 @@ MOZ_DISABLE_PARENTAL_CONTROLS=1
ENABLE_SYSTEM_EXTENSION_DIRS=1
MOZ_BRANDING_DIRECTORY=
MOZ_OFFICIAL_BRANDING=
MC_OFFICIAL=1
MC_OFFICIAL=1
MOZ_FEEDS=1
MOZ_AUTH_EXTENSION=1
MOZ_RAW=
@@ -3733,11 +3727,9 @@ MOZ_TIME_MANAGER=
MOZ_SIMPLEPUSH=
MOZ_PAY=
MOZ_AUDIO_CHANNEL_MANAGER=
NSS_NO_LIBPKIX=
MOZ_CONTENT_SANDBOX=
MOZ_GMP_SANDBOX=
MOZ_SANDBOX=
MOZ_DEVTOOLS=
case "$target_os" in
mingw*)
@@ -3917,8 +3909,6 @@ fi
case "$MOZ_BUILD_APP" in
browser)
AC_DEFINE(MOZ_PHOENIX)
BUILD_BACKENDS="$BUILD_BACKENDS FasterMake"
;;
xulrunner)
@@ -3933,6 +3923,8 @@ b2g/dev)
;;
esac
BUILD_BACKENDS="$BUILD_BACKENDS FasterMake"
if test -n "$MOZ_B2GDROID"; then
AC_DEFINE(MOZ_B2GDROID)
fi
@@ -4421,42 +4413,42 @@ MOZ_ARG_ENABLE_STRING(ui-locale,
MOZ_UI_LOCALE=$enableval )
AC_SUBST(MOZ_UI_LOCALE)
dnl ========================================================
dnl = Vendor override
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(official-vendor,
[ --disable-official-vendor Disable the use of official vendor.],
MC_OFFICIAL=,
MC_OFFICIAL=1)
dnl ========================================================
dnl = Trademarked Branding
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(official-branding,
[ --enable-official-branding
Enable official branding
Do not distribute builds with
--enable-official-branding unless you have
permission to use the name/logo per
http://www.palemoon.org/redist.shtml .],
[
dnl ========================================================
dnl = Vendor override
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(official-vendor,
[ --disable-official-vendor Disable the use of official vendor.],
MC_OFFICIAL=,
MC_OFFICIAL=1)
dnl ========================================================
dnl = Trademarked Branding
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(official-branding,
[ --enable-official-branding
Enable official branding
Do not distribute builds with
--enable-official-branding unless you have
permission to use the name/logo per
http://www.palemoon.org/redist.shtml .],
[
if test -z "$MOZ_OFFICIAL_BRANDING_DIRECTORY"; then
AC_MSG_ERROR([You must specify MOZ_OFFICIAL_BRANDING_DIRECTORY to use --enable-official-branding.])
else
MOZ_BRANDING_DIRECTORY=${MOZ_OFFICIAL_BRANDING_DIRECTORY}
MOZ_OFFICIAL_BRANDING=1
MC_OFFICIAL=1
MOZ_OFFICIAL_BRANDING=1
MC_OFFICIAL=1
fi
], MOZ_OFFICIAL_BRANDING=)
AC_SUBST(MC_OFFICIAL)
if test -n "$MC_OFFICIAL"; then
AC_DEFINE(MC_OFFICIAL)
fi
AC_SUBST(MOZ_OFFICIAL_BRANDING)
if test -n "$MOZ_OFFICIAL_BRANDING"; then
AC_DEFINE(MOZ_OFFICIAL_BRANDING)
], MOZ_OFFICIAL_BRANDING=)
AC_SUBST(MC_OFFICIAL)
if test -n "$MC_OFFICIAL"; then
AC_DEFINE(MC_OFFICIAL)
fi
AC_SUBST(MOZ_OFFICIAL_BRANDING)
if test -n "$MOZ_OFFICIAL_BRANDING"; then
AC_DEFINE(MOZ_OFFICIAL_BRANDING)
fi
MOZ_ARG_WITH_STRING(branding,
@@ -4476,22 +4468,22 @@ fi
AC_SUBST(MOZ_BRANDING_DIRECTORY)
dnl ========================================================
dnl = Private Build
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(private-build,
[ --enable-private-build Enable private builds
This allows you to build with official
branding for personal use only using any
build time configuration.],
MC_PRIVATE_BUILD=1,
MC_PRIVATE_BUILD=)
AC_SUBST(MC_PRIVATE_BUILD)
if test -n "$MC_PRIVATE_BUILD"; then
AC_DEFINE(MC_PRIVATE_BUILD)
fi
dnl ========================================================
dnl = Private Build
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(private-build,
[ --enable-private-build Enable private builds
This allows you to build with official
branding for personal use only using any
build time configuration.],
MC_PRIVATE_BUILD=1,
MC_PRIVATE_BUILD=)
AC_SUBST(MC_PRIVATE_BUILD)
if test -n "$MC_PRIVATE_BUILD"; then
AC_DEFINE(MC_PRIVATE_BUILD)
fi
dnl ========================================================
dnl = Distribution ID
dnl ========================================================
@@ -4577,7 +4569,7 @@ then
MOZ_ARG_DISABLE_BOOL(gconf,
[ --disable-gconf Disable Gconf support ],
MOZ_ENABLE_GCONF=,
MOZ_ENABLE_GCONF=force)
MOZ_ENABLE_GCONF=1)
if test "$MOZ_ENABLE_GCONF"
then
@@ -4585,11 +4577,12 @@ then
MOZ_GCONF_LIBS=`$PKG_CONFIG --libs gobject-2.0`
MOZ_ENABLE_GCONF=1
],[
if test "$MOZ_ENABLE_GCONF" = "force"
if test -n "$MOZ_ENABLE_GCONF";
then
AC_MSG_ERROR([* * * Could not find gconf-2.0 ])
else
AC_MSG_WARN([Many automated tests will fail with --disable-gconf. See bug 1167201.])
fi
MOZ_ENABLE_GCONF=
])
fi
@@ -5993,6 +5986,74 @@ elif test -n "$MOZ_LIBJPEG_TURBO"; then
AC_MSG_WARN([No assembler or assembly support for libjpeg-turbo. Using unoptimized C routines.])
fi
dnl ========================================================
dnl = libav-fft configuration
dnl ========================================================
MOZ_LIBAV_FFT=
dnl Turn on libav-fft for 32-bit windows, and all 64-bit supported platforms.
dnl 32-bit linux/os x have text relocation issues.
case "$OS_ARCH:$CPU_ARCH" in
WINNT:x86)
MOZ_LIBAV_FFT=1
;;
*:x86_64)
MOZ_LIBAV_FFT=1
;;
esac
dnl Detect if we can use yasm to compile libav's assembly
if test -n "$MOZ_LIBAV_FFT"; then
AC_DEFINE(MOZ_LIBAV_FFT)
dnl Do we support libav-fft on this platform?
case "$OS_ARCH:$CPU_ARCH" in
Darwin:x86_64)
LIBAV_FFT_ASFLAGS="-f macho64 -rnasm -pnasm -D__x86_64__ -DPIC -DMACHO"
;;
WINNT:x86)
LIBAV_FFT_ASFLAGS="-f win32 -rnasm -pnasm -DPIC -DWIN32"
;;
WINNT:x86_64)
LIBAV_FFT_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64 -DMSVC"
;;
*:x86_64)
if $CC -E -dM -</dev/null | grep -q __ELF__; then
LIBAV_FFT_ASFLAGS="-f elf64 -rnasm -pnasm -D__x86_64__ -DPIC -DELF"
fi
;;
*)
AC_MSG_ERROR([libav's FFT routines are only available for 32-bit windows or 64-bit x86 based platforms.])
;;
esac
fi
if test -n "$LIBAV_FFT_ASFLAGS"; then
dnl If we're on an x86 or x64 system which supports libav-fft's asm routines
dnl check for Yasm, and error out if it doesn't exist or we have too old of a
dnl version.
LIBAV_FFT_AS=$YASM
if test -z "$LIBAV_FFT_AS" ; then
AC_MSG_ERROR([Yasm is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed. See https://developer.mozilla.org/en/YASM for more details.])
fi
dnl Check that we have the right yasm version. We require 1.0.1 or newer
dnl on Linux and 1.1 or newer everywhere else.
if test "$OS_ARCH" = "Linux" ; then
if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -eq "0" -a "$_YASM_RELEASE" -lt "1" \) ; then
AC_MSG_ERROR([Yasm 1.0.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed. See https://developer.mozilla.org/en/YASM for more details.])
fi
else
if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
AC_MSG_ERROR([Yasm 1.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed. See https://developer.mozilla.org/en/YASM for more details.])
fi
fi
elif test -n "$MOZ_LIBAV_FFT" -a "${CPU_ARCH}" != "arm"; then
dnl Warn if we're not building either libav or opendl-max optimized routines.
AC_MSG_WARN([No assembler or assembly support for libav-fft. Using unoptimized C routines.])
fi
dnl ========================================================
dnl = Enable compilation of specific extension modules
dnl ========================================================
@@ -6029,9 +6090,6 @@ if test `echo "$MOZ_EXTENSIONS" | grep -c gio` -ne 0; then
fi
AC_SUBST(MOZ_GIO_COMPONENT)
dnl Remove dupes
MOZ_EXTENSIONS=`$PYTHON ${srcdir}/build/unix/uniq.py ${MOZ_EXTENSIONS}`
dnl Ensure every extension exists, to avoid mostly-inscrutable error messages
dnl when trying to build a nonexistent extension.
for extension in $MOZ_EXTENSIONS; do
@@ -6257,17 +6315,17 @@ if test -n "$MOZ_NO_EV_CERTS"; then
fi
AC_SUBST(MOZ_NO_EV_CERTS)
dnl ========================================================
dnl = Disable libpkix
dnl ========================================================
if test -n "$NSS_NO_LIBPKIX"; then
AC_DEFINE(NSS_NO_LIBPKIX)
fi
AC_SUBST(NSS_NO_LIBPKIX)
dnl ========================================================
dnl = Sandboxing support
dnl ========================================================
if test -n "$MOZ_TSAN"; then
# Bug 1182565: TSan conflicts with sandboxing on Linux.
case $OS_TARGET in
Linux|Android)
MOZ_SANDBOX=
;;
esac
fi
MOZ_ARG_ENABLE_BOOL(sandbox,
[ --enable-sandbox Enable sandboxing support],
MOZ_SANDBOX=1,
@@ -6726,18 +6784,10 @@ elif test "$GNU_CC"; then
fi
dnl ========================================================
dnl = Disable runtime logging checks
dnl = Enable runtime logging
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(logging,
[ --disable-logging Disable logging facilities],
NS_DISABLE_LOGGING=1,
NS_DISABLE_LOGGING= )
if test "$NS_DISABLE_LOGGING"; then
AC_DEFINE(NS_DISABLE_LOGGING)
else
AC_DEFINE(MOZ_LOGGING)
AC_DEFINE(FORCE_PR_LOG)
fi
AC_DEFINE(MOZ_LOGGING)
AC_DEFINE(FORCE_PR_LOG)
dnl ========================================================
dnl = This will enable logging of addref, release, ctor, dtor.
@@ -7851,7 +7901,7 @@ AC_SUBST_LIST(MOZ_PIXMAN_LIBS)
# Check for headers defining standard int types.
if test -n "$COMPILE_ENVIRONMENT"; then
MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h)
MOZ_CHECK_HEADERS(stdint.h inttypes.h)
if test "${ac_cv_header_inttypes_h}" = "yes"; then
HAVE_INTTYPES_H=1
@@ -8038,8 +8088,6 @@ MOZ_ARG_ENABLE_STRING(necko-protocols,
fi
done],
NECKO_PROTOCOLS="$NECKO_PROTOCOLS_DEFAULT")
dnl Remove dupes
NECKO_PROTOCOLS=`$PYTHON ${srcdir}/build/unix/uniq.py ${NECKO_PROTOCOLS}`
AC_SUBST_SET(NECKO_PROTOCOLS)
for p in $NECKO_PROTOCOLS; do
AC_DEFINE_UNQUOTED(NECKO_PROTOCOL_$p)
@@ -8209,7 +8257,7 @@ AC_SUBST(MOZ_DEBUG)
AC_SUBST(MOZ_DEBUG_SYMBOLS)
AC_SUBST(MOZ_DEBUG_LDFLAGS)
AC_SUBST(WARNINGS_AS_ERRORS)
AC_SUBST(MOZ_EXTENSIONS)
AC_SUBST_SET(MOZ_EXTENSIONS)
AC_SUBST(MOZ_JPROF)
AC_SUBST(MOZ_INSTRUMENTS)
AC_SUBST(MOZ_CALLGRIND)
@@ -8611,6 +8659,9 @@ AC_SUBST(MOZ_INSTRUMENT_EVENT_LOOP)
AC_SUBST(MOZ_CODE_COVERAGE)
AC_SUBST(LIBJPEG_TURBO_AS)
AC_SUBST_LIST(LIBJPEG_TURBO_ASFLAGS)
AC_SUBST(MOZ_LIBAV_FFT)
AC_SUBST(LIBAV_FFT_AS)
AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
AC_SUBST(MOZ_PACKAGE_JSSHELL)
AC_SUBST(MOZ_FOLD_LIBS)
@@ -8956,6 +9007,7 @@ export MOZILLA_CENTRAL_PATH=$_topsrcdir
export STLPORT_CPPFLAGS
export STLPORT_LIBS
export JS_STANDALONE=no
export DIST
export MOZ_LINKER
export ZLIB_IN_MOZGLUE
@@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can get the set of immediately dominated nodes for any given
// node and that this forms a tree.
function run_test() {
var dominatorTree = saveHeapSnapshotAndComputeDominatorTree();
equal(typeof dominatorTree.getImmediatelyDominated, "function",
"getImmediatelyDominated should be a function");
// Do a traversal of the dominator tree.
//
// Note that we don't assert directly, only if we get an unexpected
// value. There are just way too many nodes in the heap graph to assert for
// every one. This test would constantly time out and assertion messages would
// overflow the log size.
var root = dominatorTree.root;
var seen = new Set();
var stack = [root];
while (stack.length > 0) {
var top = stack.pop();
if (seen.has(top)) {
ok(false,
"This is a tree, not a graph: we shouldn't have multiple edges to the same node");
}
seen.add(top);
if (seen.size % 1000 === 0) {
dumpn("Progress update: seen size = " + seen.size);
}
var newNodes = dominatorTree.getImmediatelyDominated(top);
if (Object.prototype.toString.call(newNodes) !== "[object Array]") {
ok(false, "getImmediatelyDominated should return an array for known node ids");
}
var topSize = dominatorTree.getRetainedSize(top);
var lastSize = Infinity;
for (var i = 0; i < newNodes.length; i++) {
if (typeof newNodes[i] !== "number") {
ok(false, "Every dominated id should be a number");
}
var thisSize = dominatorTree.getRetainedSize(newNodes[i]);
if (thisSize >= topSize) {
ok(false, "the size of children in the dominator tree should always be less than that of their parent");
}
if (thisSize > lastSize) {
ok(false,
"children should be sorted by greatest to least retained size, "
+ "lastSize = " + lastSize + ", thisSize = " + thisSize);
}
lastSize = thisSize;
stack.push(newNodes[i]);
}
}
ok(true, "Successfully walked the tree");
dumpn("Walked " + seen.size + " nodes");
do_test_finished();
}
+163
View File
@@ -41,7 +41,11 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#if ANDROID_VERSION >= 21
#include <media/stagefright/MediaCodecSource.h>
#else
#include <media/stagefright/OMXCodec.h>
#endif
#include <media/MediaProfiles.h>
#include <utils/Errors.h>
@@ -195,10 +199,21 @@ GonkRecorder::GonkRecorder()
GonkRecorder::~GonkRecorder() {
RE_LOGV("Destructor");
stop();
#if ANDROID_VERSION >= 21
if (mLooper != NULL) {
mLooper->stop();
}
#endif
}
status_t GonkRecorder::init() {
RE_LOGV("init");
#if ANDROID_VERSION >= 21
mLooper = new ALooper;
mLooper->setName("recorder_looper");
mLooper->start();
#endif
return OK;
}
@@ -851,6 +866,71 @@ status_t GonkRecorder::start() {
return status;
}
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
sp<MediaSource> GonkRecorder::createAudioSource() {
sp<AudioSource> audioSource =
new AudioSource(
mAudioSource,
mSampleRate,
mAudioChannels);
status_t err = audioSource->initCheck();
if (err != OK) {
RE_LOGE("audio source is not initialized");
return NULL;
}
sp<AMessage> format = new AMessage;
switch (mAudioEncoder) {
case AUDIO_ENCODER_AMR_NB:
case AUDIO_ENCODER_DEFAULT:
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
break;
case AUDIO_ENCODER_AMR_WB:
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
break;
case AUDIO_ENCODER_AAC:
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
break;
case AUDIO_ENCODER_HE_AAC:
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
format->setInt32("aac-profile", OMX_AUDIO_AACObjectHE);
break;
case AUDIO_ENCODER_AAC_ELD:
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD);
break;
default:
RE_LOGE("Unknown audio encoder: %d", mAudioEncoder);
return NULL;
}
int32_t maxInputSize;
CHECK(audioSource->getFormat()->findInt32(
kKeyMaxInputSize, &maxInputSize));
format->setInt32("max-input-size", maxInputSize);
format->setInt32("channel-count", mAudioChannels);
format->setInt32("sample-rate", mSampleRate);
format->setInt32("bitrate", mAudioBitRate);
if (mAudioTimeScale > 0) {
format->setInt32("time-scale", mAudioTimeScale);
}
sp<MediaSource> audioEncoder =
MediaCodecSource::Create(mLooper, format, audioSource);
mAudioSourceNode = audioSource;
if (audioEncoder == NULL) {
RE_LOGE("Failed to create audio encoder");
}
return audioEncoder;
}
#else
sp<MediaSource> GonkRecorder::createAudioSource() {
sp<AudioSource> audioSource =
new AudioSource(
@@ -919,6 +999,7 @@ sp<MediaSource> GonkRecorder::createAudioSource() {
return audioEncoder;
}
#endif
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
status_t GonkRecorder::startAACRecording() {
@@ -1340,6 +1421,87 @@ status_t GonkRecorder::setupCameraSource(
return OK;
}
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
status_t GonkRecorder::setupVideoEncoder(
sp<MediaSource> cameraSource,
int32_t videoBitRate,
sp<MediaSource> *source) {
source->clear();
sp<AMessage> format = new AMessage();
switch (mVideoEncoder) {
case VIDEO_ENCODER_H263:
format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
break;
case VIDEO_ENCODER_MPEG_4_SP:
format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
break;
case VIDEO_ENCODER_H264:
format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
break;
case VIDEO_ENCODER_VP8:
format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8);
break;
default:
CHECK(!"Should not be here, unsupported video encoding.");
break;
}
sp<MetaData> meta = cameraSource->getFormat();
int32_t width, height, stride, sliceHeight, colorFormat;
CHECK(meta->findInt32(kKeyWidth, &width));
CHECK(meta->findInt32(kKeyHeight, &height));
CHECK(meta->findInt32(kKeyStride, &stride));
CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
format->setInt32("width", width);
format->setInt32("height", height);
format->setInt32("stride", stride);
format->setInt32("slice-height", sliceHeight);
format->setInt32("color-format", colorFormat);
format->setInt32("bitrate", videoBitRate);
format->setInt32("frame-rate", mFrameRate);
format->setInt32("i-frame-interval", mIFramesIntervalSec);
if (mVideoTimeScale > 0) {
format->setInt32("time-scale", mVideoTimeScale);
}
if (mVideoEncoderProfile != -1) {
format->setInt32("profile", mVideoEncoderProfile);
}
if (mVideoEncoderLevel != -1) {
format->setInt32("level", mVideoEncoderLevel);
}
uint32_t flags = 0;
if (mIsMetaDataStoredInVideoBuffers) {
flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT;
}
sp<MediaCodecSource> encoder =
MediaCodecSource::Create(mLooper, format, cameraSource, flags);
if (encoder == NULL) {
RE_LOGE("Failed to create video encoder");
// When the encoder fails to be created, we need
// release the camera source due to the camera's lock
// and unlock mechanism.
cameraSource->stop();
return UNKNOWN_ERROR;
}
*source = encoder;
return OK;
}
#else
status_t GonkRecorder::setupVideoEncoder(
sp<MediaSource> cameraSource,
int32_t videoBitRate,
@@ -1426,6 +1588,7 @@ status_t GonkRecorder::setupVideoEncoder(
return OK;
}
#endif
status_t GonkRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) {
status_t status = BAD_VALUE;
+8 -4
View File
@@ -24,6 +24,10 @@
#include <camera/CameraParameters.h>
#include <utils/String8.h>
#include <system/audio.h>
#if ANDROID_VERSION >= 21
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#endif
#include "mozilla/RefPtr.h"
#include "GonkCameraHwMgr.h"
@@ -115,10 +119,10 @@ private:
MediaProfiles *mEncoderProfiles;
bool mStarted;
// Needed when GLFrames are encoded.
// An <IGraphicBufferProducer> pointer
// will be sent to the client side using which the
// frame buffers will be queued and dequeued
#if ANDROID_VERSION >= 21
sp<ALooper> mLooper;
#endif
sp<GonkCameraHardware> mCameraHw;
-4
View File
@@ -2204,10 +2204,6 @@ HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
NS_IMETHODIMP
HTMLInputElement::SetUserInput(const nsAString& aValue)
{
if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
if (mType == NS_FORM_INPUT_FILE)
{
Sequence<nsString> list;
@@ -16,13 +16,13 @@ interface nsIEditor;
*
*/
[scriptable, uuid(b33eb56c-3120-418c-892b-774b00c7dde8)]
[scriptable, uuid(3503de34-6631-4594-b7be-c36ff6a520c4)]
interface nsIDOMNSEditableElement : nsISupports
{
readonly attribute nsIEditor editor;
[noscript] readonly attribute nsIEditor editor;
// This is similar to set .value on nsIDOMInput/TextAreaElements, but
// handling of the value change is closer to the normal user input, so
// 'change' event for example will be dispatched when focusing out the
// element.
void setUserInput(in DOMString input);
[noscript] void setUserInput(in DOMString input);
};
@@ -46,7 +46,7 @@ interface nsIContentSecurityManager : nsISupports
* https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
*
* This method should only be used when the context of the URI isn't available
* since isSecureContext is preffered as it handles parent contexts.
* since isSecureContext is preferred as it handles parent contexts.
*
* This method returns false instead of throwing upon errors.
*/
+3 -86
View File
@@ -27,6 +27,9 @@ class MediaResource;
class ReentrantMonitor;
class VideoFrameContainer;
class MediaDecoderOwner;
#ifdef MOZ_EME
class CDMProxy;
#endif
typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
@@ -54,10 +57,6 @@ public:
// by currentSrc. Returns what was passed to Load(), if Load() has been called.
virtual MediaResource* GetResource() const = 0;
// Called by the decode thread to keep track of the number of bytes read
// from the resource.
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) = 0;
// Increments the parsed, decoded and dropped frame counters by the passed in
// counts.
// Can be called on any thread.
@@ -86,46 +85,16 @@ public:
NS_DispatchToMainThread(r);
}
// Set the media as being seekable or not.
virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
void DispatchSetMediaSeekable(bool aMediaSeekable)
{
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
this, &AbstractMediaDecoder::SetMediaSeekable, aMediaSeekable);
NS_DispatchToMainThread(r);
}
virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;
// Return true if the media layer supports seeking.
virtual bool IsTransportSeekable() = 0;
// Return true if the transport layer supports seeking.
virtual bool IsMediaSeekable() = 0;
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, MediaDecoderEventVisibility aEventVisibility) = 0;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) = 0;
// May be called by the reader to notify this decoder that the metadata from
// the media file has been read. Call on the decode thread only.
virtual void OnReadMetadataCompleted() = 0;
// Returns the owner of this media decoder. The owner should only be used
// on the main thread.
virtual MediaDecoderOwner* GetOwner() = 0;
// Called by the reader's MediaResource as data arrives over the network.
// Must be called on the main thread.
virtual void NotifyDataArrived() = 0;
// Set by Reader if the current audio track can be offloaded
virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
// Called from HTMLMediaElement when owner document activity changes
virtual void SetElementVisibility(bool aIsVisible) {}
// Stack based class to assist in notifying the frame statistics of
// parsed and decoded frames. Use inside video demux & decode functions
// to ensure all parsed and decoded frames are reported on all return paths.
@@ -152,58 +121,6 @@ public:
{ MOZ_CRASH("Forbidden method"); return NS_OK; }
};
class MetadataContainer
{
protected:
MetadataContainer(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility)
: mDecoder(aDecoder),
mInfo(aInfo),
mTags(aTags),
mEventVisibility(aEventVisibility)
{}
RefPtr<AbstractMediaDecoder> mDecoder;
nsAutoPtr<MediaInfo> mInfo;
nsAutoPtr<MetadataTags> mTags;
MediaDecoderEventVisibility mEventVisibility;
};
class MetadataEventRunner : public nsRunnable, private MetadataContainer
{
public:
MetadataEventRunner(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable)
: MetadataContainer(aDecoder, aInfo, aTags, aEventVisibility)
{}
NS_IMETHOD Run() override
{
mDecoder->MetadataLoaded(mInfo, mTags, mEventVisibility);
return NS_OK;
}
};
class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer
{
public:
FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable)
: MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr), aEventVisibility)
{}
NS_IMETHOD Run() override
{
mDecoder->FirstFrameLoaded(mInfo, mEventVisibility);
return NS_OK;
}
};
} // namespace mozilla
#endif
+1 -1
View File
@@ -17,7 +17,7 @@ namespace mozilla {
namespace dom {
class Blob;
}
} // namespace dom
/**
* Data is moved into a temporary file when it grows beyond
+3
View File
@@ -13,6 +13,8 @@ namespace mozilla {
// Threadsafe.
class FrameStatistics {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FrameStatistics);
FrameStatistics() :
mReentrantMonitor("FrameStats"),
mParsedFrames(0),
@@ -80,6 +82,7 @@ public:
}
private:
~FrameStatistics() {}
// ReentrantMonitor to protect access of playback statistics.
ReentrantMonitor mReentrantMonitor;
+90 -16
View File
@@ -496,9 +496,13 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
, mLogicalPosition(0.0)
, mDuration(std::numeric_limits<double>::quiet_NaN())
, mResourceCallback(new ResourceCallback())
#ifdef MOZ_EME
, mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__))
#endif
, mIgnoreProgressData(false)
, mInfiniteStream(false)
, mOwner(aOwner)
, mFrameStats(new FrameStatistics())
, mVideoFrameContainer(aOwner->GetVideoFrameContainer())
, mPlaybackStatistics(new MediaChannelStatistics())
, mPinnedForSeek(false)
@@ -600,12 +604,24 @@ MediaDecoder::Shutdown()
mResourceCallback->Disconnect();
#ifdef MOZ_EME
mCDMProxyPromiseHolder.RejectIfExists(true, __func__);
#endif
// This changes the decoder state to SHUTDOWN and does other things
// necessary to unblock the state machine thread if it's blocked, so
// the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
if (mDecoderStateMachine) {
mDecoderStateMachine->DispatchShutdown();
mTimedMetadataListener.Disconnect();
mMetadataLoadedListener.Disconnect();
mFirstFrameLoadedListener.Disconnect();
mOnPlaybackEvent.Disconnect();
mOnSeekingStart.Disconnect();
mOnMediaNotSeekable.Disconnect();
mDecoderStateMachine->BeginShutdown()->Then(
AbstractThread::MainThread(), __func__, this,
&MediaDecoder::FinishShutdown, &MediaDecoder::FinishShutdown);
}
// Force any outstanding seek and byterange requests to complete
@@ -629,6 +645,37 @@ MediaDecoder::~MediaDecoder()
MOZ_COUNT_DTOR(MediaDecoder);
}
void
MediaDecoder::OnPlaybackEvent(MediaEventType aEvent)
{
switch (aEvent) {
case MediaEventType::PlaybackStarted:
mPlaybackStatistics->Start();
break;
case MediaEventType::PlaybackStopped:
mPlaybackStatistics->Stop();
ComputePlaybackRate();
break;
case MediaEventType::PlaybackEnded:
PlaybackEnded();
break;
case MediaEventType::DecodeError:
DecodeError();
break;
case MediaEventType::Invalidate:
Invalidate();
break;
}
}
void
MediaDecoder::FinishShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mDecoderStateMachine->BreakCycles();
SetStateMachine(nullptr);
}
MediaResourceCallback*
MediaDecoder::GetResourceCallback() const
{
@@ -685,6 +732,17 @@ MediaDecoder::SetStateMachineParameters()
}
mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate);
mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::MetadataLoaded);
mFirstFrameLoadedListener = mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::FirstFrameLoaded);
mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackEvent);
mOnSeekingStart = mDecoderStateMachine->OnSeekingStart().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::SeekingStarted);
mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::OnMediaNotSeekable);
}
void
@@ -792,15 +850,13 @@ MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility)
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown) {
return;
}
MOZ_ASSERT(!mShuttingDown);
DECODER_LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
aInfo->HasAudio(), aInfo->HasVideo());
SetMediaSeekable(aInfo->mMediaSeekable);
mInfo = aInfo.forget();
ConstructMediaTracks();
@@ -869,10 +925,7 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility)
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown) {
return;
}
MOZ_ASSERT(!mShuttingDown);
DECODER_LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d mPlayState=%s mIsDormant=%d",
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
@@ -1499,13 +1552,6 @@ MediaDecoder::GetStateMachine() const {
return mDecoderStateMachine;
}
// Drop reference to state machine. Only called during shutdown dance.
void
MediaDecoder::BreakCycles() {
MOZ_ASSERT(NS_IsMainThread());
SetStateMachine(nullptr);
}
void
MediaDecoder::FireTimeUpdate()
{
@@ -1548,6 +1594,34 @@ MediaDecoder::CanPlayThrough()
return mDecoderStateMachine->IsRealTime() || GetStatistics().CanPlayThrough();
}
#ifdef MOZ_EME
RefPtr<MediaDecoder::CDMProxyPromise>
MediaDecoder::RequestCDMProxy() const
{
return mCDMProxyPromise;
}
void
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<CDMProxy> proxy = aProxy;
{
CDMCaps::AutoLock caps(aProxy->Capabilites());
if (!caps.AreCapsKnown()) {
RefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mCDMProxyPromiseHolder.ResolveIfExists(proxy, __func__);
});
caps.CallOnMainThreadWhenCapsAvailable(r);
return;
}
}
mCDMProxyPromiseHolder.ResolveIfExists(proxy, __func__);
}
#endif
#ifdef MOZ_RAW
bool
MediaDecoder::IsRawEnabled()
+61 -48
View File
@@ -184,6 +184,10 @@ destroying the MediaDecoder object.
#if !defined(MediaDecoder_h_)
#define MediaDecoder_h_
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "mozilla/Atomics.h"
#include "mozilla/MozPromise.h"
#include "mozilla/ReentrantMonitor.h"
@@ -218,6 +222,8 @@ namespace mozilla {
class VideoFrameContainer;
class MediaDecoderStateMachine;
enum class MediaEventType : int8_t;
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
// GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
#ifdef GetCurrentTime
@@ -438,7 +444,7 @@ public:
// Called as data arrives on the stream and is read into the cache. Called
// on the main thread only.
virtual void NotifyDataArrived() override;
void NotifyDataArrived();
// Return true if we are currently seeking in the media resource.
// Call on the main thread only.
@@ -459,17 +465,20 @@ protected:
// changed, this causes a durationchanged event to fire to the media
// element.
void UpdateEstimatedMediaDuration(int64_t aDuration) override;
public:
// Called from HTMLMediaElement when owner document activity changes
virtual void SetElementVisibility(bool aIsVisible) {}
// Set a flag indicating whether seeking is supported
virtual void SetMediaSeekable(bool aMediaSeekable) override;
void SetMediaSeekable(bool aMediaSeekable);
// Returns true if this media supports seeking. False for example for WebM
// files without an index and chained ogg files.
virtual bool IsMediaSeekable() final override;
bool IsMediaSeekable();
// Returns true if seeking is supported on a transport level (e.g. the server
// supports range requests, we are playing a file, etc.).
virtual bool IsTransportSeekable() override;
bool IsTransportSeekable();
// Return the time ranges that can be seeked into.
virtual media::TimeIntervals GetSeekable();
@@ -563,26 +572,6 @@ private:
// so recompute it. The monitor must be held.
virtual void UpdatePlaybackRate();
// Used to estimate rates of data passing through the decoder's channel.
// Records activity stopping on the channel.
void DispatchPlaybackStarted() {
RefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Start(); });
AbstractThread::MainThread()->Dispatch(r.forget());
}
// Used to estimate rates of data passing through the decoder's channel.
// Records activity stopping on the channel.
void DispatchPlaybackStopped() {
RefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
self->mPlaybackStatistics->Stop();
self->ComputePlaybackRate();
});
AbstractThread::MainThread()->Dispatch(r.forget());
}
// The actual playback rate computation. The monitor must be held.
void ComputePlaybackRate();
@@ -603,21 +592,6 @@ private:
// change. Call on the main thread only.
virtual void ChangeState(PlayState aState);
// May be called by the reader to notify this decoder that the metadata from
// the media file has been read. Call on the decode thread only.
void OnReadMetadataCompleted() override { }
// Called when the metadata from the media file has been loaded by the
// state machine. Call on the main thread only.
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility) override;
// Called when the first audio and/or video from the media file has been loaded
// by the state machine. Call on the main thread only.
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility) override;
// Called from MetadataLoaded(). Creates audio tracks and adds them to its
// owner's audio track list, and implies to video tracks respectively.
// Call on the main thread only.
@@ -662,9 +636,6 @@ private:
// position.
int64_t GetDownloadPosition();
// Drop reference to state machine. Only called during shutdown dance.
virtual void BreakCycles();
// Notifies the element that decoding has failed.
void DecodeError();
@@ -673,6 +644,16 @@ private:
MediaDecoderOwner* GetOwner() override;
#ifdef MOZ_EME
typedef MozPromise<RefPtr<CDMProxy>, bool /* aIgnored */, /* IsExclusive = */ true> CDMProxyPromise;
// Resolved when a CDMProxy is available and the capabilities are known or
// rejected when this decoder is about to shut down.
RefPtr<CDMProxyPromise> RequestCDMProxy() const;
void SetCDMProxy(CDMProxy* aProxy);
#endif
void EnsureTelemetryReported();
#ifdef MOZ_RAW
@@ -716,7 +697,7 @@ private:
MediaStatistics GetStatistics();
// Return the frame decode/paint related statistics.
FrameStatistics& GetFrameStatistics() { return mFrameStats; }
FrameStatistics& GetFrameStatistics() { return *mFrameStats; }
// Increments the parsed and decoded frame counters by the passed in counts.
// Can be called on any thread.
@@ -739,6 +720,12 @@ private:
protected:
virtual ~MediaDecoder();
// Called when the first audio and/or video from the media file has been loaded
// by the state machine. Call on the main thread only.
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility);
void SetStateMachineParameters();
static void DormantTimerExpired(nsITimer *aTimer, void *aClosure);
@@ -803,15 +790,30 @@ protected:
// Media data resource.
RefPtr<MediaResource> mResource;
// Amount of buffered data ahead of current time required to consider that
// Amount of buffered data ahead of current time required to consider that
// the next frame is available.
// An arbitrary value of 250ms is used.
static const int DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED = 250000;
private:
// Called when the metadata from the media file has been loaded by the
// state machine. Call on the main thread only.
void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility);
MediaEventSource<void>*
DataArrivedEvent() override { return &mDataArrivedEvent; }
void OnPlaybackEvent(MediaEventType aEvent);
void OnMediaNotSeekable()
{
SetMediaSeekable(false);
}
void FinishShutdown();
MediaEventProducer<void> mDataArrivedEvent;
// The state machine object for handling the decoding. It is safe to
@@ -825,8 +827,12 @@ private:
RefPtr<ResourceCallback> mResourceCallback;
protected:
#ifdef MOZ_EME
MozPromiseHolder<CDMProxyPromise> mCDMProxyPromiseHolder;
RefPtr<CDMProxyPromise> mCDMProxyPromise;
#endif
protected:
virtual void CallSeek(const SeekTarget& aTarget);
// Returns true if heuristic dormant is supported.
@@ -859,7 +865,7 @@ protected:
MediaDecoderOwner* const mOwner;
// Counters related to decode and presentation of frames.
FrameStatistics mFrameStats;
const RefPtr<FrameStatistics> mFrameStats;
const RefPtr<VideoFrameContainer> mVideoFrameContainer;
@@ -926,6 +932,13 @@ protected:
// A listener to receive metadata updates from MDSM.
MediaEventListener mTimedMetadataListener;
MediaEventListener mMetadataLoadedListener;
MediaEventListener mFirstFrameLoadedListener;
MediaEventListener mOnPlaybackEvent;
MediaEventListener mOnSeekingStart;
MediaEventListener mOnMediaNotSeekable;
protected:
// Whether the state machine is shut down.
Mirror<bool> mStateMachineIsShutdown;
@@ -1074,13 +1087,13 @@ private:
// 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
// by the MediaResource read functions.
void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override;
void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset);
// Called by nsChannelToPipeListener or MediaResource when the
// download has ended. Called on the main thread only. aStatus is
// the result from OnStopRequest.
void NotifyDownloadEnded(nsresult aStatus);
bool mTelemetryReported;
};
+8
View File
@@ -129,6 +129,14 @@ public:
// Called by the media decoder object, on the main thread,
// when the connection between Rtsp server and client gets lost.
virtual void ResetConnectionState() = 0;
#ifdef MOZ_EME
// Dispatches a "encrypted" event to the HTMLMediaElement, with the
// provided init data. Actual dispatch may be delayed until HAVE_METADATA.
// Main thread only.
virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) = 0;
#endif // MOZ_EME
};
} // namespace mozilla
+11 -4
View File
@@ -20,6 +20,7 @@
namespace mozilla {
class CDMProxy;
class MediaDecoderReader;
struct WaitForDataRejectValue
@@ -190,6 +191,10 @@ public:
// when to call SetIdle().
virtual void SetIdle() {}
#ifdef MOZ_EME
virtual void SetCDMProxy(CDMProxy* aProxy) {}
#endif
// 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
// available in the audio output.
@@ -234,10 +239,6 @@ public:
return &mBuffered;
}
// Indicates if the media is seekable.
// ReadMetada should be called before calling this method.
virtual bool IsMediaSeekable() = 0;
void DispatchSetStartTime(int64_t aStartTime)
{
RefPtr<MediaDecoderReader> self = this;
@@ -274,6 +275,9 @@ public:
return mTimedMetadataEvent;
}
// Notified by the OggReader during playback when chained ogg is detected.
MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
protected:
virtual ~MediaDecoderReader();
@@ -364,6 +368,9 @@ protected:
// Used to send TimedMetadata to the listener.
TimedMetadataEventProducer mTimedMetadataEvent;
// Notify if this media is not seekable.
MediaEventProducer<void> mOnMediaNotSeekable;
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
+93 -121
View File
@@ -62,7 +62,7 @@ using namespace mozilla::media;
#undef VERBOSE_LOG
#define LOG(m, l, x, ...) \
MOZ_LOG(m, l, ("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__))
MOZ_LOG(m, l, ("Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
#define DECODER_LOG(x, ...) \
LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
#define VERBOSE_LOG(x, ...) \
@@ -74,7 +74,7 @@ using namespace mozilla::media;
// when __VA_ARGS__ expands to nothing. This is a workaround for it.
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
#define DECODER_WARN(x, ...) \
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder.get(), ##__VA_ARGS__).get()))
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
// Certain constants get stored as member variables and then adjusted by various
// scale factors on a per-decoder basis. We want to make sure to avoid using these
@@ -199,7 +199,10 @@ static void InitVideoQueuePrefs() {
MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader,
bool aRealTime) :
mDecoder(aDecoder),
mDecoderID(aDecoder),
mFrameStats(&aDecoder->GetFrameStatistics()),
mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
mAudioChannel(aDecoder->GetAudioChannel()),
mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true)),
mWatchManager(this, mTaskQueue),
@@ -280,7 +283,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
// Dispatch initialization that needs to happen on that task queue.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<RefPtr<MediaDecoder>>(
this, &MediaDecoderStateMachine::InitializationTask, aDecoder);
mTaskQueue->Dispatch(r.forget());
InitVideoQueuePrefs();
@@ -305,6 +309,13 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
mMediaSink = CreateMediaSink(mAudioCaptured);
#ifdef MOZ_EME
mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then(
OwnerThread(), __func__, this,
&MediaDecoderStateMachine::OnCDMProxyReady,
&MediaDecoderStateMachine::OnCDMProxyNotReady));
#endif
}
MediaDecoderStateMachine::~MediaDecoderStateMachine()
@@ -320,25 +331,25 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
}
void
MediaDecoderStateMachine::InitializationTask()
MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
{
MOZ_ASSERT(OnTaskQueue());
// Connect mirrors.
mBuffered.Connect(mReader->CanonicalBuffered());
mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration());
mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
mPlayState.Connect(mDecoder->CanonicalPlayState());
mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
mVolume.Connect(mDecoder->CanonicalVolume());
mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia());
mPlaybackBytesPerSecond.Connect(mDecoder->CanonicalPlaybackBytesPerSecond());
mPlaybackRateReliable.Connect(mDecoder->CanonicalPlaybackRateReliable());
mDecoderPosition.Connect(mDecoder->CanonicalDecoderPosition());
mMediaSeekable.Connect(mDecoder->CanonicalMediaSeekable());
mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
mPlayState.Connect(aDecoder->CanonicalPlayState());
mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
mLogicallySeeking.Connect(aDecoder->CanonicalLogicallySeeking());
mVolume.Connect(aDecoder->CanonicalVolume());
mLogicalPlaybackRate.Connect(aDecoder->CanonicalPlaybackRate());
mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable());
mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
mMediaSeekable.Connect(aDecoder->CanonicalMediaSeekable());
// Initialize watchers.
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
@@ -368,7 +379,7 @@ MediaDecoderStateMachine::CreateAudioSink()
MOZ_ASSERT(self->OnTaskQueue());
return new DecodedAudioDataSink(
self->mAudioQueue, self->GetMediaTime(),
self->mInfo.mAudio, self->mDecoder->GetAudioChannel());
self->mInfo.mAudio, self->mAudioChannel);
};
return new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
}
@@ -384,8 +395,8 @@ MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured)
RefPtr<media::MediaSink> mediaSink =
new VideoSink(mTaskQueue, audioSink, mVideoQueue,
mDecoder->GetVideoFrameContainer(), mRealTime,
mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
mVideoFrameContainer, mRealTime,
*mFrameStats,
sVideoQueueSendToCompositorSize);
return mediaSink.forget();
}
@@ -887,19 +898,6 @@ MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample)
StopPrerollingVideo();
}
// Schedule the state machine to send stream data as soon as possible if
// the VideoQueue() is empty or contains one frame before the Push().
//
// The state machine threads requires a frame in VideoQueue() that is `in
// the future` to gather precise timing information. The head of
// VideoQueue() is always `in the past`.
//
// Schedule the state machine as soon as possible to render the video
// frame or delay the state machine thread accurately.
if (VideoQueue().GetSize() <= 2) {
ScheduleStateMachine();
}
// For non async readers, if the requested video sample was slow to
// arrive, increase the amount of audio we buffer to ensure that we
// don't run out of audio. This is unnecessary for async readers,
@@ -1058,7 +1056,7 @@ void MediaDecoderStateMachine::StopPlayback()
MOZ_ASSERT(OnTaskQueue());
DECODER_LOG("StopPlayback()");
mDecoder->DispatchPlaybackStopped();
mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped);
if (IsPlaying()) {
mMediaSink->SetPlaying(false);
@@ -1091,7 +1089,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
}
DECODER_LOG("MaybeStartPlayback() starting playback");
mDecoder->DispatchPlaybackStarted();
mOnPlaybackEvent.Notify(MediaEventType::PlaybackStarted);
StartMediaSink();
if (!IsPlaying()) {
@@ -1303,7 +1301,8 @@ MediaDecoderStateMachine::SetDormant(bool aDormant)
}
}
void MediaDecoderStateMachine::Shutdown()
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
@@ -1317,6 +1316,10 @@ void MediaDecoderStateMachine::Shutdown()
mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__);
#ifdef MOZ_EME
mCDMProxyPromise.DisconnectIfExists();
#endif
if (IsPlaying()) {
StopPlayback();
}
@@ -1330,13 +1333,16 @@ void MediaDecoderStateMachine::Shutdown()
mStartTimeRendezvous->Destroy();
}
DECODER_LOG("Shutdown started");
// Put a task in the decode queue to shutdown the reader.
// the queue to spin down.
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown)
return InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::Shutdown)
->Then(OwnerThread(), __func__, this,
&MediaDecoderStateMachine::FinishShutdown,
&MediaDecoderStateMachine::FinishShutdown);
DECODER_LOG("Shutdown started");
&MediaDecoderStateMachine::FinishShutdown)
->CompletionPromise();
}
void MediaDecoderStateMachine::StartDecoding()
@@ -1599,12 +1605,7 @@ MediaDecoderStateMachine::InitiateSeek()
StopPlayback();
UpdatePlaybackPositionInternal(mCurrentSeek.mTarget.mTime);
nsCOMPtr<nsIRunnable> startEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mDecoder,
&MediaDecoder::SeekingStarted,
mCurrentSeek.mTarget.mEventVisibility);
AbstractThread::MainThread()->Dispatch(startEvent.forget());
mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
// Reset our state machine and decoding pipeline before seeking.
Reset();
@@ -1897,9 +1898,7 @@ MediaDecoderStateMachine::DecodeError()
// MediaDecoder::DecodeError notifies the owner, and then shuts down the state
// machine.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
AbstractThread::MainThread()->Dispatch(event.forget());
mOnPlaybackEvent.Notify(MediaEventType::DecodeError);
}
void
@@ -1916,7 +1915,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
// Set mode to PLAYBACK after reading metadata.
mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable());
mInfo = aMetadata->mInfo;
mMetadataTags = aMetadata->mTags.forget();
RefPtr<MediaDecoderStateMachine> self = this;
@@ -1964,7 +1962,11 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
// thus get the metadata). We could fix this if we could compute the start
// time by demuxing without necessaring decoding.
bool waitingForCDM =
#ifdef MOZ_EME
mInfo.IsEncrypted() && !mCDMProxy;
#else
false;
#endif
mNotifyMetadataBeforeFirstFrame = mDuration.Ref().isSome() || waitingForCDM;
if (mNotifyMetadataBeforeFirstFrame) {
EnqueueLoadedMetadataEvent();
@@ -1996,14 +1998,12 @@ void
MediaDecoderStateMachine::EnqueueLoadedMetadataEvent()
{
MOZ_ASSERT(OnTaskQueue());
nsAutoPtr<MediaInfo> info(new MediaInfo());
*info = mInfo;
MediaDecoderEventVisibility visibility = mSentLoadedMetadataEvent?
MediaDecoderEventVisibility::Suppressed :
MediaDecoderEventVisibility::Observable;
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
new MetadataEventRunner(mDecoder, info, mMetadataTags, visibility);
AbstractThread::MainThread()->Dispatch(metadataLoadedEvent.forget());
MediaDecoderEventVisibility visibility =
mSentLoadedMetadataEvent ? MediaDecoderEventVisibility::Suppressed
: MediaDecoderEventVisibility::Observable;
mMetadataLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)),
Move(mMetadataTags),
Move(visibility));
mSentLoadedMetadataEvent = true;
}
@@ -2011,14 +2011,11 @@ void
MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
{
MOZ_ASSERT(OnTaskQueue());
nsAutoPtr<MediaInfo> info(new MediaInfo());
*info = mInfo;
MediaDecoderEventVisibility visibility = mSentFirstFrameLoadedEvent?
MediaDecoderEventVisibility::Suppressed :
MediaDecoderEventVisibility::Observable;
nsCOMPtr<nsIRunnable> event =
new FirstFrameLoadedEventRunner(mDecoder, info, visibility);
AbstractThread::MainThread()->Dispatch(event.forget());
MediaDecoderEventVisibility visibility =
mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed
: MediaDecoderEventVisibility::Observable;
mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)),
Move(visibility));
mSentFirstFrameLoadedEvent = true;
}
@@ -2159,46 +2156,19 @@ MediaDecoderStateMachine::SeekCompleted()
if (video) {
mMediaSink->Redraw();
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
AbstractThread::MainThread()->Dispatch(event.forget());
mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
}
}
class DecoderDisposer
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer)
DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine)
: mDecoder(aDecoder), mStateMachine(aStateMachine) {}
void OnTaskQueueShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mStateMachine);
MOZ_ASSERT(mDecoder);
mStateMachine->BreakCycles();
mDecoder->BreakCycles();
mStateMachine = nullptr;
mDecoder = nullptr;
}
private:
virtual ~DecoderDisposer() {}
RefPtr<MediaDecoder> mDecoder;
RefPtr<MediaDecoderStateMachine> mStateMachine;
};
void
MediaDecoderStateMachine::DispatchShutdown()
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::BeginShutdown()
{
mStreamSink->BeginShutdown();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown);
OwnerThread()->Dispatch(runnable.forget());
return InvokeAsync(OwnerThread(), this, __func__,
&MediaDecoderStateMachine::Shutdown);
}
void
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::FinishShutdown()
{
MOZ_ASSERT(OnTaskQueue());
@@ -2239,25 +2209,8 @@ MediaDecoderStateMachine::FinishShutdown()
MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
"How did we escape from the shutdown state?");
// We must daisy-chain these events to destroy the decoder. We must
// destroy the decoder on the main thread, but we can't destroy the
// decoder while this thread holds the decoder monitor. We can't
// dispatch an event to the main thread to destroy the decoder from
// here, as the event may run before the dispatch returns, and we
// hold the decoder monitor here. We also want to guarantee that the
// state machine is destroyed on the main thread, and so the
// event runner running this function (which holds a reference to the
// state machine) needs to finish and be released in order to allow
// that. So we dispatch an event to run after this event runner has
// finished and released its monitor/references. That event then will
// dispatch an event to the main thread to release the decoder and
// state machine.
DECODER_LOG("Shutting down state machine task queue");
RefPtr<DecoderDisposer> disposer = new DecoderDisposer(mDecoder, this);
OwnerThread()->BeginShutdown()->Then(AbstractThread::MainThread(), __func__,
disposer.get(),
&DecoderDisposer::OnTaskQueueShutdown,
&DecoderDisposer::OnTaskQueueShutdown);
return OwnerThread()->BeginShutdown();
}
nsresult MediaDecoderStateMachine::RunStateMachine()
@@ -2414,9 +2367,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Ensure readyState is updated before firing the 'ended' event.
UpdateNextFrameStatus();
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
AbstractThread::MainThread()->Dispatch(event.forget());
mOnPlaybackEvent.Notify(MediaEventType::PlaybackEnded);
mSentPlaybackEndedEvent = true;
@@ -2482,7 +2433,7 @@ MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
// Update corrupt-frames statistics
if (aData->mImage && !aData->mImage->IsValid()) {
FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
FrameStatistics& frameStats = *mFrameStats;
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<>
@@ -2915,6 +2866,27 @@ void MediaDecoderStateMachine::OnMediaSinkAudioError()
DecodeError();
}
#ifdef MOZ_EME
void
MediaDecoderStateMachine::OnCDMProxyReady(RefPtr<CDMProxy> aProxy)
{
MOZ_ASSERT(OnTaskQueue());
mCDMProxyPromise.Complete();
mCDMProxy = aProxy;
mReader->SetCDMProxy(aProxy);
if (mState == DECODER_STATE_WAIT_FOR_CDM) {
StartDecoding();
}
}
void
MediaDecoderStateMachine::OnCDMProxyNotReady()
{
MOZ_ASSERT(OnTaskQueue());
mCDMProxyPromise.Complete();
}
#endif
void
MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
{
+52 -16
View File
@@ -110,6 +110,14 @@ class TaskQueue;
extern LazyLogModule gMediaDecoderLog;
extern LazyLogModule gMediaSampleLog;
enum class MediaEventType : int8_t {
PlaybackStarted,
PlaybackStopped,
PlaybackEnded,
DecodeError,
Invalidate
};
/*
The state machine class. This manages the decoding and seeking in the
MediaDecoderReader on the decode task queue, and A/V sync on the shared
@@ -159,7 +167,7 @@ public:
// Set/Unset dormant state.
void DispatchSetDormant(bool aDormant);
void DispatchShutdown();
RefPtr<ShutdownPromise> BeginShutdown();
void DispatchStartBuffering()
{
@@ -210,20 +218,38 @@ public:
OwnerThread()->Dispatch(r.forget());
}
// Drop reference to decoder. Only called during shutdown dance.
// Drop reference to mReader and mResource. Only called during shutdown dance.
void BreakCycles() {
MOZ_ASSERT(NS_IsMainThread());
if (mReader) {
mReader->BreakCycles();
}
mResource = nullptr;
mDecoder = nullptr;
}
TimedMetadataEventSource& TimedMetadataEvent() {
return mMetadataManager.TimedMetadataEvent();
}
MediaEventSource<void>& OnMediaNotSeekable() {
return mReader->OnMediaNotSeekable();
}
MediaEventSourceExc<nsAutoPtr<MediaInfo>,
nsAutoPtr<MetadataTags>,
MediaDecoderEventVisibility>&
MetadataLoadedEvent() { return mMetadataLoadedEvent; }
MediaEventSourceExc<nsAutoPtr<MediaInfo>,
MediaDecoderEventVisibility>&
FirstFrameLoadedEvent() { return mFirstFrameLoadedEvent; }
MediaEventSource<MediaEventType>&
OnPlaybackEvent() { return mOnPlaybackEvent; }
MediaEventSource<MediaDecoderEventVisibility>&
OnSeekingStart() { return mOnSeekingStart; }
// Immutable after construction - may be called on any thread.
bool IsRealTime() const { return mRealTime; }
@@ -249,7 +275,7 @@ private:
// 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.
void InitializationTask();
void InitializationTask(MediaDecoder* aDecoder);
void SetDormant(bool aDormant);
@@ -257,9 +283,9 @@ private:
RefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
void Shutdown();
RefPtr<ShutdownPromise> Shutdown();
void FinishShutdown();
RefPtr<ShutdownPromise> FinishShutdown();
// Update the playback position. This can result in a timeupdate event
// and an invalidate of the frame being dispatched asynchronously if
@@ -639,16 +665,10 @@ private:
void AdjustAudioThresholds();
// The decoder object that created this state machine. The state machine
// holds a strong reference to the decoder to ensure that the decoder stays
// alive once media element has started the decoder shutdown process, and has
// dropped its reference to the decoder. This enables the state machine to
// keep using the decoder's monitor until the state machine has finished
// shutting down, without fear of the monitor being destroyed. After
// shutting down, the state machine will then release this reference,
// causing the decoder to be destroyed. This is accessed on the decode,
// state machine, audio and main threads.
RefPtr<MediaDecoder> mDecoder;
void* const mDecoderID;
const RefPtr<FrameStatistics> mFrameStats;
const RefPtr<VideoFrameContainer> mVideoFrameContainer;
const dom::AudioChannel mAudioChannel;
// Task queue for running the state machine.
RefPtr<TaskQueue> mTaskQueue;
@@ -1186,10 +1206,26 @@ private:
MediaEventListener mAudioQueueListener;
MediaEventListener mVideoQueueListener;
MediaEventProducerExc<nsAutoPtr<MediaInfo>,
nsAutoPtr<MetadataTags>,
MediaDecoderEventVisibility> mMetadataLoadedEvent;
MediaEventProducerExc<nsAutoPtr<MediaInfo>,
MediaDecoderEventVisibility> mFirstFrameLoadedEvent;
MediaEventProducer<MediaEventType> mOnPlaybackEvent;
MediaEventProducer<MediaDecoderEventVisibility> mOnSeekingStart;
// True if audio is offloading.
// Playback will not start when audio is offloading.
bool mAudioOffloading;
#ifdef MOZ_EME
void OnCDMProxyReady(RefPtr<CDMProxy> aProxy);
void OnCDMProxyNotReady();
RefPtr<CDMProxy> mCDMProxy;
MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
#endif
private:
// The buffered range. Mirrored from the decoder thread.
Mirror<media::TimeIntervals> mBuffered;
+70 -4
View File
@@ -18,6 +18,10 @@
#include <algorithm>
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
using namespace mozilla::media;
using mozilla::layers::Image;
@@ -63,7 +67,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mSeekable(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mHardwareAccelerationDisabled(false)
@@ -187,11 +190,54 @@ MediaFormatReader::Init()
return NS_OK;
}
#ifdef MOZ_EME
class DispatchKeyNeededEvent : public nsRunnable {
public:
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
nsTArray<uint8_t>& aInitData,
const nsString& aInitDataType)
: mDecoder(aDecoder)
, mInitData(aInitData)
, mInitDataType(aInitDataType)
{
}
NS_IMETHOD Run() {
// Note: Null check the owner, as the decoder could have been shutdown
// since this event was dispatched.
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (owner) {
owner->DispatchEncrypted(mInitData, mInitDataType);
}
mDecoder = nullptr;
return NS_OK;
}
private:
RefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
void
MediaFormatReader::SetCDMProxy(CDMProxy* aProxy)
{
RefPtr<CDMProxy> proxy = aProxy;
RefPtr<MediaFormatReader> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
MOZ_ASSERT(self->OnTaskQueue());
self->mCDMProxy = proxy;
});
OwnerThread()->Dispatch(r.forget());
}
#endif // MOZ_EME
bool
MediaFormatReader::IsWaitingOnCDMResource() {
MOZ_ASSERT(OnTaskQueue());
// EME Stub
#ifdef MOZ_EME
return IsEncrypted() && !mCDMProxy;
#else
return false;
#endif
}
RefPtr<MediaDecoderReader::MetadataPromise>
@@ -261,6 +307,13 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
mIsEncrypted = crypto && crypto->IsEncrypted();
if (mDecoder && crypto && crypto->IsEncrypted()) {
#ifdef MOZ_EME
// Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
NS_DispatchToMainThread(
new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc")));
}
#endif // MOZ_EME
mInfo.mCrypto = *crypto;
}
@@ -272,7 +325,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
}
mSeekable = mDemuxer->IsSeekable();
mInfo.mMediaSeekable = mDemuxer->IsSeekable();
if (!videoActive && !audioActive) {
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
@@ -302,8 +355,13 @@ MediaFormatReader::EnsureDecodersCreated()
mPlatform = new PDMFactory();
NS_ENSURE_TRUE(mPlatform, false);
if (IsEncrypted()) {
#ifdef MOZ_EME
MOZ_ASSERT(mCDMProxy);
mPlatform->SetCDMProxy(mCDMProxy);
#else
// EME not supported.
return false;
#endif
}
}
@@ -446,6 +504,14 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
if (ShouldSkip(aSkipToNextKeyframe, timeThreshold)) {
// Cancel any pending demux request.
mVideo.mDemuxRequest.DisconnectIfExists();
// I think it's still possible for an output to have been sent from the decoder
// and is currently sitting in our event queue waiting to be processed. The following
// flush won't clear it, and when we return to the event loop it'll be added to our
// output queue and be used.
// This code will count that as dropped, which was the intent, but not quite true.
mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
Flush(TrackInfo::kVideoTrack);
RefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
SkipVideoDemuxToNextKeyFrame(timeThreshold);
@@ -1268,7 +1334,7 @@ MediaFormatReader::Seek(int64_t aTime, int64_t aUnused)
MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing());
MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing());
if (!mSeekable) {
if (!mInfo.mMediaSeekable) {
LOG("Seek() END (Unseekable)");
return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
+10 -8
View File
@@ -17,6 +17,8 @@
namespace mozilla {
class CDMProxy;
class MediaFormatReader final : public MediaDecoderReader
{
typedef TrackInfo::TrackType TrackType;
@@ -47,11 +49,6 @@ public:
RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aUnused) override;
bool IsMediaSeekable() override
{
return mSeekable;
}
protected:
void NotifyDataArrivedInternal() override;
@@ -95,6 +92,10 @@ public:
return mTrackDemuxersMayBlock;
}
#ifdef MOZ_EME
void SetCDMProxy(CDMProxy* aProxy) override;
#endif
private:
bool HasVideo() { return mVideo.mTrackDemuxer; }
bool HasAudio() { return mAudio.mTrackDemuxer; }
@@ -390,9 +391,6 @@ private:
// True if we've read the streams' metadata.
bool mInitDone;
MozPromiseHolder<MetadataPromise> mMetadataPromise;
// Accessed from multiple thread, in particular the MediaDecoderStateMachine,
// however the value doesn't change after reading the metadata.
bool mSeekable;
bool IsEncrypted()
{
return mIsEncrypted;
@@ -432,6 +430,10 @@ private:
RefPtr<VideoFrameContainer> mVideoFrameContainer;
layers::ImageContainer* GetImageContainer();
#ifdef MOZ_EME
RefPtr<CDMProxy> mCDMProxy;
#endif
#if defined(READER_DORMANT_HEURISTIC)
const bool mDormantEnabled;
#endif
+3
View File
@@ -427,6 +427,9 @@ public:
// a duration until we know the start time, so we need to track it separately.
media::NullableTimeUnit mUnadjustedMetadataEndTime;
// True if the media is seekable (i.e. supports random access).
bool mMediaSeekable = true;
EncryptionInfo mCrypto;
};
+4 -4
View File
@@ -46,7 +46,7 @@ public:
MOZ_ASSERT(aItem);
NS_ADDREF(aItem);
nsDeque::Push(aItem);
mPushEvent.Notify();
mPushEvent.Notify(RefPtr<T>(aItem));
}
inline void PushFront(T* aItem) {
@@ -54,7 +54,7 @@ public:
MOZ_ASSERT(aItem);
NS_ADDREF(aItem);
nsDeque::PushFront(aItem);
mPushEvent.Notify();
mPushEvent.Notify(RefPtr<T>(aItem));
}
inline already_AddRefed<T> PopFront() {
@@ -161,7 +161,7 @@ public:
return mPopEvent;
}
MediaEventSource<void>& PushEvent() {
MediaEventSource<RefPtr<T>>& PushEvent() {
return mPushEvent;
}
@@ -172,7 +172,7 @@ public:
private:
mutable ReentrantMonitor mReentrantMonitor;
MediaEventProducer<RefPtr<T>> mPopEvent;
MediaEventProducer<void> mPushEvent;
MediaEventProducer<RefPtr<T>> mPushEvent;
MediaEventProducer<void> mFinishEvent;
// True when we've decoded the last frame of data in the
// bitstream for which we're queueing frame data.
+2
View File
@@ -78,6 +78,8 @@ public:
B2G_ACL_EXPORT ImageContainer* GetImageContainer();
void ForgetElement() { mElement = nullptr; }
uint32_t GetDroppedImageCount() { return mImageContainer->GetDroppedImageCount(); }
protected:
void SetCurrentFramesLocked(const gfx::IntSize& aIntrinsicSize,
const nsTArray<ImageContainer::NonOwningImage>& aImages);
-6
View File
@@ -48,12 +48,6 @@ public:
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold);
virtual bool IsMediaSeekable()
{
// not used
return true;
}
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags);
virtual RefPtr<SeekPromise>
@@ -5,7 +5,6 @@
#include <stdint.h>
#include <assert.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "../AudioPacketizer.h"
+1 -9
View File
@@ -190,6 +190,7 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
DWORD seekCaps = 0;
hr = mMediaSeeking->GetCapabilities(&seekCaps);
mInfo.mMediaSeekable = SUCCEEDED(hr) && (AM_SEEKING_CanSeekAbsolute & seekCaps);
int64_t duration = mMP3FrameParser.GetDuration();
if (SUCCEEDED(hr)) {
@@ -210,15 +211,6 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
DirectShowReader::IsMediaSeekable()
{
DWORD seekCaps = 0;
HRESULT hr = mMediaSeeking->GetCapabilities(&seekCaps);
return ((AM_SEEKING_CanSeekAbsolute & seekCaps) ==
AM_SEEKING_CanSeekAbsolute);
}
inline float
UnsignedByteToAudioSample(uint8_t aValue)
{
-4
View File
@@ -56,12 +56,8 @@ public:
protected:
void NotifyDataArrivedInternal() override;
public:
bool IsMediaSeekable() override;
private:
// Notifies the filter graph that playback is complete. aStatus is
// the code to send to the filter graph. Always returns false, so
// that we can just "return Finish()" from DecodeAudioData().
+6 -22
View File
@@ -468,12 +468,15 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
/* report the duration */
gint64 duration;
bool isMediaSeekable = false;
if (isMP3 && mMP3FrameParser.IsMP3()) {
// The MP3FrameParser has reported a duration; use that over the gstreamer
// reported duration for inter-platform consistency.
mUseParserDuration = true;
mLastParserDuration = mMP3FrameParser.GetDuration();
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration));
isMediaSeekable = true;
} else {
LOG(LogLevel::Debug, "querying duration");
// Otherwise use the gstreamer duration.
@@ -488,9 +491,12 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration));
duration = GST_TIME_AS_USECONDS (duration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
isMediaSeekable = true;
}
}
mInfo.mMediaSeekable = isMediaSeekable;
int n_video = 0, n_audio = 0;
g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr);
@@ -518,28 +524,6 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
GStreamerReader::IsMediaSeekable()
{
if (mUseParserDuration) {
return true;
}
gint64 duration;
#if GST_VERSION_MAJOR >= 1
if (gst_element_query_duration(GST_ELEMENT(mPlayBin), GST_FORMAT_TIME,
&duration)) {
#else
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_duration(GST_ELEMENT(mPlayBin), &format, &duration) &&
format == GST_FORMAT_TIME) {
#endif
return true;
}
return false;
}
nsresult GStreamerReader::CheckSupportedFormats()
{
bool done = false;
+1 -2
View File
@@ -54,11 +54,10 @@ public:
protected:
virtual void NotifyDataArrivedInternal() override;
public:
layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() { return mInfo.HasAudio(); }
bool HasVideo() { return mInfo.HasVideo(); }
+28 -59
View File
@@ -201,44 +201,9 @@ DecodedStreamData::SetPlaying(bool aPlaying)
}
}
class OutputStreamListener : public MediaStreamListener {
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
public:
explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {}
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
{
if (event == EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &OutputStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
}
}
void Forget()
{
MOZ_ASSERT(NS_IsMainThread());
mOwner = nullptr;
}
private:
void DoNotifyFinished()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOwner) {
// Remove the finished stream so it won't block the decoded stream.
mOwner->Remove();
}
}
// Main thread only
OutputStreamData* mOwner;
};
OutputStreamData::~OutputStreamData()
{
MOZ_ASSERT(NS_IsMainThread());
mListener->Forget();
// Break the connection to the input stream if necessary.
if (mPort) {
mPort->Destroy();
@@ -250,8 +215,6 @@ OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStrea
{
mOwner = aOwner;
mStream = aStream;
mListener = new OutputStreamListener(this);
aStream->AddListener(mListener);
}
void
@@ -284,13 +247,6 @@ OutputStreamData::Disconnect()
return true;
}
void
OutputStreamData::Remove()
{
MOZ_ASSERT(NS_IsMainThread());
mOwner->Remove(mStream);
}
MediaStreamGraph*
OutputStreamData::Graph() const
{
@@ -499,8 +455,7 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
// No need to create a source stream when there are no output streams. This
// happens when RemoveOutput() is called immediately after StartPlayback().
// Also we don't create a source stream when MDSM has begun shutdown.
if (!mOutputStreamManager.Graph() || mShuttingDown) {
if (!mOutputStreamManager.Graph()) {
// Resolve the promise to indicate the end of playback.
aPromise.Resolve(true, __func__);
return;
@@ -521,16 +476,30 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
return NS_OK;
}
private:
virtual ~R()
{
// mData is not transferred when dispatch fails and Run() is not called.
// We need to dispatch a task to ensure DecodedStreamData is destroyed
// properly on the main thread.
if (mData) {
DecodedStreamData* data = mData.release();
RefPtr<DecodedStream> self = mThis.forget();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mOutputStreamManager.Disconnect();
delete data;
});
AbstractThread::MainThread()->Dispatch(r.forget());
}
}
RefPtr<DecodedStream> mThis;
Method mMethod;
UniquePtr<DecodedStreamData> mData;
};
// Post a message to ensure |mData| is only updated on the worker thread.
// Note this must be done before MDSM's shutdown since dispatch could fail
// when the worker thread is shut down.
// Note this could fail when MDSM begin to shut down the worker thread.
nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::OnDataCreated, data);
mOwnerThread->Dispatch(r.forget());
mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
}
bool
@@ -649,6 +618,9 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
MediaData* aData, AudioSegment* aOutput,
uint32_t aRate, double aVolume)
{
// The amount of audio frames that is used to fuzz rounding errors.
static const int64_t AUDIO_FUZZ_FRAMES = 1;
MOZ_ASSERT(aData);
AudioData* audio = aData->As<AudioData>();
// This logic has to mimic AudioSink closely to make sure we write
@@ -660,11 +632,11 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
if (!audioWrittenOffset.isValid() ||
!frameOffset.isValid() ||
// ignore packet that we've already processed
frameOffset.value() + audio->mFrames <= audioWrittenOffset.value()) {
audio->GetEndTime() <= aStream->mNextAudioTime) {
return;
}
if (audioWrittenOffset.value() < frameOffset.value()) {
if (audioWrittenOffset.value() + AUDIO_FUZZ_FRAMES < frameOffset.value()) {
int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value();
// Write silence to catch up
AudioSegment silence;
@@ -674,20 +646,17 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
aOutput->AppendFrom(&silence);
}
MOZ_ASSERT(audioWrittenOffset.value() >= frameOffset.value());
int64_t offset = audioWrittenOffset.value() - frameOffset.value();
size_t framesToWrite = audio->mFrames - offset;
// Always write the whole sample without truncation to be consistent with
// DecodedAudioDataSink::PlayFromAudioQueue()
audio->EnsureAudioBuffer();
RefPtr<SharedBuffer> buffer = audio->mAudioBuffer;
AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
nsAutoTArray<const AudioDataValue*, 2> channels;
for (uint32_t i = 0; i < audio->mChannels; ++i) {
channels.AppendElement(bufferData + i * audio->mFrames + offset);
channels.AppendElement(bufferData + i * audio->mFrames);
}
aOutput->AppendFrames(buffer.forget(), channels, framesToWrite);
aStream->mAudioFramesWritten += framesToWrite;
aOutput->AppendFrames(buffer.forget(), channels, audio->mFrames);
aStream->mAudioFramesWritten += audio->mFrames;
aOutput->ApplyVolume(aVolume);
aStream->mNextAudioTime = audio->GetEndTime();
-9
View File
@@ -26,17 +26,12 @@ class MediaData;
class MediaInputPort;
class MediaStream;
class MediaStreamGraph;
class OutputStreamListener;
class OutputStreamManager;
class ProcessedMediaStream;
class TimeStamp;
template <class T> class MediaQueue;
namespace layers {
class Image;
} // namespace layers
class OutputStreamData {
public:
~OutputStreamData();
@@ -47,9 +42,6 @@ public:
// Disconnect mStream from its input stream.
// Return false is mStream is already destroyed, otherwise true.
bool Disconnect();
// Called by OutputStreamListener to remove self from the output streams
// managed by OutputStreamManager.
void Remove();
// Return true if aStream points to the same object as mStream.
// Used by OutputStreamManager to remove an output stream.
bool Equals(MediaStream* aStream)
@@ -64,7 +56,6 @@ private:
RefPtr<ProcessedMediaStream> mStream;
// mPort connects our mStream to an input stream.
RefPtr<MediaInputPort> mPort;
RefPtr<OutputStreamListener> mListener;
};
class OutputStreamManager {
+22 -6
View File
@@ -26,7 +26,6 @@ VideoSink::VideoSink(AbstractThread* aThread,
VideoFrameContainer* aContainer,
bool aRealTime,
FrameStatistics& aFrameStats,
int aDelayDuration,
uint32_t aVQueueSentToCompositerSize)
: mOwnerThread(aThread)
, mAudioSink(aAudioSink)
@@ -36,9 +35,9 @@ VideoSink::VideoSink(AbstractThread* aThread,
, mRealTime(aRealTime)
, mFrameStats(aFrameStats)
, mVideoFrameEndTime(-1)
, mOldDroppedCount(0)
, mHasVideo(false)
, mUpdateScheduler(aThread)
, mDelayDuration(aDelayDuration)
, mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
{
MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
@@ -218,12 +217,18 @@ VideoSink::Shutdown()
}
void
VideoSink::OnVideoQueueEvent()
VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
{
AssertOwnerThread();
// Listen to push event, VideoSink should try rendering ASAP if first frame
// arrives but update scheduler is not triggered yet.
TryUpdateRenderedVideoFrames();
VideoData* v = aSample->As<VideoData>();
if (!v->mSentToCompositor) {
// Since we push rendered frames back to the queue, we will receive
// push events for them. We only need to trigger render loop
// when this frame is not rendered yet.
TryUpdateRenderedVideoFrames();
}
}
void
@@ -323,6 +328,10 @@ VideoSink::RenderVideoFrames(int32_t aMaxFrames,
frame->mTime, frame->mFrameID, VideoQueue().GetSize());
}
mContainer->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images);
uint32_t dropped = mContainer->GetDroppedImageCount();
mFrameStats.NotifyDecodedFrames(0, 0, dropped - mOldDroppedCount);
mOldDroppedCount = dropped;
}
void
@@ -338,7 +347,7 @@ VideoSink::UpdateRenderedVideoFrames()
// the current frame.
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
int64_t remainingTime = mDelayDuration;
int64_t remainingTime = -1;
if (VideoQueue().GetSize() > 0) {
RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
int32_t framesRemoved = 0;
@@ -365,7 +374,14 @@ VideoSink::UpdateRenderedVideoFrames()
RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);
// No next fame to render. There is no need to schedule next render
// loop. We will run render loops again upon incoming frames.
if (remainingTime < 0) {
return;
}
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);
RefPtr<VideoSink> self = this;
mUpdateScheduler.Ensure(target, [self] () {
+3 -6
View File
@@ -35,7 +35,6 @@ public:
VideoFrameContainer* aContainer,
bool aRealTime,
FrameStatistics& aFrameStats,
int aDelayDuration,
uint32_t aVQueueSentToCompositerSize);
const PlaybackParams& GetPlaybackParams() const override;
@@ -74,7 +73,7 @@ private:
virtual ~VideoSink();
// VideoQueue listener related.
void OnVideoQueueEvent();
void OnVideoQueueEvent(RefPtr<MediaData>&& aSample);
void ConnectListener();
void DisconnectListener();
@@ -130,6 +129,8 @@ private:
// in microseconds.
int64_t mVideoFrameEndTime;
uint32_t mOldDroppedCount;
// Event listeners for VideoQueue
MediaEventListener mPushListener;
@@ -139,10 +140,6 @@ private:
// Used to trigger another update of rendered frames in next round.
DelayedScheduler mUpdateScheduler;
// A delay duration to trigger next time UpdateRenderedVideoFrames().
// Based on the default value in MDSM.
const int mDelayDuration;
// Max frame number sent to compositor at a time.
// Based on the pref value obtained in MDSM.
const uint32_t mVideoQueueSendToCompositorSize;
+7 -10
View File
@@ -486,20 +486,17 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
} else {
return NS_ERROR_FAILURE;
}
{
ReentrantMonitorAutoEnter mon(mMonitor);
mInfo.mMediaSeekable = !mIsChained;
}
*aInfo = mInfo;
return NS_OK;
}
bool
OggReader::IsMediaSeekable()
{
if (mIsChained) {
return false;
}
return true;
}
nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) {
NS_ASSERTION(aPacket->granulepos != -1, "Must know vorbis granulepos!");
@@ -712,7 +709,7 @@ void OggReader::SetChained(bool aIsChained) {
ReentrantMonitorAutoEnter mon(mMonitor);
mIsChained = aIsChained;
}
mDecoder->DispatchSetMediaSeekable(false);
mOnMediaNotSeekable.Notify();
}
bool OggReader::ReadOggChain()
-2
View File
@@ -66,8 +66,6 @@ public:
Seek(int64_t aTime, int64_t aEndTime) override;
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool HasAudio() {
return (mVorbisState != 0 && mVorbisState->mActive) ||
-4
View File
@@ -80,10 +80,6 @@ MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown) {
return;
}
MediaDecoder::FirstFrameLoaded(aInfo, aEventVisibility);
if (!CheckDecoderCanOffloadAudio()) {
+2 -7
View File
@@ -311,6 +311,8 @@ void MediaOmxReader::HandleResourceAllocated()
mInfo.mAudio.mRate = sampleRate;
}
mInfo.mMediaSeekable = mExtractor->flags() & MediaExtractor::CAN_SEEK;
RefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
@@ -322,13 +324,6 @@ void MediaOmxReader::HandleResourceAllocated()
mMetadataPromise.Resolve(metadata, __func__);
}
bool
MediaOmxReader::IsMediaSeekable()
{
// Check the MediaExtract flag if the source is seekable.
return (mExtractor->flags() & MediaExtractor::CAN_SEEK);
}
bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
-2
View File
@@ -92,8 +92,6 @@ public:
virtual RefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) override;
virtual bool IsMediaSeekable() override;
virtual void SetIdle() override;
virtual RefPtr<ShutdownPromise> Shutdown() override;
-7
View File
@@ -102,13 +102,6 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
RawReader::IsMediaSeekable()
{
// not used
return true;
}
bool RawReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
-2
View File
@@ -33,8 +33,6 @@ public:
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool ReadFromResource(uint8_t *aBuf, uint32_t aLength);
-7
View File
@@ -145,13 +145,6 @@ nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
bool
WaveReader::IsMediaSeekable()
{
// not used
return true;
}
template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
template <typename T> T SignedShortToAudioSample(int16_t aValue);
-2
View File
@@ -33,8 +33,6 @@ public:
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
private:
bool ReadAll(char* aBuf, int64_t aSize, int64_t* aBytesRead = nullptr);
bool LoadRIFFChunk();
+1 -2
View File
@@ -520,8 +520,7 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
aImagData.ComputeLengthAndData();
if (aRealData.Length() != aImagData.Length() ||
aRealData.Length() == 0 ||
aRealData.Length() > 4096) {
aRealData.Length() == 0) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
-42
View File
@@ -39,12 +39,6 @@ BufferDecoder::GetResource() const
return mResource;
}
void
BufferDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
{
// ignore
}
void
BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped)
@@ -52,12 +46,6 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
// ignore
}
void
BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
{
// ignore
}
VideoFrameContainer*
BufferDecoder::GetVideoFrameContainer()
{
@@ -72,36 +60,6 @@ BufferDecoder::GetImageContainer()
return nullptr;
}
bool
BufferDecoder::IsTransportSeekable()
{
return false;
}
bool
BufferDecoder::IsMediaSeekable()
{
return false;
}
void
BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, MediaDecoderEventVisibility aEventVisibility)
{
// ignore
}
void
BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility)
{
// ignore
}
void
BufferDecoder::OnReadMetadataCompleted()
{
// ignore
}
MediaDecoderOwner*
BufferDecoder::GetOwner()
{
-18
View File
@@ -33,32 +33,14 @@ public:
virtual MediaResource* GetResource() const final override;
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override;
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) final override;
virtual void SetMediaSeekable(bool aMediaSeekable) final override;
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
virtual layers::ImageContainer* GetImageContainer() final override;
virtual bool IsTransportSeekable() final override;
virtual bool IsMediaSeekable() final override;
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility) final override;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility) final override;
virtual void OnReadMetadataCompleted() final override;
virtual MediaDecoderOwner* GetOwner() final override;
virtual void NotifyDataArrived() final override {};
private:
virtual ~BufferDecoder();
RefPtr<TaskQueue> mTaskQueueIdentity;
+1 -2
View File
@@ -96,7 +96,6 @@ public:
}
mReverb = new WebCore::Reverb(mBuffer, mBufferLength,
WEBAUDIO_BLOCK_SIZE,
MaxFFTSize, 2, mUseBackgroundThreads,
mNormalize, mSampleRate);
}
@@ -153,7 +152,7 @@ public:
}
aOutput->AllocateChannels(2);
mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE);
mReverb->process(&input, aOutput);
}
virtual bool IsActive() const override
+83 -29
View File
@@ -15,7 +15,17 @@
#include "AlignedTArray.h"
#include "AudioNodeEngine.h"
#if defined(MOZ_LIBAV_FFT)
#ifdef __cplusplus
extern "C" {
#endif
#include "libavcodec/avfft.h"
#ifdef __cplusplus
}
#endif
#else
#include "kiss_fft/kiss_fftr.h"
#endif
namespace mozilla {
@@ -25,7 +35,9 @@ namespace mozilla {
class FFTBlock final
{
union ComplexU {
#if !defined(MOZ_LIBAV_FFT)
kiss_fft_cpx c;
#endif
float f[2];
struct {
float r;
@@ -35,11 +47,16 @@ class FFTBlock final
public:
explicit FFTBlock(uint32_t aFFTSize)
#if defined(MOZ_LIBAV_FFT)
: mAvRDFT(nullptr)
, mAvIRDFT(nullptr)
#else
: mKissFFT(nullptr)
, mKissIFFT(nullptr)
#ifdef BUILD_ARM_NEON
, mOmxFFT(nullptr)
, mOmxIFFT(nullptr)
#endif
#endif
{
MOZ_COUNT_CTOR(FFTBlock);
@@ -61,6 +78,13 @@ public:
void PerformFFT(const float* aData)
{
EnsureFFT();
#if defined(MOZ_LIBAV_FFT)
PodCopy(mOutputBuffer.Elements()->f, aData, mFFTSize);
av_rdft_calc(mAvRDFT, mOutputBuffer.Elements()->f);
// Recover packed Nyquist.
mOutputBuffer[mFFTSize / 2].r = mOutputBuffer[0].i;
mOutputBuffer[0].i = 0.0f;
#else
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
omxSP_FFTFwd_RToCCS_F32_Sfs(aData, mOutputBuffer.Elements()->f, mOmxFFT);
@@ -69,6 +93,7 @@ public:
{
kiss_fftr(mKissFFT, aData, &(mOutputBuffer.Elements()->c));
}
#endif
}
// Inverse-transform internal data and store the resulting FFTSize()
// points in aDataOut.
@@ -83,6 +108,17 @@ public:
void GetInverseWithoutScaling(float* aDataOut)
{
EnsureIFFT();
#if defined(MOZ_LIBAV_FFT)
{
// Even though this function doesn't scale, the libav forward transform
// gives a value that needs scaling by 2 in order for things to turn out
// similar to how we expect from kissfft/openmax.
AudioBufferCopyWithScale(mOutputBuffer.Elements()->f, 2.0f,
aDataOut, mFFTSize);
aDataOut[1] = 2.0f * mOutputBuffer[mFFTSize/2].r; // Packed Nyquist
av_rdft_calc(mAvIRDFT, aDataOut);
}
#else
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
omxSP_FFTInv_CCSToR_F32_Sfs(mOutputBuffer.Elements()->f, aDataOut, mOmxIFFT);
@@ -94,41 +130,23 @@ public:
{
kiss_fftri(mKissIFFT, &(mOutputBuffer.Elements()->c), aDataOut);
}
}
// Inverse-transform the FFTSize()/2+1 points of data in each
// of aRealDataIn and aImagDataIn and store the resulting
// FFTSize() points in aRealDataOut.
void PerformInverseFFT(float* aRealDataIn,
float *aImagDataIn,
float *aRealDataOut)
{
EnsureIFFT();
const uint32_t inputSize = mFFTSize / 2 + 1;
AlignedTArray<ComplexU> inputBuffer(inputSize);
for (uint32_t i = 0; i < inputSize; ++i) {
inputBuffer[i].r = aRealDataIn[i];
inputBuffer[i].i = aImagDataIn[i];
}
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
omxSP_FFTInv_CCSToR_F32_Sfs(inputBuffer.Elements()->f,
aRealDataOut, mOmxIFFT);
} else
#endif
{
kiss_fftri(mKissIFFT, &(inputBuffer.Elements()->c), aRealDataOut);
for (uint32_t i = 0; i < mFFTSize; ++i) {
aRealDataOut[i] /= mFFTSize;
}
}
}
void Multiply(const FFTBlock& aFrame)
{
uint32_t halfSize = mFFTSize / 2;
// DFTs are not packed.
MOZ_ASSERT(mOutputBuffer[0].i == 0);
MOZ_ASSERT(aFrame.mOutputBuffer[0].i == 0);
BufferComplexMultiply(mOutputBuffer.Elements()->f,
aFrame.mOutputBuffer.Elements()->f,
mOutputBuffer.Elements()->f,
mFFTSize / 2 + 1);
halfSize);
mOutputBuffer[halfSize].r *= aFrame.mOutputBuffer[halfSize].r;
// This would have been set to NaN if either real component was NaN.
mOutputBuffer[0].i = 0.0f;
}
// Perform a forward FFT on |aData|, assuming zeros after dataSize samples,
@@ -165,16 +183,29 @@ public:
{
return mOutputBuffer[aIndex].r;
}
float& RealData(uint32_t aIndex)
{
return mOutputBuffer[aIndex].r;
}
float ImagData(uint32_t aIndex) const
{
return mOutputBuffer[aIndex].i;
}
float& ImagData(uint32_t aIndex)
{
return mOutputBuffer[aIndex].i;
}
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = 0;
#if defined(MOZ_LIBAV_FFT)
amount += aMallocSizeOf(mAvRDFT);
amount += aMallocSizeOf(mAvIRDFT);
#else
amount += aMallocSizeOf(mKissFFT);
amount += aMallocSizeOf(mKissIFFT);
#endif
amount += mOutputBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
return amount;
}
@@ -190,6 +221,11 @@ private:
void EnsureFFT()
{
#if defined(MOZ_LIBAV_FFT)
if (!mAvRDFT) {
mAvRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, DFT_R2C);
}
#else
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
if (!mOmxFFT) {
@@ -202,9 +238,15 @@ private:
mKissFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
}
}
#endif
}
void EnsureIFFT()
{
#if defined(MOZ_LIBAV_FFT)
if (!mAvIRDFT) {
mAvIRDFT = av_rdft_init(log((double)mFFTSize)/M_LN2, IDFT_C2R);
}
#else
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
if (!mOmxIFFT) {
@@ -217,6 +259,7 @@ private:
mKissIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
}
}
#endif
}
#ifdef BUILD_ARM_NEON
@@ -240,6 +283,11 @@ private:
void Clear()
{
#if defined(MOZ_LIBAV_FFT)
av_rdft_end(mAvRDFT);
av_rdft_end(mAvIRDFT);
mAvRDFT = mAvIRDFT = nullptr;
#else
#ifdef BUILD_ARM_NEON
free(mOmxFFT);
free(mOmxIFFT);
@@ -248,21 +296,27 @@ private:
free(mKissFFT);
free(mKissIFFT);
mKissFFT = mKissIFFT = nullptr;
#endif
}
void AddConstantGroupDelay(double sampleFrameDelay);
void InterpolateFrequencyComponents(const FFTBlock& block0,
const FFTBlock& block1, double interp);
#if defined(MOZ_LIBAV_FFT)
RDFTContext *mAvRDFT;
RDFTContext *mAvIRDFT;
#else
kiss_fftr_cfg mKissFFT;
kiss_fftr_cfg mKissIFFT;
#ifdef BUILD_ARM_NEON
OMXFFTSpec_R_F32* mOmxFFT;
OMXFFTSpec_R_F32* mOmxIFFT;
#endif
#endif
AlignedTArray<ComplexU> mOutputBuffer;
uint32_t mFFTSize;
};
}
} // namespace mozilla
#endif
-1
View File
@@ -27,7 +27,6 @@ PeriodicWave::PeriodicWave(AudioContext* aContext,
// Caller should have checked this and thrown.
MOZ_ASSERT(aLength > 0);
MOZ_ASSERT(aLength <= 4096);
mLength = aLength;
// Copy coefficient data. The two arrays share an allocation.
+1 -6
View File
@@ -26,16 +26,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// For M_PI
// VS2015 requires <math.h> to be used; <cmath> doesn't seem to honor
// _USE_MATH_DEFINES.
#define _USE_MATH_DEFINES
#include <math.h>
#include "Biquad.h"
#include <float.h>
#include <algorithm>
#include <math.h>
namespace WebCore {
@@ -25,7 +25,6 @@
#ifndef DenormalDisabler_h
#define DenormalDisabler_h
#define _USE_MATH_DEFINES
#include <cmath>
#include <float.h>
@@ -1,347 +0,0 @@
/*
* Copyright (C) 2012 Intel Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "DirectConvolver.h"
#include "mozilla/PodOperations.h"
using namespace mozilla;
namespace WebCore {
DirectConvolver::DirectConvolver(size_t inputBlockSize)
: m_inputBlockSize(inputBlockSize)
{
m_buffer.SetLength(inputBlockSize * 2);
PodZero(m_buffer.Elements(), inputBlockSize * 2);
}
void DirectConvolver::process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess)
{
MOZ_ASSERT(framesToProcess == m_inputBlockSize);
if (framesToProcess != m_inputBlockSize)
return;
// Only support kernelSize <= m_inputBlockSize
size_t kernelSize = convolutionKernel->Length();
MOZ_ASSERT(kernelSize <= m_inputBlockSize);
if (kernelSize > m_inputBlockSize)
return;
const float* kernelP = convolutionKernel->Elements();
// Sanity check
bool isCopyGood = kernelP && sourceP && destP && m_buffer.Elements();
MOZ_ASSERT(isCopyGood);
if (!isCopyGood)
return;
float* inputP = m_buffer.Elements() + m_inputBlockSize;
// Copy samples to 2nd half of input buffer.
memcpy(inputP, sourceP, sizeof(float) * framesToProcess);
// FIXME: The macro can be further optimized to avoid pipeline stalls. One possibility is to maintain 4 separate sums and change the macro to CONVOLVE_FOUR_SAMPLES.
#define CONVOLVE_ONE_SAMPLE \
sum += inputP[i - j] * kernelP[j]; \
j++;
size_t i = 0;
while (i < framesToProcess) {
size_t j = 0;
float sum = 0;
// FIXME: SSE optimization may be applied here.
if (kernelSize == 32) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
} else if (kernelSize == 64) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
CONVOLVE_ONE_SAMPLE // 33
CONVOLVE_ONE_SAMPLE // 34
CONVOLVE_ONE_SAMPLE // 35
CONVOLVE_ONE_SAMPLE // 36
CONVOLVE_ONE_SAMPLE // 37
CONVOLVE_ONE_SAMPLE // 38
CONVOLVE_ONE_SAMPLE // 39
CONVOLVE_ONE_SAMPLE // 40
CONVOLVE_ONE_SAMPLE // 41
CONVOLVE_ONE_SAMPLE // 42
CONVOLVE_ONE_SAMPLE // 43
CONVOLVE_ONE_SAMPLE // 44
CONVOLVE_ONE_SAMPLE // 45
CONVOLVE_ONE_SAMPLE // 46
CONVOLVE_ONE_SAMPLE // 47
CONVOLVE_ONE_SAMPLE // 48
CONVOLVE_ONE_SAMPLE // 49
CONVOLVE_ONE_SAMPLE // 50
CONVOLVE_ONE_SAMPLE // 51
CONVOLVE_ONE_SAMPLE // 52
CONVOLVE_ONE_SAMPLE // 53
CONVOLVE_ONE_SAMPLE // 54
CONVOLVE_ONE_SAMPLE // 55
CONVOLVE_ONE_SAMPLE // 56
CONVOLVE_ONE_SAMPLE // 57
CONVOLVE_ONE_SAMPLE // 58
CONVOLVE_ONE_SAMPLE // 59
CONVOLVE_ONE_SAMPLE // 60
CONVOLVE_ONE_SAMPLE // 61
CONVOLVE_ONE_SAMPLE // 62
CONVOLVE_ONE_SAMPLE // 63
CONVOLVE_ONE_SAMPLE // 64
} else if (kernelSize == 128) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
CONVOLVE_ONE_SAMPLE // 33
CONVOLVE_ONE_SAMPLE // 34
CONVOLVE_ONE_SAMPLE // 35
CONVOLVE_ONE_SAMPLE // 36
CONVOLVE_ONE_SAMPLE // 37
CONVOLVE_ONE_SAMPLE // 38
CONVOLVE_ONE_SAMPLE // 39
CONVOLVE_ONE_SAMPLE // 40
CONVOLVE_ONE_SAMPLE // 41
CONVOLVE_ONE_SAMPLE // 42
CONVOLVE_ONE_SAMPLE // 43
CONVOLVE_ONE_SAMPLE // 44
CONVOLVE_ONE_SAMPLE // 45
CONVOLVE_ONE_SAMPLE // 46
CONVOLVE_ONE_SAMPLE // 47
CONVOLVE_ONE_SAMPLE // 48
CONVOLVE_ONE_SAMPLE // 49
CONVOLVE_ONE_SAMPLE // 50
CONVOLVE_ONE_SAMPLE // 51
CONVOLVE_ONE_SAMPLE // 52
CONVOLVE_ONE_SAMPLE // 53
CONVOLVE_ONE_SAMPLE // 54
CONVOLVE_ONE_SAMPLE // 55
CONVOLVE_ONE_SAMPLE // 56
CONVOLVE_ONE_SAMPLE // 57
CONVOLVE_ONE_SAMPLE // 58
CONVOLVE_ONE_SAMPLE // 59
CONVOLVE_ONE_SAMPLE // 60
CONVOLVE_ONE_SAMPLE // 61
CONVOLVE_ONE_SAMPLE // 62
CONVOLVE_ONE_SAMPLE // 63
CONVOLVE_ONE_SAMPLE // 64
CONVOLVE_ONE_SAMPLE // 65
CONVOLVE_ONE_SAMPLE // 66
CONVOLVE_ONE_SAMPLE // 67
CONVOLVE_ONE_SAMPLE // 68
CONVOLVE_ONE_SAMPLE // 69
CONVOLVE_ONE_SAMPLE // 70
CONVOLVE_ONE_SAMPLE // 71
CONVOLVE_ONE_SAMPLE // 72
CONVOLVE_ONE_SAMPLE // 73
CONVOLVE_ONE_SAMPLE // 74
CONVOLVE_ONE_SAMPLE // 75
CONVOLVE_ONE_SAMPLE // 76
CONVOLVE_ONE_SAMPLE // 77
CONVOLVE_ONE_SAMPLE // 78
CONVOLVE_ONE_SAMPLE // 79
CONVOLVE_ONE_SAMPLE // 80
CONVOLVE_ONE_SAMPLE // 81
CONVOLVE_ONE_SAMPLE // 82
CONVOLVE_ONE_SAMPLE // 83
CONVOLVE_ONE_SAMPLE // 84
CONVOLVE_ONE_SAMPLE // 85
CONVOLVE_ONE_SAMPLE // 86
CONVOLVE_ONE_SAMPLE // 87
CONVOLVE_ONE_SAMPLE // 88
CONVOLVE_ONE_SAMPLE // 89
CONVOLVE_ONE_SAMPLE // 90
CONVOLVE_ONE_SAMPLE // 91
CONVOLVE_ONE_SAMPLE // 92
CONVOLVE_ONE_SAMPLE // 93
CONVOLVE_ONE_SAMPLE // 94
CONVOLVE_ONE_SAMPLE // 95
CONVOLVE_ONE_SAMPLE // 96
CONVOLVE_ONE_SAMPLE // 97
CONVOLVE_ONE_SAMPLE // 98
CONVOLVE_ONE_SAMPLE // 99
CONVOLVE_ONE_SAMPLE // 100
CONVOLVE_ONE_SAMPLE // 101
CONVOLVE_ONE_SAMPLE // 102
CONVOLVE_ONE_SAMPLE // 103
CONVOLVE_ONE_SAMPLE // 104
CONVOLVE_ONE_SAMPLE // 105
CONVOLVE_ONE_SAMPLE // 106
CONVOLVE_ONE_SAMPLE // 107
CONVOLVE_ONE_SAMPLE // 108
CONVOLVE_ONE_SAMPLE // 109
CONVOLVE_ONE_SAMPLE // 110
CONVOLVE_ONE_SAMPLE // 111
CONVOLVE_ONE_SAMPLE // 112
CONVOLVE_ONE_SAMPLE // 113
CONVOLVE_ONE_SAMPLE // 114
CONVOLVE_ONE_SAMPLE // 115
CONVOLVE_ONE_SAMPLE // 116
CONVOLVE_ONE_SAMPLE // 117
CONVOLVE_ONE_SAMPLE // 118
CONVOLVE_ONE_SAMPLE // 119
CONVOLVE_ONE_SAMPLE // 120
CONVOLVE_ONE_SAMPLE // 121
CONVOLVE_ONE_SAMPLE // 122
CONVOLVE_ONE_SAMPLE // 123
CONVOLVE_ONE_SAMPLE // 124
CONVOLVE_ONE_SAMPLE // 125
CONVOLVE_ONE_SAMPLE // 126
CONVOLVE_ONE_SAMPLE // 127
CONVOLVE_ONE_SAMPLE // 128
} else {
while (j < kernelSize) {
// Non-optimized using actual while loop.
CONVOLVE_ONE_SAMPLE
}
}
destP[i++] = sum;
}
// Copy 2nd half of input buffer to 1st half.
memcpy(m_buffer.Elements(), inputP, sizeof(float) * framesToProcess);
}
void DirectConvolver::reset()
{
PodZero(m_buffer.Elements(), m_buffer.Length());
}
} // namespace WebCore
@@ -1,61 +0,0 @@
/*
* Copyright (C) 2012 Intel Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DirectConvolver_h
#define DirectConvolver_h
#include "nsTArray.h"
#include "mozilla/MemoryReporting.h"
namespace WebCore {
class DirectConvolver {
public:
explicit DirectConvolver(size_t inputBlockSize);
void process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
void reset();
size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t amount = aMallocSizeOf(this);
amount += m_buffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
return amount;
}
private:
size_t m_inputBlockSize;
nsTArray<float> m_buffer;
};
} // namespace WebCore
#endif // DirectConvolver_h
@@ -30,9 +30,9 @@
#include "DenormalDisabler.h"
#include <algorithm>
#include <cmath>
#include "mozilla/FloatingPoint.h"
#include "mozilla/Constants.h"
#include "WebAudioUtils.h"
using namespace std;
+37 -50
View File
@@ -33,10 +33,11 @@ using namespace mozilla;
namespace WebCore {
FFTConvolver::FFTConvolver(size_t fftSize)
FFTConvolver::FFTConvolver(size_t fftSize, size_t renderPhase)
: m_frame(fftSize)
, m_readWriteIndex(0)
, m_readWriteIndex(renderPhase % (fftSize / 2))
{
MOZ_ASSERT(fftSize >= 2 * WEBAUDIO_BLOCK_SIZE);
m_inputBuffer.SetLength(fftSize);
PodZero(m_inputBuffer.Elements(), fftSize);
m_outputBuffer.SetLength(fftSize);
@@ -60,67 +61,47 @@ size_t FFTConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) co
return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
}
void FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess)
const float* FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP)
{
size_t halfSize = fftSize() / 2;
// framesToProcess must be an exact multiple of halfSize,
// or halfSize is a multiple of framesToProcess when halfSize > framesToProcess.
bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize);
MOZ_ASSERT(isGood);
if (!isGood)
return;
// WEBAUDIO_BLOCK_SIZE must be an exact multiple of halfSize,
// halfSize must be a multiple of WEBAUDIO_BLOCK_SIZE
// and > WEBAUDIO_BLOCK_SIZE.
MOZ_ASSERT(halfSize % WEBAUDIO_BLOCK_SIZE == 0 &&
WEBAUDIO_BLOCK_SIZE <= halfSize);
size_t numberOfDivisions = halfSize <= framesToProcess ? (framesToProcess / halfSize) : 1;
size_t divisionSize = numberOfDivisions == 1 ? framesToProcess : halfSize;
// Copy samples to input buffer (note contraint above!)
float* inputP = m_inputBuffer.Elements();
for (size_t i = 0; i < numberOfDivisions; ++i, sourceP += divisionSize, destP += divisionSize) {
// Copy samples to input buffer (note contraint above!)
float* inputP = m_inputBuffer.Elements();
MOZ_ASSERT(sourceP && inputP && m_readWriteIndex + WEBAUDIO_BLOCK_SIZE <= m_inputBuffer.Length());
// Sanity check
bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.Length();
MOZ_ASSERT(isCopyGood1);
if (!isCopyGood1)
return;
memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * WEBAUDIO_BLOCK_SIZE);
memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * divisionSize);
float* outputP = m_outputBuffer.Elements();
m_readWriteIndex += WEBAUDIO_BLOCK_SIZE;
// Copy samples from output buffer
float* outputP = m_outputBuffer.Elements();
// Check if it's time to perform the next FFT
if (m_readWriteIndex == halfSize) {
// The input buffer is now filled (get frequency-domain version)
m_frame.PerformFFT(m_inputBuffer.Elements());
m_frame.Multiply(*fftKernel);
m_frame.GetInverseWithoutScaling(m_outputBuffer.Elements());
// Sanity check
bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.Length();
MOZ_ASSERT(isCopyGood2);
if (!isCopyGood2)
return;
// Overlap-add 1st half from previous time
AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f,
m_outputBuffer.Elements(), halfSize);
memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * divisionSize);
m_readWriteIndex += divisionSize;
// Finally, save 2nd half of result
MOZ_ASSERT(m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize);
// Check if it's time to perform the next FFT
if (m_readWriteIndex == halfSize) {
// The input buffer is now filled (get frequency-domain version)
m_frame.PerformFFT(m_inputBuffer.Elements());
m_frame.Multiply(*fftKernel);
m_frame.GetInverseWithoutScaling(m_outputBuffer.Elements());
memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize);
// Overlap-add 1st half from previous time
AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f,
m_outputBuffer.Elements(), halfSize);
// Finally, save 2nd half of result
bool isCopyGood3 = m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize;
MOZ_ASSERT(isCopyGood3);
if (!isCopyGood3)
return;
memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize);
// Reset index back to start for next time
m_readWriteIndex = 0;
}
// Reset index back to start for next time
m_readWriteIndex = 0;
}
return outputP + m_readWriteIndex;
}
void FFTConvolver::reset()
@@ -129,4 +110,10 @@ void FFTConvolver::reset()
m_readWriteIndex = 0;
}
size_t FFTConvolver::latencyFrames() const
{
return std::max<size_t>(fftSize()/2, WEBAUDIO_BLOCK_SIZE) -
WEBAUDIO_BLOCK_SIZE;
}
} // namespace WebCore
+14 -9
View File
@@ -40,24 +40,29 @@ using mozilla::FFTBlock;
class FFTConvolver {
public:
// fftSize must be a power of two
explicit FFTConvolver(size_t fftSize);
// |fftSize| must be a power of two.
//
// |renderPhase| is the initial offset in the initially zero input buffer.
// It is coordinated with the other stages, so they don't all do their
// FFTs at the same time.
explicit FFTConvolver(size_t fftSize, size_t renderPhase = 0);
// Process WEBAUDIO_BLOCK_SIZE elements of array |sourceP| and return a
// pointer to an output array of the same size.
//
// |fftKernel| must be pre-scaled for FFTBlock::GetInverseWithoutScaling().
//
// For now, with multiple calls to Process(), framesToProcess MUST add up EXACTLY to fftSize / 2
//
// FIXME: Later, we can do more sophisticated buffering to relax this requirement...
//
// The input to output latency is equal to fftSize / 2
//
// Processing in-place is allowed...
void process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess);
const float* process(FFTBlock* fftKernel, const float* sourceP);
void reset();
size_t fftSize() const { return m_frame.FFTSize(); }
// The input to output latency is up to fftSize / 2, but alignment of the
// FFTs with the blocks reduces this by one block.
size_t latencyFrames() const;
size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+8 -8
View File
@@ -76,19 +76,19 @@ size_t HRTFElevation::fftSizeForSampleRate(float sampleRate)
// This is the size if we were to use all raw response samples.
unsigned resampledLength =
floorf(ResponseFrameSize * sampleRate / rawSampleRate);
// Keep things semi-sane, with max FFT size of 1024 and minimum of 4.
// "size |= 3" ensures a minimum of 4 (with the size++ below) and sets the
// 2 least significant bits for rounding up to the next power of 2 below.
// Keep things semi-sane, with max FFT size of 1024.
unsigned size = min(resampledLength, 1023U);
size |= 3;
// Ensure a minimum of 2 * WEBAUDIO_BLOCK_SIZE (with the size++ below) for
// FFTConvolver and set the 8 least significant bits for rounding up to
// the next power of 2 below.
size |= 2 * WEBAUDIO_BLOCK_SIZE - 1;
// Round up to the next power of 2, making the FFT size no more than twice
// the impulse response length. This doubles size for values that are
// already powers of 2. This works by filling in 7 bits to right of the
// already powers of 2. This works by filling in alls bit to right of the
// most significant bit. The most significant bit is no greater than
// 1 << 9, and the least significant 2 bits were already set above.
// 1 << 9, and the least significant 8 bits were already set above, so
// there is at most one bit to add.
size |= (size >> 1);
size |= (size >> 2);
size |= (size >> 4);
size++;
MOZ_ASSERT((size & (size - 1)) == 0);
+30 -22
View File
@@ -59,11 +59,6 @@ HRTFPanner::HRTFPanner(float sampleRate, already_AddRefed<HRTFDatabaseLoader> da
{
MOZ_ASSERT(m_databaseLoader);
MOZ_COUNT_CTOR(HRTFPanner);
m_tempL1.SetLength(RenderingQuantum);
m_tempR1.SetLength(RenderingQuantum);
m_tempL2.SetLength(RenderingQuantum);
m_tempR2.SetLength(RenderingQuantum);
}
HRTFPanner::~HRTFPanner()
@@ -81,10 +76,6 @@ size_t HRTFPanner::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) cons
amount += m_convolverL2.sizeOfExcludingThis(aMallocSizeOf);
amount += m_convolverR2.sizeOfExcludingThis(aMallocSizeOf);
amount += m_delayLine.SizeOfExcludingThis(aMallocSizeOf);
amount += m_tempL1.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += m_tempL2.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += m_tempR1.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += m_tempR2.ShallowSizeOfExcludingThis(aMallocSizeOf);
return amount;
}
@@ -256,23 +247,26 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioBlock*
bool needsCrossfading = m_crossfadeIncr;
// Have the convolvers render directly to the final destination if we're not cross-fading.
float* convolutionDestinationL1 = needsCrossfading ? m_tempL1.Elements() : destinationL;
float* convolutionDestinationR1 = needsCrossfading ? m_tempR1.Elements() : destinationR;
float* convolutionDestinationL2 = needsCrossfading ? m_tempL2.Elements() : destinationL;
float* convolutionDestinationR2 = needsCrossfading ? m_tempR2.Elements() : destinationR;
const float* convolutionDestinationL1;
const float* convolutionDestinationR1;
const float* convolutionDestinationL2;
const float* convolutionDestinationR2;
// Now do the convolutions.
// Note that we avoid doing convolutions on both sets of convolvers if we're not currently cross-fading.
if (m_crossfadeSelection == CrossfadeSelection1 || needsCrossfading) {
m_convolverL1.process(kernelL1->fftFrame(), destinationL, convolutionDestinationL1, WEBAUDIO_BLOCK_SIZE);
m_convolverR1.process(kernelR1->fftFrame(), destinationR, convolutionDestinationR1, WEBAUDIO_BLOCK_SIZE);
convolutionDestinationL1 =
m_convolverL1.process(kernelL1->fftFrame(), destinationL);
convolutionDestinationR1 =
m_convolverR1.process(kernelR1->fftFrame(), destinationR);
}
if (m_crossfadeSelection == CrossfadeSelection2 || needsCrossfading) {
m_convolverL2.process(kernelL2->fftFrame(), destinationL, convolutionDestinationL2, WEBAUDIO_BLOCK_SIZE);
m_convolverR2.process(kernelR2->fftFrame(), destinationR, convolutionDestinationR2, WEBAUDIO_BLOCK_SIZE);
convolutionDestinationL2 =
m_convolverL2.process(kernelL2->fftFrame(), destinationL);
convolutionDestinationR2 =
m_convolverR2.process(kernelR2->fftFrame(), destinationR);
}
if (needsCrossfading) {
@@ -298,6 +292,18 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioBlock*
m_crossfadeX = 0;
m_crossfadeIncr = 0;
}
} else {
const float* sourceL;
const float* sourceR;
if (m_crossfadeSelection == CrossfadeSelection1) {
sourceL = convolutionDestinationL1;
sourceR = convolutionDestinationR1;
} else {
sourceL = convolutionDestinationL2;
sourceR = convolutionDestinationR2;
}
PodCopy(destinationL, sourceL, WEBAUDIO_BLOCK_SIZE);
PodCopy(destinationR, sourceR, WEBAUDIO_BLOCK_SIZE);
}
}
@@ -307,10 +313,12 @@ int HRTFPanner::maxTailFrames() const
// response, there is additional tail time from the approximations in the
// implementation. Because HRTFPanner is implemented with a DelayKernel
// and a FFTConvolver, the tailTime of the HRTFPanner is the sum of the
// tailTime of the DelayKernel and the tailTime of the FFTConvolver.
// The FFTConvolver has a tail time of fftSize(), including latency of
// fftSize()/2.
return m_delayLine.MaxDelayTicks() + fftSize();
// tailTime of the DelayKernel and the tailTime of the FFTConvolver. The
// FFTs of the convolver are fftSize(), half of which is latency, but this
// is aligned with blocks and so is reduced by the one block which is
// processed immediately.
return m_delayLine.MaxDelayTicks() +
m_convolverL1.fftSize()/2 + m_convolverL1.latencyFrames();
}
} // namespace WebCore
+137 -110
View File
@@ -29,10 +29,11 @@
#include "PeriodicWave.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include "mozilla/FFTBlock.h"
const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two.
const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two.
const float CentsPerRange = 1200 / 3; // 1/3 Octave.
using namespace mozilla;
@@ -40,57 +41,87 @@ using mozilla::dom::OscillatorType;
namespace WebCore {
PeriodicWave* PeriodicWave::create(float sampleRate,
const float* real,
const float* imag,
size_t numberOfComponents)
already_AddRefed<PeriodicWave>
PeriodicWave::create(float sampleRate,
const float* real,
const float* imag,
size_t numberOfComponents)
{
bool isGood = real && imag && numberOfComponents > 0 &&
numberOfComponents <= PeriodicWaveSize;
bool isGood = real && imag && numberOfComponents > 0;
MOZ_ASSERT(isGood);
if (isGood) {
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
return periodicWave;
RefPtr<PeriodicWave> periodicWave =
new PeriodicWave(sampleRate, numberOfComponents);
// Limit the number of components used to those for frequencies below the
// Nyquist of the fixed length inverse FFT.
size_t halfSize = periodicWave->m_periodicWaveSize / 2;
numberOfComponents = std::min(numberOfComponents, halfSize);
periodicWave->m_numberOfComponents = numberOfComponents;
periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents);
periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents);
memcpy(periodicWave->m_realComponents->Elements(), real,
numberOfComponents * sizeof(float));
memcpy(periodicWave->m_imagComponents->Elements(), imag,
numberOfComponents * sizeof(float));
return periodicWave.forget();
}
return 0;
return nullptr;
}
PeriodicWave* PeriodicWave::createSine(float sampleRate)
already_AddRefed<PeriodicWave>
PeriodicWave::createSine(float sampleRate)
{
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
periodicWave->generateBasicWaveform(OscillatorType::Sine);
return periodicWave;
RefPtr<PeriodicWave> periodicWave =
new PeriodicWave(sampleRate, MinPeriodicWaveSize);
periodicWave->generateBasicWaveform(OscillatorType::Sine);
return periodicWave.forget();
}
PeriodicWave* PeriodicWave::createSquare(float sampleRate)
already_AddRefed<PeriodicWave>
PeriodicWave::createSquare(float sampleRate)
{
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
periodicWave->generateBasicWaveform(OscillatorType::Square);
return periodicWave;
RefPtr<PeriodicWave> periodicWave =
new PeriodicWave(sampleRate, MinPeriodicWaveSize);
periodicWave->generateBasicWaveform(OscillatorType::Square);
return periodicWave.forget();
}
PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
already_AddRefed<PeriodicWave>
PeriodicWave::createSawtooth(float sampleRate)
{
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
return periodicWave;
RefPtr<PeriodicWave> periodicWave =
new PeriodicWave(sampleRate, MinPeriodicWaveSize);
periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
return periodicWave.forget();
}
PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
already_AddRefed<PeriodicWave>
PeriodicWave::createTriangle(float sampleRate)
{
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
periodicWave->generateBasicWaveform(OscillatorType::Triangle);
return periodicWave;
RefPtr<PeriodicWave> periodicWave =
new PeriodicWave(sampleRate, MinPeriodicWaveSize);
periodicWave->generateBasicWaveform(OscillatorType::Triangle);
return periodicWave.forget();
}
PeriodicWave::PeriodicWave(float sampleRate)
PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents)
: m_sampleRate(sampleRate)
, m_periodicWaveSize(PeriodicWaveSize)
, m_numberOfRanges(NumberOfRanges)
, m_centsPerRange(CentsPerRange)
, m_lowestRequestedFundamentalFrequency(std::numeric_limits<float>::max())
{
float nyquist = 0.5 * m_sampleRate;
if (numberOfComponents <= MinPeriodicWaveSize) {
m_periodicWaveSize = MinPeriodicWaveSize;
} else {
unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
}
m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f));
m_bandLimitedTables.SetLength(m_numberOfRanges);
m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
m_rateScale = m_periodicWaveSize / m_sampleRate;
}
@@ -111,10 +142,22 @@ size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) co
void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
{
// Negative frequencies are allowed, in which case we alias
// to the positive frequency.
fundamentalFrequency = fabsf(fundamentalFrequency);
if (fundamentalFrequency < m_lowestRequestedFundamentalFrequency) {
for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
m_bandLimitedTables[rangeIndex] = 0;
}
// We need to create the first table to determine the normalization
// constant.
createBandLimitedTables(fundamentalFrequency, 0);
m_lowestRequestedFundamentalFrequency = fundamentalFrequency;
}
// Calculate the pitch range.
float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
@@ -133,11 +176,17 @@ void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, f
unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
if (!m_bandLimitedTables[rangeIndex1].get())
createBandLimitedTables(fundamentalFrequency, rangeIndex1);
if (!m_bandLimitedTables[rangeIndex2].get())
createBandLimitedTables(fundamentalFrequency, rangeIndex2);
lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
// Ranges from 0 -> 1 to interpolate between lower -> higher.
tableInterpolationFactor = pitchRange - rangeIndex1;
tableInterpolationFactor = rangeIndex2 - pitchRange;
}
unsigned PeriodicWave::maxNumberOfPartials() const
@@ -163,101 +212,81 @@ unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
// One table is created for each range for non-aliasing playback
// at different playback rates. Thus, higher ranges have more
// high-frequency partials culled out.
void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
void PeriodicWave::createBandLimitedTables(float fundamentalFrequency,
unsigned rangeIndex)
{
float normalizationScale = 1;
unsigned fftSize = m_periodicWaveSize;
unsigned halfSize = fftSize / 2 + 1;
unsigned i;
numberOfComponents = std::min(numberOfComponents, halfSize);
const float *realData = m_realComponents->Elements();
const float *imagData = m_imagComponents->Elements();
m_bandLimitedTables.SetCapacity(m_numberOfRanges);
// This FFTBlock is used to cull partials (represented by frequency bins).
FFTBlock frame(fftSize);
for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
// This FFTBlock is used to cull partials (represented by frequency bins).
FFTBlock frame(fftSize);
nsAutoArrayPtr<float> realP(new float[halfSize]);
nsAutoArrayPtr<float> imagP(new float[halfSize]);
// Find the starting bin where we should start culling the aliasing
// partials for this pitch range. We need to clear out the highest
// frequencies to band-limit the waveform.
unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
// Also limit to the number of components that are provided.
numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1);
// Copy from loaded frequency data and scale.
float scale = fftSize;
AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
// Limit number of partials to those below Nyquist frequency
float nyquist = 0.5 * m_sampleRate;
numberOfPartials = std::min(numberOfPartials,
(unsigned)(nyquist / fundamentalFrequency));
// If fewer components were provided than 1/2 FFT size,
// then clear the remaining bins.
for (i = numberOfComponents; i < halfSize; ++i) {
realP[i] = 0;
imagP[i] = 0;
}
// Generate complex conjugate because of the way the
// inverse FFT is defined.
float minusOne = -1;
AudioBufferInPlaceScale(imagP, minusOne, halfSize);
// Find the starting bin where we should start culling.
// We need to clear out the highest frequencies to band-limit
// the waveform.
unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
// Cull the aliasing partials for this pitch range.
for (i = numberOfPartials + 1; i < halfSize; ++i) {
realP[i] = 0;
imagP[i] = 0;
}
// Clear nyquist if necessary.
if (numberOfPartials < halfSize)
realP[halfSize-1] = 0;
// Clear any DC-offset.
realP[0] = 0;
// Clear values which have no effect.
imagP[0] = 0;
imagP[halfSize-1] = 0;
// Create the band-limited table.
AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize);
m_bandLimitedTables.AppendElement(table);
// Apply an inverse FFT to generate the time-domain table data.
float* data = m_bandLimitedTables[rangeIndex]->Elements();
frame.PerformInverseFFT(realP, imagP, data);
// For the first range (which has the highest power), calculate
// its peak value then compute normalization scale.
if (!rangeIndex) {
float maxValue;
maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
if (maxValue)
normalizationScale = 1.0f / maxValue;
}
// Apply normalization scale.
AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize);
// Copy from loaded frequency data and generate complex conjugate
// because of the way the inverse FFT is defined.
// The coefficients of higher partials remain zero, as initialized in
// the FFTBlock constructor.
for (i = 0; i < numberOfPartials + 1; ++i) {
frame.RealData(i) = realData[i];
frame.ImagData(i) = -imagData[i];
}
// Clear any DC-offset.
frame.RealData(0) = 0;
// Clear value which has no effect.
frame.ImagData(0) = 0;
// Create the band-limited table.
AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize);
m_bandLimitedTables[rangeIndex] = table;
// Apply an inverse FFT to generate the time-domain table data.
float* data = m_bandLimitedTables[rangeIndex]->Elements();
frame.GetInverseWithoutScaling(data);
// For the first range (which has the highest power), calculate
// its peak value then compute normalization scale.
if (!rangeIndex) {
float maxValue;
maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
if (maxValue)
m_normalizationScale = 1.0f / maxValue;
}
// Apply normalization scale.
AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
}
void PeriodicWave::generateBasicWaveform(OscillatorType shape)
{
const float piFloat = float(M_PI);
unsigned fftSize = periodicWaveSize();
unsigned halfSize = fftSize / 2 + 1;
unsigned halfSize = fftSize / 2;
AudioFloatArray real(halfSize);
AudioFloatArray imag(halfSize);
float* realP = real.Elements();
float* imagP = imag.Elements();
m_numberOfComponents = halfSize;
m_realComponents = new AudioFloatArray(halfSize);
m_imagComponents = new AudioFloatArray(halfSize);
float* realP = m_realComponents->Elements();
float* imagP = m_imagComponents->Elements();
// Clear DC and Nyquist.
// Clear DC and imag value which is ignored.
realP[0] = 0;
imagP[0] = 0;
realP[halfSize-1] = 0;
imagP[halfSize-1] = 0;
for (unsigned n = 1; n < halfSize; ++n) {
float omega = 2 * piFloat * n;
@@ -308,8 +337,6 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape)
realP[n] = a;
imagP[n] = b;
}
createBandLimitedTables(realP, imagP, halfSize);
}
} // namespace WebCore
+17 -13
View File
@@ -42,20 +42,19 @@ typedef nsTArray<float> AudioFloatArray;
class PeriodicWave {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebCore::PeriodicWave);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PeriodicWave);
static PeriodicWave* createSine(float sampleRate);
static PeriodicWave* createSquare(float sampleRate);
static PeriodicWave* createSawtooth(float sampleRate);
static PeriodicWave* createTriangle(float sampleRate);
static already_AddRefed<PeriodicWave> createSine(float sampleRate);
static already_AddRefed<PeriodicWave> createSquare(float sampleRate);
static already_AddRefed<PeriodicWave> createSawtooth(float sampleRate);
static already_AddRefed<PeriodicWave> createTriangle(float sampleRate);
// Creates an arbitrary periodic wave given the frequency components
// (Fourier coefficients).
static PeriodicWave* create(float sampleRate,
const float* real,
const float* imag,
size_t numberOfComponents);
static already_AddRefed<PeriodicWave> create(float sampleRate,
const float* real,
const float* imag,
size_t numberOfComponents);
// Returns pointers to the lower and higher wave data for the pitch range
// containing the given fundamental frequency. These two tables are in
@@ -77,7 +76,7 @@ public:
size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
explicit PeriodicWave(float sampleRate);
explicit PeriodicWave(float sampleRate, size_t numberOfComponents);
~PeriodicWave() {}
void generateBasicWaveform(mozilla::dom::OscillatorType);
@@ -86,6 +85,9 @@ private:
unsigned m_periodicWaveSize;
unsigned m_numberOfRanges;
float m_centsPerRange;
unsigned m_numberOfComponents;
nsAutoPtr<AudioFloatArray> m_realComponents;
nsAutoPtr<AudioFloatArray> m_imagComponents;
// The lowest frequency (in Hertz) where playback will include all of the
// partials. Playing back lower than this frequency will gradually lose
@@ -102,8 +104,10 @@ private:
unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
// Creates tables based on numberOfComponents Fourier coefficients.
void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents);
// Creates table for specified index based on fundamental frequency.
void createBandLimitedTables(float fundamentalFrequency, unsigned rangeIndex);
float m_lowestRequestedFundamentalFrequency;
float m_normalizationScale;
nsTArray<nsAutoPtr<AlignedAudioFloatArray> > m_bandLimitedTables;
};
+22 -33
View File
@@ -77,7 +77,7 @@ static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* respo
return scale;
}
Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate)
Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate)
{
float scale = 1;
@@ -101,7 +101,7 @@ Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulse
}
}
initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
initialize(irChannels, impulseResponseBufferLength,
maxFFTSize, numberOfChannels, useBackgroundThreads);
}
@@ -121,7 +121,7 @@ size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
size_t impulseResponseBufferLength, size_t renderSliceSize,
size_t impulseResponseBufferLength,
size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
{
m_impulseResponseLength = impulseResponseBufferLength;
@@ -135,10 +135,10 @@ void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
const float* channel = impulseResponseBuffer[i];
size_t length = impulseResponseBufferLength;
nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
m_convolvers.AppendElement(convolver.forget());
convolverRenderPhase += renderSliceSize;
convolverRenderPhase += WEBAUDIO_BLOCK_SIZE;
}
// For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
@@ -149,12 +149,12 @@ void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
}
}
void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, size_t framesToProcess)
void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus)
{
// Do a fairly comprehensive sanity check.
// If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
bool isSafeToProcess = sourceBus && destinationBus && sourceBus->ChannelCount() > 0 && destinationBus->mChannelData.Length() > 0
&& framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->GetDuration()) && framesToProcess <= size_t(destinationBus->GetDuration());
&& WEBAUDIO_BLOCK_SIZE <= MaxFrameSize && WEBAUDIO_BLOCK_SIZE <= size_t(sourceBus->GetDuration()) && WEBAUDIO_BLOCK_SIZE <= size_t(destinationBus->GetDuration());
MOZ_ASSERT(isSafeToProcess);
if (!isSafeToProcess)
@@ -175,28 +175,28 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si
// 2 -> 2 -> 2
const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess);
m_convolvers[1]->process(sourceBusR, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess);
m_convolvers[0]->process(sourceBusL, destinationChannelL);
m_convolvers[1]->process(sourceBusR, destinationChannelR);
} else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
// 1 -> 2 -> 2
for (int i = 0; i < 2; ++i) {
float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i]));
m_convolvers[i]->process(sourceBusL, sourceBus->GetDuration(), destinationChannel, destinationBus->GetDuration(), framesToProcess);
m_convolvers[i]->process(sourceBusL, destinationChannel);
}
} else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
// 1 -> 1 -> 2
m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess);
m_convolvers[0]->process(sourceBusL, destinationChannelL);
// simply copy L -> R
float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->GetDuration()) >= framesToProcess;
bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->GetDuration()) >= WEBAUDIO_BLOCK_SIZE;
MOZ_ASSERT(isCopySafe);
if (!isCopySafe)
return;
PodCopy(destinationChannelR, destinationChannelL, framesToProcess);
PodCopy(destinationChannelR, destinationChannelL, WEBAUDIO_BLOCK_SIZE);
} else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
// 1 -> 1 -> 1
m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess);
m_convolvers[0]->process(sourceBusL, destinationChannelL);
} else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
// 2 -> 4 -> 2 ("True" stereo)
const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
@@ -206,12 +206,12 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si
float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
// Process left virtual source
m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess);
m_convolvers[1]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess);
m_convolvers[0]->process(sourceBusL, destinationChannelL);
m_convolvers[1]->process(sourceBusL, destinationChannelR);
// Process right virtual source
m_convolvers[2]->process(sourceBusR, sourceBus->GetDuration(), tempChannelL, m_tempBuffer.GetDuration(), framesToProcess);
m_convolvers[3]->process(sourceBusR, sourceBus->GetDuration(), tempChannelR, m_tempBuffer.GetDuration(), framesToProcess);
m_convolvers[2]->process(sourceBusR, tempChannelL);
m_convolvers[3]->process(sourceBusR, tempChannelR);
AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
@@ -224,12 +224,12 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si
float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
// Process left virtual source
m_convolvers[0]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelL, destinationBus->GetDuration(), framesToProcess);
m_convolvers[1]->process(sourceBusL, sourceBus->GetDuration(), destinationChannelR, destinationBus->GetDuration(), framesToProcess);
m_convolvers[0]->process(sourceBusL, destinationChannelL);
m_convolvers[1]->process(sourceBusL, destinationChannelR);
// Process right virtual source
m_convolvers[2]->process(sourceBusL, sourceBus->GetDuration(), tempChannelL, m_tempBuffer.GetDuration(), framesToProcess);
m_convolvers[3]->process(sourceBusL, sourceBus->GetDuration(), tempChannelR, m_tempBuffer.GetDuration(), framesToProcess);
m_convolvers[2]->process(sourceBusL, tempChannelL);
m_convolvers[3]->process(sourceBusL, tempChannelR);
AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
@@ -240,15 +240,4 @@ void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus, si
}
}
void Reverb::reset()
{
for (size_t i = 0; i < m_convolvers.Length(); ++i)
m_convolvers[i]->reset();
}
size_t Reverb::latencyFrames() const
{
return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0;
}
} // namespace WebCore
+9 -5
View File
@@ -48,18 +48,22 @@ public:
enum { MaxFrameSize = 256 };
// renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread).
Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate);
Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer,
size_t impulseResponseBufferLength, size_t maxFFTSize,
size_t numberOfChannels, bool useBackgroundThreads, bool normalize,
float sampleRate);
void process(const mozilla::AudioBlock* sourceBus, mozilla::AudioBlock* destinationBus, size_t framesToProcess);
void reset();
void process(const mozilla::AudioBlock* sourceBus,
mozilla::AudioBlock* destinationBus);
size_t impulseResponseLength() const { return m_impulseResponseLength; }
size_t latencyFrames() const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
void initialize(const nsTArray<const float*>& impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
void initialize(const nsTArray<const float*>& impulseResponseBuffer,
size_t impulseResponseBufferLength, size_t maxFFTSize,
size_t numberOfChannels, bool useBackgroundThreads);
size_t m_impulseResponseLength;
@@ -76,7 +76,7 @@ void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFr
*readIndex = (*readIndex + numberOfFrames) % m_buffer.Length();
}
int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames)
int ReverbAccumulationBuffer::accumulate(const float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames)
{
size_t bufferLength = m_buffer.Length();
@@ -50,7 +50,7 @@ public:
// We need to pass in and update readIndex here, since each ReverbConvolverStage may be running in
// a different thread than the realtime thread calling ReadAndClear() and maintaining m_readIndex
// Returns the writeIndex where the accumulation took place
int accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames);
int accumulate(const float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames);
size_t readIndex() const { return m_readIndex; }
void updateReadIndex(int* readIndex, size_t numberOfFrames) const;
+66 -63
View File
@@ -49,29 +49,32 @@ const int InputBufferSize = 8 * 16384;
// This was found to be a good value on Mac OS X, and may work well on other platforms as well, assuming
// the very rough scheduling latencies are similar on these time-scales. Of course, this code may need to be
// tuned for individual platforms if this assumption is found to be incorrect.
const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz
const size_t RealtimeFrameLimit = 8192 + 4096 // ~278msec @ 44.1KHz
- WEBAUDIO_BLOCK_SIZE;
// First stage will have size MinFFTSize - successive stages will double in
// size each time until we hit the maximum size.
const size_t MinFFTSize = 256;
// If we are using background threads then don't exceed this FFT size for the
// stages which run in the real-time thread. This avoids having only one or
// two large stages (size 16384 or so) at the end which take a lot of time
// every several processing slices. This way we amortize the cost over more
// processing slices.
const size_t MaxRealtimeFFTSize = 4096;
const size_t MinFFTSize = 128;
const size_t MaxRealtimeFFTSize = 2048;
ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads)
ReverbConvolver::ReverbConvolver(const float* impulseResponseData,
size_t impulseResponseLength,
size_t maxFFTSize,
size_t convolverRenderPhase,
bool useBackgroundThreads)
: m_impulseResponseLength(impulseResponseLength)
, m_accumulationBuffer(impulseResponseLength + renderSliceSize)
, m_accumulationBuffer(impulseResponseLength + WEBAUDIO_BLOCK_SIZE)
, m_inputBuffer(InputBufferSize)
, m_minFFTSize(MinFFTSize) // First stage will have this size - successive stages will double in size each time
, m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize
, m_backgroundThread("ConvolverWorker")
, m_backgroundThreadCondition(&m_backgroundThreadLock)
, m_useBackgroundThreads(useBackgroundThreads)
, m_wantsToExit(false)
, m_moreInputBuffered(false)
{
// If we are using background threads then don't exceed this FFT size for the
// stages which run in the real-time thread. This avoids having only one or two
// large stages (size 16384 or so) at the end which take a lot of time every several
// processing slices. This way we amortize the cost over more processing slices.
m_maxRealtimeFFTSize = MaxRealtimeFFTSize;
// For the moment, a good way to know if we have real-time constraint is to check if we're using background threads.
// Otherwise, assume we're being run from a command-line tool.
bool hasRealtimeConstraint = useBackgroundThreads;
@@ -79,26 +82,35 @@ ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impuls
const float* response = impulseResponseData;
size_t totalResponseLength = impulseResponseLength;
// The total latency is zero because the direct-convolution is used in the leading portion.
// The total latency is zero because the first FFT stage is small enough
// to return output in the first block.
size_t reverbTotalLatency = 0;
size_t stageOffset = 0;
int i = 0;
size_t fftSize = m_minFFTSize;
size_t stagePhase = 0;
size_t fftSize = MinFFTSize;
while (stageOffset < totalResponseLength) {
size_t stageSize = fftSize / 2;
// For the last stage, it's possible that stageOffset is such that we're straddling the end
// of the impulse response buffer (if we use stageSize), so reduce the last stage's length...
if (stageSize + stageOffset > totalResponseLength)
if (stageSize + stageOffset > totalResponseLength) {
stageSize = totalResponseLength - stageOffset;
// Use smallest FFT that is large enough to cover the last stage.
fftSize = MinFFTSize;
while (stageSize * 2 > fftSize) {
fftSize *= 2;
}
}
// This "staggers" the time when each FFT happens so they don't all happen at the same time
int renderPhase = convolverRenderPhase + i * renderSliceSize;
int renderPhase = convolverRenderPhase + stagePhase;
bool useDirectConvolver = !stageOffset;
nsAutoPtr<ReverbConvolverStage> stage(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver));
nsAutoPtr<ReverbConvolverStage> stage
(new ReverbConvolverStage(response, totalResponseLength,
reverbTotalLatency, stageOffset, stageSize,
fftSize, renderPhase,
&m_accumulationBuffer));
bool isBackgroundStage = false;
@@ -108,18 +120,35 @@ ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impuls
} else
m_stages.AppendElement(stage.forget());
// Figure out next FFT size
fftSize *= 2;
stageOffset += stageSize;
++i;
if (!useDirectConvolver) {
// Figure out next FFT size
fftSize *= 2;
if (hasRealtimeConstraint && !isBackgroundStage
&& fftSize > MaxRealtimeFFTSize) {
fftSize = MaxRealtimeFFTSize;
// Custom phase positions for all but the first of the realtime
// stages of largest size. These spread out the work of the
// larger realtime stages. None of the FFTs of size 1024, 2048 or
// 4096 are performed when processing the same block. The first
// MaxRealtimeFFTSize = 4096 stage, at the end of the doubling,
// performs its FFT at block 7. The FFTs of size 2048 are
// performed in blocks 3 + 8 * n and size 1024 at 1 + 4 * n.
const uint32_t phaseLookup[] = { 14, 0, 10, 4 };
stagePhase = WEBAUDIO_BLOCK_SIZE *
phaseLookup[m_stages.Length() % ArrayLength(phaseLookup)];
} else if (fftSize > maxFFTSize) {
fftSize = maxFFTSize;
// A prime offset spreads out FFTs in a way that all
// available phase positions will be used if there are sufficient
// stages.
stagePhase += 5 * WEBAUDIO_BLOCK_SIZE;
} else if (stageSize > WEBAUDIO_BLOCK_SIZE) {
// As the stages are doubling in size, the next FFT will occur
// mid-way between FFTs for this stage.
stagePhase = stageSize - WEBAUDIO_BLOCK_SIZE;
}
if (hasRealtimeConstraint && !isBackgroundStage && fftSize > m_maxRealtimeFFTSize)
fftSize = m_maxRealtimeFFTSize;
if (fftSize > m_maxFFTSize)
fftSize = m_maxFFTSize;
}
// Start up background thread
@@ -199,25 +228,16 @@ void ReverbConvolver::backgroundThreadEntry()
int readIndex;
while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != writeIndex) { // FIXME: do better to detect buffer overrun...
// The ReverbConvolverStages need to process in amounts which evenly divide half the FFT size
const int SliceSize = MinFFTSize / 2;
// Accumulate contributions from each stage
for (size_t i = 0; i < m_backgroundStages.Length(); ++i)
m_backgroundStages[i]->processInBackground(this, SliceSize);
m_backgroundStages[i]->processInBackground(this);
}
}
}
void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChannelLength,
float* destinationChannelData, size_t destinationChannelLength,
size_t framesToProcess)
void ReverbConvolver::process(const float* sourceChannelData,
float* destinationChannelData)
{
bool isSafe = sourceChannelData && destinationChannelData && sourceChannelLength >= framesToProcess && destinationChannelLength >= framesToProcess;
MOZ_ASSERT(isSafe);
if (!isSafe)
return;
const float* source = sourceChannelData;
float* destination = destinationChannelData;
bool isDataSafe = source && destination;
@@ -226,14 +246,14 @@ void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChann
return;
// Feed input buffer (read by all threads)
m_inputBuffer.write(source, framesToProcess);
m_inputBuffer.write(source, WEBAUDIO_BLOCK_SIZE);
// Accumulate contributions from each stage
for (size_t i = 0; i < m_stages.Length(); ++i)
m_stages[i]->process(source, framesToProcess);
m_stages[i]->process(source);
// Finally read from accumulation buffer
m_accumulationBuffer.readAndClear(destination, framesToProcess);
m_accumulationBuffer.readAndClear(destination, WEBAUDIO_BLOCK_SIZE);
// Now that we've buffered more input, wake up our background thread.
@@ -249,21 +269,4 @@ void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChann
}
}
void ReverbConvolver::reset()
{
for (size_t i = 0; i < m_stages.Length(); ++i)
m_stages[i]->reset();
for (size_t i = 0; i < m_backgroundStages.Length(); ++i)
m_backgroundStages[i]->reset();
m_accumulationBuffer.reset();
m_inputBuffer.reset();
}
size_t ReverbConvolver::latencyFrames() const
{
return 0;
}
} // namespace WebCore
+5 -14
View File
@@ -50,13 +50,13 @@ public:
// For certain tweaky de-convolving applications the phase errors add up quickly and lead to non-sensical results with
// larger FFT sizes and single-precision floats. In these cases 2048 is a good size.
// If not doing multi-threaded convolution, then should not go > 8192.
ReverbConvolver(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads);
ReverbConvolver(const float* impulseResponseData,
size_t impulseResponseLength, size_t maxFFTSize,
size_t convolverRenderPhase, bool useBackgroundThreads);
~ReverbConvolver();
void process(const float* sourceChannelData, size_t sourceChannelLength,
float* destinationChannelData, size_t destinationChannelLength,
size_t framesToProcess);
void reset();
void process(const float* sourceChannelData,
float* destinationChannelData);
size_t impulseResponseLength() const { return m_impulseResponseLength; }
@@ -65,8 +65,6 @@ public:
bool useBackgroundThreads() const { return m_useBackgroundThreads; }
void backgroundThreadEntry();
size_t latencyFrames() const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
nsTArray<nsAutoPtr<ReverbConvolverStage> > m_stages;
@@ -78,13 +76,6 @@ private:
// One or more background threads read from this input buffer which is fed from the realtime thread.
ReverbInputBuffer m_inputBuffer;
// First stage will be of size m_minFFTSize. Each next stage will be twice as big until we hit m_maxFFTSize.
size_t m_minFFTSize;
size_t m_maxFFTSize;
// But don't exceed this size in the real-time thread (if we're doing background processing).
size_t m_maxRealtimeFFTSize;
// Background thread and synchronization
base::Thread m_backgroundThread;
Lock m_backgroundThreadLock;
+26 -118
View File
@@ -37,54 +37,32 @@ using namespace mozilla;
namespace WebCore {
ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength,
size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer, bool directMode)
ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t,
size_t reverbTotalLatency,
size_t stageOffset,
size_t stageLength,
size_t fftSize, size_t renderPhase,
ReverbAccumulationBuffer* accumulationBuffer)
: m_accumulationBuffer(accumulationBuffer)
, m_accumulationReadIndex(0)
, m_inputReadIndex(0)
, m_directMode(directMode)
{
MOZ_ASSERT(impulseResponse);
MOZ_ASSERT(accumulationBuffer);
if (!m_directMode) {
m_fftKernel = new FFTBlock(fftSize);
m_fftKernel->PadAndMakeScaledDFT(impulseResponse + stageOffset, stageLength);
m_fftConvolver = new FFTConvolver(fftSize);
} else {
m_directKernel.SetLength(fftSize / 2);
PodCopy(m_directKernel.Elements(), impulseResponse + stageOffset, fftSize / 2);
m_directConvolver = new DirectConvolver(renderSliceSize);
}
m_temporaryBuffer.SetLength(renderSliceSize);
PodZero(m_temporaryBuffer.Elements(), m_temporaryBuffer.Length());
m_fftKernel = new FFTBlock(fftSize);
m_fftKernel->PadAndMakeScaledDFT(impulseResponse + stageOffset, stageLength);
m_fftConvolver = new FFTConvolver(fftSize, renderPhase);
// The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset.
size_t totalDelay = stageOffset + reverbTotalLatency;
// But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out...
size_t halfSize = fftSize / 2;
if (!m_directMode) {
MOZ_ASSERT(totalDelay >= halfSize);
if (totalDelay >= halfSize)
totalDelay -= halfSize;
}
// But, the FFT convolution itself incurs latency, so subtract this out...
size_t fftLatency = m_fftConvolver->latencyFrames();
MOZ_ASSERT(totalDelay >= fftLatency);
totalDelay -= fftLatency;
// We divide up the total delay, into pre and post delay sections so that we can schedule at exactly the moment when the FFT will happen.
// This is coordinated with the other stages, so they don't all do their FFTs at the same time...
int maxPreDelayLength = std::min(halfSize, totalDelay);
m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0;
if (m_preDelayLength > totalDelay)
m_preDelayLength = 0;
m_postDelayLength = totalDelay - m_preDelayLength;
m_preReadWriteIndex = 0;
m_framesProcessed = 0; // total frames processed so far
size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
m_preDelayBuffer.SetLength(delayBufferSize);
PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
m_postDelayLength = totalDelay;
}
size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
@@ -99,101 +77,31 @@ size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSi
amount += m_fftConvolver->sizeOfIncludingThis(aMallocSizeOf);
}
amount += m_preDelayBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += m_temporaryBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
amount += m_directKernel.ShallowSizeOfExcludingThis(aMallocSizeOf);
if (m_directConvolver) {
amount += m_directConvolver->sizeOfIncludingThis(aMallocSizeOf);
}
return amount;
}
void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess)
void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver)
{
ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
process(source, framesToProcess);
float* source = inputBuffer->directReadFrom(&m_inputReadIndex,
WEBAUDIO_BLOCK_SIZE);
process(source);
}
void ReverbConvolverStage::process(const float* source, size_t framesToProcess)
void ReverbConvolverStage::process(const float* source)
{
MOZ_ASSERT(source);
if (!source)
return;
// Deal with pre-delay stream : note special handling of zero delay.
// Now, run the convolution (into the delay buffer).
// An expensive FFT will happen every fftSize / 2 frames.
const float* output = m_fftConvolver->process(m_fftKernel, source);
const float* preDelayedSource;
float* preDelayedDestination;
float* temporaryBuffer;
bool isTemporaryBufferSafe = false;
if (m_preDelayLength > 0) {
// Handles both the read case (call to process() ) and the write case (memcpy() )
bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.Length();
MOZ_ASSERT(isPreDelaySafe);
if (!isPreDelaySafe)
return;
isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.Length();
preDelayedDestination = m_preDelayBuffer.Elements() + m_preReadWriteIndex;
preDelayedSource = preDelayedDestination;
temporaryBuffer = m_temporaryBuffer.Elements();
} else {
// Zero delay
preDelayedDestination = 0;
preDelayedSource = source;
temporaryBuffer = m_preDelayBuffer.Elements();
isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.Length();
}
MOZ_ASSERT(isTemporaryBufferSafe);
if (!isTemporaryBufferSafe)
return;
if (m_framesProcessed < m_preDelayLength) {
// For the first m_preDelayLength frames don't process the convolver, instead simply buffer in the pre-delay.
// But while buffering the pre-delay, we still need to update our index.
m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex, framesToProcess);
} else {
// Now, run the convolution (into the delay buffer).
// An expensive FFT will happen every fftSize / 2 frames.
// We process in-place here...
if (!m_directMode)
m_fftConvolver->process(m_fftKernel, preDelayedSource, temporaryBuffer, framesToProcess);
else
m_directConvolver->process(&m_directKernel, preDelayedSource, temporaryBuffer, framesToProcess);
// Now accumulate into reverb's accumulation buffer.
m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength);
}
// Finally copy input to pre-delay.
if (m_preDelayLength > 0) {
memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess);
m_preReadWriteIndex += framesToProcess;
MOZ_ASSERT(m_preReadWriteIndex <= m_preDelayLength);
if (m_preReadWriteIndex >= m_preDelayLength)
m_preReadWriteIndex = 0;
}
m_framesProcessed += framesToProcess;
}
void ReverbConvolverStage::reset()
{
if (!m_directMode)
m_fftConvolver->reset();
else
m_directConvolver->reset();
PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
m_accumulationReadIndex = 0;
m_inputReadIndex = 0;
m_framesProcessed = 0;
// Now accumulate into reverb's accumulation buffer.
m_accumulationBuffer->accumulate(output, WEBAUDIO_BLOCK_SIZE,
&m_accumulationReadIndex,
m_postDelayLength);
}
} // namespace WebCore
@@ -29,7 +29,6 @@
#ifndef ReverbConvolverStage_h
#define ReverbConvolverStage_h
#include "DirectConvolver.h"
#include "FFTConvolver.h"
#include "nsTArray.h"
@@ -49,14 +48,12 @@ class ReverbConvolverStage {
public:
// renderPhase is useful to know so that we can manipulate the pre versus post delay so that stages will perform
// their heavy work (FFT processing) on different slices to balance the load in a real-time thread.
ReverbConvolverStage(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer*, bool directMode = false);
ReverbConvolverStage(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, ReverbAccumulationBuffer*);
// WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset).
void process(const float* source, size_t framesToProcess);
// |source| must point to an array of WEBAUDIO_BLOCK_SIZE elements.
void process(const float* source);
void processInBackground(ReverbConvolver* convolver, size_t framesToProcess);
void reset();
void processInBackground(ReverbConvolver* convolver);
// Useful for background processing
int inputReadIndex() const { return m_inputReadIndex; }
@@ -67,22 +64,13 @@ private:
nsAutoPtr<FFTBlock> m_fftKernel;
nsAutoPtr<FFTConvolver> m_fftConvolver;
nsTArray<float> m_preDelayBuffer;
ReverbAccumulationBuffer* m_accumulationBuffer;
int m_accumulationReadIndex;
int m_inputReadIndex;
size_t m_preDelayLength;
size_t m_postDelayLength;
size_t m_preReadWriteIndex;
size_t m_framesProcessed;
nsTArray<float> m_temporaryBuffer;
bool m_directMode;
nsTArray<float> m_directKernel;
nsAutoPtr<DirectConvolver> m_directConvolver;
};
} // namespace WebCore
-4
View File
@@ -6,7 +6,6 @@
UNIFIED_SOURCES += [
'Biquad.cpp',
'DirectConvolver.cpp',
'DynamicsCompressor.cpp',
'DynamicsCompressorKernel.cpp',
'FFTConvolver.cpp',
@@ -30,6 +29,3 @@ FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/media/webaudio',
]
if not CONFIG['GNU_CXX']:
ALLOW_COMPILER_WARNINGS = True
@@ -37,9 +37,9 @@ addLoadEvent(function() {
expectException(function() {
ac.createPeriodicWave(new Float32Array(0), new Float32Array(0));
}, DOMException.NOT_SUPPORTED_ERR);
expectException(function() {
expectNoException(function() {
ac.createPeriodicWave(new Float32Array(4097), new Float32Array(4097));
}, DOMException.NOT_SUPPORTED_ERR);
});
runTest();
});
+2 -6
View File
@@ -403,17 +403,13 @@ WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo)
}
}
mInfo.mMediaSeekable = nestegg_has_cues(mContext);
*aInfo = mInfo;
return NS_OK;
}
bool
WebMReader::IsMediaSeekable()
{
return mContext && nestegg_has_cues(mContext);
}
bool WebMReader::DecodeAudioPacket(NesteggPacketHolder* aHolder)
{
MOZ_ASSERT(OnTaskQueue());
-2
View File
@@ -92,8 +92,6 @@ public:
virtual media::TimeIntervals GetBuffered() override;
virtual bool IsMediaSeekable() override;
// Value passed to NextPacket to determine if we are reading a video or an
// audio packet.
enum TrackType {
-1
View File
@@ -8,7 +8,6 @@
#define MOZILLA_SVGCONTENTUTILS_H
// include math.h to pick up definition of M_ maths defines e.g. M_PI
#define _USE_MATH_DEFINES
#include <math.h>
#include "mozilla/gfx/2D.h" // for StrokeOptions
+28
View File
@@ -0,0 +1,28 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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/. */
#include "GeolocationUtil.h"
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
{
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
const double radsInDeg = M_PI / 180.0;
const double rNewLat = aLat * radsInDeg;
const double rNewLon = aLon * radsInDeg;
const double rOldLat = aLastLat * radsInDeg;
const double rOldLon = aLastLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
if (cosDelta > 1.0) {
cosDelta = 1.0;
} else if (cosDelta < -1.0) {
cosDelta = -1.0;
}
return acos(cosDelta) * 6378137;
}
+13
View File
@@ -0,0 +1,13 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 GEOLOCATIONUTIL_H
#define GEOLOCATIONUTIL_H
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon);
#endif
+48 -105
View File
@@ -15,12 +15,13 @@
*/
#include "GonkGPSGeolocationProvider.h"
#include "mozstumbler/MozStumbler.h"
#include <cmath>
#include <pthread.h>
#include <hardware/gps.h>
#include "mozilla/Constants.h"
#include "GeolocationUtil.h"
#include "mozstumbler/MozStumbler.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsContentUtils.h"
@@ -38,6 +39,7 @@
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#ifdef MOZ_B2G_RIL
#include "mozstumbler/MozStumbler.h"
#include "nsIIccInfo.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
@@ -88,71 +90,6 @@ AGpsCallbacks GonkGPSGeolocationProvider::mAGPSCallbacks;
AGpsRilCallbacks GonkGPSGeolocationProvider::mAGPSRILCallbacks;
#endif // MOZ_B2G_RIL
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
{
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
const double radsInDeg = M_PI / 180.0;
const double rNewLat = aLat * radsInDeg;
const double rNewLon = aLon * radsInDeg;
const double rOldLat = aLastLat * radsInDeg;
const double rOldLon = aLastLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
if (cosDelta > 1.0) {
cosDelta = 1.0;
} else if (cosDelta < -1.0) {
cosDelta = -1.0;
}
return acos(cosDelta) * 6378137;
}
class RequestCellInfoEvent : public nsRunnable {
public:
RequestCellInfoEvent(StumblerInfo *callback)
: mRequestCallback(callback)
{}
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
// Get Cell Info
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnectionService \n");
return NS_OK;
}
nsCOMPtr<nsIMobileConnection> connection;
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
service->GetNumItems(&numberOfRilServices);
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
if (!connection) {
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
} else {
cellInfoNum++;
connection->GetCellInfoList(mRequestCallback);
}
}
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
// Get Wifi AP Info
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
if (!wifi) {
mRequestCallback->SetWifiInfoResponseReceived();
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIWifi interface\n");
return NS_OK;
}
wifi->GetWifiScanResults(mRequestCallback);
return NS_OK;
}
private:
RefPtr<StumblerInfo> mRequestCallback;
};
void
GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
@@ -182,6 +119,11 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
MOZ_ASSERT(location);
const float kImpossibleAccuracy_m = 0.001;
if (location->accuracy < kImpossibleAccuracy_m) {
return;
}
RefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
location->longitude,
location->altitude,
@@ -206,35 +148,9 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
RefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
NS_DispatchToMainThread(event);
const double kMinChangeInMeters = 30;
static int64_t lastTime_ms = 0;
static double sLastLat = 0;
static double sLastLon = 0;
double delta = -1.0;
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
if (0 != sLastLon || 0 != sLastLat) {
delta = CalculateDeltaInMeter(location->latitude, location->longitude, sLastLat, sLastLon);
}
if (gDebug_isLoggingEnabled) {
nsContentUtils::LogMessageToConsole("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
location->longitude, location->latitude, timediff, delta);
}
// Consecutive GPS locations must be 30 meters and 3 seconds apart
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
sLastLat = location->latitude;
sLastLon = location->longitude;
RefPtr<StumblerInfo> requestCallback = new StumblerInfo(somewhere);
RefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
NS_DispatchToMainThread(runnable);
} else {
if (gDebug_isLoggingEnabled) {
nsContentUtils::LogMessageToConsole(
"Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
}
}
#ifdef MOZ_B2G_RIL
MozStumble(somewhere);
#endif
}
void
@@ -680,6 +596,33 @@ GonkGPSGeolocationProvider::RequestSetID(uint32_t flags)
mAGpsRilInterface->set_set_id(type, idBytes.get());
}
namespace {
int
ConvertToGpsRefLocationType(const nsAString& aConnectionType)
{
const char* GSM_TYPES[] = { "gsm", "gprs", "edge" };
const char* UMTS_TYPES[] = { "umts", "hspda", "hsupa", "hspa", "hspa+" };
for (auto type: GSM_TYPES) {
if (aConnectionType.EqualsASCII(type)) {
return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
}
}
for (auto type: UMTS_TYPES) {
if (aConnectionType.EqualsASCII(type)) {
return AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
}
}
if (gDebug_isLoggingEnabled) {
nsContentUtils::LogMessageToConsole("geo: Unsupported connection type %s\n",
NS_ConvertUTF16toUTF8(aConnectionType).get());
}
return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
}
} // namespace
void
GonkGPSGeolocationProvider::SetReferenceLocation()
{
@@ -692,9 +635,6 @@ GonkGPSGeolocationProvider::SetReferenceLocation()
AGpsRefLocation location;
// TODO: Bug 772750 - get mobile connection technology from rilcontext
location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
@@ -703,14 +643,19 @@ GonkGPSGeolocationProvider::SetReferenceLocation()
}
nsCOMPtr<nsIMobileConnection> connection;
// TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
// MultiSIM configuration
service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
NS_ENSURE_TRUE_VOID(connection);
nsCOMPtr<nsIMobileConnectionInfo> voice;
connection->GetVoice(getter_AddRefs(voice));
if (voice) {
nsAutoString connectionType;
nsresult rv = voice->GetType(connectionType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
location.type = ConvertToGpsRefLocationType(connectionType);
nsCOMPtr<nsIMobileNetworkInfo> networkInfo;
voice->GetNetwork(getter_AddRefs(networkInfo));
if (networkInfo) {
@@ -1181,9 +1126,7 @@ GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
}
nsCOMPtr<nsIMobileConnection> connection;
// TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
// MultiSIM configuration
service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
if (!connection) {
break;
}
+11 -5
View File
@@ -33,8 +33,8 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'dom_system_gonk'
EXPORTS += [
'GeolocationUtil.h',
'GonkGPSGeolocationProvider.h',
'mozstumbler/MozStumbler.h',
'nsVolume.h',
'nsVolumeService.h',
]
@@ -43,14 +43,11 @@ UNIFIED_SOURCES += [
'AudioManager.cpp',
'AutoMounter.cpp',
'AutoMounterSetting.cpp',
'GeolocationUtil.cpp',
'GonkGPSGeolocationProvider.cpp',
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'MozMtpStorage.cpp',
'mozstumbler/MozStumbler.cpp',
'mozstumbler/StumblerLogging.cpp',
'mozstumbler/UploadStumbleRunnable.cpp',
'mozstumbler/WriteStumbleOnThread.cpp',
'NetIdManager.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',
@@ -93,6 +90,15 @@ EXTRA_JS_MODULES += [
]
if CONFIG['MOZ_B2G_RIL']:
EXPORTS += [
'mozstumbler/MozStumbler.h',
]
UNIFIED_SOURCES += [
'mozstumbler/MozStumbler.cpp',
'mozstumbler/StumblerLogging.cpp',
'mozstumbler/UploadStumbleRunnable.cpp',
'mozstumbler/WriteStumbleOnThread.cpp'
]
XPIDL_SOURCES += [
'nsIDataCallInterfaceService.idl',
'nsIDataCallManager.idl',
+97 -2
View File
@@ -5,12 +5,22 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MozStumbler.h"
#include "nsDataHashtable.h"
#include "nsGeoPosition.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
#include "StumblerLogging.h"
#include "WriteStumbleOnThread.h"
#include "nsNetCID.h"
#include "nsDataHashtable.h"
#include "../GeolocationUtil.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileCellInfo.h"
#include "nsIMobileNetworkInfo.h"
#include "nsINetworkInterface.h"
#include "nsIRadioInterfaceLayer.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -18,6 +28,91 @@ using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
class RequestCellInfoEvent : public nsRunnable {
public:
RequestCellInfoEvent(StumblerInfo *callback)
: mRequestCallback(callback)
{}
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
// Get Cell Info
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n");
return NS_OK;
}
nsCOMPtr<nsIMobileConnection> connection;
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
service->GetNumItems(&numberOfRilServices);
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
if (!connection) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
} else {
cellInfoNum++;
connection->GetCellInfoList(mRequestCallback);
}
}
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
// Get Wifi AP Info
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
if (!wifi) {
mRequestCallback->SetWifiInfoResponseReceived();
STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n");
return NS_OK;
}
wifi->GetWifiScanResults(mRequestCallback);
return NS_OK;
}
private:
RefPtr<StumblerInfo> mRequestCallback;
};
void
MozStumble(nsGeoPosition* position)
{
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
position->GetCoords(getter_AddRefs(coords));
if (!coords) {
return;
}
double latitude, longitude;
coords->GetLatitude(&latitude);
coords->GetLongitude(&longitude);
const double kMinChangeInMeters = 30;
static int64_t lastTime_ms = 0;
static double sLastLat = 0;
static double sLastLon = 0;
double delta = -1.0;
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
if (0 != sLastLon || 0 != sLastLat) {
delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon);
}
STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
longitude, latitude, timediff, delta);
// Consecutive GPS locations must be 30 meters and 3 seconds apart
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
sLastLat = latitude;
sLastLon = longitude;
RefPtr<StumblerInfo> requestCallback = new StumblerInfo(position);
RefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
NS_DispatchToMainThread(runnable);
} else {
STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
}
}
void
StumblerInfo::SetWifiInfoResponseReceived()
{
@@ -15,6 +15,8 @@
class nsGeoPosition;
void MozStumble(nsGeoPosition* position);
class StumblerInfo final : public nsICellInfoListCallback,
public nsIWifiScanResultsReady
{
+8
View File
@@ -53,4 +53,12 @@ interface DominatorTree {
*/
[Throws]
NodeSize? getRetainedSize(NodeId node);
/**
* Get the set of ids of nodes immediately dominated by the node with the
* given id. The resulting array is sorted by greatest to least retained
* size. If given an invalid id, null is returned. Throws an error on OOM.
*/
[Throws]
sequence<NodeId>? getImmediatelyDominated(NodeId node);
};
+1 -1
View File
@@ -4,4 +4,4 @@
# 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/.
DIRS += CONFIG['MOZ_EXTENSIONS'].split()
DIRS += CONFIG['MOZ_EXTENSIONS']
-1
View File
@@ -11,7 +11,6 @@
#include <string.h>
#include "mozilla/CheckedInt.h"
#include "mozilla/Constants.h"
#include "2D.h"
#include "DataSurfaceHelpers.h"
-3
View File
@@ -6,8 +6,6 @@
#ifndef MOZILLA_GFX_DRAWCOMMAND_H_
#define MOZILLA_GFX_DRAWCOMMAND_H_
#define _USE_MATH_DEFINES
#include <math.h>
#include "2D.h"
@@ -592,5 +590,4 @@ public:
} // namespace mozilla
#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */
+1 -1
View File
@@ -16,12 +16,12 @@
#include "Logging.h"
#include "Tools.h"
#include <algorithm>
#include "mozilla/Constants.h"
#include "FilterNodeSoftware.h"
#include "FilterNodeD2D1.h"
#include "ExtendInputEffectD2D1.h"
#include <cmath>
#include <dwrite.h>
// decltype is not usable for overloaded functions.
-2
View File
@@ -3,8 +3,6 @@
* 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/. */
#define _USE_MATH_DEFINES
#include <cmath>
#include "DataSurfaceHelpers.h"
#include "FilterNodeSoftware.h"
-1
View File
@@ -8,7 +8,6 @@
#include <math.h>
#include "DrawTargetD2D.h"
#include "Logging.h"
#include "mozilla/Constants.h"
namespace mozilla {
namespace gfx {
-4
View File
@@ -3,9 +3,6 @@
* 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/. */
#define _USE_MATH_DEFINES
#include <cmath>
#include "PathHelpers.h"
namespace mozilla {
@@ -278,4 +275,3 @@ MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
} // namespace gfx
} // namespace mozilla
-1
View File
@@ -7,7 +7,6 @@
#define MOZILLA_GFX_PATHHELPERS_H_
#include "2D.h"
#include "mozilla/Constants.h"
#include "UserData.h"
#include <cmath>
-1
View File
@@ -28,7 +28,6 @@
#include "base/basictypes.h"
#define _USE_MATH_DEFINES
#include <algorithm>
#include <cmath>
#include <limits>
+1 -4
View File
@@ -152,6 +152,7 @@ UNIFIED_SOURCES += [
'Matrix.cpp',
'Path.cpp',
'PathCairo.cpp',
'PathHelpers.cpp',
'PathRecording.cpp',
'Quaternion.cpp',
'RecordedEvent.cpp',
@@ -162,10 +163,6 @@ UNIFIED_SOURCES += [
'SourceSurfaceRawData.cpp',
]
SOURCES += [
'PathHelpers.cpp', # Uses _USE_MATH_DEFINES
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
EXPORTS.mozilla.gfx += [
'QuartzSupport.h',
@@ -29,7 +29,6 @@
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_*
#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
#include "mozilla/Constants.h" // for M_PI
#include "mozilla/EventForwards.h" // for nsEventStatus_*
#include "mozilla/Preferences.h" // for Preferences
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
-3
View File
@@ -3,9 +3,6 @@
* 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/. */
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include "mozilla/Alignment.h"
+1 -1
View File
@@ -6,9 +6,9 @@
#ifndef GFX_PREFS_H
#define GFX_PREFS_H
#include <cmath> // for M_PI
#include <stdint.h>
#include "mozilla/Assertions.h"
#include "mozilla/Constants.h" // for M_PI
// First time gfxPrefs::GetSingleton() needs to be called on the main thread,
// before any of the methods accessing the values are used, but after

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