mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00
Bug 830171 - Use SourceReader in async mode to better allow shutdown on MediaResource close. r=padenot
This commit is contained in:
parent
f4778a33b6
commit
9e930cf22a
@ -25,6 +25,7 @@ CPPSRCS = \
|
||||
WMFDecoder.cpp \
|
||||
WMFReader.cpp \
|
||||
WMFUtils.cpp \
|
||||
WMFSourceReaderCallback.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <ole2.h>
|
||||
|
||||
#include "WMFByteStream.h"
|
||||
#include "WMFSourceReaderCallback.h"
|
||||
#include "WMFUtils.h"
|
||||
#include "MediaResource.h"
|
||||
#include "nsISeekableStream.h"
|
||||
@ -27,16 +28,6 @@ PRLogModuleInfo* gWMFByteStreamLog = nullptr;
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
HRESULT
|
||||
DoGetInterface(IUnknown* aUnknown, void** aInterface)
|
||||
{
|
||||
if (!aInterface)
|
||||
return E_POINTER;
|
||||
*aInterface = aUnknown;
|
||||
aUnknown->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Thread pool listener which ensures that MSCOM is initialized and
|
||||
// deinitialized on the thread pool thread. We can call back into WMF
|
||||
// on this thread, so we need MSCOM working.
|
||||
@ -103,8 +94,10 @@ public:
|
||||
nsRefPtr<MediaResource> mResource;
|
||||
};
|
||||
|
||||
WMFByteStream::WMFByteStream(MediaResource* aResource)
|
||||
: mResourceMonitor("WMFByteStream.MediaResource"),
|
||||
WMFByteStream::WMFByteStream(MediaResource* aResource,
|
||||
WMFSourceReaderCallback* aSourceReaderCallback)
|
||||
: mSourceReaderCallback(aSourceReaderCallback),
|
||||
mResourceMonitor("WMFByteStream.MediaResource"),
|
||||
mResource(aResource),
|
||||
mReentrantMonitor("WMFByteStream.Data"),
|
||||
mOffset(0),
|
||||
@ -112,6 +105,7 @@ WMFByteStream::WMFByteStream(MediaResource* aResource)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
NS_ASSERTION(mResource, "Must have a valid media resource");
|
||||
NS_ASSERTION(mSourceReaderCallback, "Must have a source reader callback.");
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (!gWMFByteStreamLog) {
|
||||
@ -176,8 +170,11 @@ WMFByteStream::Init()
|
||||
nsresult
|
||||
WMFByteStream::Shutdown()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mIsShutdown = true;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mIsShutdown = true;
|
||||
}
|
||||
mSourceReaderCallback->Cancel();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ namespace mozilla {
|
||||
|
||||
class MediaResource;
|
||||
class ReadRequest;
|
||||
class WMFSourceReaderCallback;
|
||||
|
||||
// Wraps a MediaResource around an IMFByteStream interface, so that it can
|
||||
// be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
|
||||
@ -37,7 +38,7 @@ class WMFByteStream MOZ_FINAL : public IMFByteStream
|
||||
, public IMFAttributes
|
||||
{
|
||||
public:
|
||||
WMFByteStream(MediaResource* aResource);
|
||||
WMFByteStream(MediaResource* aResource, WMFSourceReaderCallback* aCallback);
|
||||
~WMFByteStream();
|
||||
|
||||
nsresult Init();
|
||||
@ -123,6 +124,12 @@ private:
|
||||
// Note this is pool is shared amongst all active WMFByteStreams.
|
||||
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||
|
||||
// Reference to the source reader's callback. We use this reference to
|
||||
// notify threads waiting on a ReadSample() callback to stop waiting
|
||||
// if the stream is closed, which happens when the media element is
|
||||
// shutdown.
|
||||
RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
|
||||
|
||||
// Monitor that ensures that multiple concurrent async reads are processed
|
||||
// in serial on a resource. This prevents concurrent async reads and seeks
|
||||
// from interleaving, to ensure that reads occur at the offset they're
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "WMFDecoder.h"
|
||||
#include "WMFUtils.h"
|
||||
#include "WMFByteStream.h"
|
||||
#include "WMFSourceReaderCallback.h"
|
||||
|
||||
#ifndef MOZ_SAMPLE_TYPE_FLOAT32
|
||||
#error We expect 32bit float audio samples on desktop for the Windows Media Foundation media backend.
|
||||
@ -87,9 +88,10 @@ WMFReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Must be created on main thread.
|
||||
mByteStream = new WMFByteStream(mDecoder->GetResource());
|
||||
mSourceReaderCallback = new WMFSourceReaderCallback();
|
||||
|
||||
// Must be created on main thread.
|
||||
mByteStream = new WMFByteStream(mDecoder->GetResource(), mSourceReaderCallback);
|
||||
return mByteStream->Init();
|
||||
}
|
||||
|
||||
@ -441,7 +443,14 @@ WMFReader::ReadMetadata(VideoInfo* aInfo,
|
||||
LOG("WMFReader::ReadMetadata()");
|
||||
HRESULT hr;
|
||||
|
||||
hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, NULL, byRef(mSourceReader));
|
||||
RefPtr<IMFAttributes> attr;
|
||||
hr = wmf::MFCreateAttributes(byRef(attr), 1);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, mSourceReaderCallback);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, attr, byRef(mSourceReader));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
hr = ConfigureVideoDecoder();
|
||||
@ -483,29 +492,41 @@ WMFReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
DWORD flags;
|
||||
LONGLONG timestampHns;
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
||||
0, // control flags
|
||||
nullptr, // read stream index
|
||||
&flags,
|
||||
×tampHns,
|
||||
byRef(sample));
|
||||
0, // read stream index
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x", hr);
|
||||
// End the stream.
|
||||
mAudioQueue.Finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD flags = 0;
|
||||
LONGLONG timestampHns = 0;
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = mSourceReaderCallback->Wait(&flags, ×tampHns, byRef(sample));
|
||||
if (FAILED(hr) ||
|
||||
(flags & MF_SOURCE_READERF_ERROR) ||
|
||||
(flags & MF_SOURCE_READERF_ENDOFSTREAM) ||
|
||||
(flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
|
||||
LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x flags=0x%x",
|
||||
hr, flags);
|
||||
// End of stream.
|
||||
// End the stream.
|
||||
mAudioQueue.Finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sample) {
|
||||
// Not enough data? Try again...
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<IMFMediaBuffer> buffer;
|
||||
hr = sample->ConvertToContiguousBuffer(byRef(buffer));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
|
||||
@ -551,17 +572,26 @@ WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
uint32_t parsed = 0, decoded = 0;
|
||||
AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
|
||||
|
||||
DWORD flags;
|
||||
LONGLONG timestampHns;
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
|
||||
0, // control flags
|
||||
nullptr, // read stream index
|
||||
&flags,
|
||||
×tampHns,
|
||||
byRef(sample));
|
||||
0, // read stream index
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (FAILED(hr)) {
|
||||
LOG("WMFReader::DecodeVideoData() ReadSample failed with hr=0x%x", hr);
|
||||
// End the stream.
|
||||
mVideoQueue.Finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD flags = 0;
|
||||
LONGLONG timestampHns = 0;
|
||||
RefPtr<IMFSample> sample;
|
||||
hr = mSourceReaderCallback->Wait(&flags, ×tampHns, byRef(sample));
|
||||
|
||||
if (flags & MF_SOURCE_READERF_ERROR) {
|
||||
NS_WARNING("WMFReader: Catastrophic failure reading video sample");
|
||||
// Future ReadSample() calls will fail, so give up and report end of stream.
|
||||
@ -577,7 +607,7 @@ WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
if (!sample) {
|
||||
if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
|
||||
LOG("WMFReader; Null sample after video decode, at end of stream");
|
||||
// End of stream.
|
||||
// End the stream.
|
||||
mVideoQueue.Finish();
|
||||
return false;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace mozilla {
|
||||
|
||||
class WMFByteStream;
|
||||
class WMFSourceReaderCallback;
|
||||
|
||||
// Decoder backend for reading H.264/AAC in MP4/M4A and MP3 audio files,
|
||||
// using Windows Media Foundation.
|
||||
@ -53,6 +54,7 @@ private:
|
||||
|
||||
RefPtr<IMFSourceReader> mSourceReader;
|
||||
RefPtr<WMFByteStream> mByteStream;
|
||||
RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
|
||||
|
||||
// Region inside the video frame that makes up the picture. Pixels outside
|
||||
// of this region should not be rendered.
|
||||
|
152
content/media/wmf/WMFSourceReaderCallback.cpp
Normal file
152
content/media/wmf/WMFSourceReaderCallback.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WMFSourceReaderCallback.h"
|
||||
#include "WMFUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define LOG(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
// IUnknown Methods
|
||||
STDMETHODIMP
|
||||
WMFSourceReaderCallback::QueryInterface(REFIID aIId, void **aInterface)
|
||||
{
|
||||
LOG("WMFSourceReaderCallback::QueryInterface %s", GetGUIDName(aIId).get());
|
||||
|
||||
if (aIId == IID_IMFSourceReaderCallback) {
|
||||
return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
|
||||
}
|
||||
if (aIId == IID_IUnknown) {
|
||||
return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
|
||||
}
|
||||
|
||||
*aInterface = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(WMFSourceReaderCallback)
|
||||
NS_IMPL_THREADSAFE_RELEASE(WMFSourceReaderCallback)
|
||||
|
||||
WMFSourceReaderCallback::WMFSourceReaderCallback()
|
||||
: mResultStatus(S_OK)
|
||||
, mStreamFlags(0)
|
||||
, mTimestamp(0)
|
||||
, mSample(nullptr)
|
||||
, mReadFinished(false)
|
||||
, mMonitor("WMFSourceReaderCallback")
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFSourceReaderCallback::NotifyReadComplete(HRESULT aReadStatus,
|
||||
DWORD aStreamIndex,
|
||||
DWORD aStreamFlags,
|
||||
LONGLONG aTimestamp,
|
||||
IMFSample *aSample)
|
||||
{
|
||||
// Note: aSample can be NULL on success if more data is required!
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
|
||||
if (mSample) {
|
||||
// The WMFReader should have called Wait() to retrieve the last
|
||||
// sample returned by the last ReadSample() call, but if we're
|
||||
// aborting the read before Wait() is called the sample ref
|
||||
// can be non-null.
|
||||
mSample->Release();
|
||||
mSample = nullptr;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(aReadStatus)) {
|
||||
if (aSample) {
|
||||
mTimestamp = aTimestamp;
|
||||
mSample = aSample;
|
||||
mSample->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
mResultStatus = aReadStatus;
|
||||
mStreamFlags = aStreamFlags;
|
||||
|
||||
// Set the sentinal value and notify the monitor, so that threads waiting
|
||||
// in Wait() are awoken.
|
||||
mReadFinished = true;
|
||||
mon.NotifyAll();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
WMFSourceReaderCallback::OnReadSample(HRESULT aReadStatus,
|
||||
DWORD aStreamIndex,
|
||||
DWORD aStreamFlags,
|
||||
LONGLONG aTimestamp,
|
||||
IMFSample *aSample)
|
||||
{
|
||||
LOG("WMFSourceReaderCallback::OnReadSample() hr=0x%x flags=0x%x time=%lld sample=%p",
|
||||
aReadStatus, aStreamFlags, aTimestamp, aSample);
|
||||
return NotifyReadComplete(aReadStatus,
|
||||
aStreamIndex,
|
||||
aStreamFlags,
|
||||
aTimestamp,
|
||||
aSample);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFSourceReaderCallback::Cancel()
|
||||
{
|
||||
LOG("WMFSourceReaderCallback::Cancel()");
|
||||
return NotifyReadComplete(E_ABORT,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
WMFSourceReaderCallback::OnEvent(DWORD, IMFMediaEvent *)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
WMFSourceReaderCallback::OnFlush(DWORD)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WMFSourceReaderCallback::Wait(DWORD* aStreamFlags,
|
||||
LONGLONG* aTimeStamp,
|
||||
IMFSample** aSample)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
LOG("WMFSourceReaderCallback::Wait() starting wait");
|
||||
while (!mReadFinished) {
|
||||
mon.Wait();
|
||||
}
|
||||
mReadFinished = false;
|
||||
LOG("WMFSourceReaderCallback::Wait() done waiting");
|
||||
|
||||
*aStreamFlags = mStreamFlags;
|
||||
*aTimeStamp = mTimestamp;
|
||||
*aSample = mSample;
|
||||
HRESULT hr = mResultStatus;
|
||||
|
||||
mSample = nullptr;
|
||||
mTimestamp = 0;
|
||||
mStreamFlags = 0;
|
||||
mResultStatus = S_OK;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
83
content/media/wmf/WMFSourceReaderCallback.h
Normal file
83
content/media/wmf/WMFSourceReaderCallback.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#if !defined(WMFSourceReaderCallback_h_)
|
||||
#define WMFSourceReaderCallback_h_
|
||||
|
||||
#include "WMF.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// A listener which we pass into the IMFSourceReader upon creation which is
|
||||
// notified when an asynchronous call to IMFSourceReader::ReadSample()
|
||||
// completes. This allows us to abort ReadSample() operations when the
|
||||
// WMFByteStream's underlying MediaResource is closed. This ensures that
|
||||
// the decode threads don't get stuck in a synchronous ReadSample() call
|
||||
// when the MediaResource is unexpectedly shutdown.
|
||||
class WMFSourceReaderCallback : public IMFSourceReaderCallback
|
||||
{
|
||||
public:
|
||||
WMFSourceReaderCallback();
|
||||
|
||||
// IUnknown Methods.
|
||||
STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IMFSourceReaderCallback methods
|
||||
STDMETHODIMP OnReadSample(HRESULT hrStatus,
|
||||
DWORD dwStreamIndex,
|
||||
DWORD dwStreamFlags,
|
||||
LONGLONG llTimestamp,
|
||||
IMFSample *pSample);
|
||||
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *);
|
||||
STDMETHODIMP OnFlush(DWORD);
|
||||
|
||||
// Causes the calling thread to block waiting for the
|
||||
// IMFSourceReader::ReadSample() result callback to occur, or for the
|
||||
// WMFByteStream to be closed.
|
||||
HRESULT Wait(DWORD* aStreamFlags,
|
||||
LONGLONG* aTimeStamp,
|
||||
IMFSample** aSample);
|
||||
|
||||
// Cancels Wait() calls.
|
||||
HRESULT Cancel();
|
||||
|
||||
private:
|
||||
|
||||
// Sets state to record the result of a read, and awake threads
|
||||
// waiting in Wait().
|
||||
HRESULT NotifyReadComplete(HRESULT aReadStatus,
|
||||
DWORD aStreamIndex,
|
||||
DWORD aStreamFlags,
|
||||
LONGLONG aTimestamp,
|
||||
IMFSample *aSample);
|
||||
|
||||
// Synchronizes all member data in this class, and Wait() blocks on
|
||||
// and NotifyReadComplete() notifies this monitor.
|
||||
ReentrantMonitor mMonitor;
|
||||
|
||||
// Read result data.
|
||||
HRESULT mResultStatus;
|
||||
DWORD mStreamFlags;
|
||||
LONGLONG mTimestamp;
|
||||
IMFSample* mSample;
|
||||
|
||||
// Sentinal. Set to true when a read result is returned. Wait() won't exit
|
||||
// until this is set to true.
|
||||
bool mReadFinished;
|
||||
|
||||
// IUnknown ref counting.
|
||||
nsAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // WMFSourceReaderCallback_h_
|
@ -199,6 +199,16 @@ SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex)
|
||||
return FAILED(hr) ? false : true;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
DoGetInterface(IUnknown* aUnknown, void** aInterface)
|
||||
{
|
||||
if (!aInterface)
|
||||
return E_POINTER;
|
||||
*aInterface = aUnknown;
|
||||
aUnknown->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
namespace wmf {
|
||||
|
||||
// Some SDK versions don't define the AAC decoder CLSID.
|
||||
|
@ -11,7 +11,8 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsCString GetGUIDName(const GUID& guid);
|
||||
nsCString
|
||||
GetGUIDName(const GUID& guid);
|
||||
|
||||
// Returns true if the reader has a stream with the specified index.
|
||||
// Index can be a specific index, or one of:
|
||||
@ -45,15 +46,22 @@ private:
|
||||
// Converts from microseconds to hundreds of nanoseconds.
|
||||
// We use microseconds for our timestamps, whereas WMF uses
|
||||
// hundreds of nanoseconds.
|
||||
inline int64_t UsecsToHNs(int64_t aUsecs) {
|
||||
inline int64_t
|
||||
UsecsToHNs(int64_t aUsecs) {
|
||||
return aUsecs * 10;
|
||||
}
|
||||
|
||||
// Converts from hundreds of nanoseconds to microseconds.
|
||||
// We use microseconds for our timestamps, whereas WMF uses
|
||||
// hundreds of nanoseconds.
|
||||
inline int64_t HNsToUsecs(int64_t hNanoSecs) {
|
||||
inline int64_t
|
||||
HNsToUsecs(int64_t hNanoSecs) {
|
||||
return hNanoSecs / 10;
|
||||
}
|
||||
|
||||
// Assigns aUnknown to *aInterface, and AddRef's it.
|
||||
// Helper for MSCOM QueryInterface implementations.
|
||||
HRESULT
|
||||
DoGetInterface(IUnknown* aUnknown, void** aInterface);
|
||||
|
||||
} // namespace mozilla
|
||||
|
Loading…
x
Reference in New Issue
Block a user