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 |