2012-12-18 00:49:58 -08:00
|
|
|
/* -*- 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 "WMF.h"
|
|
|
|
|
2012-12-27 06:02:37 -08:00
|
|
|
#include <unknwn.h>
|
2012-12-18 00:49:58 -08:00
|
|
|
#include <ole2.h>
|
|
|
|
|
|
|
|
#include "WMFByteStream.h"
|
2013-02-15 00:35:48 -08:00
|
|
|
#include "WMFSourceReaderCallback.h"
|
2012-12-18 00:49:58 -08:00
|
|
|
#include "WMFUtils.h"
|
|
|
|
#include "MediaResource.h"
|
|
|
|
#include "nsISeekableStream.h"
|
2012-12-27 06:02:37 -08:00
|
|
|
#include "mozilla/RefPtr.h"
|
2013-02-04 13:33:52 -08:00
|
|
|
#include "nsIThreadPool.h"
|
|
|
|
#include "nsXPCOMCIDInternal.h"
|
2013-09-05 13:25:17 -07:00
|
|
|
#include "nsComponentManagerUtils.h"
|
|
|
|
#include "mozilla/DebugOnly.h"
|
2013-02-06 04:59:12 -08:00
|
|
|
#include <algorithm>
|
2013-09-05 13:25:17 -07:00
|
|
|
#include <cassert>
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
PRLogModuleInfo* gWMFByteStreamLog = nullptr;
|
|
|
|
#define LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
|
|
|
#else
|
|
|
|
#define LOG(...)
|
|
|
|
#endif
|
|
|
|
|
2013-06-13 14:07:59 -07:00
|
|
|
// Limit the number of threads that we use for IO.
|
|
|
|
static const uint32_t NumWMFIoThreads = 4;
|
|
|
|
|
2013-02-04 13:33:52 -08:00
|
|
|
// 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.
|
|
|
|
class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
|
|
|
|
public:
|
2013-07-18 19:21:19 -07:00
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
2013-02-04 13:33:52 -08:00
|
|
|
NS_DECL_NSITHREADPOOLLISTENER
|
|
|
|
};
|
|
|
|
|
2013-07-18 19:21:19 -07:00
|
|
|
NS_IMPL_ISUPPORTS1(ThreadPoolListener, nsIThreadPoolListener)
|
2013-02-04 13:33:52 -08:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
ThreadPoolListener::OnThreadCreated()
|
|
|
|
{
|
|
|
|
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
ThreadPoolListener::OnThreadShuttingDown()
|
|
|
|
{
|
|
|
|
CoUninitialize();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Thread pool on which read requests are processed.
|
|
|
|
// This is created and destroyed on the main thread only.
|
|
|
|
static nsIThreadPool* sThreadPool = nullptr;
|
|
|
|
|
|
|
|
// Counter of the number of WMFByteStreams that are instantiated and that need
|
|
|
|
// the thread pool. This is read/write on the main thread only.
|
|
|
|
static int32_t sThreadPoolRefCnt = 0;
|
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
class ReleaseWMFByteStreamResourcesEvent MOZ_FINAL : public nsRunnable {
|
2013-02-04 13:33:52 -08:00
|
|
|
public:
|
2013-02-04 13:34:23 -08:00
|
|
|
ReleaseWMFByteStreamResourcesEvent(already_AddRefed<MediaResource> aResource)
|
|
|
|
: mResource(aResource) {}
|
|
|
|
virtual ~ReleaseWMFByteStreamResourcesEvent() {}
|
2013-02-04 13:33:52 -08:00
|
|
|
NS_IMETHOD Run() {
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
2013-02-04 13:34:23 -08:00
|
|
|
// Explicitly release the MediaResource reference. We *must* do this on
|
|
|
|
// the main thread, so we must explicitly release it here, we can't rely
|
|
|
|
// on the destructor to release it, since if this event runs before its
|
|
|
|
// dispatch call returns the destructor may run on the non-main thread.
|
|
|
|
mResource = nullptr;
|
2013-02-04 13:33:52 -08:00
|
|
|
NS_ASSERTION(sThreadPoolRefCnt > 0, "sThreadPoolRefCnt Should be non-negative");
|
|
|
|
sThreadPoolRefCnt--;
|
|
|
|
if (sThreadPoolRefCnt == 0) {
|
|
|
|
NS_ASSERTION(sThreadPool != nullptr, "Should have thread pool ref if sThreadPoolRefCnt==0.");
|
|
|
|
// Note: store ref to thread pool, then clear global ref, then
|
|
|
|
// Shutdown() using the stored ref. Events can run during the Shutdown()
|
|
|
|
// call, so if we release after calling Shutdown(), another event may
|
|
|
|
// have incremented the refcnt in the meantime, and have a dangling
|
|
|
|
// pointer to the now destroyed threadpool!
|
|
|
|
nsCOMPtr<nsIThreadPool> pool = sThreadPool;
|
|
|
|
NS_IF_RELEASE(sThreadPool);
|
|
|
|
pool->Shutdown();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-02-04 13:34:23 -08:00
|
|
|
nsRefPtr<MediaResource> mResource;
|
2013-02-04 13:33:52 -08:00
|
|
|
};
|
|
|
|
|
2013-02-15 00:35:48 -08:00
|
|
|
WMFByteStream::WMFByteStream(MediaResource* aResource,
|
|
|
|
WMFSourceReaderCallback* aSourceReaderCallback)
|
|
|
|
: mSourceReaderCallback(aSourceReaderCallback),
|
2013-02-04 13:34:23 -08:00
|
|
|
mResource(aResource),
|
|
|
|
mReentrantMonitor("WMFByteStream.Data"),
|
2013-02-04 13:33:52 -08:00
|
|
|
mOffset(0),
|
|
|
|
mIsShutdown(false)
|
2012-12-18 00:49:58 -08:00
|
|
|
{
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
2013-02-15 00:35:48 -08:00
|
|
|
NS_ASSERTION(mSourceReaderCallback, "Must have a source reader callback.");
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
if (!gWMFByteStreamLog) {
|
|
|
|
gWMFByteStreamLog = PR_NewLogModule("WMFByteStream");
|
|
|
|
}
|
|
|
|
#endif
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream CTOR", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
MOZ_COUNT_CTOR(WMFByteStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
WMFByteStream::~WMFByteStream()
|
|
|
|
{
|
|
|
|
MOZ_COUNT_DTOR(WMFByteStream);
|
2013-02-04 13:34:23 -08:00
|
|
|
// The WMFByteStream can be deleted from a thread pool thread, so we
|
|
|
|
// dispatch an event to the main thread to deref the thread pool and
|
|
|
|
// deref the MediaResource.
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
|
|
new ReleaseWMFByteStreamResourcesEvent(mResource.forget());
|
2013-02-04 13:33:52 -08:00
|
|
|
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream DTOR", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
WMFByteStream::Init()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
2013-02-04 13:33:52 -08:00
|
|
|
|
|
|
|
if (!sThreadPool) {
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
sThreadPool = pool;
|
|
|
|
NS_ADDREF(sThreadPool);
|
|
|
|
|
|
|
|
rv = sThreadPool->SetName(NS_LITERAL_CSTRING("WMFByteStream Async Read Pool"));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-06-13 14:07:59 -07:00
|
|
|
|
|
|
|
// We limit the number of threads that we use for IO. Note that the thread
|
|
|
|
// limit is the same as the idle limit so that we're not constantly creating
|
|
|
|
// and destroying threads. When the thread pool threads shutdown they
|
|
|
|
// dispatch an event to the main thread to call nsIThread::Shutdown(),
|
|
|
|
// and if we're very busy that can take a while to run, and we end up with
|
|
|
|
// dozens of extra threads. Note that threads that are idle for 60 seconds
|
|
|
|
// are shutdown naturally.
|
|
|
|
rv = sThreadPool->SetThreadLimit(NumWMFIoThreads);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = sThreadPool->SetIdleThreadLimit(NumWMFIoThreads);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-02-04 13:33:52 -08:00
|
|
|
|
|
|
|
nsCOMPtr<nsIThreadPoolListener> listener = new ThreadPoolListener();
|
|
|
|
rv = sThreadPool->SetListener(listener);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
2013-02-04 13:33:52 -08:00
|
|
|
sThreadPoolRefCnt++;
|
|
|
|
|
|
|
|
// Store a ref to the thread pool, so that we keep the pool alive as long as
|
|
|
|
// we're alive.
|
|
|
|
mThreadPool = sThreadPool;
|
|
|
|
|
2013-02-14 17:11:05 -08:00
|
|
|
NS_ConvertUTF8toUTF16 contentTypeUTF16(mResource->GetContentType());
|
|
|
|
if (!contentTypeUTF16.IsEmpty()) {
|
|
|
|
HRESULT hr = wmf::MFCreateAttributes(byRef(mAttributes), 1);
|
|
|
|
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
hr = mAttributes->SetString(MF_BYTESTREAM_CONTENT_TYPE,
|
|
|
|
contentTypeUTF16.get());
|
|
|
|
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
|
|
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream has Content-Type=%s", this, mResource->GetContentType().get());
|
2013-02-14 17:11:05 -08:00
|
|
|
}
|
2012-12-18 00:49:58 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
WMFByteStream::Shutdown()
|
|
|
|
{
|
2013-02-15 00:35:48 -08:00
|
|
|
{
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
mIsShutdown = true;
|
|
|
|
}
|
|
|
|
mSourceReaderCallback->Cancel();
|
2012-12-18 00:49:58 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// IUnknown Methods
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::QueryInterface(REFIID aIId, void **aInterface)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::QueryInterface %s", this, GetGUIDName(aIId).get());
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
if (aIId == IID_IMFByteStream) {
|
|
|
|
return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
|
|
|
|
}
|
|
|
|
if (aIId == IID_IUnknown) {
|
|
|
|
return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
|
|
|
|
}
|
2013-02-14 17:11:05 -08:00
|
|
|
if (aIId == IID_IMFAttributes) {
|
|
|
|
return DoGetInterface(static_cast<IMFAttributes*>(this), aInterface);
|
|
|
|
}
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
*aInterface = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2013-07-18 19:21:19 -07:00
|
|
|
NS_IMPL_ADDREF(WMFByteStream)
|
|
|
|
NS_IMPL_RELEASE(WMFByteStream)
|
2012-12-18 00:49:58 -08:00
|
|
|
|
2013-02-04 13:33:52 -08:00
|
|
|
|
|
|
|
// Stores data regarding an async read opreation.
|
2013-02-14 17:10:21 -08:00
|
|
|
class ReadRequest MOZ_FINAL : public IUnknown {
|
2013-02-04 13:33:52 -08:00
|
|
|
public:
|
2013-02-14 17:10:21 -08:00
|
|
|
ReadRequest(int64_t aOffset, BYTE* aBuffer, ULONG aLength)
|
2013-02-04 13:33:52 -08:00
|
|
|
: mOffset(aOffset),
|
|
|
|
mBuffer(aBuffer),
|
|
|
|
mBufferLength(aLength),
|
|
|
|
mBytesRead(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
// IUnknown Methods
|
|
|
|
STDMETHODIMP QueryInterface(REFIID aRIID, LPVOID *aOutObject);
|
|
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
|
|
STDMETHODIMP_(ULONG) Release();
|
|
|
|
|
|
|
|
int64_t mOffset;
|
|
|
|
BYTE* mBuffer;
|
|
|
|
ULONG mBufferLength;
|
|
|
|
ULONG mBytesRead;
|
|
|
|
|
|
|
|
// IUnknown ref counting.
|
2013-07-18 19:21:19 -07:00
|
|
|
ThreadSafeAutoRefCnt mRefCnt;
|
2013-02-04 13:33:52 -08:00
|
|
|
NS_DECL_OWNINGTHREAD
|
|
|
|
};
|
|
|
|
|
2013-07-18 19:21:19 -07:00
|
|
|
NS_IMPL_ADDREF(ReadRequest)
|
|
|
|
NS_IMPL_RELEASE(ReadRequest)
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
// IUnknown Methods
|
|
|
|
STDMETHODIMP
|
2013-02-14 17:10:21 -08:00
|
|
|
ReadRequest::QueryInterface(REFIID aIId, void **aInterface)
|
2012-12-18 00:49:58 -08:00
|
|
|
{
|
2013-02-14 17:10:21 -08:00
|
|
|
LOG("ReadRequest::QueryInterface %s", GetGUIDName(aIId).get());
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
if (aIId == IID_IUnknown) {
|
|
|
|
return DoGetInterface(static_cast<IUnknown*>(this), aInterface);
|
|
|
|
}
|
|
|
|
|
|
|
|
*aInterface = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
class ProcessReadRequestEvent MOZ_FINAL : public nsRunnable {
|
2013-02-04 13:33:52 -08:00
|
|
|
public:
|
2013-02-04 13:34:23 -08:00
|
|
|
ProcessReadRequestEvent(WMFByteStream* aStream,
|
|
|
|
IMFAsyncResult* aResult,
|
2013-02-14 17:10:21 -08:00
|
|
|
ReadRequest* aRequestState)
|
2013-02-04 13:33:52 -08:00
|
|
|
: mStream(aStream),
|
|
|
|
mResult(aResult),
|
|
|
|
mRequestState(aRequestState) {}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() {
|
2013-02-04 13:34:23 -08:00
|
|
|
mStream->ProcessReadRequest(mResult, mRequestState);
|
2013-02-04 13:33:52 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
RefPtr<WMFByteStream> mStream;
|
|
|
|
RefPtr<IMFAsyncResult> mResult;
|
2013-02-14 17:10:21 -08:00
|
|
|
RefPtr<ReadRequest> mRequestState;
|
2013-02-04 13:33:52 -08:00
|
|
|
};
|
|
|
|
|
2012-12-18 00:49:58 -08:00
|
|
|
// IMFByteStream Methods
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::BeginRead(BYTE *aBuffer,
|
|
|
|
ULONG aLength,
|
|
|
|
IMFAsyncCallback *aCallback,
|
|
|
|
IUnknown *aCallerState)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aBuffer, E_POINTER);
|
|
|
|
NS_ENSURE_TRUE(aCallback, E_POINTER);
|
|
|
|
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::BeginRead() mOffset=%lld tell=%lld length=%lu mIsShutdown=%d",
|
|
|
|
this, mOffset, mResource->Tell(), aLength, mIsShutdown);
|
2013-02-04 13:33:52 -08:00
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
if (mIsShutdown || mOffset < 0) {
|
|
|
|
return E_INVALIDARG;
|
2013-02-04 13:33:52 -08:00
|
|
|
}
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
// Create an object to store our state.
|
2013-02-14 17:10:21 -08:00
|
|
|
RefPtr<ReadRequest> requestState = new ReadRequest(mOffset, aBuffer, aLength);
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
// Create an IMFAsyncResult, this is passed back to the caller as a token to
|
|
|
|
// retrieve the number of bytes read.
|
2012-12-27 06:02:37 -08:00
|
|
|
RefPtr<IMFAsyncResult> callersResult;
|
2012-12-18 00:49:58 -08:00
|
|
|
HRESULT hr = wmf::MFCreateAsyncResult(requestState,
|
|
|
|
aCallback,
|
|
|
|
aCallerState,
|
2012-12-27 06:02:37 -08:00
|
|
|
byRef(callersResult));
|
2012-12-18 00:49:58 -08:00
|
|
|
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
|
|
|
|
2013-02-04 13:33:52 -08:00
|
|
|
// Dispatch an event to perform the read in the thread pool.
|
2013-02-04 13:34:23 -08:00
|
|
|
nsCOMPtr<nsIRunnable> r = new ProcessReadRequestEvent(this,
|
|
|
|
callersResult,
|
|
|
|
requestState);
|
2013-02-04 13:33:52 -08:00
|
|
|
nsresult rv = mThreadPool->Dispatch(r, NS_DISPATCH_NORMAL);
|
2012-12-18 00:49:58 -08:00
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
if (mResource->GetLength() > -1) {
|
|
|
|
mOffset = std::min<int64_t>(mOffset + aLength, mResource->GetLength());
|
|
|
|
} else {
|
|
|
|
mOffset += aLength;
|
|
|
|
}
|
|
|
|
|
2013-02-04 13:33:52 -08:00
|
|
|
return NS_SUCCEEDED(rv) ? S_OK : E_FAIL;
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
nsresult
|
2013-02-14 17:10:21 -08:00
|
|
|
WMFByteStream::Read(ReadRequest* aRequestState)
|
2012-12-18 00:49:58 -08:00
|
|
|
{
|
|
|
|
// Read in a loop to ensure we fill the buffer, when possible.
|
|
|
|
ULONG totalBytesRead = 0;
|
|
|
|
nsresult rv = NS_OK;
|
2013-02-04 13:33:52 -08:00
|
|
|
while (totalBytesRead < aRequestState->mBufferLength) {
|
|
|
|
BYTE* buffer = aRequestState->mBuffer + totalBytesRead;
|
2012-12-18 00:49:58 -08:00
|
|
|
ULONG bytesRead = 0;
|
2013-02-04 13:33:52 -08:00
|
|
|
ULONG length = aRequestState->mBufferLength - totalBytesRead;
|
2013-07-22 15:43:22 -07:00
|
|
|
rv = mResource->ReadAt(aRequestState->mOffset + totalBytesRead,
|
|
|
|
reinterpret_cast<char*>(buffer),
|
|
|
|
length,
|
|
|
|
reinterpret_cast<uint32_t*>(&bytesRead));
|
2013-02-04 13:34:23 -08:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-12-18 00:49:58 -08:00
|
|
|
totalBytesRead += bytesRead;
|
2013-02-04 13:34:23 -08:00
|
|
|
if (bytesRead == 0) {
|
2012-12-18 00:49:58 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-02-04 13:34:23 -08:00
|
|
|
aRequestState->mBytesRead = totalBytesRead;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2012-12-18 00:49:58 -08:00
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
// Note: This is called on one of the thread pool's threads.
|
|
|
|
void
|
|
|
|
WMFByteStream::ProcessReadRequest(IMFAsyncResult* aResult,
|
2013-02-14 17:10:21 -08:00
|
|
|
ReadRequest* aRequestState)
|
2013-02-04 13:34:23 -08:00
|
|
|
{
|
|
|
|
if (mResource->GetLength() > -1 &&
|
|
|
|
aRequestState->mOffset > mResource->GetLength()) {
|
|
|
|
aResult->SetStatus(S_OK);
|
|
|
|
wmf::MFInvokeCallback(aResult);
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::ProcessReadRequest() read offset greater than length, soft-failing read", this);
|
2013-02-04 13:34:23 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = Read(aRequestState);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
Shutdown();
|
|
|
|
aResult->SetStatus(E_ABORT);
|
|
|
|
} else {
|
|
|
|
aResult->SetStatus(S_OK);
|
|
|
|
}
|
2012-12-18 00:49:58 -08:00
|
|
|
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::ProcessReadRequest() read %d at %lld finished rv=%x",
|
|
|
|
this, aRequestState->mBytesRead, aRequestState->mOffset, rv);
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
// Let caller know read is complete.
|
2013-02-04 13:33:52 -08:00
|
|
|
DebugOnly<HRESULT> hr = wmf::MFInvokeCallback(aResult);
|
|
|
|
NS_ASSERTION(SUCCEEDED(hr), "Failed to invoke callback!");
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::BeginWrite(const BYTE *, ULONG ,
|
|
|
|
IMFAsyncCallback *,
|
|
|
|
IUnknown *)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::BeginWrite()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::Close()
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Close()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aResult, E_POINTER);
|
|
|
|
NS_ENSURE_TRUE(aBytesRead, E_POINTER);
|
|
|
|
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
|
|
|
|
// Extract our state object.
|
2012-12-27 06:02:37 -08:00
|
|
|
RefPtr<IUnknown> unknown;
|
|
|
|
HRESULT hr = aResult->GetObject(byRef(unknown));
|
2012-12-18 00:49:58 -08:00
|
|
|
if (FAILED(hr) || !unknown) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
2013-02-14 17:10:21 -08:00
|
|
|
ReadRequest* requestState =
|
|
|
|
static_cast<ReadRequest*>(unknown.get());
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
// Report result.
|
|
|
|
*aBytesRead = requestState->mBytesRead;
|
|
|
|
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::EndRead() offset=%lld *aBytesRead=%u mOffset=%lld status=0x%x hr=0x%x eof=%d",
|
|
|
|
this, requestState->mOffset, *aBytesRead, mOffset, aResult->GetStatus(), hr, IsEOS());
|
2012-12-18 00:49:58 -08:00
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
return aResult->GetStatus();
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::EndWrite(IMFAsyncResult *, ULONG *)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::EndWrite()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::Flush()
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Flush()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetCapabilities(DWORD *aCapabilities)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::GetCapabilities()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
NS_ENSURE_TRUE(aCapabilities, E_POINTER);
|
2013-02-04 13:34:23 -08:00
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
bool seekable = mResource->IsTransportSeekable();
|
2013-02-10 14:31:43 -08:00
|
|
|
bool cached = mResource->IsDataCachedToEndOfResource(0);
|
2012-12-18 00:49:58 -08:00
|
|
|
*aCapabilities = MFBYTESTREAM_IS_READABLE |
|
2013-02-04 13:34:23 -08:00
|
|
|
MFBYTESTREAM_IS_SEEKABLE |
|
2013-02-10 14:31:43 -08:00
|
|
|
(!cached ? MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED : 0) |
|
2013-02-04 13:34:23 -08:00
|
|
|
(!seekable ? MFBYTESTREAM_HAS_SLOW_SEEK : 0);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetCurrentPosition(QWORD *aPosition)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aPosition, E_POINTER);
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
2013-02-04 13:34:23 -08:00
|
|
|
// Note: Returning the length of stream as position when read
|
|
|
|
// cursor is < 0 seems to be the behaviour expected by WMF, but
|
|
|
|
// also note it doesn't seem to expect that the position is an
|
|
|
|
// unsigned value since if you seek to > length and read WMF
|
|
|
|
// expects the read to succeed after reading 0 bytes, but if you
|
|
|
|
// seek to < 0 and read, the read is expected to fails... So
|
|
|
|
// go figure...
|
|
|
|
*aPosition = mOffset < 0 ? mResource->GetLength() : mOffset;
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::GetCurrentPosition() %lld", this, mOffset);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetLength(QWORD *aLength)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aLength, E_POINTER);
|
2013-02-04 13:34:23 -08:00
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
*aLength = mResource->GetLength();
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::GetLength() %lld", this, *aLength);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
bool
|
|
|
|
WMFByteStream::IsEOS()
|
|
|
|
{
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
return mResource->GetLength() > -1 &&
|
|
|
|
(mOffset < 0 ||
|
|
|
|
mOffset >= mResource->GetLength());
|
|
|
|
}
|
|
|
|
|
2012-12-18 00:49:58 -08:00
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::IsEndOfStream(BOOL *aEndOfStream)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aEndOfStream, E_POINTER);
|
2013-02-04 13:34:23 -08:00
|
|
|
*aEndOfStream = IsEOS();
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::IsEndOfStream() %d", this, *aEndOfStream);
|
2012-12-18 00:49:58 -08:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
2013-02-14 17:10:21 -08:00
|
|
|
WMFByteStream::Read(BYTE* aBuffer, ULONG aBufferLength, ULONG* aOutBytesRead)
|
2012-12-18 00:49:58 -08:00
|
|
|
{
|
2013-02-14 17:10:21 -08:00
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
ReadRequest request(mOffset, aBuffer, aBufferLength);
|
|
|
|
if (NS_FAILED(Read(&request))) {
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Read() offset=%lld failed!", this, mOffset);
|
2013-02-14 17:10:21 -08:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
if (aOutBytesRead) {
|
|
|
|
*aOutBytesRead = request.mBytesRead;
|
|
|
|
}
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Read() offset=%lld length=%u bytesRead=%u",
|
|
|
|
this, mOffset, aBufferLength, request.mBytesRead);
|
2013-02-14 17:10:21 -08:00
|
|
|
mOffset += request.mBytesRead;
|
|
|
|
return S_OK;
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
|
|
|
|
LONGLONG aSeekOffset,
|
|
|
|
DWORD aSeekFlags,
|
|
|
|
QWORD *aCurrentPosition)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Seek(%d, %lld)", this, aSeekOrigin, aSeekOffset);
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
|
|
|
|
2013-02-04 13:34:23 -08:00
|
|
|
int64_t offset = mOffset;
|
2012-12-18 00:49:58 -08:00
|
|
|
if (aSeekOrigin == msoBegin) {
|
2013-02-04 13:34:23 -08:00
|
|
|
offset = aSeekOffset;
|
2012-12-18 00:49:58 -08:00
|
|
|
} else {
|
2013-02-04 13:34:23 -08:00
|
|
|
offset += aSeekOffset;
|
|
|
|
}
|
|
|
|
int64_t length = mResource->GetLength();
|
|
|
|
if (length > -1) {
|
|
|
|
mOffset = std::min<int64_t>(offset, length);
|
|
|
|
} else {
|
|
|
|
mOffset = offset;
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
if (aCurrentPosition) {
|
|
|
|
*aCurrentPosition = mOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetCurrentPosition(QWORD aPosition)
|
|
|
|
{
|
|
|
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::SetCurrentPosition(%lld)",
|
|
|
|
this, aPosition);
|
2012-12-18 00:49:58 -08:00
|
|
|
|
|
|
|
int64_t length = mResource->GetLength();
|
2013-02-04 13:34:23 -08:00
|
|
|
if (length > -1) {
|
|
|
|
mOffset = std::min<int64_t>(aPosition, length);
|
|
|
|
} else {
|
|
|
|
mOffset = aPosition;
|
2012-12-18 00:49:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetLength(QWORD)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::SetLength()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::Write(const BYTE *, ULONG, ULONG *)
|
|
|
|
{
|
2013-06-28 03:10:01 -07:00
|
|
|
LOG("[%p] WMFByteStream::Write()", this);
|
2012-12-18 00:49:58 -08:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2013-02-14 17:11:05 -08:00
|
|
|
// IMFAttributes methods
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetItem(REFGUID guidKey, PROPVARIANT* pValue)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mAttributes);
|
|
|
|
return mAttributes->GetItem(guidKey, pValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetItemType(guidKey, pType);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->CompareItem(guidKey, Value, pbResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::Compare(IMFAttributes* pTheirs,
|
|
|
|
MF_ATTRIBUTES_MATCH_TYPE MatchType,
|
|
|
|
BOOL* pbResult)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->Compare(pTheirs, MatchType, pbResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetUINT32(REFGUID guidKey, UINT32* punValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetUINT32(guidKey, punValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetUINT64(REFGUID guidKey, UINT64* punValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetUINT64(guidKey, punValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetDouble(REFGUID guidKey, double* pfValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetDouble(guidKey, pfValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetGUID(REFGUID guidKey, GUID* pguidValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetGUID(guidKey, pguidValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetStringLength(REFGUID guidKey, UINT32* pcchLength)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetStringLength(guidKey, pcchLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetBlobSize(guidKey, pcbBlobSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetUnknown(guidKey, riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetItem(REFGUID guidKey, REFPROPVARIANT Value)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetItem(guidKey, Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::DeleteItem(REFGUID guidKey)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->DeleteItem(guidKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::DeleteAllItems()
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->DeleteAllItems();
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetUINT32(REFGUID guidKey, UINT32 unValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetUINT32(guidKey, unValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetUINT64(REFGUID guidKey,UINT64 unValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetUINT64(guidKey, unValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetDouble(REFGUID guidKey, double fValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetDouble(guidKey, fValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetGUID(REFGUID guidKey, REFGUID guidValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetGUID(guidKey, guidValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetString(REFGUID guidKey, LPCWSTR wszValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetString(guidKey, wszValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetBlob(guidKey, pBuf, cbBufSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::SetUnknown(REFGUID guidKey, IUnknown* pUnknown)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->SetUnknown(guidKey, pUnknown);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::LockStore()
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->LockStore();
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::UnlockStore()
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->UnlockStore();
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetCount(UINT32* pcItems)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetCount(pcItems);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->GetItemByIndex(unIndex, pguidKey, pValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
WMFByteStream::CopyAllItems(IMFAttributes* pDest)
|
|
|
|
{
|
|
|
|
assert(mAttributes);
|
|
|
|
return mAttributes->CopyAllItems(pDest);
|
|
|
|
}
|
|
|
|
|
2012-12-18 00:49:58 -08:00
|
|
|
} // namespace mozilla
|