384 lines
9.2 KiB
C++
384 lines
9.2 KiB
C++
// Copyright (c) Microsoft. All rights reserved.
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files(the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions :
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "MediaStreamSink.hpp"
|
|
#include "MFIncludes.hpp"
|
|
|
|
using namespace Media;
|
|
using namespace Microsoft::WRL;
|
|
using namespace Platform;
|
|
using namespace Windows::Foundation;
|
|
|
|
MediaStreamSink::MediaStreamSink(
|
|
__in const MW::ComPtr<IMFMediaSink>& sink,
|
|
__in DWORD id,
|
|
__in const MW::ComPtr<IMFMediaType>& mt,
|
|
__in MediaSampleHandler^ sampleHandler
|
|
)
|
|
: _shutdown(false)
|
|
, _id(-1)
|
|
, _width(0)
|
|
, _height(0)
|
|
{
|
|
CHK(MFCreateEventQueue(&_eventQueue));
|
|
CHK(MFCreateMediaType(&_curMT));
|
|
|
|
_UpdateMediaType(mt);
|
|
|
|
_sink = sink;
|
|
_id = id;
|
|
_sampleHandler = sampleHandler;
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetMediaSink(__deref_out IMFMediaSink **sink)
|
|
{
|
|
return ExceptionBoundary([this, sink]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(sink);
|
|
*sink = nullptr;
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_sink.CopyTo(sink));
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetIdentifier(__out DWORD *identifier)
|
|
{
|
|
return ExceptionBoundary([this, identifier]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(identifier);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
*identifier = _id;
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetMediaTypeHandler(__deref_out IMFMediaTypeHandler **handler)
|
|
{
|
|
return ExceptionBoundary([this, handler]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(handler);
|
|
*handler = nullptr;
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
*handler = this;
|
|
this->AddRef();
|
|
|
|
});
|
|
}
|
|
|
|
void MediaStreamSink::RequestSample()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_eventQueue->QueueEventParamVar(MEStreamSinkRequestSample, GUID_NULL, S_OK, nullptr));
|
|
}
|
|
|
|
HRESULT MediaStreamSink::ProcessSample(__in_opt IMFSample *sample)
|
|
{
|
|
return ExceptionBoundary([this, sample]()
|
|
{
|
|
MediaSampleHandler^ sampleHandler;
|
|
auto mediaSample = ref new MediaSample();
|
|
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
if (sample == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mediaSample->Sample = sample;
|
|
sampleHandler = _sampleHandler;
|
|
}
|
|
|
|
// Call back without the lock taken to avoid deadlocks
|
|
sampleHandler(mediaSample);
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::PlaceMarker(__in MFSTREAMSINK_MARKER_TYPE /*markerType*/, __in const PROPVARIANT * /*markerValue*/, __in const PROPVARIANT * contextValue)
|
|
{
|
|
return ExceptionBoundary([this, contextValue]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
CHKNULL(contextValue);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_eventQueue->QueueEventParamVar(MEStreamSinkMarker, GUID_NULL, S_OK, contextValue));
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::Flush()
|
|
{
|
|
return ExceptionBoundary([this]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetEvent(__in DWORD flags, __deref_out IMFMediaEvent **event)
|
|
{
|
|
return ExceptionBoundary([this, flags, event]()
|
|
{
|
|
CHKNULL(event);
|
|
*event = nullptr;
|
|
|
|
ComPtr<IMFMediaEventQueue> eventQueue;
|
|
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
eventQueue = _eventQueue;
|
|
}
|
|
|
|
// May block for a while
|
|
CHK(eventQueue->GetEvent(flags, event));
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::BeginGetEvent(__in IMFAsyncCallback *callback, __in_opt IUnknown *state)
|
|
{
|
|
return ExceptionBoundary([this, callback, state]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_eventQueue->BeginGetEvent(callback, state));
|
|
});
|
|
}
|
|
|
|
|
|
HRESULT MediaStreamSink::EndGetEvent(__in IMFAsyncResult *result, __deref_out IMFMediaEvent **event)
|
|
{
|
|
return ExceptionBoundary([this, result, event]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(event);
|
|
*event = nullptr;
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_eventQueue->EndGetEvent(result, event));
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::QueueEvent(
|
|
__in MediaEventType met,
|
|
__in REFGUID extendedType,
|
|
__in HRESULT status,
|
|
__in_opt const PROPVARIANT *value
|
|
)
|
|
{
|
|
return ExceptionBoundary([this, met, extendedType, status, value]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
CHK(_eventQueue->QueueEventParamVar(met, extendedType, status, value));
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::IsMediaTypeSupported(__in IMFMediaType *mediaType, __deref_out_opt IMFMediaType **closestMediaType)
|
|
{
|
|
bool supported = false;
|
|
|
|
HRESULT hr = ExceptionBoundary([this, mediaType, closestMediaType, &supported]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
if (closestMediaType != nullptr)
|
|
{
|
|
*closestMediaType = nullptr;
|
|
}
|
|
|
|
CHKNULL(mediaType);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
supported = _IsMediaTypeSupported(mediaType);
|
|
});
|
|
|
|
// Avoid throwing an exception to return MF_E_INVALIDMEDIATYPE as this is not a exceptional case
|
|
return FAILED(hr) ? hr : supported ? S_OK : MF_E_INVALIDMEDIATYPE;
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetMediaTypeCount(__out DWORD *typeCount)
|
|
{
|
|
return ExceptionBoundary([this, typeCount]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(typeCount);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
// No media type provided by default (app needs to specify it)
|
|
*typeCount = 0;
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetMediaTypeByIndex(__in DWORD /*index*/, __deref_out IMFMediaType **mediaType)
|
|
{
|
|
HRESULT hr = ExceptionBoundary([this, mediaType]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(mediaType);
|
|
*mediaType = nullptr;
|
|
|
|
_VerifyNotShutdown();
|
|
});
|
|
|
|
// Avoid throwing an exception to return MF_E_NO_MORE_TYPES as this is not a exceptional case
|
|
return FAILED(hr) ? hr : MF_E_NO_MORE_TYPES;
|
|
}
|
|
|
|
HRESULT MediaStreamSink::SetCurrentMediaType(__in IMFMediaType *mediaType)
|
|
{
|
|
return ExceptionBoundary([this, mediaType]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(mediaType);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
if (!_IsMediaTypeSupported(mediaType))
|
|
{
|
|
CHK(MF_E_INVALIDMEDIATYPE);
|
|
}
|
|
|
|
_UpdateMediaType(mediaType);
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetCurrentMediaType(__deref_out_opt IMFMediaType **mediaType)
|
|
{
|
|
return ExceptionBoundary([this, mediaType]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(mediaType);
|
|
*mediaType = nullptr;
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
ComPtr<IMFMediaType> mt;
|
|
CHK(MFCreateMediaType(&mt));
|
|
CHK(_curMT->CopyAllItems(mt.Get()));
|
|
*mediaType = mt.Detach();
|
|
});
|
|
}
|
|
|
|
HRESULT MediaStreamSink::GetMajorType(__out GUID *majorType)
|
|
{
|
|
return ExceptionBoundary([this, majorType]()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(majorType);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
*majorType = _majorType;
|
|
});
|
|
}
|
|
|
|
void MediaStreamSink::InternalSetCurrentMediaType(__in const ComPtr<IMFMediaType>& mediaType)
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
CHKNULL(mediaType);
|
|
|
|
_VerifyNotShutdown();
|
|
|
|
_UpdateMediaType(mediaType);
|
|
}
|
|
|
|
void MediaStreamSink::Shutdown()
|
|
{
|
|
auto lock = _lock.LockExclusive();
|
|
|
|
if (_shutdown)
|
|
{
|
|
return;
|
|
}
|
|
_shutdown = true;
|
|
|
|
(void)_eventQueue->Shutdown();
|
|
_eventQueue = nullptr;
|
|
|
|
_curMT = nullptr;
|
|
_sink = nullptr;
|
|
_sampleHandler = nullptr;
|
|
}
|
|
|
|
bool MediaStreamSink::_IsMediaTypeSupported(__in const ComPtr<IMFMediaType>& mt) const
|
|
{
|
|
GUID majorType;
|
|
GUID subType;
|
|
if (SUCCEEDED(mt->GetGUID(MF_MT_MAJOR_TYPE, &majorType)) &&
|
|
SUCCEEDED(mt->GetGUID(MF_MT_SUBTYPE, &subType)) &&
|
|
(majorType == _majorType) &&
|
|
(subType == _subType))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MediaStreamSink::_UpdateMediaType(__in const ComPtr<IMFMediaType>& mt)
|
|
{
|
|
CHK(mt->GetGUID(MF_MT_MAJOR_TYPE, &_majorType));
|
|
CHK(mt->GetGUID(MF_MT_SUBTYPE, &_subType));
|
|
|
|
if (_majorType == MFMediaType_Video)
|
|
{
|
|
CHK(MFGetAttributeSize(mt.Get(), MF_MT_FRAME_SIZE, &_width, &_height));
|
|
}
|
|
|
|
CHK(mt->CopyAllItems(_curMT.Get()));
|
|
} |