mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:25:44 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -12,6 +12,7 @@ EXPORTS.mozilla.a11y += [
|
||||
'AccessibleWrap.h',
|
||||
'Compatibility.h',
|
||||
'HyperTextAccessibleWrap.h',
|
||||
'IDSet.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:]))))
|
||||
Vendored
+5
-1
@@ -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'
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Blob;
|
||||
}
|
||||
} // namespace dom
|
||||
|
||||
/**
|
||||
* Data is moved into a temporary file when it grows beyond
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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] () {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -80,10 +80,6 @@ MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaDecoder::FirstFrameLoaded(aInfo, aEventVisibility);
|
||||
|
||||
if (!CheckDecoderCanOffloadAudio()) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -33,8 +33,6 @@ public:
|
||||
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
bool ReadFromResource(uint8_t *aBuf, uint32_t aLength);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Constants.h"
|
||||
|
||||
#include "2D.h"
|
||||
#include "DataSurfaceHelpers.h"
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <math.h>
|
||||
#include "DrawTargetD2D.h"
|
||||
#include "Logging.h"
|
||||
#include "mozilla/Constants.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#define MOZILLA_GFX_PATHHELPERS_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/Constants.h"
|
||||
#include "UserData.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
+1
-4
@@ -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,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"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user