Merge pull request #4005 from MSOpenTech:videoio-contrib
@ -0,0 +1,130 @@ |
|||||||
|
// Video support for Windows Runtime
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 <ppl.h> |
||||||
|
#include <functional> |
||||||
|
#include <concrt.h> |
||||||
|
#include <agile.h> |
||||||
|
|
||||||
|
using namespace Windows::UI::Xaml::Controls; |
||||||
|
|
||||||
|
namespace cv |
||||||
|
{ |
||||||
|
|
||||||
|
//! @addtogroup videoio_winrt
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
enum { |
||||||
|
OPEN_CAMERA = 300, |
||||||
|
CLOSE_CAMERA, |
||||||
|
UPDATE_IMAGE_ELEMENT, |
||||||
|
SHOW_TRACKBAR |
||||||
|
}; |
||||||
|
|
||||||
|
/********************************** WinRT API ************************************************/ |
||||||
|
|
||||||
|
template <typename ...Args> |
||||||
|
CV_EXPORTS void winrt_startMessageLoop(std::function<void(Args...)>&& callback, Args... args); |
||||||
|
|
||||||
|
template <typename ...Args> |
||||||
|
CV_EXPORTS void winrt_startMessageLoop(void callback(Args...), Args... args); |
||||||
|
|
||||||
|
/** @brief
|
||||||
|
@note |
||||||
|
Sets the reporter method for the HighguiAssist singleton. Starts the main OpenCV as |
||||||
|
an async thread in WinRT. See VideoCapture for the example of callback implementation. |
||||||
|
Here is how the class can be used: |
||||||
|
@code |
||||||
|
void cvMain() |
||||||
|
{ |
||||||
|
Mat frame; |
||||||
|
VideoCapture cam; |
||||||
|
cam.open(0); |
||||||
|
|
||||||
|
while (1) |
||||||
|
{ |
||||||
|
cam >> frame; |
||||||
|
|
||||||
|
// don't reprocess the same frame again
|
||||||
|
if (!cam.grab()) continue; |
||||||
|
|
||||||
|
// your processing logic goes here
|
||||||
|
|
||||||
|
// obligatory step to get XAML image component updated
|
||||||
|
winrt_imshow(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MainPage::MainPage() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
cv::winrt_setFrameContainer(cvImage); |
||||||
|
cv::winrt_startMessageLoop(cvMain); |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
template |
||||||
|
CV_EXPORTS void winrt_startMessageLoop(void callback(void)); |
||||||
|
|
||||||
|
/** @brief
|
||||||
|
@note |
||||||
|
Must be called from WinRT specific callback to handle image grabber state. |
||||||
|
Here is how the class can be used: |
||||||
|
@code |
||||||
|
MainPage::MainPage() |
||||||
|
{ |
||||||
|
// ...
|
||||||
|
Window::Current->VisibilityChanged += ref new Windows::UI::Xaml::WindowVisibilityChangedEventHandler(this, &Application::MainPage::OnVisibilityChanged); |
||||||
|
// ...
|
||||||
|
} |
||||||
|
|
||||||
|
void Application::MainPage::OnVisibilityChanged(Platform::Object ^sender, |
||||||
|
Windows::UI::Core::VisibilityChangedEventArgs ^e) |
||||||
|
{ |
||||||
|
cv::winrt_onVisibilityChanged(e->Visible); |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
CV_EXPORTS void winrt_onVisibilityChanged(bool visible); |
||||||
|
|
||||||
|
/** @brief
|
||||||
|
@note |
||||||
|
Must be called to assign WinRT control holding image you're working with. |
||||||
|
Code sample is available for winrt_startMessageLoop(). |
||||||
|
*/ |
||||||
|
CV_EXPORTS void winrt_setFrameContainer(::Windows::UI::Xaml::Controls::Image^ image); |
||||||
|
|
||||||
|
/** @brief
|
||||||
|
@note |
||||||
|
Must be called to update attached image source. |
||||||
|
Code sample is available for winrt_startMessageLoop(). |
||||||
|
*/ |
||||||
|
CV_EXPORTS void winrt_imshow(); |
||||||
|
|
||||||
|
//! @} videoio_winrt
|
||||||
|
|
||||||
|
} // cv
|
@ -0,0 +1,568 @@ |
|||||||
|
//
|
||||||
|
// Copyright (C) Microsoft Corporation
|
||||||
|
// All rights reserved.
|
||||||
|
// Modified for native C++ WRL support by Gregory Morse
|
||||||
|
//
|
||||||
|
// Code in Details namespace is for internal usage within the library code
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _PLATFORM_AGILE_H_ |
||||||
|
#define _PLATFORM_AGILE_H_ |
||||||
|
|
||||||
|
#ifdef _MSC_VER |
||||||
|
#pragma once |
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <wrl\client.h> |
||||||
|
|
||||||
|
template <typename T, bool TIsNotAgile> class Agile; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
struct UnwrapAgile |
||||||
|
{ |
||||||
|
static const bool _IsAgile = false; |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
struct UnwrapAgile<Agile<T, false>> |
||||||
|
{ |
||||||
|
static const bool _IsAgile = true; |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
struct UnwrapAgile<Agile<T, true>> |
||||||
|
{ |
||||||
|
static const bool _IsAgile = true; |
||||||
|
}; |
||||||
|
|
||||||
|
#define IS_AGILE(T) UnwrapAgile<T>::_IsAgile |
||||||
|
|
||||||
|
#define __is_winrt_agile(T) (std::is_same<T, HSTRING__>::value || std::is_base_of<Microsoft::WRL::FtmBase, T>::value || std::is_base_of<IAgileObject, T>::value) //derived from Microsoft::WRL::FtmBase or IAgileObject
|
||||||
|
|
||||||
|
#define __is_win_interface(T) (std::is_base_of<IUnknown, T>::value || std::is_base_of<IInspectable, T>::value) //derived from IUnknown or IInspectable
|
||||||
|
|
||||||
|
#define __is_win_class(T) (std::is_same<T, HSTRING__>::value || std::is_base_of<Microsoft::WRL::Details::RuntimeClassBase, T>::value) //derived from Microsoft::WRL::RuntimeClass or HSTRING
|
||||||
|
|
||||||
|
namespace Details |
||||||
|
{ |
||||||
|
IUnknown* __stdcall GetObjectContext(); |
||||||
|
HRESULT __stdcall GetProxyImpl(IUnknown*, REFIID, IUnknown*, IUnknown**); |
||||||
|
HRESULT __stdcall ReleaseInContextImpl(IUnknown*, IUnknown*); |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
__declspec(no_refcount) inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) |
||||||
|
#else |
||||||
|
inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
return GetProxyImpl(*reinterpret_cast<IUnknown**>(&ObjectIn), __uuidof(T*), ContextCallBack, reinterpret_cast<IUnknown**>(Proxy)); |
||||||
|
#else |
||||||
|
return GetProxyImpl(*reinterpret_cast<IUnknown**>(&const_cast<T*>(ObjectIn)), __uuidof(T*), ContextCallBack, reinterpret_cast<IUnknown**>(Proxy)); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
inline HRESULT ReleaseInContext(T *ObjectIn, IUnknown *ContextCallBack) |
||||||
|
{ |
||||||
|
return ReleaseInContextImpl(ObjectIn, ContextCallBack); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
class AgileHelper |
||||||
|
{ |
||||||
|
__abi_IUnknown* _p; |
||||||
|
bool _release; |
||||||
|
public: |
||||||
|
AgileHelper(__abi_IUnknown* p, bool release = true) : _p(p), _release(release) |
||||||
|
{ |
||||||
|
} |
||||||
|
AgileHelper(AgileHelper&& other) : _p(other._p), _release(other._release) |
||||||
|
{ |
||||||
|
_other._p = nullptr; |
||||||
|
_other._release = true; |
||||||
|
} |
||||||
|
AgileHelper operator=(AgileHelper&& other) |
||||||
|
{ |
||||||
|
_p = other._p; |
||||||
|
_release = other._release; |
||||||
|
_other._p = nullptr; |
||||||
|
_other._release = true; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
~AgileHelper() |
||||||
|
{ |
||||||
|
if (_release && _p) |
||||||
|
{ |
||||||
|
_p->__abi_Release(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
__declspec(no_refcount) __declspec(no_release_return) |
||||||
|
T* operator->() |
||||||
|
{ |
||||||
|
return reinterpret_cast<T*>(_p); |
||||||
|
} |
||||||
|
|
||||||
|
__declspec(no_refcount) __declspec(no_release_return) |
||||||
|
operator T * () |
||||||
|
{ |
||||||
|
return reinterpret_cast<T*>(_p); |
||||||
|
} |
||||||
|
private: |
||||||
|
AgileHelper(const AgileHelper&); |
||||||
|
AgileHelper operator=(const AgileHelper&); |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
struct __remove_hat |
||||||
|
{ |
||||||
|
typedef T type; |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
struct __remove_hat<T*> |
||||||
|
{ |
||||||
|
typedef T type; |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
struct AgileTypeHelper |
||||||
|
{ |
||||||
|
typename typedef __remove_hat<T>::type type; |
||||||
|
typename typedef __remove_hat<T>::type* agileMemberType; |
||||||
|
}; |
||||||
|
} // namespace Details
|
||||||
|
|
||||||
|
#pragma warning(push) |
||||||
|
#pragma warning(disable: 4451) // Usage of ref class inside this context can lead to invalid marshaling of object across contexts
|
||||||
|
|
||||||
|
template < |
||||||
|
typename T, |
||||||
|
bool TIsNotAgile = (__is_win_class(typename Details::AgileTypeHelper<T>::type) && !__is_winrt_agile(typename Details::AgileTypeHelper<T>::type)) || |
||||||
|
__is_win_interface(typename Details::AgileTypeHelper<T>::type) |
||||||
|
> |
||||||
|
class Agile |
||||||
|
{ |
||||||
|
static_assert(__is_win_class(typename Details::AgileTypeHelper<T>::type) || __is_win_interface(typename Details::AgileTypeHelper<T>::type), "Agile can only be used with ref class or interface class types"); |
||||||
|
typename typedef Details::AgileTypeHelper<T>::agileMemberType TypeT; |
||||||
|
TypeT _object; |
||||||
|
::Microsoft::WRL::ComPtr<IUnknown> _contextCallback; |
||||||
|
ULONG_PTR _contextToken; |
||||||
|
|
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
enum class AgileState |
||||||
|
{ |
||||||
|
NonAgilePointer = 0, |
||||||
|
AgilePointer = 1, |
||||||
|
Unknown = 2 |
||||||
|
}; |
||||||
|
AgileState _agileState; |
||||||
|
#endif |
||||||
|
|
||||||
|
void CaptureContext() |
||||||
|
{ |
||||||
|
_contextCallback = Details::GetObjectContext(); |
||||||
|
__abi_ThrowIfFailed(CoGetContextToken(&_contextToken)); |
||||||
|
} |
||||||
|
|
||||||
|
void SetObject(TypeT object) |
||||||
|
{ |
||||||
|
// Capture context before setting the pointer
|
||||||
|
// If context capture fails then nothing to cleanup
|
||||||
|
Release(); |
||||||
|
if (object != nullptr) |
||||||
|
{ |
||||||
|
::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; |
||||||
|
HRESULT hr = reinterpret_cast<IUnknown*>(object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); |
||||||
|
// Don't Capture context if object is agile
|
||||||
|
if (hr != S_OK) |
||||||
|
{ |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
_agileState = AgileState::NonAgilePointer; |
||||||
|
#endif |
||||||
|
CaptureContext(); |
||||||
|
} |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
else |
||||||
|
{ |
||||||
|
_agileState = AgileState::AgilePointer; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
_object = object; |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
Agile() throw() : _object(nullptr), _contextToken(0) |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
, _agileState(AgileState::Unknown) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Agile(nullptr_t) throw() : _object(nullptr), _contextToken(0) |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
, _agileState(AgileState::Unknown) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
explicit Agile(TypeT object) throw() : _object(nullptr), _contextToken(0) |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
, _agileState(AgileState::Unknown) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
// Assumes that the source object is from the current context
|
||||||
|
SetObject(object); |
||||||
|
} |
||||||
|
|
||||||
|
Agile(const Agile& object) throw() : _object(nullptr), _contextToken(0) |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
, _agileState(AgileState::Unknown) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
// Get returns pointer valid for current context
|
||||||
|
SetObject(object.Get()); |
||||||
|
} |
||||||
|
|
||||||
|
Agile(Agile&& object) throw() : _object(nullptr), _contextToken(0) |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
, _agileState(AgileState::Unknown) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
// Assumes that the source object is from the current context
|
||||||
|
Swap(object); |
||||||
|
} |
||||||
|
|
||||||
|
~Agile() throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
} |
||||||
|
|
||||||
|
TypeT Get() const |
||||||
|
{ |
||||||
|
// Agile object, no proxy required
|
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
if (_agileState == AgileState::AgilePointer || _object == nullptr) |
||||||
|
#else |
||||||
|
if (_contextToken == 0 || _contextCallback == nullptr || _object == nullptr) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
return _object; |
||||||
|
} |
||||||
|
|
||||||
|
// Do the check for same context
|
||||||
|
ULONG_PTR currentContextToken; |
||||||
|
__abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); |
||||||
|
if (currentContextToken == _contextToken) |
||||||
|
{ |
||||||
|
return _object; |
||||||
|
} |
||||||
|
|
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
// Different context and holding on to a non agile object
|
||||||
|
// Do the costly work of getting a proxy
|
||||||
|
TypeT localObject; |
||||||
|
__abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); |
||||||
|
|
||||||
|
if (_agileState == AgileState::Unknown) |
||||||
|
#else |
||||||
|
// Object is agile if it implements IAgileObject
|
||||||
|
// GetAddressOf captures the context with out knowing the type of object that it will hold
|
||||||
|
if (_object != nullptr) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
// Object is agile if it implements IAgileObject
|
||||||
|
// GetAddressOf captures the context with out knowing the type of object that it will hold
|
||||||
|
::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; |
||||||
|
HRESULT hr = reinterpret_cast<IUnknown*>(localObject)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); |
||||||
|
#else |
||||||
|
::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; |
||||||
|
HRESULT hr = reinterpret_cast<IUnknown*>(_object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); |
||||||
|
#endif |
||||||
|
if (hr == S_OK) |
||||||
|
{ |
||||||
|
auto pThis = const_cast<Agile*>(this); |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
pThis->_agileState = AgileState::AgilePointer; |
||||||
|
#endif |
||||||
|
pThis->_contextToken = 0; |
||||||
|
pThis->_contextCallback = nullptr; |
||||||
|
return _object; |
||||||
|
} |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
else |
||||||
|
{ |
||||||
|
auto pThis = const_cast<Agile*>(this); |
||||||
|
pThis->_agileState = AgileState::NonAgilePointer; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#if _MSC_VER < 1800 |
||||||
|
// Different context and holding on to a non agile object
|
||||||
|
// Do the costly work of getting a proxy
|
||||||
|
TypeT localObject; |
||||||
|
__abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); |
||||||
|
#endif |
||||||
|
return localObject; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT* GetAddressOf() throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
CaptureContext(); |
||||||
|
return &_object; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT* GetAddressOfForInOut() throw() |
||||||
|
{ |
||||||
|
CaptureContext(); |
||||||
|
return &_object; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT operator->() const throw() |
||||||
|
{ |
||||||
|
return Get(); |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(nullptr_t) throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(TypeT object) throw() |
||||||
|
{ |
||||||
|
Agile(object).Swap(*this); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(Agile object) throw() |
||||||
|
{ |
||||||
|
// parameter is by copy which gets pointer valid for current context
|
||||||
|
object.Swap(*this); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
#if _MSC_VER < 1800 |
||||||
|
Agile& operator=(IUnknown* lp) throw() |
||||||
|
{ |
||||||
|
// bump ref count
|
||||||
|
::Microsoft::WRL::ComPtr<IUnknown> spObject(lp); |
||||||
|
|
||||||
|
// put it into Platform Object
|
||||||
|
Platform::Object object; |
||||||
|
*(IUnknown**)(&object) = spObject.Detach(); |
||||||
|
|
||||||
|
SetObject(object); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void Swap(Agile& object) |
||||||
|
{ |
||||||
|
std::swap(_object, object._object); |
||||||
|
std::swap(_contextCallback, object._contextCallback); |
||||||
|
std::swap(_contextToken, object._contextToken); |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
std::swap(_agileState, object._agileState); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
// Release the interface and set to NULL
|
||||||
|
void Release() throw() |
||||||
|
{ |
||||||
|
if (_object) |
||||||
|
{ |
||||||
|
// Cast to IInspectable (no QI)
|
||||||
|
IUnknown* pObject = *(IUnknown**)(&_object); |
||||||
|
// Set * to null without release
|
||||||
|
*(IUnknown**)(&_object) = nullptr; |
||||||
|
|
||||||
|
ULONG_PTR currentContextToken; |
||||||
|
__abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); |
||||||
|
if (_contextToken == 0 || _contextCallback == nullptr || _contextToken == currentContextToken) |
||||||
|
{ |
||||||
|
pObject->Release(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Details::ReleaseInContext(pObject, _contextCallback.Get()); |
||||||
|
} |
||||||
|
_contextCallback = nullptr; |
||||||
|
_contextToken = 0; |
||||||
|
#if _MSC_VER >= 1800 |
||||||
|
_agileState = AgileState::Unknown; |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(nullptr_t) const throw() |
||||||
|
{ |
||||||
|
return _object == nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(const Agile& other) const throw() |
||||||
|
{ |
||||||
|
return _object == other._object && _contextToken == other._contextToken; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator<(const Agile& other) const throw() |
||||||
|
{ |
||||||
|
if (reinterpret_cast<void*>(_object) < reinterpret_cast<void*>(other._object)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return _object == other._object && _contextToken < other._contextToken; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
class Agile<T, false> |
||||||
|
{ |
||||||
|
static_assert(__is_win_class(typename Details::AgileTypeHelper<T>::type) || __is_win_interface(typename Details::AgileTypeHelper<T>::type), "Agile can only be used with ref class or interface class types"); |
||||||
|
typename typedef Details::AgileTypeHelper<T>::agileMemberType TypeT; |
||||||
|
TypeT _object; |
||||||
|
|
||||||
|
public: |
||||||
|
Agile() throw() : _object(nullptr) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Agile(nullptr_t) throw() : _object(nullptr) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
explicit Agile(TypeT object) throw() : _object(object) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Agile(const Agile& object) throw() : _object(object._object) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Agile(Agile&& object) throw() : _object(nullptr) |
||||||
|
{ |
||||||
|
Swap(object); |
||||||
|
} |
||||||
|
|
||||||
|
~Agile() throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
} |
||||||
|
|
||||||
|
TypeT Get() const |
||||||
|
{ |
||||||
|
return _object; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT* GetAddressOf() throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
return &_object; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT* GetAddressOfForInOut() throw() |
||||||
|
{ |
||||||
|
return &_object; |
||||||
|
} |
||||||
|
|
||||||
|
TypeT operator->() const throw() |
||||||
|
{ |
||||||
|
return Get(); |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(nullptr_t) throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(TypeT object) throw() |
||||||
|
{ |
||||||
|
if (_object != object) |
||||||
|
{ |
||||||
|
_object = object; |
||||||
|
} |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Agile& operator=(Agile object) throw() |
||||||
|
{ |
||||||
|
object.Swap(*this); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
#if _MSC_VER < 1800 |
||||||
|
Agile& operator=(IUnknown* lp) throw() |
||||||
|
{ |
||||||
|
Release(); |
||||||
|
// bump ref count
|
||||||
|
::Microsoft::WRL::ComPtr<IUnknown> spObject(lp); |
||||||
|
|
||||||
|
// put it into Platform Object
|
||||||
|
Platform::Object object; |
||||||
|
*(IUnknown**)(&object) = spObject.Detach(); |
||||||
|
|
||||||
|
_object = object; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
// Release the interface and set to NULL
|
||||||
|
void Release() throw() |
||||||
|
{ |
||||||
|
_object = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void Swap(Agile& object) |
||||||
|
{ |
||||||
|
std::swap(_object, object._object); |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(nullptr_t) const throw() |
||||||
|
{ |
||||||
|
return _object == nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator==(const Agile& other) const throw() |
||||||
|
{ |
||||||
|
return _object == other._object; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator<(const Agile& other) const throw() |
||||||
|
{ |
||||||
|
return reinterpret_cast<void*>(_object) < reinterpret_cast<void*>(other._object); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma warning(pop) |
||||||
|
|
||||||
|
template<class U> |
||||||
|
bool operator==(nullptr_t, const Agile<U>& a) throw() |
||||||
|
{ |
||||||
|
return a == nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
template<class U> |
||||||
|
bool operator!=(const Agile<U>& a, nullptr_t) throw() |
||||||
|
{ |
||||||
|
return !(a == nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
template<class U> |
||||||
|
bool operator!=(nullptr_t, const Agile<U>& a) throw() |
||||||
|
{ |
||||||
|
return !(a == nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
template<class U> |
||||||
|
bool operator!=(const Agile<U>& a, const Agile<U>& b) throw() |
||||||
|
{ |
||||||
|
return !(a == b); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#endif // _PLATFORM_AGILE_H_
|
@ -0,0 +1,173 @@ |
|||||||
|
// 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 "MediaSink.hpp" |
||||||
|
#include "CaptureFrameGrabber.hpp" |
||||||
|
|
||||||
|
using namespace Media; |
||||||
|
using namespace Platform; |
||||||
|
using namespace Windows::Foundation; |
||||||
|
using namespace Windows::Media; |
||||||
|
using namespace Windows::Media::Capture; |
||||||
|
using namespace Windows::Media::MediaProperties; |
||||||
|
using namespace concurrency; |
||||||
|
using namespace Microsoft::WRL::Details; |
||||||
|
using namespace Microsoft::WRL; |
||||||
|
|
||||||
|
task<Media::CaptureFrameGrabber^> Media::CaptureFrameGrabber::CreateAsync(_In_ MediaCapture^ capture, _In_ VideoEncodingProperties^ props, CaptureStreamType streamType) |
||||||
|
{ |
||||||
|
auto reader = ref new Media::CaptureFrameGrabber(capture, props, streamType); |
||||||
|
|
||||||
|
auto profile = ref new MediaEncodingProfile(); |
||||||
|
profile->Video = props; |
||||||
|
|
||||||
|
task<void> task; |
||||||
|
if (reader->_streamType == CaptureStreamType::Preview) |
||||||
|
{ |
||||||
|
task = create_task(capture->StartPreviewToCustomSinkAsync(profile, reader->_mediaExtension)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
task = create_task(capture->StartRecordToCustomSinkAsync(profile, reader->_mediaExtension)); |
||||||
|
} |
||||||
|
|
||||||
|
return task.then([reader]() |
||||||
|
{ |
||||||
|
reader->_state = State::Started; |
||||||
|
return reader; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
Media::CaptureFrameGrabber::CaptureFrameGrabber(_In_ MediaCapture^ capture, _In_ VideoEncodingProperties^ props, CaptureStreamType streamType) |
||||||
|
: _state(State::Created) |
||||||
|
, _streamType(streamType) |
||||||
|
, _capture(capture) |
||||||
|
{ |
||||||
|
auto videoSampleHandler = ref new MediaSampleHandler(this, &Media::CaptureFrameGrabber::ProcessSample); |
||||||
|
|
||||||
|
_mediaSink = Make<MediaSink>(nullptr, props, nullptr, videoSampleHandler); |
||||||
|
_mediaExtension = reinterpret_cast<IMediaExtension^>(static_cast<AWM::IMediaExtension*>(_mediaSink.Get())); |
||||||
|
} |
||||||
|
|
||||||
|
Media::CaptureFrameGrabber::~CaptureFrameGrabber() |
||||||
|
{ |
||||||
|
if (_state == State::Started) |
||||||
|
{ |
||||||
|
if (_streamType == CaptureStreamType::Preview) |
||||||
|
{ |
||||||
|
(void)_capture->StopPreviewAsync(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
(void)_capture->StopRecordAsync(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (_mediaSink != nullptr) |
||||||
|
{ |
||||||
|
(void)_mediaSink->Shutdown(); |
||||||
|
_mediaSink = nullptr; |
||||||
|
} |
||||||
|
_mediaExtension = nullptr; |
||||||
|
_capture = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void Media::CaptureFrameGrabber::ShowCameraSettings() |
||||||
|
{ |
||||||
|
#if WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP |
||||||
|
if (_state == State::Started) |
||||||
|
{ |
||||||
|
CameraOptionsUI::Show(_capture.Get()); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
task<void> Media::CaptureFrameGrabber::FinishAsync() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
if (_state != State::Started) |
||||||
|
{ |
||||||
|
throw ref new COMException(E_UNEXPECTED, L"State"); |
||||||
|
} |
||||||
|
_state = State::Closing; |
||||||
|
|
||||||
|
if (_mediaSink != nullptr) |
||||||
|
{ |
||||||
|
(void)_mediaSink->Shutdown(); |
||||||
|
_mediaSink = nullptr; |
||||||
|
} |
||||||
|
_mediaExtension = nullptr; |
||||||
|
|
||||||
|
task<void> task; |
||||||
|
if (_streamType == CaptureStreamType::Preview) |
||||||
|
{ |
||||||
|
task = create_task(_capture->StopPreviewAsync()); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
task = create_task(_capture->StopRecordAsync()); |
||||||
|
} |
||||||
|
|
||||||
|
return task.then([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
_state = State::Closed; |
||||||
|
_capture = nullptr; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
task<ComPtr<IMF2DBuffer2>> Media::CaptureFrameGrabber::GetFrameAsync() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
if (_state != State::Started) |
||||||
|
{ |
||||||
|
throw ref new COMException(E_UNEXPECTED, L"State"); |
||||||
|
} |
||||||
|
|
||||||
|
_mediaSink->RequestVideoSample(); |
||||||
|
|
||||||
|
task_completion_event<ComPtr<IMF2DBuffer2>> taskEvent; |
||||||
|
_videoSampleRequestQueue.push(taskEvent); |
||||||
|
|
||||||
|
return create_task(taskEvent); |
||||||
|
} |
||||||
|
|
||||||
|
void Media::CaptureFrameGrabber::ProcessSample(_In_ MediaSample^ sample) |
||||||
|
{ |
||||||
|
task_completion_event<ComPtr<IMF2DBuffer2>> t; |
||||||
|
|
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
t = _videoSampleRequestQueue.front(); |
||||||
|
_videoSampleRequestQueue.pop(); |
||||||
|
} |
||||||
|
|
||||||
|
ComPtr<IMFMediaBuffer> buffer; |
||||||
|
CHK(sample->Sample->ConvertToContiguousBuffer(&buffer)); |
||||||
|
|
||||||
|
// Dispatch without the lock taken to avoid deadlocks
|
||||||
|
t.set(As<IMF2DBuffer2>(buffer)); |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "MFIncludes.hpp" |
||||||
|
|
||||||
|
|
||||||
|
namespace Media { |
||||||
|
|
||||||
|
class MediaSink; |
||||||
|
|
||||||
|
enum class CaptureStreamType |
||||||
|
{ |
||||||
|
Preview = 0, |
||||||
|
Record |
||||||
|
}; |
||||||
|
|
||||||
|
ref class CaptureFrameGrabber sealed |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
// IClosable
|
||||||
|
virtual ~CaptureFrameGrabber(); |
||||||
|
|
||||||
|
virtual void ShowCameraSettings(); |
||||||
|
|
||||||
|
internal: |
||||||
|
|
||||||
|
static concurrency::task<CaptureFrameGrabber^> CreateAsync(_In_ WMC::MediaCapture^ capture, _In_ WMMp::VideoEncodingProperties^ props) |
||||||
|
{ |
||||||
|
return CreateAsync(capture, props, CaptureStreamType::Preview); |
||||||
|
} |
||||||
|
|
||||||
|
static concurrency::task<CaptureFrameGrabber^> CreateAsync(_In_ WMC::MediaCapture^ capture, _In_ WMMp::VideoEncodingProperties^ props, CaptureStreamType streamType); |
||||||
|
|
||||||
|
concurrency::task<MW::ComPtr<IMF2DBuffer2>> GetFrameAsync(); |
||||||
|
concurrency::task<void> FinishAsync(); |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
CaptureFrameGrabber(_In_ WMC::MediaCapture^ capture, _In_ WMMp::VideoEncodingProperties^ props, CaptureStreamType streamType); |
||||||
|
|
||||||
|
void ProcessSample(_In_ MediaSample^ sample); |
||||||
|
|
||||||
|
Platform::Agile<WMC::MediaCapture> _capture; |
||||||
|
::Windows::Media::IMediaExtension^ _mediaExtension; |
||||||
|
|
||||||
|
MW::ComPtr<MediaSink> _mediaSink; |
||||||
|
|
||||||
|
CaptureStreamType _streamType; |
||||||
|
|
||||||
|
enum class State |
||||||
|
{ |
||||||
|
Created, |
||||||
|
Started, |
||||||
|
Closing, |
||||||
|
Closed |
||||||
|
} _state; |
||||||
|
|
||||||
|
std::queue<concurrency::task_completion_event<MW::ComPtr<IMF2DBuffer2>>> _videoSampleRequestQueue; |
||||||
|
AutoMF _mf; |
||||||
|
MWW::SRWLock _lock; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,396 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "MediaStreamSink.hpp" |
||||||
|
#include "MFIncludes.hpp" |
||||||
|
|
||||||
|
namespace Media { |
||||||
|
|
||||||
|
const unsigned int c_audioStreamSinkId = 0; |
||||||
|
const unsigned int c_videoStreamSinkId = 1; |
||||||
|
|
||||||
|
class MediaSink WrlSealed |
||||||
|
: public MW::RuntimeClass< |
||||||
|
MW::RuntimeClassFlags< |
||||||
|
MW::RuntimeClassType::WinRtClassicComMix> |
||||||
|
, AWM::IMediaExtension |
||||||
|
, IMFMediaSink |
||||||
|
, IMFClockStateSink |
||||||
|
, MW::FtmBase |
||||||
|
> |
||||||
|
{ |
||||||
|
InspectableClass(L"MediaSink", BaseTrust) |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
MediaSink( |
||||||
|
_In_opt_ WMMp::AudioEncodingProperties^ audioProps, |
||||||
|
_In_opt_ WMMp::VideoEncodingProperties^ videoProps, |
||||||
|
_In_opt_ MediaSampleHandler^ audioSampleHandler, |
||||||
|
_In_opt_ MediaSampleHandler^ videoSampleHandler |
||||||
|
) |
||||||
|
: _shutdown(false) |
||||||
|
{ |
||||||
|
MW::ComPtr<IMFMediaType> audioMT; |
||||||
|
if (audioProps != nullptr) |
||||||
|
{ |
||||||
|
CHK(MFCreateMediaTypeFromProperties(As<IUnknown>(audioProps).Get(), &audioMT)); |
||||||
|
_audioStreamSink = MW::Make<MediaStreamSink>( |
||||||
|
this, |
||||||
|
c_audioStreamSinkId, |
||||||
|
audioMT, |
||||||
|
audioSampleHandler |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
MW::ComPtr<IMFMediaType> videoMT; |
||||||
|
if (videoProps != nullptr) |
||||||
|
{ |
||||||
|
CHK(MFCreateMediaTypeFromProperties(As<IUnknown>(videoProps).Get(), &videoMT)); |
||||||
|
_videoStreamSink = MW::Make<MediaStreamSink>( |
||||||
|
this, |
||||||
|
c_videoStreamSinkId, |
||||||
|
videoMT, |
||||||
|
videoSampleHandler |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void RequestAudioSample() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
_audioStreamSink->RequestSample(); |
||||||
|
} |
||||||
|
|
||||||
|
void RequestVideoSample() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
_videoStreamSink->RequestSample(); |
||||||
|
} |
||||||
|
|
||||||
|
void SetCurrentAudioMediaType(_In_ IMFMediaType* mt) |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
_audioStreamSink->InternalSetCurrentMediaType(mt); |
||||||
|
} |
||||||
|
|
||||||
|
void SetCurrentVideoMediaType(_In_ IMFMediaType* mt) |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
_videoStreamSink->InternalSetCurrentMediaType(mt); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// IMediaExtension
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP SetProperties(_In_ AWFC::IPropertySet * /*configuration*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// IMFMediaSink
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP GetCharacteristics(_Out_ DWORD *characteristics) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, characteristics]() |
||||||
|
{ |
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
CHKNULL(characteristics); |
||||||
|
*characteristics = MEDIASINK_RATELESS | MEDIASINK_FIXED_STREAMS; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP AddStreamSink( |
||||||
|
DWORD /*streamSinkIdentifier*/, |
||||||
|
_In_ IMFMediaType * /*mediaType*/, |
||||||
|
_COM_Outptr_ IMFStreamSink **streamSink |
||||||
|
) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, streamSink]() |
||||||
|
{ |
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
CHKNULL(streamSink); |
||||||
|
*streamSink = nullptr; |
||||||
|
|
||||||
|
CHK(MF_E_STREAMSINKS_FIXED); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP RemoveStreamSink(DWORD /*streamSinkIdentifier*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
CHK(MF_E_STREAMSINKS_FIXED); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP GetStreamSinkCount(_Out_ DWORD *streamSinkCount) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, streamSinkCount]() |
||||||
|
{ |
||||||
|
CHKNULL(streamSinkCount); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
*streamSinkCount = (_audioStreamSink != nullptr) + (_videoStreamSink != nullptr); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP GetStreamSinkByIndex(DWORD index, _COM_Outptr_ IMFStreamSink **streamSink) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, index, streamSink]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
CHKNULL(streamSink); |
||||||
|
*streamSink = nullptr; |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
switch (index) |
||||||
|
{ |
||||||
|
case 0: |
||||||
|
if (_audioStreamSink != nullptr) |
||||||
|
{ |
||||||
|
CHK(_audioStreamSink.CopyTo(streamSink)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CHK(_videoStreamSink.CopyTo(streamSink)); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case 1: |
||||||
|
if ((_audioStreamSink != nullptr) && (_videoStreamSink != nullptr)) |
||||||
|
{ |
||||||
|
CHK(_videoStreamSink.CopyTo(streamSink)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CHK(E_INVALIDARG); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
CHK(E_INVALIDARG); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP GetStreamSinkById(DWORD identifier, _COM_Outptr_ IMFStreamSink **streamSink) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, identifier, streamSink]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
CHKNULL(streamSink); |
||||||
|
*streamSink = nullptr; |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
if ((identifier == 0) && (_audioStreamSink != nullptr)) |
||||||
|
{ |
||||||
|
CHK(_audioStreamSink.CopyTo(streamSink)); |
||||||
|
} |
||||||
|
else if ((identifier == 1) && (_videoStreamSink != nullptr)) |
||||||
|
{ |
||||||
|
CHK(_videoStreamSink.CopyTo(streamSink)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CHK(E_INVALIDARG); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP SetPresentationClock(_In_ IMFPresentationClock *clock) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, clock]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
if (_clock != nullptr) |
||||||
|
{ |
||||||
|
CHK(_clock->RemoveClockStateSink(this)); |
||||||
|
_clock = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
if (clock != nullptr) |
||||||
|
{ |
||||||
|
CHK(clock->AddClockStateSink(this)); |
||||||
|
_clock = clock; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP GetPresentationClock(_COM_Outptr_ IMFPresentationClock **clock) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this, clock]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
CHKNULL(clock); |
||||||
|
*clock = nullptr; |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
|
||||||
|
if (_clock != nullptr) |
||||||
|
{ |
||||||
|
CHK(_clock.CopyTo(clock)) |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP Shutdown() |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
if (_shutdown) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
_shutdown = true; |
||||||
|
|
||||||
|
if (_audioStreamSink != nullptr) |
||||||
|
{ |
||||||
|
_audioStreamSink->Shutdown(); |
||||||
|
_audioStreamSink = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
if (_videoStreamSink != nullptr) |
||||||
|
{ |
||||||
|
_videoStreamSink->Shutdown(); |
||||||
|
_videoStreamSink = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
if (_clock != nullptr) |
||||||
|
{ |
||||||
|
(void)_clock->RemoveClockStateSink(this); |
||||||
|
_clock = nullptr; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// IMFClockStateSink methods
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP OnClockStart(MFTIME /*hnsSystemTime*/, LONGLONG /*llClockStartOffset*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP OnClockStop(MFTIME /*hnsSystemTime*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP OnClockPause(MFTIME /*hnsSystemTime*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP OnClockRestart(MFTIME /*hnsSystemTime*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
IFACEMETHODIMP OnClockSetRate(MFTIME /*hnsSystemTime*/, float /*flRate*/) |
||||||
|
{ |
||||||
|
return ExceptionBoundary([this]() |
||||||
|
{ |
||||||
|
auto lock = _lock.LockExclusive(); |
||||||
|
|
||||||
|
_VerifyNotShutdown(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
bool _shutdown; |
||||||
|
|
||||||
|
void _VerifyNotShutdown() |
||||||
|
{ |
||||||
|
if (_shutdown) |
||||||
|
{ |
||||||
|
CHK(MF_E_SHUTDOWN); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MW::ComPtr<MediaStreamSink> _audioStreamSink; |
||||||
|
MW::ComPtr<MediaStreamSink> _videoStreamSink; |
||||||
|
MW::ComPtr<IMFPresentationClock> _clock; |
||||||
|
|
||||||
|
MWW::SRWLock _lock; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,386 @@ |
|||||||
|
// 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(); |
||||||
|
HRESULT hr = S_OK; |
||||||
|
|
||||||
|
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(); |
||||||
|
HRESULT hr = S_OK; |
||||||
|
|
||||||
|
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())); |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "MFIncludes.hpp" |
||||||
|
|
||||||
|
namespace Media { |
||||||
|
|
||||||
|
class MediaStreamSink WrlSealed : |
||||||
|
public Microsoft::WRL::RuntimeClass< |
||||||
|
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, |
||||||
|
IMFStreamSink, |
||||||
|
IMFMediaEventGenerator, |
||||||
|
IMFMediaTypeHandler |
||||||
|
> |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
MediaStreamSink( |
||||||
|
__in const MW::ComPtr<IMFMediaSink>& sink, |
||||||
|
__in DWORD id, |
||||||
|
__in const MW::ComPtr<IMFMediaType>& mt, |
||||||
|
__in MediaSampleHandler^ sampleHandler |
||||||
|
); |
||||||
|
|
||||||
|
//
|
||||||
|
// IMFStreamSink
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP GetMediaSink(__deref_out IMFMediaSink **sink); |
||||||
|
IFACEMETHODIMP GetIdentifier(__out DWORD *identifier); |
||||||
|
IFACEMETHODIMP GetMediaTypeHandler(__deref_out IMFMediaTypeHandler **handler); |
||||||
|
IFACEMETHODIMP ProcessSample(__in_opt IMFSample *sample); |
||||||
|
IFACEMETHODIMP PlaceMarker(__in MFSTREAMSINK_MARKER_TYPE markerType, __in const PROPVARIANT * markerValue, __in const PROPVARIANT * contextValue); |
||||||
|
IFACEMETHODIMP Flush(); |
||||||
|
|
||||||
|
//
|
||||||
|
// IMFMediaEventGenerator
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP GetEvent(__in DWORD flags, __deref_out IMFMediaEvent **event); |
||||||
|
IFACEMETHODIMP BeginGetEvent(__in IMFAsyncCallback *callback, __in_opt IUnknown *state); |
||||||
|
IFACEMETHODIMP EndGetEvent(__in IMFAsyncResult *result, __deref_out IMFMediaEvent **event); |
||||||
|
IFACEMETHODIMP QueueEvent(__in MediaEventType met, __in REFGUID extendedType, __in HRESULT status, __in_opt const PROPVARIANT *value); |
||||||
|
|
||||||
|
//
|
||||||
|
// IMFMediaTypeHandler
|
||||||
|
//
|
||||||
|
|
||||||
|
IFACEMETHODIMP IsMediaTypeSupported(__in IMFMediaType *mediaType, __deref_out_opt IMFMediaType **closestMediaType); |
||||||
|
IFACEMETHODIMP GetMediaTypeCount(__out DWORD *typeCount); |
||||||
|
IFACEMETHODIMP GetMediaTypeByIndex(__in DWORD index, __deref_out IMFMediaType **mediaType); |
||||||
|
IFACEMETHODIMP SetCurrentMediaType(__in IMFMediaType *mediaType); |
||||||
|
IFACEMETHODIMP GetCurrentMediaType(__deref_out_opt IMFMediaType **mediaType); |
||||||
|
IFACEMETHODIMP GetMajorType(__out GUID *majorType); |
||||||
|
|
||||||
|
//
|
||||||
|
// Misc
|
||||||
|
//
|
||||||
|
|
||||||
|
void InternalSetCurrentMediaType(__in const MW::ComPtr<IMFMediaType>& mediaType); |
||||||
|
void RequestSample(); |
||||||
|
void Shutdown(); |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
bool _IsMediaTypeSupported(__in const MW::ComPtr<IMFMediaType>& mt) const; |
||||||
|
void _UpdateMediaType(__in const MW::ComPtr<IMFMediaType>& mt); |
||||||
|
|
||||||
|
void _VerifyNotShutdown() |
||||||
|
{ |
||||||
|
if (_shutdown) |
||||||
|
{ |
||||||
|
CHK(MF_E_SHUTDOWN); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MW::ComPtr<IMFMediaSink> _sink; |
||||||
|
MW::ComPtr<IMFMediaEventQueue> _eventQueue; |
||||||
|
MW::ComPtr<IMFMediaType> _curMT; |
||||||
|
|
||||||
|
MediaSampleHandler^ _sampleHandler; |
||||||
|
|
||||||
|
GUID _majorType; |
||||||
|
GUID _subType; |
||||||
|
unsigned int _width; |
||||||
|
unsigned int _height; |
||||||
|
DWORD _id; |
||||||
|
bool _shutdown; |
||||||
|
|
||||||
|
MWW::SRWLock _lock; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
// videoio to XAML bridge for OpenCV
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 "opencv2\videoio\cap_winrt.hpp" |
||||||
|
#include "cap_winrt_capture.hpp" |
||||||
|
#include "cap_winrt_bridge.hpp" |
||||||
|
#include "cap_winrt_video.hpp" |
||||||
|
|
||||||
|
using namespace Windows::Foundation; |
||||||
|
using namespace Windows::Media::Capture; |
||||||
|
using namespace Windows::Media::MediaProperties; |
||||||
|
using namespace Windows::Devices::Enumeration; |
||||||
|
|
||||||
|
using namespace Windows::UI::Xaml::Media::Imaging; |
||||||
|
using namespace Microsoft::WRL; |
||||||
|
|
||||||
|
using namespace Platform; |
||||||
|
using namespace ::Concurrency; |
||||||
|
|
||||||
|
using namespace ::std; |
||||||
|
|
||||||
|
/***************************** VideoioBridge class ******************************/ |
||||||
|
|
||||||
|
// non-blocking
|
||||||
|
void VideoioBridge::requestForUIthreadAsync(int action, int widthp, int heightp) |
||||||
|
{ |
||||||
|
reporter.report(action); |
||||||
|
} |
||||||
|
|
||||||
|
VideoioBridge& VideoioBridge::getInstance() |
||||||
|
{ |
||||||
|
static VideoioBridge instance; |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
void VideoioBridge::swapInputBuffers() |
||||||
|
{ |
||||||
|
// TODO: already locked, check validity
|
||||||
|
// lock_guard<mutex> lock(inputBufferMutex);
|
||||||
|
swap(backInputPtr, frontInputPtr); |
||||||
|
//if (currentFrame != frameCounter)
|
||||||
|
//{
|
||||||
|
// currentFrame = frameCounter;
|
||||||
|
// swap(backInputPtr, frontInputPtr);
|
||||||
|
//}
|
||||||
|
} |
||||||
|
|
||||||
|
void VideoioBridge::swapOutputBuffers() |
||||||
|
{ |
||||||
|
lock_guard<mutex> lock(outputBufferMutex); |
||||||
|
swap(frontOutputBuffer, backOutputBuffer); |
||||||
|
} |
||||||
|
|
||||||
|
void VideoioBridge::allocateOutputBuffers() |
||||||
|
{ |
||||||
|
frontOutputBuffer = ref new WriteableBitmap(width, height); |
||||||
|
backOutputBuffer = ref new WriteableBitmap(width, height); |
||||||
|
} |
||||||
|
|
||||||
|
void VideoioBridge::imshow() |
||||||
|
{ |
||||||
|
VideoioBridge::getInstance().swapOutputBuffers(); |
||||||
|
VideoioBridge::getInstance().requestForUIthreadAsync(cv::UPDATE_IMAGE_ELEMENT); |
||||||
|
} |
||||||
|
|
||||||
|
// end
|
@ -0,0 +1,96 @@ |
|||||||
|
// videoio to XAML bridge for OpenCV
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
// this header is included in the XAML App, so it cannot include any
|
||||||
|
// OpenCV headers, or a static assert will be raised
|
||||||
|
|
||||||
|
#include <ppl.h> |
||||||
|
#include <ppltasks.h> |
||||||
|
#include <concrt.h> |
||||||
|
#include <agile.h> |
||||||
|
#include <opencv2\core.hpp> |
||||||
|
|
||||||
|
#include <mutex> |
||||||
|
#include <memory> |
||||||
|
#include <atomic> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
|
||||||
|
// Class VideoioBridge (singleton) is needed because the interface for
|
||||||
|
// VideoCapture_WinRT in cap_winrt_capture.hpp is fixed by OpenCV.
|
||||||
|
class VideoioBridge |
||||||
|
{ |
||||||
|
public: |
||||||
|
|
||||||
|
static VideoioBridge& getInstance(); |
||||||
|
|
||||||
|
// call after initialization
|
||||||
|
void setReporter(Concurrency::progress_reporter<int> pr) { reporter = pr; } |
||||||
|
|
||||||
|
// to be called from cvMain via cap_winrt on bg thread - non-blocking (async)
|
||||||
|
void requestForUIthreadAsync( int action, int width=0, int height=0 ); |
||||||
|
|
||||||
|
// TODO: modify in window.cpp: void cv::imshow( const String& winname, InputArray _img )
|
||||||
|
void imshow(/*cv::InputArray matToShow*/); // shows Mat in the cvImage element
|
||||||
|
void swapInputBuffers(); |
||||||
|
void allocateOutputBuffers(); |
||||||
|
void swapOutputBuffers(); |
||||||
|
|
||||||
|
|
||||||
|
int deviceIndex, width, height; |
||||||
|
std::atomic<bool> bIsFrameNew; |
||||||
|
std::mutex inputBufferMutex; // input is double buffered
|
||||||
|
unsigned char * frontInputPtr; // OpenCV reads this
|
||||||
|
unsigned char * backInputPtr; // Video grabber writes this
|
||||||
|
std::atomic<unsigned long> frameCounter; |
||||||
|
unsigned long currentFrame; |
||||||
|
|
||||||
|
std::mutex outputBufferMutex; // output is double buffered
|
||||||
|
Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ frontOutputBuffer; // OpenCV write this
|
||||||
|
Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ backOutputBuffer; // XAML reads this
|
||||||
|
Windows::UI::Xaml::Controls::Image ^cvImage; |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
VideoioBridge() { |
||||||
|
deviceIndex = 0; |
||||||
|
width = 640; |
||||||
|
height = 480; |
||||||
|
deviceReady = false; |
||||||
|
bIsFrameNew = false; |
||||||
|
currentFrame = 0; |
||||||
|
frameCounter = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
// singleton
|
||||||
|
VideoioBridge(VideoioBridge const &); |
||||||
|
void operator=(const VideoioBridge &); |
||||||
|
|
||||||
|
std::atomic<bool> deviceReady; |
||||||
|
Concurrency::progress_reporter<int> reporter; |
||||||
|
}; |
@ -0,0 +1,279 @@ |
|||||||
|
// Capture support for WinRT
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 "precomp.hpp" |
||||||
|
#include "cap_winrt_capture.hpp" |
||||||
|
#include "cap_winrt_bridge.hpp" |
||||||
|
#include "cap_winrt_video.hpp" |
||||||
|
#include <opencv2\videoio\cap_winrt.hpp> |
||||||
|
|
||||||
|
using namespace Windows::Foundation; |
||||||
|
using namespace Windows::Media::Capture; |
||||||
|
using namespace Windows::Media::MediaProperties; |
||||||
|
using namespace Windows::Devices::Enumeration; |
||||||
|
|
||||||
|
using namespace Platform; |
||||||
|
|
||||||
|
using namespace Windows::UI::Xaml::Media::Imaging; |
||||||
|
using namespace Microsoft::WRL; |
||||||
|
|
||||||
|
using namespace ::std; |
||||||
|
|
||||||
|
|
||||||
|
// nb. VideoCapture_WinRT is not a singleton, so the Mats are made file statics
|
||||||
|
// we do not support more than one capture device simultaneously with the
|
||||||
|
// design at this time
|
||||||
|
|
||||||
|
// nb. inputBufferMutex was not able to guarantee that OpenCV Mats were
|
||||||
|
// ready to accept data in the UI thread (memory access exceptions were thrown
|
||||||
|
// even though buffer address was good).
|
||||||
|
// Therefore allocation of Mats is also done on the UI thread before the video
|
||||||
|
// device is initialized.
|
||||||
|
|
||||||
|
static cv::Mat frontInputMat; |
||||||
|
static cv::Mat backInputMat; |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
|
||||||
|
/***************************** exported control functions ******************************/ |
||||||
|
|
||||||
|
template <typename ...Args> |
||||||
|
void winrt_startMessageLoop(std::function<void(Args...)>&& callback, Args... args) |
||||||
|
{ |
||||||
|
auto asyncTask = ::concurrency::create_async([=](::concurrency::progress_reporter<int> reporter) |
||||||
|
{ |
||||||
|
VideoioBridge::getInstance().setReporter(reporter); |
||||||
|
|
||||||
|
// frame reading loop
|
||||||
|
callback(args...); |
||||||
|
}); |
||||||
|
|
||||||
|
asyncTask->Progress = ref new AsyncActionProgressHandler<int>([=](IAsyncActionWithProgress<int>^ act, int progress) |
||||||
|
{ |
||||||
|
int action = progress; |
||||||
|
|
||||||
|
// these actions will be processed on the UI thread asynchronously
|
||||||
|
switch (action) |
||||||
|
{ |
||||||
|
case OPEN_CAMERA: |
||||||
|
winrt_openCamera(); |
||||||
|
break; |
||||||
|
case CLOSE_CAMERA: |
||||||
|
winrt_closeGrabber(); |
||||||
|
break; |
||||||
|
case UPDATE_IMAGE_ELEMENT: |
||||||
|
winrt_updateFrameContainer(); |
||||||
|
break; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename ...Args> |
||||||
|
void winrt_startMessageLoop(void callback(Args...), Args... args) |
||||||
|
{ |
||||||
|
winrt_startMessageLoop(std::function<void(Args...)>(callback), args...); |
||||||
|
} |
||||||
|
|
||||||
|
void winrt_onVisibilityChanged(bool visible) { |
||||||
|
if (visible) |
||||||
|
{ |
||||||
|
VideoioBridge& bridge = VideoioBridge::getInstance(); |
||||||
|
|
||||||
|
// only start the grabber if the camera was opened in OpenCV
|
||||||
|
if (bridge.backInputPtr != nullptr) |
||||||
|
{ |
||||||
|
if (Video::getInstance().isStarted()) return; |
||||||
|
|
||||||
|
int device = bridge.deviceIndex; |
||||||
|
int width = bridge.width; |
||||||
|
int height = bridge.height; |
||||||
|
|
||||||
|
winrt_initGrabber(device, width, height); |
||||||
|
} |
||||||
|
} else |
||||||
|
{ |
||||||
|
//grabberStarted = false;
|
||||||
|
winrt_closeGrabber(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void winrt_imshow() { |
||||||
|
VideoioBridge::getInstance().imshow(); |
||||||
|
} |
||||||
|
|
||||||
|
void winrt_setFrameContainer(::Windows::UI::Xaml::Controls::Image^ image) { |
||||||
|
VideoioBridge::getInstance().cvImage = image; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/********************************* Internal helpers ************************************/ |
||||||
|
|
||||||
|
void winrt_updateFrameContainer() |
||||||
|
{ |
||||||
|
// copy output Mat to WBM
|
||||||
|
winrt_copyOutput(); |
||||||
|
|
||||||
|
// set XAML image element with image WBM
|
||||||
|
VideoioBridge::getInstance().cvImage->Source = VideoioBridge::getInstance().backOutputBuffer; |
||||||
|
} |
||||||
|
|
||||||
|
// performed on UI thread
|
||||||
|
bool winrt_openCamera() |
||||||
|
{ |
||||||
|
VideoioBridge& bridge = VideoioBridge::getInstance(); |
||||||
|
|
||||||
|
int device = bridge.deviceIndex; |
||||||
|
int width = bridge.width; |
||||||
|
int height = bridge.height; |
||||||
|
|
||||||
|
// buffers must alloc'd on UI thread
|
||||||
|
winrt_allocateBuffers(width, height); |
||||||
|
|
||||||
|
// nb. video capture device init must be done on UI thread;
|
||||||
|
if (!Video::getInstance().isStarted()) |
||||||
|
{ |
||||||
|
winrt_initGrabber(device, width, height); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// performed on UI thread
|
||||||
|
void winrt_allocateBuffers(int width, int height) |
||||||
|
{ |
||||||
|
VideoioBridge& bridge = VideoioBridge::getInstance(); |
||||||
|
|
||||||
|
// allocate input Mats (bgra8 = CV_8UC4, RGB24 = CV_8UC3)
|
||||||
|
frontInputMat.create(height, width, CV_8UC3); |
||||||
|
backInputMat.create(height, width, CV_8UC3); |
||||||
|
|
||||||
|
bridge.frontInputPtr = frontInputMat.ptr(0); |
||||||
|
bridge.backInputPtr = backInputMat.ptr(0); |
||||||
|
|
||||||
|
bridge.allocateOutputBuffers(); |
||||||
|
} |
||||||
|
|
||||||
|
// non-blocking
|
||||||
|
bool winrt_initGrabber(int device, int w, int h) { |
||||||
|
// nb. Video class is not exported outside of this DLL
|
||||||
|
// due to complexities in the CaptureFrameGrabber ref class
|
||||||
|
// as written in the header not mixing well with pure C++ classes
|
||||||
|
return Video::getInstance().initGrabber(device, w, h); |
||||||
|
} |
||||||
|
|
||||||
|
void winrt_closeGrabber() { |
||||||
|
Video::getInstance().closeGrabber(); |
||||||
|
} |
||||||
|
|
||||||
|
// nb on UI thread
|
||||||
|
void winrt_copyOutput() { |
||||||
|
Video::getInstance().CopyOutput(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/********************************* VideoCapture_WinRT class ****************************/ |
||||||
|
|
||||||
|
VideoCapture_WinRT::VideoCapture_WinRT(int device) : started(false) |
||||||
|
{ |
||||||
|
VideoioBridge::getInstance().deviceIndex = device; |
||||||
|
} |
||||||
|
|
||||||
|
bool VideoCapture_WinRT::isOpened() const |
||||||
|
{ |
||||||
|
return true; // started;
|
||||||
|
} |
||||||
|
|
||||||
|
// grab a frame:
|
||||||
|
// this will NOT block per spec
|
||||||
|
// should be called on the image processing thread, not the UI thread
|
||||||
|
bool VideoCapture_WinRT::grabFrame() |
||||||
|
{ |
||||||
|
// if device is not started we must return true so retrieveFrame() is called to start device
|
||||||
|
// nb. we cannot start the device here because we do not know the size of the input Mat
|
||||||
|
if (!started) return true; |
||||||
|
|
||||||
|
if (VideoioBridge::getInstance().bIsFrameNew) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// nb. if blocking is to be added:
|
||||||
|
// unique_lock<mutex> lock(VideoioBridge::getInstance().frameReadyMutex);
|
||||||
|
// VideoioBridge::getInstance().frameReadyEvent.wait(lock);
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// should be called on the image processing thread after grabFrame
|
||||||
|
// see VideoCapture::read
|
||||||
|
bool VideoCapture_WinRT::retrieveFrame(int channel, cv::OutputArray outArray) |
||||||
|
{ |
||||||
|
if (!started) { |
||||||
|
|
||||||
|
int width, height; |
||||||
|
width = outArray.size().width; |
||||||
|
height = outArray.size().height; |
||||||
|
if (width == 0) width = 640; |
||||||
|
if (height == 0) height = 480; |
||||||
|
|
||||||
|
VideoioBridge::getInstance().width = width; |
||||||
|
VideoioBridge::getInstance().height = height; |
||||||
|
|
||||||
|
// nb. Mats will be alloc'd on UI thread
|
||||||
|
|
||||||
|
// request device init on UI thread - this does not block, and is async
|
||||||
|
VideoioBridge::getInstance().requestForUIthreadAsync(OPEN_CAMERA, |
||||||
|
outArray.size().width, outArray.size().height); |
||||||
|
|
||||||
|
started = true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!started) return false; |
||||||
|
|
||||||
|
return VideoioBridge::getInstance().bIsFrameNew; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool VideoCapture_WinRT::setProperty(int property_id, double value) |
||||||
|
{ |
||||||
|
switch (property_id) |
||||||
|
{ |
||||||
|
case CAP_PROP_FRAME_WIDTH: |
||||||
|
size.width = (int)value; |
||||||
|
break; |
||||||
|
case CAP_PROP_FRAME_HEIGHT: |
||||||
|
size.height = (int)value; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// end
|
@ -0,0 +1,82 @@ |
|||||||
|
// Capture support for WinRT
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "precomp.hpp" |
||||||
|
|
||||||
|
#include <mutex> |
||||||
|
#include <memory> |
||||||
|
#include <condition_variable> |
||||||
|
#include <atomic> |
||||||
|
|
||||||
|
#include <agile.h> |
||||||
|
|
||||||
|
|
||||||
|
// nb. implemented the newer IVideoCapture C++ interface so that we can work
|
||||||
|
// directly with Mat, not the older C cv interface
|
||||||
|
// (which may have added overhead for IPL file conversion)
|
||||||
|
|
||||||
|
namespace cv { |
||||||
|
|
||||||
|
/******************* Internal helpers **************************************/ |
||||||
|
|
||||||
|
void winrt_updateFrameContainer(); |
||||||
|
bool winrt_openCamera(); |
||||||
|
bool winrt_initGrabber(int device, int w, int h); |
||||||
|
void winrt_closeGrabber(); |
||||||
|
void winrt_copyOutput(); |
||||||
|
void winrt_allocateBuffers(int width, int height); |
||||||
|
|
||||||
|
/******************* VideoCapture_WinRT class ******************************/ |
||||||
|
|
||||||
|
class VideoCapture_WinRT : public IVideoCapture |
||||||
|
{ |
||||||
|
public: |
||||||
|
VideoCapture_WinRT() : started(false) {} |
||||||
|
VideoCapture_WinRT(int device); |
||||||
|
virtual ~VideoCapture_WinRT() {} |
||||||
|
|
||||||
|
// from base class IVideoCapture
|
||||||
|
virtual double getProperty(int) { return 0; } |
||||||
|
virtual bool setProperty(int, double); |
||||||
|
virtual bool grabFrame(); |
||||||
|
virtual bool retrieveFrame(int channel, cv::OutputArray outArray); |
||||||
|
|
||||||
|
// Return the type of the capture object
|
||||||
|
virtual int getCaptureDomain() { return CAP_WINRT; } |
||||||
|
|
||||||
|
virtual bool isOpened() const; |
||||||
|
|
||||||
|
protected: |
||||||
|
|
||||||
|
bool started; |
||||||
|
CvSize size; |
||||||
|
int bytesPerPixel; |
||||||
|
unsigned long frameCurrent; |
||||||
|
std::atomic<bool> isFrameNew; |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,322 @@ |
|||||||
|
// Video support with XAML
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 "cap_winrt_video.hpp" |
||||||
|
|
||||||
|
#include <ppl.h> |
||||||
|
#include <ppltasks.h> |
||||||
|
#include <concrt.h> |
||||||
|
#include <agile.h> |
||||||
|
|
||||||
|
#include <atomic> |
||||||
|
#include <future> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
|
||||||
|
using namespace ::concurrency; |
||||||
|
using namespace ::Windows::Foundation; |
||||||
|
using namespace ::std; |
||||||
|
|
||||||
|
using namespace Microsoft::WRL; |
||||||
|
using namespace Windows::Media::Devices; |
||||||
|
using namespace Windows::Media::MediaProperties; |
||||||
|
using namespace Windows::Media::Capture; |
||||||
|
using namespace Windows::UI::Xaml::Media::Imaging; |
||||||
|
using namespace Windows::Devices::Enumeration; |
||||||
|
|
||||||
|
#include "cap_winrt/CaptureFrameGrabber.hpp" |
||||||
|
|
||||||
|
// pull in Media Foundation libs
|
||||||
|
#pragma comment(lib, "mfplat") |
||||||
|
#pragma comment(lib, "mf") |
||||||
|
#pragma comment(lib, "mfuuid") |
||||||
|
|
||||||
|
#if (WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP) && !defined(_M_ARM) |
||||||
|
#pragma comment(lib, "Shlwapi") |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "cap_winrt_bridge.hpp" |
||||||
|
|
||||||
|
Video::Video() {} |
||||||
|
|
||||||
|
Video &Video::getInstance() { |
||||||
|
static Video v; |
||||||
|
return v; |
||||||
|
} |
||||||
|
|
||||||
|
bool Video::isStarted() { |
||||||
|
return bGrabberInited.load(); |
||||||
|
} |
||||||
|
|
||||||
|
void Video::closeGrabber() { |
||||||
|
// assigning nullptr causes deref of grabber and thus closes the device
|
||||||
|
m_frameGrabber = nullptr; |
||||||
|
bGrabberInited = false; |
||||||
|
bGrabberInitInProgress = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool Video::initGrabber(int device, int w, int h) { |
||||||
|
// already started?
|
||||||
|
if (bGrabberInited || bGrabberInitInProgress) return false; |
||||||
|
|
||||||
|
width = w; |
||||||
|
height = h; |
||||||
|
|
||||||
|
bGrabberInited = false; |
||||||
|
bGrabberInitInProgress = true; |
||||||
|
|
||||||
|
m_deviceID = device; |
||||||
|
|
||||||
|
create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)) |
||||||
|
.then([this](task<DeviceInformationCollection^> findTask) |
||||||
|
{ |
||||||
|
m_devices = findTask.get(); |
||||||
|
|
||||||
|
// got selected device?
|
||||||
|
if ((unsigned)m_deviceID >= m_devices.Get()->Size) |
||||||
|
{ |
||||||
|
OutputDebugStringA("Video::initGrabber - no video device found\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
auto devInfo = m_devices.Get()->GetAt(m_deviceID); |
||||||
|
|
||||||
|
auto settings = ref new MediaCaptureInitializationSettings(); |
||||||
|
settings->StreamingCaptureMode = StreamingCaptureMode::Video; // Video-only capture
|
||||||
|
settings->VideoDeviceId = devInfo->Id; |
||||||
|
|
||||||
|
auto location = devInfo->EnclosureLocation; |
||||||
|
bFlipImageX = true; |
||||||
|
if (location != nullptr && location->Panel == Windows::Devices::Enumeration::Panel::Back) |
||||||
|
{ |
||||||
|
bFlipImageX = false; |
||||||
|
} |
||||||
|
|
||||||
|
m_capture = ref new MediaCapture(); |
||||||
|
create_task(m_capture->InitializeAsync(settings)).then([this](){ |
||||||
|
|
||||||
|
auto props = safe_cast<VideoEncodingProperties^>(m_capture->VideoDeviceController->GetMediaStreamProperties(MediaStreamType::VideoPreview)); |
||||||
|
|
||||||
|
// for 24 bpp
|
||||||
|
props->Subtype = MediaEncodingSubtypes::Rgb24; bytesPerPixel = 3; |
||||||
|
|
||||||
|
// format used by XAML & WBM (for testing)
|
||||||
|
// props->Subtype = MediaEncodingSubtypes::Bgra8; bytesPerPixel = 4;
|
||||||
|
|
||||||
|
props->Width = width; |
||||||
|
props->Height = height; |
||||||
|
|
||||||
|
return ::Media::CaptureFrameGrabber::CreateAsync(m_capture.Get(), props); |
||||||
|
|
||||||
|
}).then([this](::Media::CaptureFrameGrabber^ frameGrabber) |
||||||
|
{ |
||||||
|
m_frameGrabber = frameGrabber; |
||||||
|
bGrabberInited = true; |
||||||
|
bGrabberInitInProgress = false; |
||||||
|
//ready = true;
|
||||||
|
_GrabFrameAsync(frameGrabber); |
||||||
|
}); |
||||||
|
|
||||||
|
return true; |
||||||
|
}); |
||||||
|
|
||||||
|
// nb. cannot block here - this will lock the UI thread:
|
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Video::_GrabFrameAsync(::Media::CaptureFrameGrabber^ frameGrabber) { |
||||||
|
// use rgb24 layout
|
||||||
|
create_task(frameGrabber->GetFrameAsync()).then([this, frameGrabber](const ComPtr<IMF2DBuffer2>& buffer) |
||||||
|
{ |
||||||
|
// do the RGB swizzle while copying the pixels from the IMF2DBuffer2
|
||||||
|
BYTE *pbScanline; |
||||||
|
LONG plPitch; |
||||||
|
unsigned int colBytes = width * bytesPerPixel; |
||||||
|
CHK(buffer->Lock2D(&pbScanline, &plPitch)); |
||||||
|
|
||||||
|
// flip
|
||||||
|
if (bFlipImageX) |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex); |
||||||
|
|
||||||
|
// ptr to input Mat data array
|
||||||
|
auto buf = VideoioBridge::getInstance().backInputPtr; |
||||||
|
|
||||||
|
for (unsigned int row = 0; row < height; row++) |
||||||
|
{ |
||||||
|
unsigned int i = 0; |
||||||
|
unsigned int j = colBytes - 1; |
||||||
|
|
||||||
|
while (i < colBytes) |
||||||
|
{ |
||||||
|
// reverse the scan line
|
||||||
|
// as a side effect this also swizzles R and B channels
|
||||||
|
buf[j--] = pbScanline[i++]; |
||||||
|
buf[j--] = pbScanline[i++]; |
||||||
|
buf[j--] = pbScanline[i++]; |
||||||
|
} |
||||||
|
pbScanline += plPitch; |
||||||
|
buf += colBytes; |
||||||
|
} |
||||||
|
VideoioBridge::getInstance().bIsFrameNew = true; |
||||||
|
} else |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex); |
||||||
|
|
||||||
|
// ptr to input Mat data array
|
||||||
|
auto buf = VideoioBridge::getInstance().backInputPtr; |
||||||
|
|
||||||
|
for (unsigned int row = 0; row < height; row++) |
||||||
|
{ |
||||||
|
// used for Bgr8:
|
||||||
|
//for (unsigned int i = 0; i < colBytes; i++ )
|
||||||
|
// buf[i] = pbScanline[i];
|
||||||
|
|
||||||
|
// used for RGB24:
|
||||||
|
for (unsigned int i = 0; i < colBytes; i += bytesPerPixel) |
||||||
|
{ |
||||||
|
// swizzle the R and B values (BGR to RGB)
|
||||||
|
buf[i] = pbScanline[i + 2]; |
||||||
|
buf[i + 1] = pbScanline[i + 1]; |
||||||
|
buf[i + 2] = pbScanline[i]; |
||||||
|
|
||||||
|
// no swizzle
|
||||||
|
//buf[i] = pbScanline[i];
|
||||||
|
//buf[i + 1] = pbScanline[i + 1];
|
||||||
|
//buf[i + 2] = pbScanline[i + 2];
|
||||||
|
} |
||||||
|
|
||||||
|
pbScanline += plPitch; |
||||||
|
buf += colBytes; |
||||||
|
} |
||||||
|
VideoioBridge::getInstance().bIsFrameNew = true; |
||||||
|
} |
||||||
|
CHK(buffer->Unlock2D()); |
||||||
|
|
||||||
|
VideoioBridge::getInstance().frameCounter++; |
||||||
|
|
||||||
|
if (bGrabberInited) |
||||||
|
{ |
||||||
|
_GrabFrameAsync(frameGrabber); |
||||||
|
} |
||||||
|
}, task_continuation_context::use_current()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// copy from input Mat to output WBM
|
||||||
|
// must be on UI thread
|
||||||
|
void Video::CopyOutput() { |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().outputBufferMutex); |
||||||
|
|
||||||
|
auto inAr = VideoioBridge::getInstance().frontInputPtr; |
||||||
|
auto outAr = GetData(VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer); |
||||||
|
|
||||||
|
const unsigned int bytesPerPixel = 3; |
||||||
|
auto pbScanline = inAr; |
||||||
|
auto plPitch = width * bytesPerPixel; |
||||||
|
|
||||||
|
auto buf = outAr; |
||||||
|
unsigned int colBytes = width * 4; |
||||||
|
|
||||||
|
// copy RGB24 to bgra8
|
||||||
|
for (unsigned int row = 0; row < height; row++) |
||||||
|
{ |
||||||
|
// used for Bgr8:
|
||||||
|
// nb. no alpha
|
||||||
|
// for (unsigned int i = 0; i < colBytes; i++ ) buf[i] = pbScanline[i];
|
||||||
|
|
||||||
|
// used for RGB24:
|
||||||
|
// nb. alpha is set to full opaque
|
||||||
|
for (unsigned int i = 0, j = 0; i < plPitch; i += bytesPerPixel, j += 4) |
||||||
|
{ |
||||||
|
// swizzle the R and B values (RGB24 to Bgr8)
|
||||||
|
buf[j] = pbScanline[i + 2]; |
||||||
|
buf[j + 1] = pbScanline[i + 1]; |
||||||
|
buf[j + 2] = pbScanline[i]; |
||||||
|
buf[j + 3] = 0xff; |
||||||
|
|
||||||
|
// if no swizzle is desired:
|
||||||
|
//buf[i] = pbScanline[i];
|
||||||
|
//buf[i + 1] = pbScanline[i + 1];
|
||||||
|
//buf[i + 2] = pbScanline[i + 2];
|
||||||
|
//buf[i + 3] = 0xff;
|
||||||
|
} |
||||||
|
|
||||||
|
pbScanline += plPitch; |
||||||
|
buf += colBytes; |
||||||
|
} |
||||||
|
VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer->Length = width * height * 4; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool Video::listDevicesTask() { |
||||||
|
std::atomic<bool> ready(false); |
||||||
|
|
||||||
|
auto settings = ref new MediaCaptureInitializationSettings(); |
||||||
|
|
||||||
|
//vector <int> devices;
|
||||||
|
|
||||||
|
create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture)) |
||||||
|
.then([this, &ready](task<DeviceInformationCollection^> findTask) |
||||||
|
{ |
||||||
|
m_devices = findTask.get(); |
||||||
|
|
||||||
|
for (size_t i = 0; i < m_devices->Size; i++) |
||||||
|
{ |
||||||
|
// ofVideoDevice deviceInfo;
|
||||||
|
auto d = m_devices->GetAt(i); |
||||||
|
//deviceInfo.bAvailable = true;
|
||||||
|
//deviceInfo.deviceName = PlatformStringToString(d->Name);
|
||||||
|
//deviceInfo.hardwareName = deviceInfo.deviceName;
|
||||||
|
// devices.push_back(deviceInfo);
|
||||||
|
} |
||||||
|
|
||||||
|
ready = true; |
||||||
|
}); |
||||||
|
|
||||||
|
// wait for async task to complete
|
||||||
|
int count = 0; |
||||||
|
while (!ready) |
||||||
|
{ |
||||||
|
count++; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool Video::listDevices() { |
||||||
|
// synchronous version of listing video devices on WinRT
|
||||||
|
std::future<bool> result = std::async(std::launch::async, &Video::listDevicesTask, this); |
||||||
|
return result.get(); |
||||||
|
} |
||||||
|
|
||||||
|
// end
|
@ -0,0 +1,73 @@ |
|||||||
|
// Video support with XAML
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "cap_winrt/CaptureFrameGrabber.hpp" |
||||||
|
|
||||||
|
#include <mutex> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
class Video { |
||||||
|
public: |
||||||
|
|
||||||
|
bool initGrabber(int device, int w, int h); |
||||||
|
void closeGrabber(); |
||||||
|
bool isStarted(); |
||||||
|
|
||||||
|
// singleton
|
||||||
|
static Video &getInstance(); |
||||||
|
|
||||||
|
void CopyOutput(); |
||||||
|
|
||||||
|
private: |
||||||
|
// singleton
|
||||||
|
Video(); |
||||||
|
|
||||||
|
void _GrabFrameAsync(::Media::CaptureFrameGrabber^ frameGrabber); |
||||||
|
|
||||||
|
bool listDevices(); |
||||||
|
|
||||||
|
Platform::Agile<Windows::Media::Capture::MediaCapture> m_capture; |
||||||
|
Platform::Agile<Windows::Devices::Enumeration::DeviceInformationCollection> m_devices; |
||||||
|
|
||||||
|
::Media::CaptureFrameGrabber^ m_frameGrabber; |
||||||
|
|
||||||
|
bool listDevicesTask(); |
||||||
|
|
||||||
|
bool bChooseDevice; |
||||||
|
bool bVerbose; |
||||||
|
bool bFlipImageX; |
||||||
|
//std::atomic<bool> bGrabberInited;
|
||||||
|
int m_deviceID; |
||||||
|
int attemptFramerate; |
||||||
|
std::atomic<bool> bIsFrameNew; |
||||||
|
std::atomic<bool> bGrabberInited; |
||||||
|
std::atomic<bool> bGrabberInitInProgress; |
||||||
|
unsigned int width, height; |
||||||
|
int bytesPerPixel; |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,87 @@ |
|||||||
|
// main.cpp
|
||||||
|
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// (3 - clause BSD License)
|
||||||
|
//
|
||||||
|
// 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 the copyright holder 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 "pch.h" |
||||||
|
|
||||||
|
#include <opencv2/core.hpp> |
||||||
|
#include <opencv2/imgproc.hpp> |
||||||
|
#include <opencv2/features2d.hpp> |
||||||
|
#include <opencv2/videoio.hpp> |
||||||
|
#include <opencv2/videoio/cap_winrt.hpp> |
||||||
|
|
||||||
|
using namespace cv; |
||||||
|
|
||||||
|
namespace video_capture_xaml { |
||||||
|
|
||||||
|
void cvMain() |
||||||
|
{ |
||||||
|
VideoCapture cam; |
||||||
|
|
||||||
|
// open the default camera
|
||||||
|
cam.open(0); |
||||||
|
|
||||||
|
Mat edges; |
||||||
|
Mat frame; |
||||||
|
|
||||||
|
// process frames
|
||||||
|
while (1) |
||||||
|
{ |
||||||
|
// get a new frame from camera - this is non-blocking per spec
|
||||||
|
cam >> frame; |
||||||
|
|
||||||
|
// don't reprocess the same frame again
|
||||||
|
// nb if commented then flashing may occur
|
||||||
|
if (!cam.grab()) continue; |
||||||
|
|
||||||
|
// image processing calculations here
|
||||||
|
// nb Mat frame is in RGB24 format (8UC3)
|
||||||
|
|
||||||
|
// select processing type 1 or 2
|
||||||
|
#if 0 |
||||||
|
// image manipulation example #1
|
||||||
|
// write color bar at row 100 for 200 rows
|
||||||
|
auto ar = frame.ptr(100); |
||||||
|
int bytesPerPixel = 3; |
||||||
|
int adjust = (int)(((float)30 / 100.0f) * 255.0); |
||||||
|
for (int i = 0; i < 640 * 100 * bytesPerPixel;) |
||||||
|
{ |
||||||
|
ar[i++] = adjust; // R
|
||||||
|
i++; // G
|
||||||
|
ar[i++] = 255 - adjust; // B
|
||||||
|
} |
||||||
|
#else |
||||||
|
// image processing example #2
|
||||||
|
// apply 'canny' filter
|
||||||
|
cvtColor(frame, edges, COLOR_RGB2GRAY); |
||||||
|
GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5); |
||||||
|
Canny(edges, edges, 0, 30, 3); |
||||||
|
cvtColor(edges, frame, COLOR_GRAY2RGB); |
||||||
|
#endif |
||||||
|
// important step to get XAML image component updated
|
||||||
|
winrt_imshow(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 801 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 429 B |
@ -0,0 +1,30 @@ |
|||||||
|
notes for OpenCV WinRT implementation: |
||||||
|
|
||||||
|
cvMain() in main.cpp |
||||||
|
implements the image processing and OpenCV app control |
||||||
|
it is running on a background thread, started by XAML |
||||||
|
see file main.cpp |
||||||
|
in the Application project |
||||||
|
|
||||||
|
class VideoCapture_WinRT: |
||||||
|
implements the IVideoCapture interface from OpenCV |
||||||
|
video is initialized and frames are grabbed on the UI thread |
||||||
|
see files cap_winrt.hpp/cpp |
||||||
|
|
||||||
|
class HighguiBridge, a singleton |
||||||
|
implements the OpenCV Highgui functions for XAML (limited at this time), |
||||||
|
and also bridges to the UI thread functions for XAML and video operations. |
||||||
|
see files cap_winrt_highgui.hpp/cpp |
||||||
|
|
||||||
|
class Video, a singleton |
||||||
|
encapsulates the Media Foundation interface needed for video initialization and grabbing. |
||||||
|
called through Highgui and XAML, only on the UI thread |
||||||
|
see files cap_winrt_video.hpp/cpp |
||||||
|
|
||||||
|
threading: |
||||||
|
requests from the OpenCV bg thread to the Video/XAML UI thread |
||||||
|
are made through HighguiBridge::requestForUIthreadAsync(), which uses |
||||||
|
the "progress reporter" method provided by the WinRT class |
||||||
|
IAsyncActionWithProgress. Also the bg thread is started by create_async(). |
||||||
|
see file MainPage.xaml.cpp |
||||||
|
in the Application project |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 753 B |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |