mediafoundation: Add support video capture on UWP app

New video capture implementation using WinRT Media APIs for UWP app.
Due to the strict permission policy of UWP, device enumeration and
open should be done via new WinRT APIs and to get permission from users,
it will invoke permission dialog on UI.
Strictly saying, this implementation is not a part of MediaFoundation
but structurally it's very similar to MediaFoundation API.
So we can avoid some code duplication by adding this implementation
into MediaFoundation plugin.

This implementation requires UniversalApiContract version >= 6.0
which is part of Windows 10 version 1803 (Redstone 4)

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1241>
This commit is contained in:
Seungha Yang 2020-05-20 23:23:08 +09:00 committed by GStreamer Merge Bot
parent e580676747
commit c4cb51c63d
10 changed files with 2217 additions and 119 deletions

View file

@ -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 <wrl.h>
#include <wrl\async.h>
#include <Windows.System.Threading.h>
#include <functional>
template <typename TDelegate, typename TOperation, typename TLambda>
HRESULT StartAsyncThen(_In_ TOperation* pOperation, _In_ TLambda&& tFunc)
{
if (nullptr == pOperation)
{
return E_INVALIDARG;
}
auto spCallback = Microsoft::WRL::Callback<TDelegate>(
[tFunc](_In_ TOperation* pOperation, _In_ AsyncStatus status) -> HRESULT
{
HRESULT hr = S_OK;
// wrap the operation
if (status != AsyncStatus::Completed)
{
Microsoft::WRL::ComPtr<TOperation> spOperation(pOperation);
Microsoft::WRL::ComPtr<IAsyncInfo> 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 <typename TLambda>
HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncAction* pOperation, _In_ TLambda&& tFunc)
{
return StartAsyncThen<ABI::Windows::Foundation::IAsyncActionCompletedHandler, ABI::Windows::Foundation::IAsyncAction>(pOperation, static_cast<TLambda&&>(tFunc));
}
template <typename TProgress, typename TLambda>
HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress<TProgress>* pOperation, _In_ TLambda&& tFunc)
{
return StartAsyncThen<ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<TProgress>, Windows::Foundation::IAsyncActionWithProgress<TProgress>>(pOperation, static_cast<TLambda&&>(tFunc));
}
template <typename TResult, typename TLambda>
HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* pOperation, _In_ TLambda&& tFunc)
{
return StartAsyncThen<ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>, ABI::Windows::Foundation::IAsyncOperation<TResult>>(pOperation, static_cast<TLambda&&>(tFunc));
}
template <typename TResult, typename TProgress, typename TLambda>
HRESULT StartAsyncThen(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* pOperation, _In_ TLambda&& tFunc)
{
return StartAsyncThen<ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<TResult, TProgress>, ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>>(pOperation, static_cast<TLambda&&>(tFunc));
}
// eg. TOperation = IAsyncOperationWithProgress<UINT32, UINT32>
// eg. THandler = IAsyncOperationWithProgressCompletedHandler<UINT, UINT>
template<typename TOperation, typename THandler>
class AsyncEventDelegate
: public Microsoft::WRL::RuntimeClass
< Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::Delegate>
, THandler
, Microsoft::WRL::FtmBase >
{
public:
AsyncEventDelegate()
: _completedEvent(CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS))
{
ComPtr<AsyncEventDelegate> 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<TOperation> spOperation(pOperation);
Microsoft::WRL::ComPtr<IAsyncInfo> 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<void(HRESULT, TOperation*)> _func;
Microsoft::WRL::Wrappers::Event _completedEvent;
};
template <typename TOperation, typename THandler>
HRESULT SyncWait(_In_ TOperation* pOperation, _In_ DWORD dwMilliseconds)
{
auto spCallback = Microsoft::WRL::Make<AsyncEventDelegate<TOperation, THandler>>();
return spCallback->SyncWait(pOperation, dwMilliseconds);
}
template <typename TResult>
HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncAction* pOperation, _In_ DWORD dwMilliseconds = INFINITE)
{
return SyncWait<ABI::Windows::Foundation::IAsyncAction, ABI::Windows::Foundation::IAsyncActionCompletedHandler>(pOperation, dwMilliseconds);
}
template <typename TResult>
HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* pOperation, _In_ DWORD dwMilliseconds = INFINITE)
{
return SyncWait<ABI::Windows::Foundation::IAsyncOperation<TResult>, ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>>(pOperation, dwMilliseconds);
}
template <typename TResult, typename TProgress>
HRESULT SyncWait(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* pOperation, _In_ DWORD dwMilliseconds = INFINITE)
{
return SyncWait<ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>, ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<TResult, TProgress>>(pOperation, dwMilliseconds);
}

View file

@ -0,0 +1,597 @@
/* GStreamer
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/video/video.h>
#include "gstmfcapturewinrt.h"
#include "gstmfutils.h"
#include "mediacapturewrapper.h"
#include <memorybuffer.h>
#include <memory>
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<GstWinRTMediaFrameSourceGroup> target_group;
std::vector<GstWinRTMediaFrameSourceGroup> 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<GstWinRTMediaFrameSourceGroup>(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<ISoftwareBitmap> bitmap;
ComPtr<IBitmapBuffer> bitmap_buffer;
ComPtr<IMemoryBuffer> mem_buf;
ComPtr<IMemoryBufferReference> mem_ref;
ComPtr<Windows::Foundation::IMemoryBufferByteAccess> 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<GstWinRTMediaDescription> 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;
}

View file

@ -0,0 +1,39 @@
/* GStreamer
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#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__ */

View file

@ -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;

View file

@ -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

View file

@ -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 <string.h>
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;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,217 @@
/* GStreamer
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#include <windows.media.capture.h>
#include <vector>
#include <memory>
#include <string>
#include <functional>
#include <mutex>
#include <condition_variable>
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<IMediaCaptureVideoProfileMediaDescription>& 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<IMediaFrameSourceGroup> &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<IMediaFrameSourceGroup> source_group_;
std::vector<GstWinRTMediaDescription> 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<GstWinRTMediaFrameSourceGroup> &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<GstWinRTMediaDescription> &desc_list);
private:
ComPtr<IMediaCapture> media_capture_;
ComPtr<IMediaFrameReader> frame_reader_;
ComPtr<ICoreDispatcher> dispatcher_;
bool init_done_;
std::mutex lock_;
std::condition_variable cond_;
EventRegistrationToken token_frame_arrived_;
EventRegistrationToken token_capture_failed_;
std::unique_ptr<GstWinRTMediaFrameSourceGroup> source_group_;
std::unique_ptr<GstWinRTMediaDescription> media_desc_;
MediaCaptureWrapperCallbacks user_cb_;
void *user_data_;
private:
HRESULT openMediaCapture();
HRESULT mediaCaptureInitPre();
HRESULT mediaCaptureInitPost(ComPtr<IAsyncAction> init_async,
ComPtr<IMediaCapture> 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<GstWinRTMediaFrameSourceGroup> &list);
template <typename CB>
HRESULT runOnUIThread(DWORD timeout, CB && cb)
{
ComPtr<IAsyncAction> 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<Implements<RuntimeClassFlags<ClassicCom>,
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__ */

View file

@ -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 <winapifamily.h>
dependencies: mf_lib_deps,
name: 'checking if building for Win32')
if winapi_desktop
if runtimeobject_lib.found()
winapi_app = cxx.compiles('''#include <winapifamily.h>
#include <windows.applicationmodel.core.h>
#include <wrl.h>
#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,

View file

@ -22,22 +22,20 @@
#include "config.h"
#endif
#include "gstmfconfig.h"
#include <winapifamily.h>
#include <gst/gst.h>
#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);