diff --git a/sys/mediafoundation/AsyncOperations.h b/sys/mediafoundation/AsyncOperations.h new file mode 100644 index 0000000000..412886065b --- /dev/null +++ b/sys/mediafoundation/AsyncOperations.h @@ -0,0 +1,168 @@ +// MIT License +// +// Copyright (c) 2016 Microsoft Corporation +// +// 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. + +// Source taken from https://github.com/microsoft/MixedRealityCompanionKit + +#pragma once + +#include +#include +#include +#include + +template +HRESULT StartAsyncThen(_In_ TOperation* pOperation, _In_ TLambda&& tFunc) +{ + if (nullptr == pOperation) + { + return E_INVALIDARG; + } + + auto spCallback = Microsoft::WRL::Callback( + [tFunc](_In_ TOperation* pOperation, _In_ AsyncStatus status) -> HRESULT + { + HRESULT hr = S_OK; + + // wrap the operation + if (status != AsyncStatus::Completed) + { + Microsoft::WRL::ComPtr spOperation(pOperation); + Microsoft::WRL::ComPtr spAsyncInfo; + hr = spOperation.As(&spAsyncInfo); + if (SUCCEEDED(hr)) + { + spAsyncInfo->get_ErrorCode(&hr); + } + } + + return tFunc(hr, pOperation, status); + }); + + // start + return (nullptr != spCallback) ? pOperation->put_Completed(spCallback.Get()) : E_OUTOFMEMORY; +} +template +HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncAction* pOperation, _In_ TLambda&& tFunc) +{ + return StartAsyncThen(pOperation, static_cast(tFunc)); +} +template +HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress* pOperation, _In_ TLambda&& tFunc) +{ + return StartAsyncThen, Windows::Foundation::IAsyncActionWithProgress>(pOperation, static_cast(tFunc)); +} +template +HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncOperation* pOperation, _In_ TLambda&& tFunc) +{ + return StartAsyncThen, ABI::Windows::Foundation::IAsyncOperation>(pOperation, static_cast(tFunc)); +} +template +HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* pOperation, _In_ TLambda&& tFunc) +{ + return StartAsyncThen, ABI::Windows::Foundation::IAsyncOperationWithProgress>(pOperation, static_cast(tFunc)); +} + + +// eg. TOperation = IAsyncOperationWithProgress +// eg. THandler = IAsyncOperationWithProgressCompletedHandler +template +class AsyncEventDelegate + : public Microsoft::WRL::RuntimeClass + < Microsoft::WRL::RuntimeClassFlags + , THandler + , Microsoft::WRL::FtmBase > +{ +public: + AsyncEventDelegate() + : _completedEvent(CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS)) + { + ComPtr spThis(this); + auto lambda = ([this, spThis](_In_ HRESULT hr, _In_ TOperation* pOperation) + { + SetEvent(_completedEvent.Get()); + }); + _func = std::move(lambda); + } + + STDMETHOD(Invoke)( + _In_ TOperation* pOperation, + _In_ AsyncStatus status) + { + HRESULT hr = S_OK; + + // if we completed successfully, then there is no need for getting hresult + if (status != AsyncStatus::Completed) + { + Microsoft::WRL::ComPtr spOperation(pOperation); + Microsoft::WRL::ComPtr spAsyncInfo; + if (SUCCEEDED(spOperation.As(&spAsyncInfo))) + { + spAsyncInfo->get_ErrorCode(&hr); + } + } + + _func(hr, pOperation); + + return S_OK; + } + + STDMETHOD(SyncWait)(_In_ TOperation* pOperation, _In_ DWORD dwMilliseconds) + { + HRESULT hr = pOperation->put_Completed(this); + if (FAILED(hr)) + { + return hr; + } + + DWORD dwWait = WaitForSingleObjectEx(_completedEvent.Get(), dwMilliseconds, TRUE); + if (WAIT_IO_COMPLETION == dwWait || WAIT_OBJECT_0 == dwWait) + return S_OK; + + return HRESULT_FROM_WIN32(GetLastError()); + } + +private: + std::function _func; + Microsoft::WRL::Wrappers::Event _completedEvent; +}; +template +HRESULT SyncWait(_In_ TOperation* pOperation, _In_ DWORD dwMilliseconds) +{ + auto spCallback = Microsoft::WRL::Make>(); + + return spCallback->SyncWait(pOperation, dwMilliseconds); +} +template +HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncAction* pOperation, _In_ DWORD dwMilliseconds = INFINITE) +{ + return SyncWait(pOperation, dwMilliseconds); +} +template +HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncOperation* pOperation, _In_ DWORD dwMilliseconds = INFINITE) +{ + return SyncWait, ABI::Windows::Foundation::IAsyncOperationCompletedHandler>(pOperation, dwMilliseconds); +} +template +HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* pOperation, _In_ DWORD dwMilliseconds = INFINITE) +{ + return SyncWait, ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler>(pOperation, dwMilliseconds); +} diff --git a/sys/mediafoundation/gstmfcapturewinrt.cpp b/sys/mediafoundation/gstmfcapturewinrt.cpp new file mode 100644 index 0000000000..b0515f9a03 --- /dev/null +++ b/sys/mediafoundation/gstmfcapturewinrt.cpp @@ -0,0 +1,597 @@ +/* GStreamer + * Copyright (C) 2020 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstmfcapturewinrt.h" +#include "gstmfutils.h" +#include "mediacapturewrapper.h" +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Media::MediaProperties; +using namespace ABI::Windows::Graphics::Imaging; +using namespace ABI::Windows::Foundation; + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug); +#define GST_CAT_DEFAULT gst_mf_source_object_debug +} + +struct _GstMFCaptureWinRT +{ + GstMFSourceObject parent; + + MediaCaptureWrapper *capture; + + GThread *thread; + GMutex lock; + GCond cond; + GMainContext *context; + GMainLoop *loop; + + /* protected by lock */ + GQueue *queue; + + GstCaps *supported_caps; + GstVideoInfo info; + gboolean flushing; + gboolean got_error; +}; + +static void gst_mf_capture_winrt_constructed (GObject * object); +static void gst_mf_capture_winrt_finalize (GObject * object); + +static gboolean gst_mf_capture_winrt_start (GstMFSourceObject * object); +static gboolean gst_mf_capture_winrt_stop (GstMFSourceObject * object); +static GstFlowReturn gst_mf_capture_winrt_fill (GstMFSourceObject * object, + GstBuffer * buffer); +static gboolean gst_mf_capture_winrt_unlock (GstMFSourceObject * object); +static gboolean gst_mf_capture_winrt_unlock_stop (GstMFSourceObject * object); +static GstCaps * gst_mf_capture_winrt_get_caps (GstMFSourceObject * object); +static gboolean gst_mf_capture_winrt_set_caps (GstMFSourceObject * object, + GstCaps * caps); +static HRESULT gst_mf_capture_winrt_on_frame (ISoftwareBitmap * bitmap, + void * user_data); +static HRESULT gst_mf_capture_winrt_on_failed (const std::string &error, + UINT32 error_code, void * user_data); + +static gpointer gst_mf_capture_winrt_thread_func (GstMFCaptureWinRT * self); + +#define gst_mf_capture_winrt_parent_class parent_class +G_DEFINE_TYPE (GstMFCaptureWinRT, gst_mf_capture_winrt, + GST_TYPE_MF_SOURCE_OBJECT); + +static void +gst_mf_capture_winrt_class_init (GstMFCaptureWinRTClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstMFSourceObjectClass *source_class = GST_MF_SOURCE_OBJECT_CLASS (klass); + + gobject_class->constructed = gst_mf_capture_winrt_constructed; + gobject_class->finalize = gst_mf_capture_winrt_finalize; + + source_class->start = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_start); + source_class->stop = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_stop); + source_class->fill = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_fill); + source_class->unlock = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_unlock); + source_class->unlock_stop = + GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_unlock_stop); + source_class->get_caps = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_get_caps); + source_class->set_caps = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_set_caps); +} + +static void +gst_mf_capture_winrt_init (GstMFCaptureWinRT * self) +{ + self->queue = g_queue_new (); + g_mutex_init (&self->lock); + g_cond_init (&self->cond); +} + +static void +gst_mf_capture_winrt_constructed (GObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + + self->context = g_main_context_new (); + self->loop = g_main_loop_new (self->context, FALSE); + + /* Create a new thread to ensure that COM thread can be MTA thread */ + g_mutex_lock (&self->lock); + self->thread = g_thread_new ("GstMFCaptureWinRT", + (GThreadFunc) gst_mf_capture_winrt_thread_func, self); + while (!g_main_loop_is_running (self->loop)) + g_cond_wait (&self->cond, &self->lock); + g_mutex_unlock (&self->lock); + +done: + G_OBJECT_CLASS (parent_class)->constructed (object); +} + +static void +gst_mf_capture_winrt_finalize (GObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + + g_main_loop_quit (self->loop); + g_thread_join (self->thread); + g_main_loop_unref (self->loop); + g_main_context_unref (self->context); + + g_queue_free (self->queue); + gst_clear_caps (&self->supported_caps); + g_mutex_clear (&self->lock); + g_cond_clear (&self->cond); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_mf_capture_winrt_main_loop_running_cb (GstMFCaptureWinRT * self) +{ + GST_DEBUG_OBJECT (self, "Main loop running now"); + + g_mutex_lock (&self->lock); + g_cond_signal (&self->cond); + g_mutex_unlock (&self->lock); + + return G_SOURCE_REMOVE; +} + +static gpointer +gst_mf_capture_winrt_thread_func (GstMFCaptureWinRT * self) +{ + GstMFSourceObject *source = GST_MF_SOURCE_OBJECT (self); + HRESULT hr; + guint index; + GSource *idle_source; + std::shared_ptr target_group; + std::vector group_list; + MediaCaptureWrapperCallbacks callbacks; + + RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED); + + self->capture = new MediaCaptureWrapper; + callbacks.frame_arrived = gst_mf_capture_winrt_on_frame; + callbacks.failed = gst_mf_capture_winrt_on_failed; + self->capture->RegisterCb (callbacks, self); + + g_main_context_push_thread_default (self->context); + + idle_source = g_idle_source_new (); + g_source_set_callback (idle_source, + (GSourceFunc) gst_mf_capture_winrt_main_loop_running_cb, self, NULL); + g_source_attach (idle_source, self->context); + g_source_unref (idle_source); + + hr = self->capture->EnumrateFrameSourceGroup(group_list); + +#ifndef GST_DISABLE_GST_DEBUG + index = 0; + for (const auto& iter: group_list) { + GST_DEBUG_OBJECT (self, "device %d, name: \"%s\", path: \"%s\"", + index, iter.display_name_.c_str(), iter.id_.c_str()); + index++; + } +#endif + + GST_DEBUG_OBJECT (self, + "Requested device index: %d, name: \"%s\", path \"%s\"", + source->device_index, GST_STR_NULL (source->device_name), + GST_STR_NULL (source->device_path)); + + index = 0; + for (const auto& iter: group_list) { + gboolean match; + + if (source->device_path) { + match = g_ascii_strcasecmp (iter.id_.c_str(), source->device_path) == 0; + } else if (source->device_name) { + match = g_ascii_strcasecmp (iter.display_name_.c_str(), + source->device_name) == 0; + } else if (source->device_index >= 0) { + match = index == source->device_index; + } else { + /* pick the first entry */ + match = TRUE; + } + + if (match) { + target_group = std::make_shared(iter); + break; + } + + index++; + } + + if (!target_group) { + GST_WARNING_OBJECT (self, "No matching device"); + goto run_loop; + } + + self->capture->SetSourceGroup(*target_group); + + for (auto iter: target_group->source_list_) { + if (!self->supported_caps) + self->supported_caps = gst_caps_ref (iter.caps_); + else + self->supported_caps = + gst_caps_merge (self->supported_caps, gst_caps_ref (iter.caps_)); + } + + GST_DEBUG_OBJECT (self, "Available output caps %" GST_PTR_FORMAT, + self->supported_caps); + + source->opened = !!self->supported_caps; + + if (source->opened) { + g_free (source->device_path); + source->device_path = g_strdup (target_group->id_.c_str()); + + g_free (source->device_name); + source->device_name = g_strdup (target_group->display_name_.c_str()); + + source->device_index = index; + } + +run_loop: + GST_DEBUG_OBJECT (self, "Starting main loop"); + g_main_loop_run (self->loop); + GST_DEBUG_OBJECT (self, "Stopped main loop"); + + g_main_context_pop_thread_default (self->context); + + gst_mf_capture_winrt_stop (source); + + delete self->capture; + self->capture = NULL; + + return NULL; +} + +static gboolean +gst_mf_capture_winrt_start (GstMFSourceObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + HRESULT hr; + + if (!self->capture) { + GST_ERROR_OBJECT (self, "No capture object was configured"); + return FALSE; + } + + hr = self->capture->StartCapture(); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Capture object doesn't want to start capture"); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_mf_capture_winrt_stop (GstMFSourceObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + HRESULT hr; + + if (!self->capture) { + GST_ERROR_OBJECT (self, "No capture object was configured"); + return FALSE; + } + + hr = self->capture->StopCapture(); + + while (!g_queue_is_empty (self->queue)) { + IMFMediaBuffer *buffer = (IMFMediaBuffer *) g_queue_pop_head (self->queue); + buffer->Release (); + } + + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Capture object doesn't want to stop capture"); + return FALSE; + } + + return TRUE; +} + +static HRESULT +gst_mf_capture_winrt_on_frame (ISoftwareBitmap * bitmap, + void * user_data) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (user_data); + + g_mutex_lock (&self->lock); + if (self->flushing) { + g_mutex_unlock (&self->lock); + return S_OK; + } + + g_queue_push_tail (self->queue, bitmap); + bitmap->AddRef (); + + g_cond_broadcast (&self->cond); + g_mutex_unlock (&self->lock); + + return S_OK; +} + +static HRESULT +gst_mf_capture_winrt_on_failed (const std::string &error, + UINT32 error_code, void * user_data) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (user_data); + + GST_DEBUG_OBJECT (self, "Have error %s (%d)", error.c_str(), error_code); + + g_mutex_lock (&self->lock); + self->got_error = TRUE; + g_cond_broadcast (&self->cond); + g_mutex_unlock (&self->lock); + + return S_OK; +} + +static GstFlowReturn +gst_mf_capture_winrt_fill (GstMFSourceObject * object, GstBuffer * buffer) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + GstFlowReturn ret = GST_FLOW_OK; + HRESULT hr; + GstVideoFrame frame; + BYTE *data; + UINT32 size; + gint i, j; + ComPtr bitmap; + ComPtr bitmap_buffer; + ComPtr mem_buf; + ComPtr mem_ref; + ComPtr byte_access; + INT32 plane_count; + BitmapPlaneDescription desc[GST_VIDEO_MAX_PLANES]; + + g_mutex_lock (&self->lock); + if (self->got_error) { + g_mutex_unlock (&self->lock); + return GST_FLOW_ERROR; + } + + if (self->flushing) { + g_mutex_unlock (&self->lock); + return GST_FLOW_FLUSHING; + } + + while (!self->flushing && !self->got_error && g_queue_is_empty (self->queue)) + g_cond_wait (&self->cond, &self->lock); + + if (self->got_error) { + g_mutex_unlock (&self->lock); + return GST_FLOW_ERROR; + } + + if (self->flushing) { + g_mutex_unlock (&self->lock); + return GST_FLOW_FLUSHING; + } + + bitmap.Attach ((ISoftwareBitmap *) g_queue_pop_head (self->queue)); + g_mutex_unlock (&self->lock); + + hr = bitmap->LockBuffer (BitmapBufferAccessMode::BitmapBufferAccessMode_Read, + &bitmap_buffer); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot lock ISoftwareBitmap"); + return GST_FLOW_ERROR; + } + + hr = bitmap_buffer->GetPlaneCount (&plane_count); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get plane count"); + return GST_FLOW_ERROR; + } + + if (plane_count > GST_VIDEO_MAX_PLANES) { + GST_ERROR_OBJECT (self, "Invalid plane count %d", plane_count); + return GST_FLOW_ERROR; + } + + if (plane_count != GST_VIDEO_INFO_N_PLANES (&self->info)) { + GST_ERROR_OBJECT (self, "Ambiguous plane count %d", plane_count); + return GST_FLOW_ERROR; + } + + for (i = 0; i < plane_count; i++) { + hr = bitmap_buffer->GetPlaneDescription (i, &desc[i]); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get description for plane %d", i); + return GST_FLOW_ERROR; + } + } + + hr = bitmap_buffer.As (&mem_buf); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get IMemoryBuffer"); + return GST_FLOW_ERROR; + } + + hr = mem_buf->CreateReference (&mem_ref); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get IMemoryBufferReference"); + return GST_FLOW_ERROR; + } + + hr = mem_ref.As(&byte_access); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get IMemoryBufferByteAccess"); + return GST_FLOW_ERROR; + } + + hr = byte_access->GetBuffer (&data, &size); + if (!gst_mf_result (hr)) { + GST_ERROR_OBJECT (self, "Cannot get raw buffer data"); + return GST_FLOW_ERROR; + } + + if (size < GST_VIDEO_INFO_SIZE (&self->info)) { + GST_ERROR_OBJECT (self, "Too small buffer size %d", size); + return GST_FLOW_ERROR; + } + + if (!gst_video_frame_map (&frame, &self->info, buffer, GST_MAP_WRITE)) { + GST_ERROR_OBJECT (self, "Failed to map buffer"); + return GST_FLOW_ERROR; + } + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&self->info); i++) { + guint8 *src, *dst; + gint src_stride, dst_stride; + gint width; + + src = data + desc[i].StartIndex; + dst = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, i); + + src_stride = desc[i].Stride; + dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, i); + width = GST_VIDEO_INFO_COMP_WIDTH (&self->info, i) + * GST_VIDEO_INFO_COMP_PSTRIDE (&self->info, i); + + for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (&self->info, i); j++) { + memcpy (dst, src, width); + src += src_stride; + dst += dst_stride; + } + } + + gst_video_frame_unmap (&frame); + + return ret; +} + +static gboolean +gst_mf_capture_winrt_unlock (GstMFSourceObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + + g_mutex_lock (&self->lock); + if (self->flushing) { + g_mutex_unlock (&self->lock); + return TRUE; + } + + self->flushing = TRUE; + g_cond_broadcast (&self->cond); + g_mutex_unlock (&self->lock); + + return TRUE; +} + +static gboolean +gst_mf_capture_winrt_unlock_stop (GstMFSourceObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + + g_mutex_lock (&self->lock); + if (!self->flushing) { + g_mutex_unlock (&self->lock); + return TRUE; + } + + self->flushing = FALSE; + g_cond_broadcast (&self->cond); + g_mutex_unlock (&self->lock); + + return TRUE; +} + +static GstCaps * +gst_mf_capture_winrt_get_caps (GstMFSourceObject * object) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + + if (self->supported_caps) + return gst_caps_ref (self->supported_caps); + + return NULL; +} + +static gboolean +gst_mf_capture_winrt_set_caps (GstMFSourceObject * object, GstCaps * caps) +{ + GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object); + std::vector desc_list; + HRESULT hr; + GstCaps *target_caps = NULL; + + hr = self->capture->GetAvailableDescriptions(desc_list); + if (!gst_mf_result (hr) || desc_list.empty()) { + GST_ERROR_OBJECT (self, "No available media description"); + return FALSE; + } + + for (const auto& iter: desc_list) { + if (gst_caps_is_subset (iter.caps_, caps)) { + target_caps = gst_caps_ref (iter.caps_); + self->capture->SetMediaDescription(iter); + break; + } + } + + if (!target_caps) { + GST_ERROR_OBJECT (self, + "Could not determine target media type with given caps %" + GST_PTR_FORMAT, caps); + + return FALSE; + } + + gst_video_info_from_caps (&self->info, target_caps); + gst_caps_unref (target_caps); + + return TRUE; +} + +GstMFSourceObject * +gst_mf_capture_winrt_new (GstMFSourceType type, gint device_index, + const gchar * device_name, const gchar * device_path) +{ + GstMFSourceObject *self; + + /* TODO: Add audio capture support */ + g_return_val_if_fail (type == GST_MF_SOURCE_TYPE_VIDEO, NULL); + + self = (GstMFSourceObject *) g_object_new (GST_TYPE_MF_CAPTURE_WINRT, + "source-type", type, "device-index", device_index, "device-name", + device_name, "device-path", device_path, NULL); + + gst_object_ref_sink (self); + + if (!self->opened) { + GST_WARNING_OBJECT (self, "Couldn't open device"); + gst_object_unref (self); + return NULL; + } + + return self; +} diff --git a/sys/mediafoundation/gstmfcapturewinrt.h b/sys/mediafoundation/gstmfcapturewinrt.h new file mode 100644 index 0000000000..2f4cf15524 --- /dev/null +++ b/sys/mediafoundation/gstmfcapturewinrt.h @@ -0,0 +1,39 @@ +/* GStreamer + * Copyright (C) 2020 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_MF_CAPTURE_WINRT_H__ +#define __GST_MF_CAPTURE_WINRT_H__ + +#include +#include "gstmfsourceobject.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MF_CAPTURE_WINRT (gst_mf_capture_winrt_get_type()) +G_DECLARE_FINAL_TYPE (GstMFCaptureWinRT, gst_mf_capture_winrt, + GST, MF_CAPTURE_WINRT, GstMFSourceObject); + +GstMFSourceObject * gst_mf_capture_winrt_new (GstMFSourceType type, + gint device_index, + const gchar * device_name, + const gchar * device_path); + +G_END_DECLS + +#endif /* __GST_MF_CAPTURE_WINRT_H__ */ \ No newline at end of file diff --git a/sys/mediafoundation/gstmfdevice.c b/sys/mediafoundation/gstmfdevice.c index f11b7a8a8a..71356bec66 100644 --- a/sys/mediafoundation/gstmfdevice.c +++ b/sys/mediafoundation/gstmfdevice.c @@ -25,10 +25,14 @@ #include "gstmfvideosrc.h" #include "gstmfutils.h" +#if GST_MF_WINAPI_ONLY_APP +#include "gstmfcapturewinrt.h" +#else /* GST_MF_WINAPI_ONLY_APP */ #include "gstmfsourcereader.h" -#if HAVE_CAPTURE_ENGINE +#if GST_MF_HAVE_CAPTURE_ENGINE #include "gstmfcaptureengine.h" -#endif +#endif /* GST_MF_HAVE_CAPTURE_ENGINE */ +#endif /* GST_MF_WINAPI_ONLY_APP */ #include "gstmfdevice.h" @@ -179,13 +183,16 @@ gst_mf_device_provider_probe (GstDeviceProvider * provider) gchar *device_name = NULL; gchar *device_path = NULL; - -#if HAVE_CAPTURE_ENGINE - obj = gst_mf_capture_engine_new (GST_MF_SOURCE_TYPE_VIDEO, i, NULL, NULL); -#endif - +#if GST_MF_WINAPI_ONLY_APP + obj = gst_mf_capture_winrt_new (GST_MF_SOURCE_TYPE_VIDEO, i, NULL, NULL); +#else /* !GST_MF_WINAPI_ONLY_APP */ +#if GST_MF_HAVE_CAPTURE_ENGINE + if (!obj) + obj = gst_mf_capture_engine_new (GST_MF_SOURCE_TYPE_VIDEO, i, NULL, NULL); +#endif /* GST_MF_HAVE_CAPTURE_ENGINE */ if (!obj) obj = gst_mf_source_reader_new (GST_MF_SOURCE_TYPE_VIDEO, i, NULL, NULL); +#endif /* GST_MF_WINAPI_ONLY_APP */ if (!obj) break; diff --git a/sys/mediafoundation/gstmfsourceobject.c b/sys/mediafoundation/gstmfsourceobject.c index 4fccb14314..0bfbad1c5e 100644 --- a/sys/mediafoundation/gstmfsourceobject.c +++ b/sys/mediafoundation/gstmfsourceobject.c @@ -22,6 +22,8 @@ #include "config.h" #endif +#include "gstmfconfig.h" + #include "gstmfsourceobject.h" GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug); @@ -81,10 +83,12 @@ static void gst_mf_source_object_get_property (GObject * object, guint prop_id, static void gst_mf_source_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); +#if !(GST_MF_WINAPI_ONLY_APP) static gpointer gst_mf_source_object_thread_func (GstMFSourceObject * self); static gboolean gst_mf_source_enum_device_activate (GstMFSourceObject * self, GstMFSourceType source_type, GList ** device_activates); static void gst_mf_device_activate_free (GstMFDeviceActivate * activate); +#endif #define gst_mf_source_object_parent_class parent_class G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstMFSourceObject, gst_mf_source_object, @@ -132,17 +136,18 @@ gst_mf_source_object_init (GstMFSourceObject * self) g_mutex_init (&priv->lock); g_cond_init (&priv->cond); - - priv->context = g_main_context_new (); - priv->loop = g_main_loop_new (priv->context, FALSE); } static void gst_mf_source_object_constructed (GObject * object) { +#if !(GST_MF_WINAPI_ONLY_APP) GstMFSourceObject *self = GST_MF_SOURCE_OBJECT (object); GstMFSourceObjectPrivate *priv = self->priv; + priv->context = g_main_context_new (); + priv->loop = g_main_loop_new (priv->context, FALSE); + /* Create a new thread to ensure that COM thread can be MTA thread */ g_mutex_lock (&priv->lock); priv->thread = g_thread_new ("GstMFSourceObject", @@ -151,6 +156,7 @@ gst_mf_source_object_constructed (GObject * object) g_cond_wait (&priv->cond, &priv->lock); g_mutex_unlock (&priv->lock); +#endif G_OBJECT_CLASS (parent_class)->constructed (object); } @@ -254,6 +260,92 @@ gst_mf_source_object_main_loop_running_cb (GstMFSourceObject * self) return G_SOURCE_REMOVE; } +gboolean +gst_mf_source_object_start (GstMFSourceObject * object) +{ + GstMFSourceObjectClass *klass; + + g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + g_assert (klass->start != NULL); + + return klass->start (object); +} + +gboolean +gst_mf_source_object_stop (GstMFSourceObject * object) +{ + GstMFSourceObjectClass *klass; + + g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + g_assert (klass->stop != NULL); + + return klass->stop (object); +} + +GstFlowReturn +gst_mf_source_object_fill (GstMFSourceObject * object, GstBuffer * buffer) +{ + GstMFSourceObjectClass *klass; + + g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + g_assert (klass->fill != NULL); + + return klass->fill (object, buffer); +} + +void +gst_mf_source_object_set_flushing (GstMFSourceObject * object, + gboolean flushing) +{ + GstMFSourceObjectClass *klass; + + g_return_if_fail (GST_IS_MF_SOURCE_OBJECT (object)); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + + if (flushing) { + if (klass->unlock) + klass->unlock (object); + } else { + if (klass->unlock_stop) + klass->unlock_stop (object); + } +} + +gboolean +gst_mf_source_object_set_caps (GstMFSourceObject * object, GstCaps * caps) +{ + GstMFSourceObjectClass *klass; + + g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + g_assert (klass->set_caps != NULL); + + return klass->set_caps (object, caps); +} + +GstCaps * +gst_mf_source_object_get_caps (GstMFSourceObject * object) +{ + GstMFSourceObjectClass *klass; + + g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), NULL); + + klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); + g_assert (klass->get_caps != NULL); + + return klass->get_caps (object); +} + +#if !(GST_MF_WINAPI_ONLY_APP) static gpointer gst_mf_source_object_thread_func (GstMFSourceObject * self) { @@ -344,91 +436,6 @@ run_loop: return NULL; } -gboolean -gst_mf_source_object_start (GstMFSourceObject * object) -{ - GstMFSourceObjectClass *klass; - - g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - g_assert (klass->start != NULL); - - return klass->start (object); -} - -gboolean -gst_mf_source_object_stop (GstMFSourceObject * object) -{ - GstMFSourceObjectClass *klass; - - g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - g_assert (klass->stop != NULL); - - return klass->stop (object); -} - -GstFlowReturn -gst_mf_source_object_fill (GstMFSourceObject * object, GstBuffer * buffer) -{ - GstMFSourceObjectClass *klass; - - g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), GST_FLOW_ERROR); - g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - g_assert (klass->fill != NULL); - - return klass->fill (object, buffer); -} - -void -gst_mf_source_object_set_flushing (GstMFSourceObject * object, - gboolean flushing) -{ - GstMFSourceObjectClass *klass; - - g_return_if_fail (GST_IS_MF_SOURCE_OBJECT (object)); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - - if (flushing) { - if (klass->unlock) - klass->unlock (object); - } else { - if (klass->unlock_stop) - klass->unlock_stop (object); - } -} - -gboolean -gst_mf_source_object_set_caps (GstMFSourceObject * object, GstCaps * caps) -{ - GstMFSourceObjectClass *klass; - - g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), FALSE); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - g_assert (klass->set_caps != NULL); - - return klass->set_caps (object, caps); -} - -GstCaps * -gst_mf_source_object_get_caps (GstMFSourceObject * object) -{ - GstMFSourceObjectClass *klass; - - g_return_val_if_fail (GST_IS_MF_SOURCE_OBJECT (object), NULL); - - klass = GST_MF_SOURCE_OBJECT_GET_CLASS (object); - g_assert (klass->get_caps != NULL); - - return klass->get_caps (object); -} - static gboolean gst_mf_source_enum_device_activate (GstMFSourceObject * self, GstMFSourceType source_type, GList ** device_sources) @@ -522,3 +529,4 @@ gst_mf_device_activate_free (GstMFDeviceActivate * activate) g_free (activate->path); g_free (activate); } +#endif diff --git a/sys/mediafoundation/gstmfvideosrc.c b/sys/mediafoundation/gstmfvideosrc.c index 33cbb8bd67..ad532dba1e 100644 --- a/sys/mediafoundation/gstmfvideosrc.c +++ b/sys/mediafoundation/gstmfvideosrc.c @@ -42,10 +42,14 @@ #include "gstmfvideosrc.h" #include "gstmfutils.h" +#if GST_MF_WINAPI_ONLY_APP +#include "gstmfcapturewinrt.h" +#else /* GST_MF_WINAPI_ONLY_APP */ #include "gstmfsourcereader.h" -#if HAVE_CAPTURE_ENGINE +#if GST_MF_HAVE_CAPTURE_ENGINE #include "gstmfcaptureengine.h" -#endif +#endif /* GST_MF_HAVE_CAPTURE_ENGINE */ +#endif /* !GST_MF_WINAPI_ONLY_APP */ #include GST_DEBUG_CATEGORY (gst_mf_video_src_debug); @@ -230,14 +234,19 @@ gst_mf_video_src_start (GstBaseSrc * src) GST_DEBUG_OBJECT (self, "Start"); -#if HAVE_CAPTURE_ENGINE - self->source = gst_mf_capture_engine_new (GST_MF_SOURCE_TYPE_VIDEO, +#if GST_MF_WINAPI_ONLY_APP + self->source = gst_mf_capture_winrt_new (GST_MF_SOURCE_TYPE_VIDEO, self->device_index, self->device_name, self->device_path); -#endif - +#else /* GST_MF_WINAPI_ONLY_APP */ +#if GST_MF_HAVE_CAPTURE_ENGINE + if (!self->source) + self->source = gst_mf_capture_engine_new (GST_MF_SOURCE_TYPE_VIDEO, + self->device_index, self->device_name, self->device_path); +#endif /* GST_MF_HAVE_CAPTURE_ENGINE */ if (!self->source) self->source = gst_mf_source_reader_new (GST_MF_SOURCE_TYPE_VIDEO, self->device_index, self->device_name, self->device_path); +#endif /* GST_MF_WINAPI_ONLY_APP */ self->first_pts = GST_CLOCK_TIME_NONE; self->n_frames = 0; diff --git a/sys/mediafoundation/mediacapturewrapper.cpp b/sys/mediafoundation/mediacapturewrapper.cpp new file mode 100644 index 0000000000..f8c27bf633 --- /dev/null +++ b/sys/mediafoundation/mediacapturewrapper.cpp @@ -0,0 +1,1027 @@ +/* GStreamer + * Copyright (C) 2020 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstmfutils.h" +#include "mediacapturewrapper.h" + +#include "AsyncOperations.h" +#include +#include +#include +#include + +using namespace ABI::Windows::ApplicationModel::Core; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Media::Devices; +using namespace ABI::Windows::Media::MediaProperties; + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug); +#define GST_CAT_DEFAULT gst_mf_source_object_debug +} + +static std::string +convert_hstring_to_string (HString * hstr) +{ + const wchar_t *raw_hstr; + + if (!hstr) + return std::string(); + + raw_hstr = hstr->GetRawBuffer (nullptr); + if (!raw_hstr) + return std::string(); + + std::wstring_convert, wchar_t> converter; + + return converter.to_bytes (raw_hstr); +} + +static std::string +gst_media_capture_subtype_to_video_format (const std::string &subtype) +{ + /* https://docs.microsoft.com/en-us/uwp/api/windows.media.mediaproperties.videoencodingproperties.subtype#Windows_Media_MediaProperties_VideoEncodingProperties_Subtype */ + if (g_ascii_strcasecmp (subtype.c_str(), "RGB32") == 0) + return "BGRx"; + else if (g_ascii_strcasecmp (subtype.c_str(), "ARGB32") == 0) + return "BGRA"; + else if (g_ascii_strcasecmp (subtype.c_str(), "RGB24") == 0) + return "BGR"; + else if (g_ascii_strcasecmp (subtype.c_str(), "NV12") == 0) + return "NV12"; + else if (g_ascii_strcasecmp (subtype.c_str(), "YV12") == 0) + return "YV12"; + else if (g_ascii_strcasecmp (subtype.c_str(), "IYUV") == 0) + return "I420"; + /* FIXME: add more */ + + return std::string(); +} + +GstWinRTMediaDescription::GstWinRTMediaDescription() + : caps_(nullptr) +{ +} + +GstWinRTMediaDescription::GstWinRTMediaDescription + (const GstWinRTMediaDescription& other) + : caps_(nullptr) +{ + if (other.source_id_.IsValid()) + other.source_id_.CopyTo(source_id_.GetAddressOf()); + if (other.subtype_.IsValid()) + other.subtype_.CopyTo(subtype_.GetAddressOf()); + gst_caps_replace (&caps_, other.caps_); +} + +GstWinRTMediaDescription::~GstWinRTMediaDescription() +{ + Release(); +} + +void +GstWinRTMediaDescription::Release() +{ + source_id_.Release(); + subtype_.Release(); + gst_clear_caps(&caps_); +} + +bool +GstWinRTMediaDescription::IsValid() const +{ + if (!source_id_.IsValid()) + return false; + if (!subtype_.IsValid()) + return false; + if (!caps_) + return false; + + return true; +} + +HRESULT +GstWinRTMediaDescription::Fill(HString &source_id, + const ComPtr& desc) +{ + Release(); + + if (!source_id.IsValid()) { + GST_WARNING("Invalid source id"); + return E_FAIL; + } + + ComPtr desc2; + UINT32 width = 0; + UINT32 height = 0; + DOUBLE framerate = 0; + gint fps_n = 0, fps_d = 1; + HString hstr_subtype; + std::string subtype; + std::string format; + GstCaps *caps; + HRESULT hr; + + hr = desc.As (&desc2); + if (!gst_mf_result (hr)) + return hr; + + hr = desc->get_Width (&width); + if (!gst_mf_result (hr)) + return hr; + + hr = desc->get_Height (&height); + if (!gst_mf_result (hr)) + return hr; + + hr = desc->get_FrameRate (&framerate); + if (gst_mf_result (hr) && framerate > 0) + gst_util_double_to_fraction (framerate, &fps_n, &fps_d); + + hr = desc2->get_Subtype (hstr_subtype.GetAddressOf ()); + if (!gst_mf_result (hr)) + return hr; + + subtype = convert_hstring_to_string (&hstr_subtype); + if (subtype.empty()) + return E_FAIL; + + format = gst_media_capture_subtype_to_video_format (subtype); + if (format.empty()) { + GST_FIXME ("Unhandled subtype %s", subtype.c_str()); + return E_FAIL; + } + + caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, format.c_str(), "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, NULL); + + if (fps_n > 0 && fps_d > 0) + gst_caps_set_simple (caps, + "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); + + source_id.CopyTo (source_id_.GetAddressOf()); + hstr_subtype.CopyTo (subtype_.GetAddressOf()); + caps_ = caps; + + return S_OK; +} + +GstWinRTMediaFrameSourceGroup::GstWinRTMediaFrameSourceGroup() +{ +} + +GstWinRTMediaFrameSourceGroup::GstWinRTMediaFrameSourceGroup + (const GstWinRTMediaFrameSourceGroup& other) +{ + id_ = other.id_; + display_name_ = other.display_name_; + source_group_ = other.source_group_; + source_list_ = other.source_list_; +} + +GstWinRTMediaFrameSourceGroup::~GstWinRTMediaFrameSourceGroup() +{ + Release (); +} + +void +GstWinRTMediaFrameSourceGroup::Release() +{ + id_.clear (); + display_name_.clear (); + source_group_.Reset (); + source_list_.clear (); +} + +bool +GstWinRTMediaFrameSourceGroup::Contain(const GstWinRTMediaDescription& desc) +{ + if (!desc.IsValid()) + return false; + + if (source_list_.empty()) + return false; + + for (const auto& iter: source_list_) { + unsigned int dummy; + if (wcscmp (iter.source_id_.GetRawBuffer (&dummy), + desc.source_id_.GetRawBuffer (&dummy))) + continue; + + if (wcscmp (iter.subtype_.GetRawBuffer (&dummy), + desc.subtype_.GetRawBuffer (&dummy))) + continue; + + if (gst_caps_is_equal (iter.caps_, desc.caps_)) + return true; + } + + return false; +} + +HRESULT +GstWinRTMediaFrameSourceGroup::Fill + (const ComPtr &source_group) +{ + HRESULT hr = S_OK; + HString hstr_id; + HString hstr_display_name; + ComPtr> info_list; + UINT32 count = 0; + + Release(); + + hr = source_group->get_Id(hstr_id.GetAddressOf()); + if (!gst_mf_result(hr)) + goto done; + + id_ = convert_hstring_to_string (&hstr_id); + if (id_.empty()) { + GST_WARNING ("Emptry source group id"); + hr = E_FAIL; + goto done; + } + + hr = source_group->get_DisplayName (hstr_display_name.GetAddressOf()); + if (!gst_mf_result (hr)) + goto done; + + display_name_ = convert_hstring_to_string (&hstr_display_name); + if (display_name_.empty()) { + GST_WARNING ("Empty display name"); + hr = E_FAIL; + goto done; + } + + hr = source_group->get_SourceInfos (&info_list); + if (!gst_mf_result (hr)) + goto done; + + hr = info_list->get_Size (&count); + if (!gst_mf_result (hr)) + goto done; + + if (count == 0) { + GST_WARNING ("No available source info"); + hr = E_FAIL; + goto done; + } + + source_group_ = source_group; + + GST_DEBUG ("source-group has %d entries", count); + + for (UINT32 i = 0; i < count; i++) { + ComPtr info; + ComPtr info2; + ComPtr> desc_list; + MediaFrameSourceKind source_kind; + MediaStreamType source_type; + UINT32 desc_count = 0; + HString source_id; + + hr = info_list->GetAt(i, &info); + if (!gst_mf_result (hr)) + continue; + + hr = info.As (&info2); + if (!gst_mf_result (hr)) + continue; + + hr = info->get_SourceKind (&source_kind); + if (!gst_mf_result (hr)) + continue; + + /* This can be depth, infrared or others */ + /* FIXME: add audio support */ + if (source_kind != MediaFrameSourceKind::MediaFrameSourceKind_Color) { + GST_FIXME ("source-info has non-color source kind %d", + (gint) source_kind); + continue; + } + + hr = info->get_MediaStreamType (&source_type); + if (!gst_mf_result (hr)) + continue; + + /* FIXME: support audio */ + if (source_type != MediaStreamType::MediaStreamType_VideoPreview && + source_type != MediaStreamType::MediaStreamType_VideoRecord) { + continue; + } + + hr = info->get_Id (source_id.GetAddressOf ()); + if (!gst_mf_result (hr)) + continue; + + hr = info2->get_VideoProfileMediaDescription (&desc_list); + if (!gst_mf_result (hr)) + continue; + + hr = desc_list->get_Size (&desc_count); + if (!gst_mf_result (hr)) + continue; + + if (desc_count == 0) { + GST_WARNING("source-info has empty media description"); + continue; + } + + for (UINT32 j = 0; j < desc_count; j++) { + ComPtr desc; + + hr = desc_list->GetAt (j, &desc); + if (!gst_mf_result (hr)) + continue; + + GstWinRTMediaDescription media_desc; + hr = media_desc.Fill(source_id, desc); + if (!gst_mf_result(hr)) + continue; + + source_list_.push_back(media_desc); + } + } + +done: + if (source_list_.empty()) { + GST_WARNING ("No usable source infos"); + hr = E_FAIL; + } + + if (!gst_mf_result(hr)) + Release(); + + return hr; +} + +MediaCaptureWrapper::MediaCaptureWrapper() + : user_data_(nullptr) +{ + user_cb_.frame_arrived = nullptr; + user_cb_.failed = nullptr; + + /* Store CoreDispatecher if available */ + findCoreDispatcher(); +} + +MediaCaptureWrapper::~MediaCaptureWrapper() +{ + stopCapture(); + + if (frame_reader_) + frame_reader_->remove_FrameArrived (token_frame_arrived_); + + if (media_capture_) + media_capture_->remove_Failed (token_capture_failed_); +} + +void +MediaCaptureWrapper::RegisterCb (const MediaCaptureWrapperCallbacks &cb, + void * user_data) +{ + user_cb_.frame_arrived = cb.frame_arrived; + user_cb_.failed = cb.failed; + user_data_ = user_data; +} + +HRESULT +MediaCaptureWrapper::EnumrateFrameSourceGroup + (std::vector &group_list) +{ + HRESULT hr = S_OK; + + if (dispatcher_) { + hr = runOnUIThread (INFINITE, + [this, &group_list] { + return enumrateFrameSourceGroup(group_list); + }); + + } else { + hr = enumrateFrameSourceGroup(group_list); + } + + return hr; +} + +HRESULT +MediaCaptureWrapper::SetSourceGroup(const GstWinRTMediaFrameSourceGroup &group) +{ + if (group.source_group_ == nullptr) { + GST_WARNING ("Invalid MediaFrameSourceGroup"); + return E_FAIL; + } + + if (group.source_list_.empty()) { + GST_WARNING ("group doesn't include source lifo"); + return E_FAIL; + } + + source_group_ = + std::unique_ptr + (new GstWinRTMediaFrameSourceGroup(group)); + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::SetMediaDescription(const GstWinRTMediaDescription &desc) +{ + /* Must be source group was specified before this */ + if (source_group_ == nullptr) { + GST_WARNING ("No frame source group was specified"); + return E_FAIL; + } + + if (!desc.IsValid()) { + GST_WARNING("Invalid MediaDescription"); + return E_FAIL; + } + + if (!source_group_->Contain(desc)) { + GST_WARNING ("MediaDescription is not part of current source group"); + return E_FAIL; + } + + media_desc_ = + std::unique_ptr + (new GstWinRTMediaDescription(desc)); + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::StartCapture() +{ + HRESULT hr = S_OK; + + hr = openMediaCapture(); + if (!gst_mf_result (hr)) + return hr; + + if (dispatcher_) { + hr = runOnUIThread (INFINITE, + [this] { + return startCapture(); + }); + + } else { + hr = startCapture(); + } + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::StopCapture() +{ + HRESULT hr = S_OK; + + if (dispatcher_) { + hr = runOnUIThread (INFINITE, + [this] { + return stopCapture(); + }); + + } else { + hr = stopCapture(); + } + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::GetAvailableDescriptions + (std::vector &desc_list) +{ + desc_list.clear(); + + if (!source_group_) { + GST_WARNING ("No frame source group available"); + return E_FAIL; + } + + desc_list = source_group_->source_list_; + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::openMediaCapture() +{ + HRESULT hr; + + if (frame_reader_) { + GST_INFO ("Frame reader was configured"); + return S_OK; + } + + if (source_group_ == nullptr) { + GST_WARNING ("No frame source group was specified"); + return E_FAIL; + } + + if (media_desc_ == nullptr) { + GST_WARNING ("No media description was specified"); + return E_FAIL; + } + + hr = mediaCaptureInitPre (); + if (!gst_mf_result (hr)) + return hr; + + /* Wait user action and resulting mediaCaptureInitPost */ + std::unique_lock Lock(lock_); + if (!init_done_) + cond_.wait (Lock); + + return frame_reader_ ? S_OK : E_FAIL; +} + +HRESULT +MediaCaptureWrapper::mediaCaptureInitPre() +{ + ComPtr async_action; + HRESULT hr; + + auto work_item = Callback, + IDispatchedHandler, FtmBase>>([this]{ + ComPtr settings; + ComPtr settings5; + ComPtr media_capture; + ComPtr media_capture5; + ComPtr init_async; + HRESULT hr; + HStringReference hstr_setting = + HStringReference ( + RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings); + HStringReference hstr_capture = + HStringReference (RuntimeClass_Windows_Media_Capture_MediaCapture); + IMediaFrameSourceGroup * source_group = + source_group_->source_group_.Get(); + + hr = ActivateInstance (hstr_setting.Get(), &settings); + if (!gst_mf_result (hr)) + return hr; + + hr = settings->put_StreamingCaptureMode ( + StreamingCaptureMode::StreamingCaptureMode_Video); + if (!gst_mf_result (hr)) + return hr; + + hr = settings.As (&settings5); + if (!gst_mf_result (hr)) + return hr; + + hr = settings5->put_SourceGroup (source_group); + if (!gst_mf_result (hr)) + return hr; + + /* TODO: support D3D11 memory */ + hr = settings5->put_MemoryPreference ( + MediaCaptureMemoryPreference::MediaCaptureMemoryPreference_Cpu); + if (!gst_mf_result (hr)) + return hr; + + hr = settings5.As (&settings); + if (!gst_mf_result (hr)) + return hr; + + hr = ActivateInstance (hstr_capture.Get(), &media_capture5); + if (!gst_mf_result (hr)) + return hr; + + hr = media_capture5.As (&media_capture); + if (!gst_mf_result (hr)) + return hr; + + hr = media_capture->InitializeWithSettingsAsync (settings.Get(), &init_async); + if (!gst_mf_result (hr)) + return hr; + + return StartAsyncThen( + init_async.Get(), + [this, init_async, media_capture](_In_ HRESULT hr, + _In_ IAsyncAction *asyncResult, _In_ AsyncStatus asyncStatus) -> HRESULT + { + return mediaCaptureInitPost (init_async, media_capture); + }); + }); + + init_done_ = false; + + if (dispatcher_) { + hr = dispatcher_->RunAsync(CoreDispatcherPriority_Normal, work_item.Get(), + &async_action); + } else { + hr = work_item->Invoke (); + } + + return hr; +} + +HRESULT +MediaCaptureWrapper::mediaCaptureInitPost (ComPtr init_async, + ComPtr media_capture) +{ + std::unique_lock Lock(lock_); + ComPtr frame_source; + ComPtr> frameSources; + ComPtr source; + ComPtr format; + ComPtr> formatList; + ComPtr media_capture5; + ComPtr set_format_async; + ComPtr> create_reader_async; + ComPtr frame_reader; + boolean has_key; + UINT32 count = 0; + GstVideoInfo videoInfo; + HRESULT hr; + ComPtr> + frame_arrived_handler = Callback> ([&] + (IMediaFrameReader * reader, IMediaFrameArrivedEventArgs* args) + { + return onFrameArrived(reader, args); + } + ); + + GST_DEBUG ("InitializeWithSettingsAsync done"); + + hr = init_async->GetResults (); + if (!gst_mf_result (hr)) + goto done; + + if (!gst_video_info_from_caps (&videoInfo, media_desc_->caps_)) { + GST_WARNING ("Couldn't convert caps to videoinfo"); + hr = E_FAIL; + goto done; + } + + hr = media_capture.As (&media_capture5); + if (!gst_mf_result (hr)) + goto done; + + hr = media_capture5->get_FrameSources (&frameSources); + if (!gst_mf_result (hr)) + goto done; + + hr = frameSources->HasKey (media_desc_->source_id_.Get(), &has_key); + if (!gst_mf_result (hr)) + goto done; + + if (!has_key) { + GST_ERROR ("MediaFrameSource unavailable"); + hr = E_FAIL; + goto done; + } + + hr = frameSources->Lookup (media_desc_->source_id_.Get(), &source); + if (!gst_mf_result (hr)) + goto done; + + hr = source->get_SupportedFormats (&formatList); + if (!gst_mf_result (hr)) + goto done; + + hr = formatList->get_Size (&count); + if (!gst_mf_result (hr)) + goto done; + + if (count == 0) { + GST_ERROR ("No supported format object"); + hr = E_FAIL; + goto done; + } + + /* FIXME: support audio */ + for (UINT32 i = 0; i < count; i++) { + ComPtr fmt; + ComPtr videoFmt; + ComPtr ratio; + HString subtype; + UINT32 width = 0; + UINT32 height = 0; + + hr = formatList->GetAt (i, &fmt); + if (!gst_mf_result (hr)) + continue; + + hr = fmt->get_VideoFormat (&videoFmt); + if (!gst_mf_result (hr)) + continue; + + hr = videoFmt->get_Width (&width); + if (!gst_mf_result (hr)) + continue; + + hr = videoFmt->get_Height (&height); + if (!gst_mf_result (hr)) + continue; + + if (width != GST_VIDEO_INFO_WIDTH (&videoInfo)) + continue; + + if (height != GST_VIDEO_INFO_HEIGHT (&videoInfo)) + continue; + + /* TODO: check major type for audio */ + hr = fmt->get_Subtype (subtype.GetAddressOf ()); + if (!gst_mf_result (hr)) + continue; + + if (wcscmp (subtype.GetRawBuffer (nullptr), + media_desc_->subtype_.GetRawBuffer (nullptr))) + continue; + + format = fmt; + break; + } + + if (!format) { + GST_ERROR ( + "Couldn't find matching IMediaFrameFormat interface"); + hr = E_FAIL; + goto done; + } + + hr = source->SetFormatAsync (format.Get (), &set_format_async); + if (!gst_mf_result (hr)) + goto done; + + hr = SyncWait(set_format_async.Get ()); + if (!gst_mf_result (hr)) + goto done; + + hr = set_format_async->GetResults (); + if (!gst_mf_result (hr)) + goto done; + + hr = media_capture5->CreateFrameReaderAsync (source.Get(), + &create_reader_async); + if (!gst_mf_result (hr)) + goto done; + + hr = SyncWait(create_reader_async.Get()); + if (!gst_mf_result (hr)) + goto done; + + hr = create_reader_async->GetResults(&frame_reader); + if (!gst_mf_result (hr)) + goto done; + + hr = frame_reader->add_FrameArrived (frame_arrived_handler.Get(), + &token_frame_arrived_); + if (!gst_mf_result (hr)) + goto done; + + hr = media_capture->add_Failed + (Callback ([this] + (IMediaCapture * capture, IMediaCaptureFailedEventArgs* args) + { + return onCaptureFailed(capture, args); + } + ).Get(), + &token_capture_failed_); + + if (!gst_mf_result (hr)) + goto done; + + frame_reader_ = frame_reader; + media_capture_ = media_capture; + +done: + init_done_ = true; + cond_.notify_all(); + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::startCapture() +{ + HRESULT hr; + + if (!frame_reader_) { + GST_ERROR ("Frame reader wasn't configured"); + return E_FAIL; + } + + ComPtr> start_async; + hr = frame_reader_->StartAsync (&start_async); + if (!gst_mf_result (hr)) + return hr; + + hr = SyncWait(start_async.Get()); + if (!gst_mf_result (hr)) + return hr; + + MediaFrameReaderStartStatus reader_status; + hr = start_async->GetResults(&reader_status); + if (!gst_mf_result (hr)) + return hr; + + if (reader_status != + MediaFrameReaderStartStatus::MediaFrameReaderStartStatus_Success) { + GST_ERROR ("Cannot start frame reader, status %d", + (gint) reader_status); + return E_FAIL; + } + + return S_OK; +} + +HRESULT +MediaCaptureWrapper::stopCapture() +{ + HRESULT hr = S_OK; + + if (frame_reader_) { + ComPtr async_action; + + hr = frame_reader_->StopAsync (&async_action); + if (gst_mf_result (hr)) + hr = SyncWait(async_action.Get ()); + } + + return hr; +} + +HRESULT +MediaCaptureWrapper::onFrameArrived(IMediaFrameReader *reader, + IMediaFrameArrivedEventArgs *args) +{ + HRESULT hr; + ComPtr frame_ref; + ComPtr video_frame; + ComPtr bitmap; + + hr = reader->TryAcquireLatestFrame (&frame_ref); + if (!gst_mf_result (hr)) + return hr; + + if (!frame_ref) + return S_OK; + + hr = frame_ref->get_VideoMediaFrame (&video_frame); + if (!gst_mf_result (hr)) + return hr; + + hr = video_frame->get_SoftwareBitmap (&bitmap); + if (!gst_mf_result (hr) || !bitmap) + return hr; + + /* nothing to do if no callback was installed */ + if (!user_cb_.frame_arrived) + return S_OK; + + return user_cb_.frame_arrived (bitmap.Get(), user_data_); +} + +HRESULT +MediaCaptureWrapper::onCaptureFailed(IMediaCapture *capture, + IMediaCaptureFailedEventArgs *args) +{ + HRESULT hr; + UINT32 error_code = 0; + HString hstr_error_msg; + std::string error_msg; + + hr = args->get_Code (&error_code); + gst_mf_result (hr); + + hr = args->get_Message (hstr_error_msg.GetAddressOf()); + gst_mf_result (hr); + + error_msg = convert_hstring_to_string (&hstr_error_msg); + + GST_WARNING ("Have error %s (%d)", error_msg.c_str(), error_code); + + if (user_cb_.failed) + user_cb_.failed (error_msg, error_code, user_data_); + + return S_OK; +} + +void +MediaCaptureWrapper::findCoreDispatcher() +{ + HStringReference hstr_core_app = + HStringReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication); + HRESULT hr; + + ComPtr core_app; + hr = GetActivationFactory (hstr_core_app.Get(), &core_app); + if (!gst_mf_result(hr)) + return; + + ComPtr core_immersive_app; + hr = core_app.As(&core_immersive_app); + if (!gst_mf_result(hr)) + return; + + ComPtr core_app_view; + hr = core_immersive_app->get_MainView (&core_app_view); + if (!gst_mf_result(hr)) + return; + + ComPtr core_window; + hr = core_app_view->get_CoreWindow (&core_window); + if (!gst_mf_result(hr)) + return; + + hr = core_window->get_Dispatcher (&dispatcher_); + if (!gst_mf_result(hr)) + return; + + GST_DEBUG("Main UI dispatcher is available"); +} + +HRESULT +MediaCaptureWrapper::enumrateFrameSourceGroup + (std::vector &groupList) +{ + ComPtr frame_source_group_statics; + ComPtr*>> async_op; + ComPtr> source_group_list; + HRESULT hr; + unsigned int cnt = 0; + HStringReference hstr_frame_source_group = + HStringReference ( + RuntimeClass_Windows_Media_Capture_Frames_MediaFrameSourceGroup); + + groupList.clear(); + + hr = GetActivationFactory (hstr_frame_source_group.Get(), + &frame_source_group_statics); + if (!gst_mf_result(hr)) + return hr; + + hr = frame_source_group_statics->FindAllAsync (&async_op); + if (!gst_mf_result(hr)) + return hr; + + hr = SyncWait*>(async_op.Get(), 5000); + if (!gst_mf_result(hr)) + return hr; + + hr = async_op->GetResults (&source_group_list); + if (!gst_mf_result(hr)) + return hr; + + hr = source_group_list->get_Size (&cnt); + if (!gst_mf_result(hr)) + return hr; + + if (cnt == 0) { + GST_WARNING ("No available source group"); + return E_FAIL; + } + + GST_DEBUG("Have %u source group", cnt); + + for (unsigned int i = 0; i < cnt; i++) { + ComPtr group; + + hr = source_group_list->GetAt (i, &group); + if (!gst_mf_result(hr)) + continue; + + GstWinRTMediaFrameSourceGroup source_group; + hr = source_group.Fill(group); + if (!gst_mf_result (hr)) + continue; + + groupList.push_back (source_group); + } + + if (groupList.empty ()) { + GST_WARNING("No available source group"); + return E_FAIL; + } + + return S_OK; +} diff --git a/sys/mediafoundation/mediacapturewrapper.h b/sys/mediafoundation/mediacapturewrapper.h new file mode 100644 index 0000000000..2d029a1109 --- /dev/null +++ b/sys/mediafoundation/mediacapturewrapper.h @@ -0,0 +1,217 @@ +/* GStreamer + * Copyright (C) 2020 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_MEDIA_CAPTURE_WRAPPER_H__ +#define __GST_MEDIA_CAPTURE_WRAPPER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::UI::Core; +using namespace ABI::Windows::Media::Capture; +using namespace ABI::Windows::Media::Capture::Frames; +using namespace ABI::Windows::Graphics::Imaging; + +/* Store Format info and its caps representation */ +class GstWinRTMediaDescription +{ +public: + GstWinRTMediaDescription(); + GstWinRTMediaDescription(const GstWinRTMediaDescription& other); + ~GstWinRTMediaDescription(); + void Release(); + bool IsValid() const; + HRESULT Fill(HString &source_id, + const ComPtr& desc); + + GstWinRTMediaDescription& operator=(const GstWinRTMediaDescription& rhs) + { + if (this == &rhs) + return *this; + + Release(); + if (rhs.source_id_.IsValid()) + rhs.source_id_.CopyTo(source_id_.GetAddressOf()); + if (rhs.subtype_.IsValid()) + rhs.subtype_.CopyTo(subtype_.GetAddressOf()); + gst_caps_replace (&caps_, rhs.caps_); + + return *this; + } + +public: + HString source_id_; + /* TODO: need to cover audio too */ + HString subtype_; + /* Source ID which is mapped to MediaFormatSource */ + GstCaps *caps_; +}; + +/* holds GstWinRTMediaFrameSourceInfo, corresponding to per device info */ +class GstWinRTMediaFrameSourceGroup +{ +public: + GstWinRTMediaFrameSourceGroup(); + GstWinRTMediaFrameSourceGroup(const GstWinRTMediaFrameSourceGroup& other); + ~GstWinRTMediaFrameSourceGroup(); + void Release(); + bool Contain(const GstWinRTMediaDescription &desc); + HRESULT Fill(const ComPtr &source_group); + + GstWinRTMediaFrameSourceGroup& operator=(const GstWinRTMediaFrameSourceGroup& rhs) + { + if (this == &rhs) + return *this; + + Release(); + id_ = rhs.id_; + display_name_ = rhs.display_name_; + source_group_ = rhs.source_group_; + source_list_ = rhs.source_list_; + + return *this; + } + +public: + std::string id_; + std::string display_name_; + ComPtr source_group_; + std::vector source_list_; +}; + +typedef struct +{ + HRESULT (*frame_arrived) (ISoftwareBitmap * bitmap, void * user_data); + HRESULT (*failed) (const std::string &error, + UINT32 error_code, + void * user_data); +} MediaCaptureWrapperCallbacks; +class MediaCaptureWrapper +{ +public: + MediaCaptureWrapper(); + ~MediaCaptureWrapper(); + + void RegisterCb(const MediaCaptureWrapperCallbacks &cb, + void * user_data); + + /* Enumerating available source devices */ + /* Fill enumerated device infos into list */ + HRESULT EnumrateFrameSourceGroup(std::vector &group_list); + /* Select target device which should be one of enumerated be fore */ + HRESULT SetSourceGroup(const GstWinRTMediaFrameSourceGroup &group); + /* Select target format (resolution, video format) to use */ + HRESULT SetMediaDescription(const GstWinRTMediaDescription &desc); + + /* Start and Stop capturing operation */ + HRESULT StartCapture(); + HRESULT StopCapture(); + HRESULT GetAvailableDescriptions(std::vector &desc_list); + +private: + ComPtr media_capture_; + ComPtr frame_reader_; + ComPtr dispatcher_; + bool init_done_; + std::mutex lock_; + std::condition_variable cond_; + + EventRegistrationToken token_frame_arrived_; + EventRegistrationToken token_capture_failed_; + + std::unique_ptr source_group_; + std::unique_ptr media_desc_; + MediaCaptureWrapperCallbacks user_cb_; + void *user_data_; + +private: + HRESULT openMediaCapture(); + HRESULT mediaCaptureInitPre(); + HRESULT mediaCaptureInitPost(ComPtr init_async, + ComPtr media_capture); + HRESULT startCapture(); + HRESULT stopCapture(); + HRESULT onFrameArrived(IMediaFrameReader *reader, + IMediaFrameArrivedEventArgs *args); + HRESULT onCaptureFailed(IMediaCapture *capture, + IMediaCaptureFailedEventArgs *args); + void findCoreDispatcher(); + static HRESULT enumrateFrameSourceGroup(std::vector &list); + + template + HRESULT runOnUIThread(DWORD timeout, CB && cb) + { + ComPtr asyncAction; + HRESULT hr; + HRESULT hr_callback; + boolean can_now; + DWORD wait_ret; + + if (!dispatcher_) + return cb(); + + hr = dispatcher_->get_HasThreadAccess (&can_now); + + if (FAILED (hr)) + return hr; + + if (can_now) + return cb (); + + Event event (CreateEventEx (NULL, NULL, CREATE_EVENT_MANUAL_RESET, + EVENT_ALL_ACCESS)); + + if (!event.IsValid()) + return E_FAIL; + + auto handler = + Callback, + IDispatchedHandler, FtmBase>>([&hr_callback, &cb, &event] { + hr_callback = cb (); + SetEvent (event.Get()); + return S_OK; + }); + + hr = dispatcher_->RunAsync(CoreDispatcherPriority_Normal, + handler.Get(), &asyncAction); + + if (FAILED (hr)) + return hr; + + wait_ret = WaitForSingleObject(event.Get(), timeout); + if (wait_ret != WAIT_OBJECT_0) + return E_FAIL; + + return hr_callback; + } +}; + +#endif /* __GST_MEDIA_CAPTURE_WRAPPER_H__ */ \ No newline at end of file diff --git a/sys/mediafoundation/meson.build b/sys/mediafoundation/meson.build index 3be068c3c6..710ccb2d4e 100644 --- a/sys/mediafoundation/meson.build +++ b/sys/mediafoundation/meson.build @@ -5,13 +5,18 @@ mf_sources = [ 'gstmfvideoenc.cpp', 'gstmfh264enc.cpp', 'gstmfh265enc.cpp', + 'gstmfvideosrc.c', + 'gstmfsourceobject.c', + 'gstmfdevice.c', ] mf_desktop_sources = [ - 'gstmfvideosrc.c', - 'gstmfsourceobject.c', 'gstmfsourcereader.cpp', - 'gstmfdevice.c' +] + +mf_app_sources = [ + 'gstmfcapturewinrt.cpp', + 'mediacapturewrapper.cpp', ] mf_header_deps = [ @@ -25,8 +30,8 @@ mf_header_deps = [ ] winapi_desktop = false +winapi_app = false have_capture_engine = false -extra_c_args = ['-DCOBJMACROS'] mf_lib_deps = [] mf_config = configuration_data() @@ -47,6 +52,7 @@ mfplat_lib = cc.find_library('mfplat', required : mf_option) mfreadwrite_lib = cc.find_library('mfreadwrite', required : mf_option) mfuuid_lib = cc.find_library('mfuuid', required : mf_option) strmiids_lib = cc.find_library('strmiids', required : mf_option) +runtimeobject_lib = cc.find_library('runtimeobject', required : false) have_mf_lib = mf_lib.found() and mfplat_lib.found() and mfreadwrite_lib.found() and mfuuid_lib.found() and strmiids_lib.found() if not have_mf_lib @@ -80,7 +86,26 @@ winapi_desktop = cxx.compiles('''#include dependencies: mf_lib_deps, name: 'checking if building for Win32') -if winapi_desktop +if runtimeobject_lib.found() + winapi_app = cxx.compiles('''#include + #include + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #error "not winrt" + #endif''', + dependencies: [mf_lib_deps, runtimeobject_lib], + name: 'checking if building for WinRT') +endif + +if not winapi_desktop and not winapi_app + error('Neither Desktop partition nor App partition') +endif + +winapi_app_only = winapi_app and not winapi_desktop +if winapi_app_only + mf_sources += mf_app_sources + mf_lib_deps += [runtimeobject_lib] +else mf_sources += mf_desktop_sources have_capture_engine = cc.has_header('mfcaptureengine.h') if have_capture_engine @@ -88,7 +113,8 @@ if winapi_desktop endif endif -mf_config.set10('HAVE_CAPTURE_ENGINE', have_capture_engine) +mf_config.set10('GST_MF_HAVE_CAPTURE_ENGINE', have_capture_engine) +mf_config.set10('GST_MF_WINAPI_ONLY_APP', winapi_app_only) configure_file( output: 'gstmfconfig.h', @@ -97,7 +123,7 @@ configure_file( gstmediafoundation = library('gstmediafoundation', mf_sources, - c_args : gst_plugins_bad_args + extra_c_args, + c_args : gst_plugins_bad_args + ['-DCOBJMACROS'], cpp_args : gst_plugins_bad_args, include_directories : [configinc], dependencies : [gstbase_dep, gstvideo_dep, gstpbutils_dep] + mf_lib_deps, diff --git a/sys/mediafoundation/plugin.c b/sys/mediafoundation/plugin.c index 189ed71f7a..2d3312b134 100644 --- a/sys/mediafoundation/plugin.c +++ b/sys/mediafoundation/plugin.c @@ -22,22 +22,20 @@ #include "config.h" #endif +#include "gstmfconfig.h" + #include #include -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #include "gstmfvideosrc.h" #include "gstmfdevice.h" -#endif #include "gstmfutils.h" #include "gstmfh264enc.h" #include "gstmfh265enc.h" GST_DEBUG_CATEGORY (gst_mf_debug); GST_DEBUG_CATEGORY (gst_mf_utils_debug); -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) GST_DEBUG_CATEGORY (gst_mf_source_object_debug); -#endif GST_DEBUG_CATEGORY (gst_mf_transform_debug); #define GST_CAT_DEFAULT gst_mf_debug @@ -46,14 +44,13 @@ static gboolean plugin_init (GstPlugin * plugin) { HRESULT hr; + GstRank rank = GST_RANK_SECONDARY; GST_DEBUG_CATEGORY_INIT (gst_mf_debug, "mf", 0, "media foundation"); GST_DEBUG_CATEGORY_INIT (gst_mf_utils_debug, "mfutils", 0, "media foundation utility functions"); -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) GST_DEBUG_CATEGORY_INIT (gst_mf_source_object_debug, "mfsourceobject", 0, "mfsourceobject"); -#endif GST_DEBUG_CATEGORY_INIT (gst_mf_transform_debug, "mftransform", 0, "mftransform"); @@ -62,13 +59,16 @@ plugin_init (GstPlugin * plugin) GST_WARNING ("MFStartup failure, hr: 0x%x", hr); return TRUE; } -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - gst_element_register (plugin, - "mfvideosrc", GST_RANK_SECONDARY, GST_TYPE_MF_VIDEO_SRC); - gst_device_provider_register (plugin, "mfdeviceprovider", - GST_RANK_SECONDARY, GST_TYPE_MF_DEVICE_PROVIDER); + + /* mfvideosrc should be primary rank for UWP */ +#if GST_MF_WINAPI_ONLY_APP + rank = GST_RANK_PRIMARY + 1; #endif + gst_element_register (plugin, "mfvideosrc", rank, GST_TYPE_MF_VIDEO_SRC); + gst_device_provider_register (plugin, "mfdeviceprovider", + rank, GST_TYPE_MF_DEVICE_PROVIDER); + gst_mf_h264_enc_plugin_init (plugin, GST_RANK_SECONDARY); gst_mf_h265_enc_plugin_init (plugin, GST_RANK_SECONDARY);