Projects
Essentials
gstreamer-plugins-bad-codecs
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 38
View file
gstreamer-plugins-bad-codecs.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Thu Aug 14 16:20:34 UTC 2025 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 1.26.5 + +------------------------------------------------------------------- Mon Jul 28 18:30:23 UTC 2025 - Bjørn Lie <zaitor@opensuse.org> - Update to version 1.26.4
View file
gstreamer-plugins-bad-codecs.spec
Changed
@@ -7,7 +7,7 @@ %define _version 1.26.0 Name: gstreamer-plugins-bad-codecs -Version: 1.26.4 +Version: 1.26.5 Release: 0 Summary: Codecs/plugins for gstreamer-plugins-bad License: LGPL-2.1-or-later
View file
_service
Changed
@@ -2,7 +2,7 @@ <service name="download_url"> <param name="host">gstreamer.freedesktop.org</param> <param name="protocol">https</param> - <param name="path">/src/gst-plugins-bad/gst-plugins-bad-1.26.4.tar.xz</param> + <param name="path">/src/gst-plugins-bad/gst-plugins-bad-1.26.5.tar.xz</param> </service> <service name="set_version" mode="buildtime"/> </services>
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/AsyncOperations.h
Deleted
@@ -1,168 +0,0 @@ -// 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
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2client.cpp
Deleted
@@ -1,1342 +0,0 @@ -/* - * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> - * Copyright (C) 2013 Collabora Ltd. - * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk> - * Copyright (C) 2018 Centricular Ltd. - * Author: Nirbheek Chauhan <nirbheek@centricular.com> - * 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 "AsyncOperations.h" -#include "gstwasapi2client.h" -#include "gstwasapi2util.h" -#include <initguid.h> -#include <windows.foundation.h> -#include <windows.ui.core.h> -#include <wrl.h> -#include <wrl/wrappers/corewrappers.h> -#include <audioclient.h> -#include <mmdeviceapi.h> -#include <string.h> -#include <string> -#include <locale> -#include <codecvt> -#include <atomic> - -/* *INDENT-OFF* */ -using namespace ABI::Windows::ApplicationModel::Core; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::Foundation::Collections; -using namespace ABI::Windows::UI::Core; -using namespace ABI::Windows::Media::Devices; -using namespace ABI::Windows::Devices::Enumeration; - -using namespace Microsoft::WRL; -using namespace Microsoft::WRL::Wrappers; - -/* Copy of audioclientactivationparams.h since those types are defined only for - * NTDDI_VERSION >= NTDDI_WIN10_FE */ -#define GST_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" -typedef enum -{ - GST_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE = 0, - GST_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE = 1 -} GST_PROCESS_LOOPBACK_MODE; - -typedef struct -{ - DWORD TargetProcessId; - GST_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; -} GST_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; - -typedef enum -{ - GST_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT = 0, - GST_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK = 1 -} GST_AUDIOCLIENT_ACTIVATION_TYPE; - -typedef struct -{ - GST_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; - union - { - GST_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; - } DUMMYUNIONNAME; -} GST_AUDIOCLIENT_ACTIVATION_PARAMS; -/* End of audioclientactivationparams.h */ - -G_BEGIN_DECLS - -GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_client_debug); -#define GST_CAT_DEFAULT gst_wasapi2_client_debug - -G_END_DECLS -/* *INDENT-ON* */ - -static void -gst_wasapi2_client_on_device_activated (GstWasapi2Client * client, - IAudioClient * audio_client); - -static void -gst_wasapi2_client_on_endpoint_volume_activated (GstWasapi2Client * client, - IAudioEndpointVolume * audio_endpoint_volume); - -static void -gst_wasapi2_client_set_endpoint_muted (GstWasapi2Client * client, - gboolean muted); - -/* *INDENT-OFF* */ -class GstWasapiDeviceActivator - : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, - IActivateAudioInterfaceCompletionHandler> -{ -public: - typedef enum { - WASAPI_IFACE_AUDIO_CLIENT, - WASAPI_IFACE_AUDIO_ENDPOINT_VOLUME, - } WasapiInterface; - - GstWasapiDeviceActivator () - { - g_weak_ref_init (&listener_, nullptr); - interface_to_activate_ = WASAPI_IFACE_AUDIO_CLIENT; - } - - ~GstWasapiDeviceActivator () - { - g_weak_ref_set (&listener_, nullptr); - } - - HRESULT - RuntimeClassInitialize (GstWasapi2Client * listener, - gpointer dispatcher, - WasapiInterface interface_to_activate) - { - if (!listener) - return E_INVALIDARG; - - g_weak_ref_set (&listener_, listener); - - if (dispatcher) { - ComPtr<IInspectable> inspectable = - reinterpret_cast<IInspectable*> (dispatcher); - HRESULT hr; - - hr = inspectable.As (&dispatcher_); - if (gst_wasapi2_result (hr)) - GST_INFO("Main UI dispatcher is available"); - } - - interface_to_activate_ = interface_to_activate; - - return S_OK; - } - - STDMETHOD(ActivateCompleted) - (IActivateAudioInterfaceAsyncOperation *async_op) - { - ComPtr<IAudioClient> audio_client; - ComPtr<IAudioEndpointVolume> audio_endpoint_volume; - HRESULT hr = S_OK; - HRESULT hr_async_op = S_OK; - ComPtr<IUnknown> audio_interface; - GstWasapi2Client *client; - - client = (GstWasapi2Client *) g_weak_ref_get (&listener_); - - if (!client) { - GST_WARNING ("No listener was configured"); - return S_OK; - } - - GST_INFO_OBJECT (client, "AsyncOperation done"); - - hr = async_op->GetActivateResult(&hr_async_op, &audio_interface); - - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (client, "Failed to get activate result, hr: 0x%x", hr); - goto done; - } - - if (!gst_wasapi2_result (hr_async_op)) { - GST_WARNING_OBJECT (client, "Failed to activate device"); - goto done; - } - - switch (interface_to_activate_) { - case WASAPI_IFACE_AUDIO_CLIENT: - hr = audio_interface.As (&audio_client); - if (!gst_wasapi2_result (hr)) { - GST_ERROR_OBJECT (client, "Failed to get IAudioClient3 interface"); - goto done; - } - break; - case WASAPI_IFACE_AUDIO_ENDPOINT_VOLUME: - hr = audio_interface.As (&audio_endpoint_volume); - if (!gst_wasapi2_result (hr)) { - GST_ERROR_OBJECT (client, "Failed to get IAudioEndpointVolume interface"); - goto done; - } - break; - } - - done: - /* Should call this method anyway, listener will wait this event */ - switch (interface_to_activate_) { - case WASAPI_IFACE_AUDIO_CLIENT: - gst_wasapi2_client_on_device_activated (client, audio_client.Get()); - break; - case WASAPI_IFACE_AUDIO_ENDPOINT_VOLUME: - gst_wasapi2_client_on_endpoint_volume_activated (client, audio_endpoint_volume.Get()); - break; - } - - gst_object_unref (client); - /* return S_OK anyway, but listener can know it's succeeded or not - * by passed IAudioClient handle via gst_wasapi2_client_on_device_activated - */ - - return S_OK; - } - - HRESULT - ActivateDeviceAsync(const std::wstring &device_id, - GST_AUDIOCLIENT_ACTIVATION_PARAMS * params) - { - ComPtr<IAsyncAction> async_action; - bool run_async = false; - HRESULT hr; - - auto work_item = Callback<Implements<RuntimeClassFlags<ClassicCom>, - IDispatchedHandler, FtmBase>>(this, device_id, params{ - ComPtr<IActivateAudioInterfaceAsyncOperation> async_op; - HRESULT async_hr = S_OK; - PROPVARIANT activate_params = {}; - IID iid = {}; - - switch (interface_to_activate_) { - case WASAPI_IFACE_AUDIO_CLIENT: - iid = __uuidof (IAudioClient); - break; - case WASAPI_IFACE_AUDIO_ENDPOINT_VOLUME: - iid = __uuidof (IAudioEndpointVolume); - break; - } - - if (params) { - activate_params.vt = VT_BLOB; - activate_params.blob.cbSize = sizeof(GST_AUDIOCLIENT_ACTIVATION_PARAMS); - activate_params.blob.pBlobData = (BYTE *) params; - - async_hr = ActivateAudioInterfaceAsync (device_id.c_str (), - iid, &activate_params, this, &async_op); - } else { - async_hr = ActivateAudioInterfaceAsync (device_id.c_str (), - iid, nullptr, this, &async_op); - } - - /* for debugging */ - gst_wasapi2_result (async_hr); - - return async_hr; - }); - - if (dispatcher_) { - boolean can_now; - hr = dispatcher_->get_HasThreadAccess (&can_now); - - if (!gst_wasapi2_result (hr)) - return hr; - - if (!can_now) - run_async = true; - } - - if (run_async && dispatcher_) { - hr = dispatcher_->RunAsync (CoreDispatcherPriority_Normal, - work_item.Get (), &async_action); - } else { - hr = work_item->Invoke (); - } - - return hr; - } - -private: - GWeakRef listener_; - ComPtr<ICoreDispatcher> dispatcher_; - WasapiInterface interface_to_activate_; -}; - -class GstWasapiEndpointVolumeCallback - : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, - IAudioEndpointVolumeCallback> -{ -public: - GstWasapiEndpointVolumeCallback () - { - g_weak_ref_init (&client_, nullptr); - } - - ~GstWasapiEndpointVolumeCallback () - { - g_weak_ref_set (&client_, nullptr); - } - - HRESULT - RuntimeClassInitialize (GstWasapi2Client * client) - { - if (!client) - return E_INVALIDARG; - g_weak_ref_set (&client_, client); - return S_OK; - } - - STDMETHOD(OnNotify) - (AUDIO_VOLUME_NOTIFICATION_DATA * notify) - { - GstWasapi2Client *client = (GstWasapi2Client *) g_weak_ref_get (&client_); - if (client) { - gst_wasapi2_client_set_endpoint_muted (client, notify->bMuted); - gst_object_unref (client); - } - return S_OK; - } - -private: - GWeakRef client_; -}; -/* *INDENT-ON* */ - -typedef enum -{ - GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND = -2, - GST_WASAPI2_CLIENT_ACTIVATE_FAILED = -1, - GST_WASAPI2_CLIENT_ACTIVATE_INIT = 0, - GST_WASAPI2_CLIENT_ACTIVATE_WAIT, - GST_WASAPI2_CLIENT_ACTIVATE_DONE, -} GstWasapi2ClientActivateState; - -enum -{ - PROP_0, - PROP_DEVICE, - PROP_DEVICE_NAME, - PROP_DEVICE_INDEX, - PROP_DEVICE_CLASS, - PROP_DISPATCHER, - PROP_CAN_AUTO_ROUTING, - PROP_LOOPBACK_TARGET_PID, -}; - -#define DEFAULT_DEVICE_INDEX -1 -#define DEFAULT_DEVICE_CLASS GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE - -struct GstWasapi2ClientPrivate -{ - std::atomic < bool >is_endpoint_muted; -}; - -struct _GstWasapi2Client -{ - GstObject parent; - - GstWasapi2ClientPrivate *priv; - - GstWasapi2ClientDeviceClass device_class; - gchar *device_id; - gchar *device_name; - gint device_index; - gpointer dispatcher; - gboolean can_auto_routing; - guint target_pid; - - IAudioClient *audio_client; - - GMutex endpoint_volume_lock; - IAudioEndpointVolume *audio_endpoint_volume; - GstWasapiEndpointVolumeCallback *endpoint_volume_callback; - - GstCaps *supported_caps; - - GThread *thread; - GMutex lock; - GCond cond; - GMainContext *context; - GMainLoop *loop; - - /* To wait ActivateCompleted event */ - GMutex init_lock; - GCond init_cond; - GstWasapi2ClientActivateState activate_state; -}; - -GType -gst_wasapi2_client_device_class_get_type (void) -{ - static GType class_type = 0; - static const GEnumValue types = { - {GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE, "Capture", "capture"}, - {GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, "Render", "render"}, - {GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE, "Loopback-Capture", - "loopback-capture"}, - {GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE, - "Include-Process-Loopback-Capture", - "include-process-loopback-capture"}, - {GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE, - "Exclude-Process-Loopback-Capture", - "exclude-process-loopback-capture"}, - {0, nullptr, nullptr} - }; - - if (g_once_init_enter (&class_type)) { - GType gtype = g_enum_register_static ("GstWasapi2ClientDeviceClass", types); - g_once_init_leave (&class_type, gtype); - } - - return class_type; -} - -static void gst_wasapi2_client_constructed (GObject * object); -static void gst_wasapi2_client_finalize (GObject * object); -static void gst_wasapi2_client_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_wasapi2_client_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - -static gpointer gst_wasapi2_client_thread_func (GstWasapi2Client * self); -static gboolean -gst_wasapi2_client_main_loop_running_cb (GstWasapi2Client * self); - -#define gst_wasapi2_client_parent_class parent_class -G_DEFINE_TYPE (GstWasapi2Client, gst_wasapi2_client, GST_TYPE_OBJECT); - -static void -gst_wasapi2_client_class_init (GstWasapi2ClientClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamFlags param_flags = - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - gobject_class->constructed = gst_wasapi2_client_constructed; - gobject_class->finalize = gst_wasapi2_client_finalize; - gobject_class->get_property = gst_wasapi2_client_get_property; - gobject_class->set_property = gst_wasapi2_client_set_property; - - g_object_class_install_property (gobject_class, PROP_DEVICE, - g_param_spec_string ("device", "Device", - "Audio device ID as provided by " - "Windows.Devices.Enumeration.DeviceInformation.Id", - nullptr, param_flags)); - g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, - g_param_spec_string ("device-name", "Device Name", - "The human-readable device name", nullptr, param_flags)); - g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX, - g_param_spec_int ("device-index", "Device Index", - "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX, - param_flags)); - g_object_class_install_property (gobject_class, PROP_DEVICE_CLASS, - g_param_spec_enum ("device-class", "Device Class", - "Device class", GST_TYPE_WASAPI2_CLIENT_DEVICE_CLASS, - DEFAULT_DEVICE_CLASS, param_flags)); - g_object_class_install_property (gobject_class, PROP_DISPATCHER, - g_param_spec_pointer ("dispatcher", "Dispatcher", - "ICoreDispatcher COM object to use", param_flags)); - g_object_class_install_property (gobject_class, PROP_CAN_AUTO_ROUTING, - g_param_spec_boolean ("auto-routing", "Auto Routing", - "Whether client can support automatic stream routing", FALSE, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - g_object_class_install_property (gobject_class, PROP_LOOPBACK_TARGET_PID, - g_param_spec_uint ("loopback-target-pid", "Loopback Target PID", - "Target process id to record", 0, G_MAXUINT32, 0, param_flags)); -} - -static void -gst_wasapi2_client_init (GstWasapi2Client * self) -{ - self->device_index = DEFAULT_DEVICE_INDEX; - self->device_class = DEFAULT_DEVICE_CLASS; - self->can_auto_routing = FALSE; - - g_mutex_init (&self->lock); - g_cond_init (&self->cond); - - g_mutex_init (&self->init_lock); - g_cond_init (&self->init_cond); - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_INIT; - - g_mutex_init (&self->endpoint_volume_lock); - - self->context = g_main_context_new (); - self->loop = g_main_loop_new (self->context, FALSE); - - self->priv = new GstWasapi2ClientPrivate (); - self->priv->is_endpoint_muted.store (false, std::memory_order_release); -} - -static void -gst_wasapi2_client_constructed (GObject * object) -{ - GstWasapi2Client *self = GST_WASAPI2_CLIENT (object); - - /* Create a new thread to ensure that COM thread can be MTA thread. - * We cannot ensure whether CoInitializeEx() was called outside of here for - * this thread or not. If it was called with non-COINIT_MULTITHREADED option, - * we cannot update it */ - g_mutex_lock (&self->lock); - self->thread = g_thread_new ("GstWasapi2ClientWinRT", - (GThreadFunc) gst_wasapi2_client_thread_func, self); - while (!self->loop || !g_main_loop_is_running (self->loop)) - g_cond_wait (&self->cond, &self->lock); - g_mutex_unlock (&self->lock); - - G_OBJECT_CLASS (parent_class)->constructed (object); -} - -static void -gst_wasapi2_client_finalize (GObject * object) -{ - GstWasapi2Client *self = GST_WASAPI2_CLIENT (object); - - if (self->loop) { - g_main_loop_quit (self->loop); - g_thread_join (self->thread); - g_main_context_unref (self->context); - g_main_loop_unref (self->loop); - - self->thread = nullptr; - self->context = nullptr; - self->loop = nullptr; - } - - gst_clear_caps (&self->supported_caps); - - g_free (self->device_id); - g_free (self->device_name); - - g_mutex_clear (&self->lock); - g_cond_clear (&self->cond); - - g_mutex_clear (&self->init_lock); - g_cond_clear (&self->init_cond); - - g_mutex_clear (&self->endpoint_volume_lock); - - delete self->priv; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_wasapi2_client_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstWasapi2Client *self = GST_WASAPI2_CLIENT (object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string (value, self->device_id); - break; - case PROP_DEVICE_NAME: - g_value_set_string (value, self->device_name); - break; - case PROP_DEVICE_INDEX: - g_value_set_int (value, self->device_index); - break; - case PROP_DEVICE_CLASS: - g_value_set_enum (value, self->device_class); - break; - case PROP_DISPATCHER: - g_value_set_pointer (value, self->dispatcher); - break; - case PROP_CAN_AUTO_ROUTING: - g_value_set_boolean (value, self->can_auto_routing); - break; - case PROP_LOOPBACK_TARGET_PID: - g_value_set_uint (value, self->target_pid); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_wasapi2_client_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstWasapi2Client *self = GST_WASAPI2_CLIENT (object); - - switch (prop_id) { - case PROP_DEVICE: - g_free (self->device_id); - self->device_id = g_value_dup_string (value); - break; - case PROP_DEVICE_NAME: - g_free (self->device_name); - self->device_name = g_value_dup_string (value); - break; - case PROP_DEVICE_INDEX: - self->device_index = g_value_get_int (value); - break; - case PROP_DEVICE_CLASS: - self->device_class = - (GstWasapi2ClientDeviceClass) g_value_get_enum (value); - break; - case PROP_DISPATCHER: - self->dispatcher = g_value_get_pointer (value); - break; - case PROP_LOOPBACK_TARGET_PID: - self->target_pid = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gst_wasapi2_client_main_loop_running_cb (GstWasapi2Client * 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 void -gst_wasapi2_client_on_device_activated (GstWasapi2Client * self, - IAudioClient * audio_client) -{ - GST_INFO_OBJECT (self, "Device activated"); - - g_mutex_lock (&self->init_lock); - if (audio_client) { - audio_client->AddRef (); - self->audio_client = audio_client; - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_DONE; - } else { - GST_WARNING_OBJECT (self, "IAudioClient is unavailable"); - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_FAILED; - } - g_cond_broadcast (&self->init_cond); - g_mutex_unlock (&self->init_lock); -} - -static void -gst_wasapi2_client_on_endpoint_volume_activated (GstWasapi2Client * self, - IAudioEndpointVolume * audio_endpoint_volume) -{ - GST_INFO_OBJECT (self, "Audio Endpoint Volume activated"); - - if (audio_endpoint_volume) { - HRESULT hr; - ComPtr < GstWasapiEndpointVolumeCallback > callback; - - g_mutex_lock (&self->endpoint_volume_lock); - audio_endpoint_volume->AddRef (); - self->audio_endpoint_volume = audio_endpoint_volume; - - hr = MakeAndInitialize < GstWasapiEndpointVolumeCallback > (&callback, - self); - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (self, - "Could not create endpoint volume callback object"); - } else { - hr = audio_endpoint_volume->RegisterControlChangeNotify (callback.Get ()); - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (self, - "Failed to register endpoint volume callback"); - } else { - BOOL initially_muted = FALSE; - - self->endpoint_volume_callback = callback.Detach (); - - hr = audio_endpoint_volume->GetMute (&initially_muted); - if (gst_wasapi2_result (hr)) { - gst_wasapi2_client_set_endpoint_muted (self, initially_muted); - } - } - } - g_mutex_unlock (&self->endpoint_volume_lock); - } else { - GST_WARNING_OBJECT (self, "IAudioEndpointVolume is unavailable"); - } -} - -static void -gst_wasapi2_client_set_endpoint_muted (GstWasapi2Client * self, gboolean muted) -{ - GST_DEBUG_OBJECT (self, "Audio Endpoint Volume: muted=%d", muted); - - self->priv->is_endpoint_muted.store (muted, std::memory_order_release); -} - -/* *INDENT-OFF* */ -static std::string -convert_wstring_to_string (const std::wstring &wstr) -{ - std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter; - - return converter.to_bytes (wstr.c_str()); -} - -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(); - - return convert_wstring_to_string (std::wstring (raw_hstr)); -} - -static std::wstring -gst_wasapi2_client_get_default_device_id (GstWasapi2Client * self) -{ - HRESULT hr; - PWSTR default_device_id_wstr = nullptr; - - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE) - hr = StringFromIID (DEVINTERFACE_AUDIO_CAPTURE, &default_device_id_wstr); - else - hr = StringFromIID (DEVINTERFACE_AUDIO_RENDER, &default_device_id_wstr); - - if (!gst_wasapi2_result (hr)) - return std::wstring(); - - std::wstring ret = std::wstring (default_device_id_wstr); - CoTaskMemFree (default_device_id_wstr); - - return ret; -} -/* *INDENT-ON* */ - -static void -gst_wasapi2_client_activate_async (GstWasapi2Client * self, - GstWasapiDeviceActivator * activator, - GstWasapiDeviceActivator * endpoint_volume_activator) -{ - /* *INDENT-OFF* */ - ComPtr<IDeviceInformationStatics> device_info_static; - ComPtr<IAsyncOperation<DeviceInformationCollection*>> async_op; - ComPtr<IVectorView<DeviceInformation*>> device_list; - HStringReference hstr_device_info = - HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation); - /* *INDENT-ON* */ - HRESULT hr; - DeviceClass device_class; - unsigned int count = 0; - gint device_index = 0; - std::wstring default_device_id_wstring; - std::string default_device_id; - std::wstring target_device_id_wstring; - std::string target_device_id; - std::string target_device_name; - gboolean use_default_device = FALSE; - GST_AUDIOCLIENT_ACTIVATION_PARAMS activation_params; - gboolean process_loopback = FALSE; - - memset (&activation_params, 0, sizeof (GST_AUDIOCLIENT_ACTIVATION_PARAMS)); - activation_params.ActivationType = GST_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT; - - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND; - - if (self->device_class == - GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE || - self->device_class == - GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE) { - if (self->target_pid == 0) { - GST_ERROR_OBJECT (self, "Process loopback mode without PID"); - return; - } - - if (!gst_wasapi2_can_process_loopback ()) { - GST_ERROR_OBJECT (self, "Process loopback is not supported"); - return; - } - - process_loopback = TRUE; - activation_params.ActivationType = - GST_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; - activation_params.ProcessLoopbackParams.TargetProcessId = - (DWORD) self->target_pid; - target_device_id_wstring = GST_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK; - target_device_id = convert_wstring_to_string (target_device_id_wstring); - - if (self->device_class == - GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE) { - activation_params.ProcessLoopbackParams.ProcessLoopbackMode = - GST_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; - } else { - activation_params.ProcessLoopbackParams.ProcessLoopbackMode = - GST_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE; - } - - target_device_name = "Process-loopback"; - goto activate; - } - - GST_INFO_OBJECT (self, - "requested device info, device-class: %s, device: %s, device-index: %d", - self->device_class == - GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE ? "capture" : "render", - GST_STR_NULL (self->device_id), self->device_index); - - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE) { - device_class = DeviceClass::DeviceClass_AudioCapture; - } else { - device_class = DeviceClass::DeviceClass_AudioRender; - } - - default_device_id_wstring = gst_wasapi2_client_get_default_device_id (self); - if (default_device_id_wstring.empty ()) { - GST_WARNING_OBJECT (self, "Couldn't get default device id"); - return; - } - - default_device_id = convert_wstring_to_string (default_device_id_wstring); - GST_DEBUG_OBJECT (self, "Default device id: %s", default_device_id.c_str ()); - - /* When - * 1) default device was requested or - * 2) no explicitly requested device or - * 3) requested device string id is null but device index is zero - * will use default device - * - * Note that default device is much preferred - * See https://docs.microsoft.com/en-us/windows/win32/coreaudio/automatic-stream-routing - */ - - /* DEVINTERFACE_AUDIO_CAPTURE and DEVINTERFACE_AUDIO_RENDER are available - * as of Windows 10 */ - if (gst_wasapi2_can_automatic_stream_routing ()) { - if (self->device_id && - g_ascii_strcasecmp (self->device_id, default_device_id.c_str ()) == 0) { - GST_DEBUG_OBJECT (self, "Default device was requested"); - use_default_device = TRUE; - } else if (self->device_index < 0 && !self->device_id) { - GST_DEBUG_OBJECT (self, - "No device was explicitly requested, use default device"); - use_default_device = TRUE; - } else if (!self->device_id && self->device_index == 0) { - GST_DEBUG_OBJECT (self, "device-index == zero means default device"); - use_default_device = TRUE; - } - } - - if (use_default_device) { - target_device_id_wstring = default_device_id_wstring; - target_device_id = default_device_id; - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE) - target_device_name = "Default Audio Capture Device"; - else - target_device_name = "Default Audio Render Device"; - goto activate; - } - - hr = GetActivationFactory (hstr_device_info.Get (), &device_info_static); - if (!gst_wasapi2_result (hr)) - return; - - hr = device_info_static->FindAllAsyncDeviceClass (device_class, &async_op); - device_info_static.Reset (); - if (!gst_wasapi2_result (hr)) - return; - - /* *INDENT-OFF* */ - hr = SyncWait<DeviceInformationCollection*>(async_op.Get ()); - /* *INDENT-ON* */ - if (!gst_wasapi2_result (hr)) - return; - - hr = async_op->GetResults (&device_list); - async_op.Reset (); - if (!gst_wasapi2_result (hr)) - return; - - hr = device_list->get_Size (&count); - if (!gst_wasapi2_result (hr)) - return; - - if (count == 0) { - GST_INFO_OBJECT (self, "No available device"); - return; - } - - /* device_index 0 will be assigned for default device - * so the number of available device is count + 1 (for default device) */ - if (self->device_index >= 0 && self->device_index > (gint) count) { - GST_INFO_OBJECT (self, "Device index %d is unavailable", - self->device_index); - return; - } - - GST_DEBUG_OBJECT (self, "Available device count: %d", count); - - if (gst_wasapi2_can_automatic_stream_routing ()) { - /* zero is for default device */ - device_index = 1; - } else { - device_index = 0; - } - - for (unsigned int i = 0; i < count; i++) { - /* *INDENT-OFF* */ - ComPtr<IDeviceInformation> device_info; - /* *INDENT-ON* */ - HString id; - HString name; - boolean b_value; - std::string cur_device_id; - std::string cur_device_name; - - hr = device_list->GetAt (i, &device_info); - if (!gst_wasapi2_result (hr)) - continue; - - hr = device_info->get_IsEnabled (&b_value); - if (!gst_wasapi2_result (hr)) - continue; - - /* select only enabled device */ - if (!b_value) { - GST_DEBUG_OBJECT (self, "Device index %d is disabled", i); - continue; - } - - /* To ensure device id and device name are available, - * will query this later again once target device is determined */ - hr = device_info->get_Id (id.GetAddressOf ()); - if (!gst_wasapi2_result (hr)) - continue; - - if (!id.IsValid ()) { - GST_WARNING_OBJECT (self, "Device index %d has invalid id", i); - continue; - } - - hr = device_info->get_Name (name.GetAddressOf ()); - if (!gst_wasapi2_result (hr)) - continue; - - if (!name.IsValid ()) { - GST_WARNING_OBJECT (self, "Device index %d has invalid name", i); - continue; - } - - cur_device_id = convert_hstring_to_string (&id); - if (cur_device_id.empty ()) { - GST_WARNING_OBJECT (self, "Device index %d has empty id", i); - continue; - } - - cur_device_name = convert_hstring_to_string (&name); - if (cur_device_name.empty ()) { - GST_WARNING_OBJECT (self, "Device index %d has empty device name", i); - continue; - } - - GST_DEBUG_OBJECT (self, "device %d id: %s, name: %s", - device_index, cur_device_id.c_str (), cur_device_name.c_str ()); - - if (self->device_index < 0 && !self->device_id) { - GST_INFO_OBJECT (self, "Select the first device, device id %s", - cur_device_id.c_str ()); - target_device_id_wstring = id.GetRawBuffer (nullptr); - target_device_id = cur_device_id; - target_device_name = cur_device_name; - break; - } - - if (self->device_id && - g_ascii_strcasecmp (self->device_id, cur_device_id.c_str ()) == 0) { - GST_INFO_OBJECT (self, - "Device index %d has matching device id %s", device_index, - cur_device_id.c_str ()); - target_device_id_wstring = id.GetRawBuffer (nullptr); - target_device_id = cur_device_id; - target_device_name = cur_device_name; - break; - } - - if (self->device_index >= 0 && self->device_index == device_index) { - GST_INFO_OBJECT (self, "Select device index %d, device id %s", - device_index, cur_device_id.c_str ()); - target_device_id_wstring = id.GetRawBuffer (nullptr); - target_device_id = cur_device_id; - target_device_name = cur_device_name; - break; - } - - /* count only available devices */ - device_index++; - } - - if (target_device_id_wstring.empty ()) { - GST_WARNING_OBJECT (self, "Couldn't find target device"); - return; - } - -activate: - /* fill device id and name */ - g_free (self->device_id); - self->device_id = g_strdup (target_device_id.c_str ()); - - g_free (self->device_name); - self->device_name = g_strdup (target_device_name.c_str ()); - - self->device_index = device_index; - /* default device supports automatic stream routing */ - self->can_auto_routing = use_default_device; - - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_INIT; - - if (process_loopback) { - hr = activator->ActivateDeviceAsync (target_device_id_wstring, - &activation_params); - } else { - hr = activator->ActivateDeviceAsync (target_device_id_wstring, nullptr); - } - - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (self, "Failed to activate device"); - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_FAILED; - return; - } - - /* activate the endpoint volume interface */ - if (endpoint_volume_activator) { - if (use_default_device) { - GST_INFO_OBJECT (self, - "Endpoint volume monitoring for the default device is not implemented."); - } else { - hr = endpoint_volume_activator->ActivateDeviceAsync - (target_device_id_wstring, nullptr); - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (self, "Failed to activate device"); - } - } - } - - g_mutex_lock (&self->lock); - if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_INIT) - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_WAIT; - g_mutex_unlock (&self->lock); -} - -static const gchar * -activate_state_to_string (GstWasapi2ClientActivateState state) -{ - switch (state) { - case GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND: - return "NOT-FOUND"; - case GST_WASAPI2_CLIENT_ACTIVATE_FAILED: - return "FAILED"; - case GST_WASAPI2_CLIENT_ACTIVATE_INIT: - return "INIT"; - case GST_WASAPI2_CLIENT_ACTIVATE_WAIT: - return "WAIT"; - case GST_WASAPI2_CLIENT_ACTIVATE_DONE: - return "DONE"; - } - - g_assert_not_reached (); - - return "Undefined"; -} - -static gpointer -gst_wasapi2_client_thread_func (GstWasapi2Client * self) -{ - RoInitializeWrapper initialize (RO_INIT_MULTITHREADED); - GSource *source; - HRESULT hr; - /* *INDENT-OFF* */ - ComPtr<GstWasapiDeviceActivator> client_activator; - ComPtr<GstWasapiDeviceActivator> endpoint_volume_activator; - - hr = MakeAndInitialize<GstWasapiDeviceActivator> (&client_activator, - self, self->dispatcher, GstWasapiDeviceActivator::WASAPI_IFACE_AUDIO_CLIENT); - /* *INDENT-ON* */ - - if (!gst_wasapi2_result (hr)) { - GST_ERROR_OBJECT (self, "Could not create activator object"); - self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND; - goto run_loop; - } - - /* Initialize audio endpoint volume activator */ - hr = MakeAndInitialize < GstWasapiDeviceActivator > - (&endpoint_volume_activator, self, self->dispatcher, - GstWasapiDeviceActivator::WASAPI_IFACE_AUDIO_ENDPOINT_VOLUME); - if (!gst_wasapi2_result (hr)) { - GST_WARNING_OBJECT (self, - "Could not create endpoint volume activator object"); - } - - gst_wasapi2_client_activate_async (self, client_activator.Get (), - endpoint_volume_activator.Get ()); - - if (!self->dispatcher) { - /* In case that dispatcher is unavailable, wait activation synchroniously */ - GST_DEBUG_OBJECT (self, "Wait device activation"); - gst_wasapi2_client_ensure_activation (self); - GST_DEBUG_OBJECT (self, "Device activation result %s", - activate_state_to_string (self->activate_state)); - } - -run_loop: - g_main_context_push_thread_default (self->context); - - source = g_idle_source_new (); - g_source_set_callback (source, - (GSourceFunc) gst_wasapi2_client_main_loop_running_cb, self, nullptr); - g_source_attach (source, self->context); - g_source_unref (source); - - 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); - - /* Wait for pending async op if any */ - if (self->dispatcher) - gst_wasapi2_client_ensure_activation (self); - - GST_WASAPI2_CLEAR_COM (self->audio_client); - - g_mutex_lock (&self->endpoint_volume_lock); - if (self->audio_endpoint_volume && self->endpoint_volume_callback) { - self->audio_endpoint_volume-> - UnregisterControlChangeNotify (self->endpoint_volume_callback); - } - GST_WASAPI2_CLEAR_COM (self->endpoint_volume_callback); - GST_WASAPI2_CLEAR_COM (self->audio_endpoint_volume); - g_mutex_unlock (&self->endpoint_volume_lock); - - /* Reset explicitly to ensure that it happens before - * RoInitializeWrapper dtor is called */ - client_activator.Reset (); - endpoint_volume_activator.Reset (); - - GST_DEBUG_OBJECT (self, "Exit thread function"); - - return nullptr; -} - -GstCaps * -gst_wasapi2_client_get_caps (GstWasapi2Client * client) -{ - WAVEFORMATEX *mix_format = nullptr; - static GstStaticCaps static_caps = GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS); - GstCaps *scaps; - HRESULT hr; - - g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), nullptr); - - if (client->supported_caps) - return gst_caps_ref (client->supported_caps); - - if (!client->audio_client) { - GST_WARNING_OBJECT (client, "IAudioClient3 wasn't configured"); - return nullptr; - } - - hr = client->audio_client->GetMixFormat (&mix_format); - if (!gst_wasapi2_result (hr)) { - if (gst_wasapi2_device_class_is_process_loopback (client->device_class)) { - mix_format = gst_wasapi2_get_default_mix_format (); - } else { - GST_WARNING_OBJECT (client, "Failed to get mix format"); - return nullptr; - } - } - - scaps = gst_static_caps_get (&static_caps); - gst_wasapi2_util_parse_waveformatex (mix_format, - scaps, &client->supported_caps, nullptr); - gst_caps_unref (scaps); - - CoTaskMemFree (mix_format); - - if (!client->supported_caps) { - GST_ERROR_OBJECT (client, "No caps from subclass"); - return nullptr; - } - - return gst_caps_ref (client->supported_caps); -} - -gboolean -gst_wasapi2_client_ensure_activation (GstWasapi2Client * client) -{ - g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE); - - /* should not happen */ - g_assert (client->activate_state != GST_WASAPI2_CLIENT_ACTIVATE_INIT); - - g_mutex_lock (&client->init_lock); - while (client->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_WAIT) - g_cond_wait (&client->init_cond, &client->init_lock); - g_mutex_unlock (&client->init_lock); - - return client->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_DONE; -} - -static HRESULT -find_dispatcher (ICoreDispatcher ** dispatcher) -{ - /* *INDENT-OFF* */ - HStringReference hstr_core_app = - HStringReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication); - ComPtr<ICoreApplication> core_app; - ComPtr<ICoreApplicationView> core_app_view; - ComPtr<ICoreWindow> core_window; - /* *INDENT-ON* */ - HRESULT hr; - - hr = GetActivationFactory (hstr_core_app.Get (), &core_app); - if (FAILED (hr)) - return hr; - - hr = core_app->GetCurrentView (&core_app_view); - if (FAILED (hr)) - return hr; - - hr = core_app_view->get_CoreWindow (&core_window); - if (FAILED (hr)) - return hr; - - return core_window->get_Dispatcher (dispatcher); -} - -GstWasapi2Client * -gst_wasapi2_client_new (GstWasapi2ClientDeviceClass device_class, - gint device_index, const gchar * device_id, guint32 target_pid, - gpointer dispatcher) -{ - GstWasapi2Client *self; - /* *INDENT-OFF* */ - ComPtr<ICoreDispatcher> core_dispatcher; - /* *INDENT-ON* */ - /* Multiple COM init is allowed */ - RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED); - - /* If application didn't pass ICoreDispatcher object, - * try to get dispatcher object for the current thread */ - if (!dispatcher) { - HRESULT hr; - - hr = find_dispatcher (&core_dispatcher); - if (SUCCEEDED (hr)) { - GST_DEBUG ("UI dispatcher is available"); - dispatcher = core_dispatcher.Get (); - } else { - GST_DEBUG ("UI dispatcher is unavailable"); - } - } else { - GST_DEBUG ("Use user passed UI dispatcher"); - } - - self = (GstWasapi2Client *) g_object_new (GST_TYPE_WASAPI2_CLIENT, - "device-class", device_class, "device-index", device_index, - "device", device_id, "loopback-target-pid", target_pid, - "dispatcher", dispatcher, nullptr); - - /* Reset explicitly to ensure that it happens before - * RoInitializeWrapper dtor is called */ - core_dispatcher.Reset (); - - if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_FAILED || - self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND) { - gst_object_unref (self); - return nullptr; - } - - gst_object_ref_sink (self); - - return self; -} - -GstWasapi2Result -gst_wasapi2_client_enumerate (GstWasapi2ClientDeviceClass device_class, - gint device_index, GstWasapi2Client ** client) -{ - GstWasapi2Client *self; - /* *INDENT-OFF* */ - ComPtr<ICoreDispatcher> core_dispatcher; - /* *INDENT-ON* */ - /* Multiple COM init is allowed */ - RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED); - - *client = nullptr; - - find_dispatcher (&core_dispatcher); - - self = (GstWasapi2Client *) g_object_new (GST_TYPE_WASAPI2_CLIENT, - "device-class", device_class, "device-index", device_index, - "dispatcher", core_dispatcher.Get (), nullptr); - - /* Reset explicitly to ensure that it happens before - * RoInitializeWrapper dtor is called */ - core_dispatcher.Reset (); - - if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_NOT_FOUND) { - gst_object_unref (self); - return GST_WASAPI2_DEVICE_NOT_FOUND; - } else if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_FAILED) { - gst_object_unref (self); - return GST_WASAPI2_ACTIVATION_FAILED; - } - - gst_object_ref_sink (self); - - *client = self; - return GST_WASAPI2_OK; -} - -IAudioClient * -gst_wasapi2_client_get_handle (GstWasapi2Client * client) -{ - g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), nullptr); - - return client->audio_client; -} - -gboolean -gst_wasapi2_client_is_endpoint_muted (GstWasapi2Client * client) -{ - g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE); - - return client->priv->is_endpoint_muted.load (std::memory_order_acquire); -}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2client.h
Deleted
@@ -1,99 +0,0 @@ -/* - * 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_WASAPI2_CLIENT_H__ -#define __GST_WASAPI2_CLIENT_H__ - -#include <gst/gst.h> -#include <gst/audio/audio.h> -#include "gstwasapi2util.h" - -G_BEGIN_DECLS - -typedef enum -{ - GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE = 0, - GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, - GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE, - GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE, - GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE, -} GstWasapi2ClientDeviceClass; - -typedef enum -{ - GST_WASAPI2_OK, - GST_WASAPI2_DEVICE_NOT_FOUND, - GST_WASAPI2_ACTIVATION_FAILED, -} GstWasapi2Result; - -static inline gboolean -gst_wasapi2_device_class_is_loopback (GstWasapi2ClientDeviceClass device_class) -{ - switch (device_class) { - case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE: - return TRUE; - default: - break; - } - - return FALSE; -} - -static inline gboolean -gst_wasapi2_device_class_is_process_loopback (GstWasapi2ClientDeviceClass device_class) -{ - switch (device_class) { - case GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE: - case GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE: - return TRUE; - default: - break; - } - - return FALSE; -} - -#define GST_TYPE_WASAPI2_CLIENT_DEVICE_CLASS (gst_wasapi2_client_device_class_get_type()) -GType gst_wasapi2_client_device_class_get_type (void); - -#define GST_TYPE_WASAPI2_CLIENT (gst_wasapi2_client_get_type()) -G_DECLARE_FINAL_TYPE (GstWasapi2Client, - gst_wasapi2_client, GST, WASAPI2_CLIENT, GstObject); - -GstWasapi2Client * gst_wasapi2_client_new (GstWasapi2ClientDeviceClass device_class, - gint device_index, - const gchar * device_id, - guint target_pid, - gpointer dispatcher); - -gboolean gst_wasapi2_client_ensure_activation (GstWasapi2Client * client); - -IAudioClient * gst_wasapi2_client_get_handle (GstWasapi2Client * client); - -gboolean gst_wasapi2_client_is_endpoint_muted (GstWasapi2Client * client); - -GstCaps * gst_wasapi2_client_get_caps (GstWasapi2Client * client); - -GstWasapi2Result gst_wasapi2_client_enumerate (GstWasapi2ClientDeviceClass device_class, - gint device_index, - GstWasapi2Client ** client); - -G_END_DECLS - -#endif /* __GST_WASAPI2_CLIENT_H__ */
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2device.c
Deleted
@@ -1,601 +0,0 @@ -/* GStreamer - * Copyright (C) 2018 Nirbheek Chauhan <nirbheek@centricular.com> - * 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 "gstwasapi2device.h" -#include "gstwasapi2client.h" -#include "gstwasapi2util.h" -#include <gst/winrt/gstwinrt.h> - -GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); -#define GST_CAT_DEFAULT gst_wasapi2_debug - -enum -{ - PROP_0, - PROP_DEVICE, -}; - -struct _GstWasapi2Device -{ - GstDevice parent; - - gchar *device_id; - const gchar *factory_name; - GstWasapi2ClientDeviceClass device_class; -}; - -G_DEFINE_TYPE (GstWasapi2Device, gst_wasapi2_device, GST_TYPE_DEVICE); - -static void gst_wasapi2_device_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_wasapi2_device_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_wasapi2_device_finalize (GObject * object); -static GstElement *gst_wasapi2_device_create_element (GstDevice * device, - const gchar * name); - -static void -gst_wasapi2_device_class_init (GstWasapi2DeviceClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); - - dev_class->create_element = gst_wasapi2_device_create_element; - - gobject_class->get_property = gst_wasapi2_device_get_property; - gobject_class->set_property = gst_wasapi2_device_set_property; - gobject_class->finalize = gst_wasapi2_device_finalize; - - g_object_class_install_property (gobject_class, PROP_DEVICE, - g_param_spec_string ("device", "Device", - "Audio device ID as provided by " - "Windows.Devices.Enumeration.DeviceInformation.Id", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_wasapi2_device_init (GstWasapi2Device * self) -{ -} - -static void -gst_wasapi2_device_finalize (GObject * object) -{ - GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); - - g_free (self->device_id); - - G_OBJECT_CLASS (gst_wasapi2_device_parent_class)->finalize (object); -} - -static GstElement * -gst_wasapi2_device_create_element (GstDevice * device, const gchar * name) -{ - GstWasapi2Device *self = GST_WASAPI2_DEVICE (device); - GstElement *elem; - - elem = gst_element_factory_make (self->factory_name, name); - - g_object_set (elem, "device", self->device_id, NULL); - - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE) - g_object_set (elem, "loopback", TRUE, NULL); - - return elem; -} - -static void -gst_wasapi2_device_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string (value, self->device_id); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_wasapi2_device_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); - - switch (prop_id) { - case PROP_DEVICE: - /* G_PARAM_CONSTRUCT_ONLY */ - self->device_id = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -typedef struct _GstWasapi2DeviceProvider -{ - GstDeviceProvider parent; - - GstWinRTDeviceWatcher *watcher; - - GMutex lock; - GCond cond; - - gboolean enum_completed; -} GstWasapi2DeviceProvider; - -typedef struct _GstWasapi2DeviceProviderClass -{ - GstDeviceProviderClass parent_class; - - GstWinRTDeviceClass winrt_device_class; -} GstWasapi2DeviceProviderClass; - -static GstDeviceProviderClass *parent_class = NULL; - -#define GST_WASAPI2_DEVICE_PROVIDER(object) \ - ((GstWasapi2DeviceProvider *) (object)) -#define GST_WASAPI2_DEVICE_PROVIDER_GET_CLASS(object) \ - (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object),GstWasapi2DeviceProviderClass)) - -static void gst_wasapi2_device_provider_dispose (GObject * object); -static void gst_wasapi2_device_provider_finalize (GObject * object); - -static GList *gst_wasapi2_device_provider_probe (GstDeviceProvider * provider); -static gboolean -gst_wasapi2_device_provider_start (GstDeviceProvider * provider); -static void gst_wasapi2_device_provider_stop (GstDeviceProvider * provider); - -static void -gst_wasapi2_device_provider_device_added (GstWinRTDeviceWatcher * watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformation * info, - gpointer user_data); -static void -gst_wasapi2_device_provider_device_updated (GstWinRTDeviceWatcher * watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * - info_update, gpointer user_data); -static void gst_wasapi2_device_provider_device_removed (GstWinRTDeviceWatcher * - watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * - info_update, gpointer user_data); -static void -gst_wasapi2_device_provider_device_enum_completed (GstWinRTDeviceWatcher * - watcher, gpointer user_data); - -static void -gst_wasapi2_device_provider_class_init (GstWasapi2DeviceProviderClass * klass, - gpointer data) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS (klass); - - gobject_class->dispose = gst_wasapi2_device_provider_dispose; - gobject_class->finalize = gst_wasapi2_device_provider_finalize; - - provider_class->probe = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_probe); - provider_class->start = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_start); - provider_class->stop = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_stop); - - parent_class = (GstDeviceProviderClass *) g_type_class_peek_parent (klass); - - klass->winrt_device_class = (GstWinRTDeviceClass) GPOINTER_TO_INT (data); - if (klass->winrt_device_class == GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE) { - gst_device_provider_class_set_static_metadata (provider_class, - "WASAPI (Windows Audio Session API) Capture Device Provider", - "Source/Audio", "List WASAPI source devices", - "Nirbheek Chauhan <nirbheek@centricular.com>, " - "Seungha Yang <seungha@centricular.com>"); - } else { - gst_device_provider_class_set_static_metadata (provider_class, - "WASAPI (Windows Audio Session API) Render and Loopback Capture Device Provider", - "Source/Sink/Audio", "List WASAPI loop back source and sink devices", - "Nirbheek Chauhan <nirbheek@centricular.com>, " - "Seungha Yang <seungha@centricular.com>"); - } -} - -static void -gst_wasapi2_device_provider_init (GstWasapi2DeviceProvider * self) -{ - GstWasapi2DeviceProviderClass *klass = - GST_WASAPI2_DEVICE_PROVIDER_GET_CLASS (self); - GstWinRTDeviceWatcherCallbacks callbacks; - - g_mutex_init (&self->lock); - g_cond_init (&self->cond); - - callbacks.added = gst_wasapi2_device_provider_device_added; - callbacks.updated = gst_wasapi2_device_provider_device_updated; - callbacks.removed = gst_wasapi2_device_provider_device_removed; - callbacks.enumeration_completed = - gst_wasapi2_device_provider_device_enum_completed; - - self->watcher = - gst_winrt_device_watcher_new (klass->winrt_device_class, - &callbacks, self); -} - -static void -gst_wasapi2_device_provider_dispose (GObject * object) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (object); - - gst_clear_object (&self->watcher); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_wasapi2_device_provider_finalize (GObject * object) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (object); - - g_mutex_clear (&self->lock); - g_cond_clear (&self->cond); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_wasapi2_device_provider_probe_internal (GstWasapi2DeviceProvider * self, - GstWasapi2ClientDeviceClass client_class, GList ** devices) -{ - gint i; - const gchar *device_class, *factory_name; - - if (client_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) { - device_class = "Audio/Sink"; - factory_name = "wasapi2sink"; - } else { - device_class = "Audio/Source"; - factory_name = "wasapi2src"; - } - - for (i = 0;; i++) { - GstWasapi2Client *client = NULL; - GstDevice *device; - GstStructure *props = NULL; - GstCaps *caps = NULL; - gchar *device_id = NULL; - gchar *device_name = NULL; - GstWasapi2Result result; - - result = gst_wasapi2_client_enumerate (client_class, i, &client); - if (result == GST_WASAPI2_DEVICE_NOT_FOUND) - return; - else if (result == GST_WASAPI2_ACTIVATION_FAILED) - continue; - - g_assert (client); - - caps = gst_wasapi2_client_get_caps (client); - if (!caps) { - GST_WARNING_OBJECT (self, "Couldn't get caps from client %d", i); - /* this might be a case where device activation is not finished yet */ - caps = gst_caps_from_string (GST_WASAPI2_STATIC_CAPS); - } - - g_object_get (client, - "device", &device_id, "device-name", &device_name, NULL); - if (!device_id) { - GST_WARNING_OBJECT (self, "Couldn't get device name from client %d", i); - goto next; - } - - if (!device_name) { - GST_WARNING_OBJECT (self, "Couldn't get device name from client %d", i); - goto next; - } - - props = gst_structure_new ("wasapi2-proplist", - "device.api", G_TYPE_STRING, "wasapi2", - "device.id", G_TYPE_STRING, device_id, - "device.default", G_TYPE_BOOLEAN, i == 0, - "wasapi2.device.description", G_TYPE_STRING, device_name, NULL); - switch (client_class) { - case GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE: - gst_structure_set (props, - "wasapi2.device.loopback", G_TYPE_BOOLEAN, FALSE, NULL); - break; - case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE: - gst_structure_set (props, - "wasapi2.device.loopback", G_TYPE_BOOLEAN, TRUE, NULL); - break; - default: - break; - } - - device = g_object_new (GST_TYPE_WASAPI2_DEVICE, "device", device_id, - "display-name", device_name, "caps", caps, - "device-class", device_class, "properties", props, NULL); - GST_WASAPI2_DEVICE (device)->factory_name = factory_name; - GST_WASAPI2_DEVICE (device)->device_class = client_class; - - *devices = g_list_append (*devices, device); - - next: - gst_clear_object (&client); - gst_clear_caps (&caps); - g_free (device_id); - g_free (device_name); - if (props) - gst_structure_free (props); - } - - return; -} - -static GList * -gst_wasapi2_device_provider_probe (GstDeviceProvider * provider) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (provider); - GstWasapi2DeviceProviderClass *klass = - GST_WASAPI2_DEVICE_PROVIDER_GET_CLASS (self); - GList *devices = NULL; - - if (klass->winrt_device_class == GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE) { - gst_wasapi2_device_provider_probe_internal (self, - GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE, &devices); - } else { - gst_wasapi2_device_provider_probe_internal (self, - GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE, &devices); - gst_wasapi2_device_provider_probe_internal (self, - GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, &devices); - } - - return devices; -} - -static gboolean -gst_wasapi2_device_provider_start (GstDeviceProvider * provider) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (provider); - GList *devices = NULL; - GList *iter; - - if (!self->watcher) { - GST_ERROR_OBJECT (self, "DeviceWatcher object wasn't configured"); - return FALSE; - } - - self->enum_completed = FALSE; - - if (!gst_winrt_device_watcher_start (self->watcher)) - return FALSE; - - /* Wait for initial enumeration to be completed */ - g_mutex_lock (&self->lock); - while (!self->enum_completed) - g_cond_wait (&self->cond, &self->lock); - - devices = gst_wasapi2_device_provider_probe (provider); - if (devices) { - for (iter = devices; iter; iter = g_list_next (iter)) { - gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); - } - - g_list_free (devices); - } - g_mutex_unlock (&self->lock); - - return TRUE; -} - -static void -gst_wasapi2_device_provider_stop (GstDeviceProvider * provider) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (provider); - - if (self->watcher) - gst_winrt_device_watcher_stop (self->watcher); -} - -static gboolean -gst_wasapi2_device_is_in_list (GList * list, GstDevice * device) -{ - GList *iter; - GstStructure *s; - const gchar *device_id; - gboolean found = FALSE; - - s = gst_device_get_properties (device); - g_assert (s); - - device_id = gst_structure_get_string (s, "device.id"); - g_assert (device_id); - - for (iter = list; iter; iter = g_list_next (iter)) { - GstStructure *other_s; - const gchar *other_id; - - other_s = gst_device_get_properties (GST_DEVICE (iter->data)); - g_assert (other_s); - - other_id = gst_structure_get_string (other_s, "device.id"); - g_assert (other_id); - - if (g_ascii_strcasecmp (device_id, other_id) == 0) { - found = TRUE; - } - - gst_structure_free (other_s); - if (found) - break; - } - - gst_structure_free (s); - - return found; -} - -static void -gst_wasapi2_device_provider_update_devices (GstWasapi2DeviceProvider * self) -{ - GstDeviceProvider *provider = GST_DEVICE_PROVIDER_CAST (self); - GList *prev_devices = NULL; - GList *new_devices = NULL; - GList *to_add = NULL; - GList *to_remove = NULL; - GList *iter; - - GST_OBJECT_LOCK (self); - prev_devices = g_list_copy_deep (provider->devices, - (GCopyFunc) gst_object_ref, NULL); - GST_OBJECT_UNLOCK (self); - - new_devices = gst_wasapi2_device_provider_probe (provider); - - /* Ownership of GstDevice for gst_device_provider_device_add() - * and gst_device_provider_device_remove() is a bit complicated. - * Remove floating reference here for things to be clear */ - for (iter = new_devices; iter; iter = g_list_next (iter)) - gst_object_ref_sink (iter->data); - - /* Check newly added devices */ - for (iter = new_devices; iter; iter = g_list_next (iter)) { - if (!gst_wasapi2_device_is_in_list (prev_devices, GST_DEVICE (iter->data))) { - to_add = g_list_prepend (to_add, gst_object_ref (iter->data)); - } - } - - /* Check removed device */ - for (iter = prev_devices; iter; iter = g_list_next (iter)) { - if (!gst_wasapi2_device_is_in_list (new_devices, GST_DEVICE (iter->data))) { - to_remove = g_list_prepend (to_remove, gst_object_ref (iter->data)); - } - } - - for (iter = to_remove; iter; iter = g_list_next (iter)) - gst_device_provider_device_remove (provider, GST_DEVICE (iter->data)); - - for (iter = to_add; iter; iter = g_list_next (iter)) - gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); - - if (prev_devices) - g_list_free_full (prev_devices, (GDestroyNotify) gst_object_unref); - - if (to_add) - g_list_free_full (to_add, (GDestroyNotify) gst_object_unref); - - if (to_remove) - g_list_free_full (to_remove, (GDestroyNotify) gst_object_unref); -} - -static void -gst_wasapi2_device_provider_device_added (GstWinRTDeviceWatcher * watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformation * info, - gpointer user_data) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (user_data); - - if (self->enum_completed) - gst_wasapi2_device_provider_update_devices (self); -} - -static void -gst_wasapi2_device_provider_device_removed (GstWinRTDeviceWatcher * watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * - info_update, gpointer user_data) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (user_data); - - if (self->enum_completed) - gst_wasapi2_device_provider_update_devices (self); -} - -static void -gst_wasapi2_device_provider_device_updated (GstWinRTDeviceWatcher * watcher, - __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * info, - gpointer user_data) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (user_data); - - gst_wasapi2_device_provider_update_devices (self); -} - -static void -gst_wasapi2_device_provider_device_enum_completed (GstWinRTDeviceWatcher * - watcher, gpointer user_data) -{ - GstWasapi2DeviceProvider *self = GST_WASAPI2_DEVICE_PROVIDER (user_data); - - g_mutex_lock (&self->lock); - GST_DEBUG_OBJECT (self, "Enumeration completed"); - self->enum_completed = TRUE; - g_cond_signal (&self->cond); - g_mutex_unlock (&self->lock); -} - -static void -gst_wasapi2_device_provider_register_internal (GstPlugin * plugin, - guint rank, GstWinRTDeviceClass winrt_device_class) -{ - GType type; - const gchar *type_name = NULL; - const gchar *feature_name = NULL; - GTypeInfo type_info = { - sizeof (GstWasapi2DeviceProviderClass), - NULL, - NULL, - (GClassInitFunc) gst_wasapi2_device_provider_class_init, - NULL, - NULL, - sizeof (GstWasapi2DeviceProvider), - 0, - (GInstanceInitFunc) gst_wasapi2_device_provider_init, - }; - - type_info.class_data = GINT_TO_POINTER (winrt_device_class); - - if (winrt_device_class == GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE) { - type_name = "GstWasapi2CaputreDeviceProvider"; - feature_name = "wasapi2capturedeviceprovider"; - } else if (winrt_device_class == GST_WINRT_DEVICE_CLASS_AUDIO_RENDER) { - type_name = "GstWasapi2RenderDeviceProvider"; - feature_name = "wasapi2renderdeviceprovider"; - } else { - g_assert_not_reached (); - return; - } - - type = g_type_register_static (GST_TYPE_DEVICE_PROVIDER, - type_name, &type_info, (GTypeFlags) 0); - - if (!gst_device_provider_register (plugin, feature_name, rank, type)) - GST_WARNING ("Failed to register provider '%s'", type_name); -} - -void -gst_wasapi2_device_provider_register (GstPlugin * plugin, guint rank) -{ - gst_wasapi2_device_provider_register_internal (plugin, rank, - GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE); - gst_wasapi2_device_provider_register_internal (plugin, rank, - GST_WINRT_DEVICE_CLASS_AUDIO_RENDER); -}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2util.c
Deleted
@@ -1,564 +0,0 @@ -/* - * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> - * Copyright (C) 2018 Centricular Ltd. - * Author: Nirbheek Chauhan <nirbheek@centricular.com> - * 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 "gstwasapi2util.h" -#include <audioclient.h> -#include <mmdeviceapi.h> -#include <winternl.h> - -GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); -#define GST_CAT_DEFAULT gst_wasapi2_debug - -/* Desktop only defines */ -#ifndef KSAUDIO_SPEAKER_MONO -#define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER) -#endif -#ifndef KSAUDIO_SPEAKER_1POINT1 -#define KSAUDIO_SPEAKER_1POINT1 (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) -#endif -#ifndef KSAUDIO_SPEAKER_STEREO -#define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) -#endif -#ifndef KSAUDIO_SPEAKER_2POINT1 -#define KSAUDIO_SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY) -#endif -#ifndef KSAUDIO_SPEAKER_3POINT0 -#define KSAUDIO_SPEAKER_3POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER) -#endif -#ifndef KSAUDIO_SPEAKER_3POINT1 -#define KSAUDIO_SPEAKER_3POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) -#endif -#ifndef KSAUDIO_SPEAKER_QUAD -#define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) -#endif -#define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) -#ifndef KSAUDIO_SPEAKER_5POINT0 -#define KSAUDIO_SPEAKER_5POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ - SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) -#endif -#define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) -#ifndef KSAUDIO_SPEAKER_7POINT0 -#define KSAUDIO_SPEAKER_7POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ - SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) -#endif -#ifndef KSAUDIO_SPEAKER_7POINT1 -#define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ - SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) -#endif - -/* *INDENT-OFF* */ -static struct -{ - guint64 wasapi_pos; - GstAudioChannelPosition gst_pos; -} wasapi_to_gst_pos = { - {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, - {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, - {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, - {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1}, - {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, - {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, - {SPEAKER_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, - {SPEAKER_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, - {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, - /* Enum values diverge from this point onwards */ - {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, - {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, - {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, - {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, - {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, - {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, - {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, - {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, - {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT} -}; - -static DWORD default_ch_masks = { - 0, - KSAUDIO_SPEAKER_MONO, - /* 2ch */ - KSAUDIO_SPEAKER_STEREO, - /* 2.1ch */ - /* KSAUDIO_SPEAKER_3POINT0 ? */ - KSAUDIO_SPEAKER_2POINT1, - /* 4ch */ - /* KSAUDIO_SPEAKER_3POINT1 or KSAUDIO_SPEAKER_SURROUND ? */ - KSAUDIO_SPEAKER_QUAD, - /* 5ch */ - KSAUDIO_SPEAKER_5POINT0, - /* 5.1ch */ - KSAUDIO_SPEAKER_5POINT1, - /* 7ch */ - KSAUDIO_SPEAKER_7POINT0, - /* 7.1ch */ - KSAUDIO_SPEAKER_7POINT1, -}; -/* *INDENT-ON* */ - -static const gchar * -hresult_to_string_fallback (HRESULT hr) -{ - const gchar *s = "unknown error"; - - switch (hr) { - case AUDCLNT_E_NOT_INITIALIZED: - s = "AUDCLNT_E_NOT_INITIALIZED"; - break; - case AUDCLNT_E_ALREADY_INITIALIZED: - s = "AUDCLNT_E_ALREADY_INITIALIZED"; - break; - case AUDCLNT_E_WRONG_ENDPOINT_TYPE: - s = "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; - break; - case AUDCLNT_E_DEVICE_INVALIDATED: - s = "AUDCLNT_E_DEVICE_INVALIDATED"; - break; - case AUDCLNT_E_NOT_STOPPED: - s = "AUDCLNT_E_NOT_STOPPED"; - break; - case AUDCLNT_E_BUFFER_TOO_LARGE: - s = "AUDCLNT_E_BUFFER_TOO_LARGE"; - break; - case AUDCLNT_E_OUT_OF_ORDER: - s = "AUDCLNT_E_OUT_OF_ORDER"; - break; - case AUDCLNT_E_UNSUPPORTED_FORMAT: - s = "AUDCLNT_E_UNSUPPORTED_FORMAT"; - break; - case AUDCLNT_E_INVALID_DEVICE_PERIOD: - s = "AUDCLNT_E_INVALID_DEVICE_PERIOD"; - break; - case AUDCLNT_E_INVALID_SIZE: - s = "AUDCLNT_E_INVALID_SIZE"; - break; - case AUDCLNT_E_DEVICE_IN_USE: - s = "AUDCLNT_E_DEVICE_IN_USE"; - break; - case AUDCLNT_E_BUFFER_OPERATION_PENDING: - s = "AUDCLNT_E_BUFFER_OPERATION_PENDING"; - break; - case AUDCLNT_E_BUFFER_SIZE_ERROR: - s = "AUDCLNT_E_BUFFER_SIZE_ERROR"; - break; - case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: - s = "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; - break; - case AUDCLNT_E_THREAD_NOT_REGISTERED: - s = "AUDCLNT_E_THREAD_NOT_REGISTERED"; - break; - case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: - s = "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; - break; - case AUDCLNT_E_ENDPOINT_CREATE_FAILED: - s = "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; - break; - case AUDCLNT_E_SERVICE_NOT_RUNNING: - s = "AUDCLNT_E_SERVICE_NOT_RUNNING"; - break; - case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: - s = "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; - break; - case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: - s = "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; - break; - case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: - s = "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; - break; - case AUDCLNT_E_EVENTHANDLE_NOT_SET: - s = "AUDCLNT_E_EVENTHANDLE_NOT_SET"; - break; - case AUDCLNT_E_INCORRECT_BUFFER_SIZE: - s = "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; - break; - case AUDCLNT_E_CPUUSAGE_EXCEEDED: - s = "AUDCLNT_E_CPUUSAGE_EXCEEDED"; - break; - case AUDCLNT_S_BUFFER_EMPTY: - s = "AUDCLNT_S_BUFFER_EMPTY"; - break; - case AUDCLNT_S_THREAD_ALREADY_REGISTERED: - s = "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; - break; - case AUDCLNT_S_POSITION_STALLED: - s = "AUDCLNT_S_POSITION_STALLED"; - break; - case E_POINTER: - s = "E_POINTER"; - break; - case E_INVALIDARG: - s = "E_INVALIDARG"; - break; - } - - return s; -} - -gchar * -gst_wasapi2_util_get_error_message (HRESULT hr) -{ - gchar *error_text = NULL; - - error_text = g_win32_error_message ((gint) hr); - if (!error_text || strlen (error_text) == 0) { - g_free (error_text); - error_text = g_strdup (hresult_to_string_fallback (hr)); - } - - return error_text; -} - -gboolean -_gst_wasapi2_result (HRESULT hr, GstDebugCategory * cat, const gchar * file, - const gchar * function, gint line) -{ -#ifndef GST_DISABLE_GST_DEBUG - gboolean ret = TRUE; - - if (FAILED (hr)) { - gchar *error_text = NULL; - gboolean free_string = TRUE; - - error_text = g_win32_error_message ((gint) hr); - /* g_win32_error_message() seems to be returning empty string for - * AUDCLNT_* cases */ - if (!error_text || strlen (error_text) == 0) { - g_free (error_text); - error_text = (gchar *) hresult_to_string_fallback (hr); - - free_string = FALSE; - } - - gst_debug_log (cat, GST_LEVEL_WARNING, file, function, line, - NULL, "WASAPI call failed: 0x%x, %s", (guint) hr, error_text); - - if (free_string) - g_free (error_text); - - ret = FALSE; - } - - return ret; -#else - return SUCCEEDED (hr); -#endif -} - -static void -gst_wasapi_util_channel_position_all_none (guint channels, - GstAudioChannelPosition * position) -{ - guint i; - - for (i = 0; i < channels; i++) - positioni = GST_AUDIO_CHANNEL_POSITION_NONE; -} - -guint64 -gst_wasapi2_util_waveformatex_to_channel_mask (WAVEFORMATEX * format, - GstAudioChannelPosition ** out_position) -{ - guint i, ch; - guint64 mask = 0; - GstAudioChannelPosition *pos = NULL; - WORD nChannels = 0; - DWORD dwChannelMask = 0; - - nChannels = format->nChannels; - if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE *extensible = (WAVEFORMATEXTENSIBLE *) format; - dwChannelMask = extensible->dwChannelMask; - } - - if (out_position) - *out_position = NULL; - - if (nChannels > 2 && !dwChannelMask) { - GST_WARNING ("Unknown channel mask value for %d channel stream", nChannels); - - if (nChannels >= G_N_ELEMENTS (default_ch_masks)) { - GST_ERROR ("To may channels %d", nChannels); - return 0; - } - - dwChannelMask = default_ch_masksnChannels; - } - - pos = g_new (GstAudioChannelPosition, nChannels); - gst_wasapi_util_channel_position_all_none (nChannels, pos); - - /* Too many channels, have to assume that they are all non-positional */ - if (nChannels > G_N_ELEMENTS (wasapi_to_gst_pos)) { - GST_INFO ("Got too many (%i) channels, assuming non-positional", nChannels); - goto out; - } - - /* Too many bits in the channel mask, and the bits don't match nChannels */ - if (dwChannelMask >> (G_N_ELEMENTS (wasapi_to_gst_pos) + 1) != 0) { - GST_WARNING ("Too many bits in channel mask (%lu), assuming " - "non-positional", dwChannelMask); - goto out; - } - - /* Map WASAPI's channel mask to Gstreamer's channel mask and positions. - * If the no. of bits in the mask > nChannels, we will ignore the extra. */ - for (i = 0, ch = 0; i < G_N_ELEMENTS (wasapi_to_gst_pos) && ch < nChannels; - i++) { - if (!(dwChannelMask & wasapi_to_gst_posi.wasapi_pos)) - /* no match, try next */ - continue; - mask |= G_GUINT64_CONSTANT (1) << wasapi_to_gst_posi.gst_pos; - posch++ = wasapi_to_gst_posi.gst_pos; - } - - /* XXX: Warn if some channel masks couldn't be mapped? */ - - GST_DEBUG ("Converted WASAPI mask 0x%" G_GINT64_MODIFIER "x -> 0x%" - G_GINT64_MODIFIER "x", (guint64) dwChannelMask, (guint64) mask); - -out: - if (out_position) { - *out_position = pos; - } else { - g_free (pos); - } - - return mask; -} - -const gchar * -gst_wasapi2_util_waveformatex_to_audio_format (WAVEFORMATEX * format) -{ - const gchar *fmt_str = NULL; - GstAudioFormat fmt = GST_AUDIO_FORMAT_UNKNOWN; - - switch (format->wFormatTag) { - case WAVE_FORMAT_PCM: - fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, - format->wBitsPerSample, format->wBitsPerSample); - break; - case WAVE_FORMAT_IEEE_FLOAT: - if (format->wBitsPerSample == 32) - fmt = GST_AUDIO_FORMAT_F32LE; - else if (format->wBitsPerSample == 64) - fmt = GST_AUDIO_FORMAT_F64LE; - break; - case WAVE_FORMAT_EXTENSIBLE: - { - WAVEFORMATEXTENSIBLE *ex = (WAVEFORMATEXTENSIBLE *) format; - if (IsEqualGUID (&ex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { - fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, - format->wBitsPerSample, ex->Samples.wValidBitsPerSample); - } else if (IsEqualGUID (&ex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (format->wBitsPerSample == 32 - && ex->Samples.wValidBitsPerSample == 32) - fmt = GST_AUDIO_FORMAT_F32LE; - else if (format->wBitsPerSample == 64 && - ex->Samples.wValidBitsPerSample == 64) - fmt = GST_AUDIO_FORMAT_F64LE; - } - break; - } - default: - break; - } - - if (fmt != GST_AUDIO_FORMAT_UNKNOWN) - fmt_str = gst_audio_format_to_string (fmt); - - return fmt_str; -} - -gboolean -gst_wasapi2_util_parse_waveformatex (WAVEFORMATEX * format, - GstCaps * template_caps, GstCaps ** out_caps, - GstAudioChannelPosition ** out_positions) -{ - const gchar *afmt; - guint64 channel_mask; - - *out_caps = NULL; - - /* TODO: handle SPDIF and other encoded formats */ - - /* 1 or 2 channels <= 16 bits sample size OR - * 1 or 2 channels > 16 bits sample size or >2 channels */ - if (format->wFormatTag != WAVE_FORMAT_PCM && - format->wFormatTag != WAVE_FORMAT_IEEE_FLOAT && - format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) - /* Unhandled format tag */ - return FALSE; - - /* WASAPI can only tell us one canonical mix format that it will accept. The - * alternative is calling IsFormatSupported on all combinations of formats. - * Instead, it's simpler and faster to require conversion inside gstreamer */ - afmt = gst_wasapi2_util_waveformatex_to_audio_format (format); - if (afmt == NULL) - return FALSE; - - *out_caps = gst_caps_copy (template_caps); - - channel_mask = gst_wasapi2_util_waveformatex_to_channel_mask (format, - out_positions); - - gst_caps_set_simple (*out_caps, - "format", G_TYPE_STRING, afmt, - "channels", G_TYPE_INT, format->nChannels, - "rate", G_TYPE_INT, format->nSamplesPerSec, NULL); - - if (channel_mask) { - gst_caps_set_simple (*out_caps, - "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); - } - - return TRUE; -} - -gboolean -gst_wasapi2_can_automatic_stream_routing (void) -{ -#ifdef GST_WASAPI2_WINAPI_ONLY_APP - /* Assume we are on very recent OS */ - return TRUE; -#else - static gboolean ret = FALSE; - static gsize version_once = 0; - - if (g_once_init_enter (&version_once)) { - OSVERSIONINFOEXW osverinfo; - typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW); - fRtlGetVersion *RtlGetVersion = NULL; - HMODULE hmodule = NULL; - - memset (&osverinfo, 0, sizeof (OSVERSIONINFOEXW)); - osverinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW); - - hmodule = LoadLibraryW (L"ntdll.dll"); - if (hmodule) - RtlGetVersion = - (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion"); - - if (RtlGetVersion) { - RtlGetVersion (&osverinfo); - - /* automatic stream routing requires Windows 10 - * Anniversary Update (version 1607, build number 14393.0) */ - if (osverinfo.dwMajorVersion > 10 || - (osverinfo.dwMajorVersion == 10 && osverinfo.dwBuildNumber >= 14393)) - ret = TRUE; - } - - if (hmodule) - FreeLibrary (hmodule); - - g_once_init_leave (&version_once, 1); - } - - GST_INFO ("Automatic stream routing support: %d", ret); - - return ret; -#endif -} - -gboolean -gst_wasapi2_can_process_loopback (void) -{ -#ifdef GST_WASAPI2_WINAPI_ONLY_APP - /* FIXME: Needs WinRT (Windows.System.Profile) API call - * for OS version check */ - return FALSE; -#else - static gboolean ret = FALSE; - static gsize version_once = 0; - - if (g_once_init_enter (&version_once)) { - OSVERSIONINFOEXW osverinfo; - typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW); - fRtlGetVersion *RtlGetVersion = NULL; - HMODULE hmodule = NULL; - - memset (&osverinfo, 0, sizeof (OSVERSIONINFOEXW)); - osverinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW); - - hmodule = LoadLibraryW (L"ntdll.dll"); - if (hmodule) - RtlGetVersion = - (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion"); - - if (RtlGetVersion) { - RtlGetVersion (&osverinfo); - - /* Process loopback requires Windows 10 build 20348 - * https://learn.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params - * - * Note: "Windows 10 build 20348" would mean "Windows server 2022" or - * "Windows 11", since build number of "Windows 10 version 21H2" is - * still 19044.XXX - */ - - /* But other software enables this for build number 19041 or higher... */ - if (osverinfo.dwMajorVersion > 10 || - (osverinfo.dwMajorVersion == 10 && osverinfo.dwBuildNumber >= 19041)) - ret = TRUE; - } - - if (hmodule) - FreeLibrary (hmodule); - - g_once_init_leave (&version_once, 1); - } - - GST_INFO ("Process loopback support: %d", ret); - - return ret; -#endif -} - -WAVEFORMATEX * -gst_wasapi2_get_default_mix_format (void) -{ - WAVEFORMATEX *format; - - /* virtual loopback device might not provide mix format. Create our default - * mix format */ - format = CoTaskMemAlloc (sizeof (WAVEFORMATEX)); - format->wFormatTag = WAVE_FORMAT_PCM; - format->nChannels = 2; - format->nSamplesPerSec = 44100; - format->wBitsPerSample = 16; - format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8; - format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; - - return format; -}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/plugin.c
Deleted
@@ -1,79 +0,0 @@ -/* 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 <winapifamily.h> - -#include "gstwasapi2sink.h" -#include "gstwasapi2src.h" -#include "gstwasapi2device.h" -#include "gstwasapi2util.h" -#include <mfapi.h> - -GST_DEBUG_CATEGORY (gst_wasapi2_debug); -GST_DEBUG_CATEGORY (gst_wasapi2_client_debug); - -static void -plugin_deinit (gpointer data) -{ - MFShutdown (); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GstRank rank = GST_RANK_PRIMARY + 1; - HRESULT hr; - - /** - * plugin-wasapi2: - * - * Since: 1.18 - */ - - hr = MFStartup (MF_VERSION, MFSTARTUP_NOSOCKET); - if (!gst_wasapi2_result (hr)) { - GST_WARNING ("MFStartup failure, hr: 0x%x", hr); - return TRUE; - } - - GST_DEBUG_CATEGORY_INIT (gst_wasapi2_debug, "wasapi2", 0, "wasapi2"); - GST_DEBUG_CATEGORY_INIT (gst_wasapi2_client_debug, "wasapi2client", - 0, "wasapi2client"); - - gst_element_register (plugin, "wasapi2sink", rank, GST_TYPE_WASAPI2_SINK); - gst_element_register (plugin, "wasapi2src", rank, GST_TYPE_WASAPI2_SRC); - - gst_wasapi2_device_provider_register (plugin, rank); - - g_object_set_data_full (G_OBJECT (plugin), - "plugin-wasapi2-shutdown", "shutdown-data", - (GDestroyNotify) plugin_deinit); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - wasapi2, - "Windows audio session API plugin", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/ChangeLog -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/ChangeLog
Changed
@@ -1,3 +1,238 @@ +=== release 1.26.5 === + +2025-08-07 19:06:46 +0100 Tim-Philipp Müller <tim@centricular.com> + + * NEWS: + * RELEASE: + * gst-plugins-bad.doap: + * meson.build: + Release 1.26.5 + +2025-08-07 13:58:27 +0300 Sebastian Dröge <sebastian@centricular.com> + + * gst-libs/gst/codecparsers/gstav1parser.c: + av1parser: Don't error out on "currently" undefined seq-level indices + They might very well be defined in the future and that shouldn't affect the + parsing in any way. Specifically, ffmpeg with `av1_nvenc` seems to create + `GST_AV1_SEQ_LEVEL_7_3` currently and parsing such streams would fail otherwise. + Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4589 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9504> + +2025-08-01 14:55:12 +0200 Víctor Manuel Jáquez Leal <vjaquez@igalia.com> + + * gst-libs/gst/vulkan/gstvkphysicaldevice-private.h: + * gst-libs/gst/vulkan/gstvkphysicaldevice.c: + vkphysicaldevice: enable sampler ycbcr, sync2 and timeline semaphore features + For the features VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR, + VkPhysicalDeviceSynchronization2Features and + VkPhisicalDeviceTimelineSemaphoreFeatures + The Vulkan specification states: + If the `VkPhysicalDeviceXXXFeatures` structure is included in the `pNext` + chain of the `VkPhysicalDeviceFeatures2` structure passed to + `vkGetPhysicalDeviceFeatures2`, it is filled in to indicate whether each + corresponding feature is supported. If the application wishes to use a + `VkDevice` with any features described by `VkPhysicalDeviceXXXFeatures`, + it **must** add an instance of the structure, with the desired feature members + set to `VK_TRUE`, to the `pNext` chain of `VkDeviceCreateInfo` when creating + the `VkDevice`. + And that was missing in the code. + Strangely, that functionality doesn't have a Valid-Usage ID assigned, so it + isn't caught by the validation layer. + This patch adds the structures in the physical devices to get them and later set + them in the device creation. + In order to link these features, videomaintenance1, and others to come, without + knowing if Vulkan 1.3 features are chained in the device properties structure, a + static and inlined function was added in gstvkphysicaldevice-private.h. It was + added in a header file to avoid compiler warnings if it's not used because of + old Vulkan headers. + Also the value dump videomaintenance1 was moved to another function to pack + there all these queried features. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9489> + +2025-08-01 11:52:17 +0200 Víctor Manuel Jáquez Leal <vjaquez@igalia.com> + + * sys/va/gstvaav1enc.c: + * sys/va/gstvah264enc.c: + * sys/va/gstvah265enc.c: + * sys/va/gstvavp8enc.c: + * sys/va/gstvavp9enc.c: + vaXXXenc: fix potential race condition + VA encoders, at reconfiguration, have to check if the rate-control was changed + by the user, but since user parameters setting are in another thread, the + comparison was racy. + This patch locks the object to compare the current rate-control with the one set + by the user. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9484> + +2025-05-29 13:20:59 +0200 Víctor Manuel Jáquez Leal <vjaquez@igalia.com> + + * gst-libs/gst/vulkan/gstvkdevice.c: + vulkan: ycbcr conversion extension got promoted in 1.1.0 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9482> + +2025-08-01 01:03:10 +0900 Seungha Yang <seungha@centricular.com> + + * sys/d3d12/gstd3d12screencapturedevice.cpp: + d3d12screencapturedevice: Avoid false device removal on monitor reconfiguration + Post device-changed instead of device-removed/device-added when + only HMONITOR or display position changed without actual device change. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9481> + +2025-07-30 15:50:56 +0400 Marc-André Lureau <marcandre.lureau@redhat.com> + + * gst-libs/gst/d3d12/meson.build: + * sys/d3d12/meson.build: + meson: d3d12: Add support for MinGW DirectXMath package + This is a similar issue that was found for d3d11: + https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6495 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9473> + +2025-07-29 11:42:54 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvavp9dec.c: + vavp9dec: Always chain to parent class negotiate vmethod + When the base videodecoder class re-attempts a negotiation after flush, the + vabasedec `need_negotiation` flag isn't necessarily set to TRUE, because in that + situation the input state hasn't changed. + By always chaining up we are sure that buffer pool negotiation will always be + attempted. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-29 11:42:44 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvajpegdec.c: + vajpegdec: Always chain to parent class negotiate vmethod + When the base videodecoder class re-attempts a negotiation after flush, the + vabasedec `need_negotiation` flag isn't necessarily set to TRUE, because in that + situation the input state hasn't changed. + By always chaining up we are sure that buffer pool negotiation will always be + attempted. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-29 11:42:33 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvaav1dec.c: + vaav1dec: Always chain to parent class negotiate vmethod + When the base videodecoder class re-attempts a negotiation after flush, the + vabasedec `need_negotiation` flag isn't necessarily set to TRUE, because in that + situation the input state hasn't changed. + By always chaining up we are sure that buffer pool negotiation will always be + attempted. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-28 12:52:22 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvabasedec.c: + vabasedec: Instrument negotiate function with debug statements + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-28 12:49:36 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvabasedec.c: + vabasedec: Always chain to parent class negotiate vmethod + When the base videodecoder class re-attempts a negotiation after flush, the + vabasedec `need_negotiation` flag isn't necessarily set to TRUE, because in that + situation the input state hasn't changed. + By always chaining up we are sure that buffer pool negotiation will always be + attempted. + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-28 12:45:25 +0100 Philippe Normand <philn@igalia.com> + + * sys/va/gstvah264dec.c: + vah264dec: Spelling fix in warning debug statement + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-28 12:44:48 +0100 Philippe Normand <philn@igalia.com> + + * gst-libs/gst/codecs/gsth264decoder.c: + h264decoder: Spelling fix in warning debug statement + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9461> + +2025-07-24 18:16:51 +0200 Víctor Manuel Jáquez Leal <vjaquez@igalia.com> + + * sys/va/gstvaav1enc.c: + * sys/va/gstvah264enc.c: + * sys/va/gstvah265enc.c: + * sys/va/gstvajpegenc.c: + * sys/va/gstvavp8enc.c: + * sys/va/gstvavp9enc.c: + vaXXXenc: calculate latency with corrected framerate + Fixes: #4558 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9447> + +2025-06-29 22:52:37 +0900 Seungha Yang <seungha@centricular.com> + + * sys/wasapi2/gstwasapi2ringbuffer.cpp: + wasapi2: Fix various MinGW build warnings + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9428> + +2025-06-29 20:45:51 +0900 Seungha Yang <seungha@centricular.com> + + * sys/wasapi2/AsyncOperations.h: + * sys/wasapi2/gstwasapi2client.cpp: + * sys/wasapi2/gstwasapi2client.h: + * sys/wasapi2/gstwasapi2util.cpp: + * sys/wasapi2/meson.build: + waapi2: Remove unused WinRT deps and implementations + Removing unused WinRT API based implementations + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9428> + +2025-06-29 01:46:44 +0900 Seungha Yang <seungha@centricular.com> + + * sys/wasapi2/gstwasapi2object.cpp: + * sys/wasapi2/gstwasapi2object.h: + * sys/wasapi2/gstwasapi2ringbuffer.cpp: + * sys/wasapi2/gstwasapi2ringbuffer.h: + * sys/wasapi2/gstwasapi2sink.c: + * sys/wasapi2/gstwasapi2src.c: + * sys/wasapi2/meson.build: + wasapi2: Port to IMMDevice based device selection + Because of a couple of issues reported related to WinRT device + enumeration, porting to IMMDevice device id based device selection. + Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4311 + Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3936 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9428> + +2025-06-27 21:36:53 +0900 Seungha Yang <seungha@centricular.com> + + * sys/wasapi2/gstwasapi2activator.cpp: + * sys/wasapi2/gstwasapi2activator.h: + * sys/wasapi2/gstwasapi2client.cpp: + * sys/wasapi2/gstwasapi2device.c: + * sys/wasapi2/gstwasapi2device.cpp: + * sys/wasapi2/gstwasapi2device.h: + * sys/wasapi2/gstwasapi2enumerator.cpp: + * sys/wasapi2/gstwasapi2enumerator.h: + * sys/wasapi2/gstwasapi2util.cpp: + * sys/wasapi2/gstwasapi2util.h: + * sys/wasapi2/meson.build: + * sys/wasapi2/plugin.cpp: + wasapi2: Implement IMMDeviceEnumerator based enumerator + ... and merge wasapi2{capture,render}deviceprovider into single + wasapi2deviceprovider since we can enumerate input/output audio + devices at once using IMMDeviceEnumerator + This is a preparation for complete porting to Win32 API + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9428> + +2025-06-01 00:02:16 +0900 Seungha Yang <seungha@centricular.com> + + * sys/d3d12/gstd3d12graphicscapture.cpp: + * sys/d3d12/gstd3d12graphicscapture.h: + * sys/d3d12/plugin.cpp: + d3d12screencapturesrc: Fix OS handle leaks/random crash in WGC mode + Multiple DispatcherQueues per thread seems to be causing OS handle leak + and random crashes were observed. Instead of creating + thread/DispatcherQueue per GstD3D12GraphicsCapture object, + reuse only single thread and DispatcherQueue + Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4351 + Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9424> + +2025-07-16 15:33:19 +0200 Tim-Philipp Müller <tim@centricular.com> + + * meson.build: + Back to development after 1.26.4 + === release 1.26.4 === 2025-07-16 15:26:21 +0200 Tim-Philipp Müller <tim@centricular.com>
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/NEWS -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/NEWS
Changed
@@ -2,11 +2,11 @@ GStreamer 1.26.0 was originally released on 11 March 2025. -The latest bug-fix release in the stable 1.26 series is 1.26.4 and was released on 16 July 2025. +The latest bug-fix release in the stable 1.26 series is 1.26.5 and was released on 07 August 2025. See https://gstreamer.freedesktop.org/releases/1.26/ for the latest version of this document. -Last updated: Wednesday 16 July 2025, 15:00 UTC (log) +Last updated: Thursday 07 August 2025, 18:00 UTC (log) ## Introduction @@ -2189,6 +2189,169 @@ - List of Merge Requests applied in 1.26.4 - List of Issues fixed in 1.26.4 +1.26.5 + +The fifth 1.26 bug-fix release (1.26.5) was released on 7 August 2025. + +This release only contains bugfixes including some important playback fixes, and it should be safe to update from 1.26.x. + +Highlighted bugfixes in 1.26.5 + +- audioconvert: Fix caps negotiation regression when using a mix matrix +- aws: Add support for brevity in awstranslate and add option to partition speakers in the transcription output of + awstranscriber2 +- speechmatics speech-to-text: Expose mask-profanities property +- cea708mux: Add support for discarding select services on each input +- cea608overlay, cea708overlay: Accept GPU memory buffers if downstream supports the overlay composition meta +- d3d12screencapture source element and device provider fixes +- decodebin3: Don’t error on an incoming ONVIF metadata stream +- uridecodebin3: Fix potential crash when adding URIs to messages, e.g. if no decoder is available +- v4l2: Fix memory leak for dynamic resolution change +- VA encoder fixes +- videorate, imagefreeze: Add support for JPEG XS +- Vulkan integration fixes +- wasapi2 audio device monitor improvements +- webrtc: Add WHEP client signaller and add whepclientsrc element on top of webrtcsrc using that +- threadshare: Many improvements and fixes to the generic threadshare and RTP threadshare elements +- rtpbin2 improvements and fixes +- gst-device-monitor-1.0 command line tool improvements +- Various bug fixes, build fixes, memory leak fixes, and other stability and reliability improvements + +gstreamer + +- aggregator: add sub_latency_min to pad queue size +- build: Disable C5287 warning on MSVC + +gst-plugins-base + +- audioconvert: Fix regression when using a mix matrix +- audioconvert: mix-matrix causes caps negotiation failure +- decodebin3: Don’t error on an incoming ONVIF metadata stream +- gloverlay: Recompute geometry when caps change, and load texture after stopping and starting again +- uridecodebin3: Add missing locking and NULL checks when adding URIs to messages +- uridecodebin3: segfault in update_message_with_uri() if no decoder available +- videorate, imagefreeze: add support for JPEG XS +- gst-device-monitor-1.0: Add shell quoting for launch lines +- gst-device-monitor-1.0: Fix criticals, and also accept utf8 in launch lines +- gst-device-monitor-1.0: Use gst_print instead of g_print + +gst-plugins-good + +- v4l2: fix memory leak for dynamic resolution change +- videorate, imagefreeze: add support for JPEG XS + +gst-plugins-bad + +- av1parse: Don’t error out on “currently” undefined seq-level indices +- av1parse: fails to parse AV1 bitstreams generated by FFmpeg using the av1_nvenc hardware encoder +- d3d12screencapturedevice: Avoid false device removal on monitor reconfiguration +- d3d12screencapturesrc: Fix OS handle leaks/random crash in WGC mode +- meson: d3d12: Add support for MinGW DirectXMath package +- va: Re-negotiate after FLUSH +- vaXXXenc: calculate latency with corrected framerate +- vaXXXenc: fix potential race condition +- vkphysicaldevice: enable sampler ycbcr conversion, synchronization2 and timeline semaphore features +- vulkan: ycbcr conversion extension got promoted in 1.1.0 +- wasapi2: Port to IMMDevice based device selection + +gst-plugins-ugly + +- No changes + +GStreamer Rust plugins + +- awstranscriber2,awstranslate: Handle multiple stream-start event +- awstranslate: expose property for turning brevity on +- awstranscriber2: add property for setting show_speaker_labels +- cea708mux: expose “discarded-services” property on sink pads +- ceaX08overlay: support ANY caps features, allowing e.g. memory:GLMemory if downstream supports the overlay composition meta +- hlsmultivariantsink: Fix master playlist version +- rtprecv: Drop state lock before chaining RTCP packets from the RTP chain function +- rtpbin2: Add examples +- rtpmp4apay2: fix payload size prefix +- rtp: threadshare: fix some property ranges +- mpegtslivesrc: Remove leftover debug message +- speechmatics: expose mask-profanities property +- ts-audiotestsrc fixes +- threadshare: fix flush for ts-queue ts-proxy & ts-intersrc +- threadshare: fix regression in ts-proxysrc +- threadshare: improvements to some elements +- threadshare: udp: avoid getifaddrs in android +- threadshare: Enable windows Win32_Networking feature +- threadshare: queue & proxy: fix race condition stopping +- threadshare: Also enable windows Win32_Networking_WinSock feature +- tracers: pipeline-snapshot: reduce WebSocket connection log level +- tracers: queue-levels: add support for threadshare DataQueue related elements +- tracers: Update to etherparse 0.19 +- transcriberbin: Fix handling of upstream latency query +- webrtc: android example: fix media handling initialization sequence +- webrtcsink: Move videorate before videoconvert and videoscale to avoid processing frames that would be dropped +- whep: add WHEP client signaller +- Fix various new clippy 1.89 warnings + +gst-libav + +- No changes + +gst-rtsp-server + +- No changes + +gstreamer-vaapi + +- No changes + +gstreamer-sharp + +- No changes + +gst-python + +- No changes + +gst-editing-services + +- No changes + +gst-devtools, gst-validate + gst-integration-testsuites + +- No changes + +gst-examples + +- No changes + +gstreamer-docs + +- No changes + +Development build environment + +- gst-env: only-environment: only dump added and updated vars +- gst-full: Fix detection of duplicate plugin entries +- ci: Fix gst-full breakage due to a typo +- build: Disable C5287 warning on MSVC + +Cerbero build tool and packaging changes in 1.26.5 + +- a52dec: update to 0.8.0 and port to Meson +- build: Fix passing multiple steps +- expat: update to 2.7.1 +- tar: Refactor in preparation for xcframework support + +Contributors to 1.26.5 + +François Laignel, Jan Schmidt, Jaslo Ziska, L. E. Segovia, Marc-André Lureau, Mathieu Duponchelle, Matthew Waters, Nirbheek +Chauhan, Philippe Normand, Qian Hu (胡骞), Sanchayan Maity, Sebastian Dröge, Seungha Yang, Thibault Saunier, Tim-Philipp Müller, +Víctor Manuel Jáquez Leal, Xavier Claessens, + +… and many others who have contributed bug reports, translations, sent suggestions or helped testing. Thank you all! + +List of merge requests and issues fixed in 1.26.5 + +- List of Merge Requests applied in 1.26.5 +- List of Issues fixed in 1.26.5 + Schedule for 1.28 Our next major feature release will be 1.28, and 1.27 will be the unstable development version leading up to the stable 1.28
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/RELEASE -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/RELEASE
Changed
@@ -1,4 +1,4 @@ -This is GStreamer gst-plugins-bad 1.26.4. +This is GStreamer gst-plugins-bad 1.26.5. The GStreamer team is thrilled to announce a new major feature release of your favourite cross-platform multimedia framework!
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/codecparsers/gstav1parser.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/codecparsers/gstav1parser.c
Changed
@@ -486,18 +486,7 @@ av1_seq_level_idx_is_valid (GstAV1SeqLevels seq_level_idx) { return seq_level_idx == GST_AV1_SEQ_LEVEL_MAX - || (seq_level_idx < GST_AV1_SEQ_LEVELS - /* The following levels are currently undefined. */ - && seq_level_idx != GST_AV1_SEQ_LEVEL_2_2 - && seq_level_idx != GST_AV1_SEQ_LEVEL_2_3 - && seq_level_idx != GST_AV1_SEQ_LEVEL_3_2 - && seq_level_idx != GST_AV1_SEQ_LEVEL_3_3 - && seq_level_idx != GST_AV1_SEQ_LEVEL_4_2 - && seq_level_idx != GST_AV1_SEQ_LEVEL_4_3 - && seq_level_idx != GST_AV1_SEQ_LEVEL_7_0 - && seq_level_idx != GST_AV1_SEQ_LEVEL_7_1 - && seq_level_idx != GST_AV1_SEQ_LEVEL_7_2 - && seq_level_idx != GST_AV1_SEQ_LEVEL_7_3); + || seq_level_idx < GST_AV1_SEQ_LEVELS; } static void
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/codecs/gsth264decoder.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/codecs/gsth264decoder.c
Changed
@@ -1323,7 +1323,8 @@ ret = klass->new_picture (self, priv->current_frame, picture); if (ret != GST_FLOW_OK) { - GST_WARNING_OBJECT (self, "subclass does not want accept new picture"); + GST_WARNING_OBJECT (self, + "subclass does not want to accept new picture"); priv->current_picture = NULL; gst_h264_picture_unref (picture); return ret;
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/d3d12/meson.build -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/d3d12/meson.build
Changed
@@ -122,7 +122,7 @@ name: 'DirectXMath support in Windows SDK') if not have_dx_math - directxmath_dep = dependency('directxmath', + directxmath_dep = dependency('DirectXMath', 'directxmath', allow_fallback: true, required: d3d12_option) if not directxmath_dep.found()
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/vulkan/gstvkdevice.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/vulkan/gstvkdevice.c
Changed
@@ -241,7 +241,7 @@ OPTIONAL_EXTENSION_VERSION (VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_MAKE_VERSION (1, 0, 0), NEVER_VK_VERSION), OPTIONAL_EXTENSION_VERSION (VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, - VK_MAKE_VERSION (1, 0, 0), NEVER_VK_VERSION), + VK_MAKE_VERSION (1, 0, 0), VK_MAKE_VERSION (1, 1, 0)), #if defined(VK_KHR_timeline_semaphore) OPTIONAL_EXTENSION_VERSION (VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, VK_MAKE_VERSION (1, 1, 0), VK_MAKE_VERSION (1, 2, 0)),
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/vulkan/gstvkphysicaldevice-private.h -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/vulkan/gstvkphysicaldevice-private.h
Changed
@@ -28,6 +28,17 @@ const VkPhysicalDeviceFeatures2 * gst_vulkan_physical_device_get_features (GstVulkanPhysicalDevice * device); +static inline void +vk_link_struct (gpointer chain, gconstpointer in) +{ + VkBaseOutStructure *out = chain; + + while (out->pNext) + out = out->pNext; + + out->pNext = (void *) in; +} + G_END_DECLS #endif /* __GST_VULKAN_PHYSICAL_DEVICE_PRIVATE_H__ */
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-libs/gst/vulkan/gstvkphysicaldevice.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-libs/gst/vulkan/gstvkphysicaldevice.c
Changed
@@ -70,10 +70,19 @@ #if defined (VK_API_VERSION_1_3) VkPhysicalDeviceVulkan13Features features13; VkPhysicalDeviceVulkan13Properties properties13; +#endif +#if defined (VK_KHR_sampler_ycbcr_conversion) + VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR sampler_ycbcr_conversion; +#endif +#if defined (VK_KHR_synchronization2) + VkPhysicalDeviceSynchronization2FeaturesKHR synchronization2; +#endif +#if defined (VK_KHR_timeline_semaphore) + VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore; +#endif #if defined (VK_KHR_video_maintenance1) VkPhysicalDeviceVideoMaintenance1FeaturesKHR videomaintenance1; #endif -#endif }; static void @@ -204,11 +213,26 @@ priv->features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; priv->features12.pNext = &priv->features13; +#endif +#if defined (VK_KHR_sampler_ycbcr_conversion) + priv->sampler_ycbcr_conversion.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR; + vk_link_struct (&priv->features12, &priv->sampler_ycbcr_conversion); +#endif +#if defined (VK_KHR_synchronization2) + priv->synchronization2.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR; + vk_link_struct (&priv->features12, &priv->synchronization2); +#endif +#if defined (VK_KHR_timeline_semaphore) + priv->timeline_semaphore.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR; + vk_link_struct (&priv->features12, &priv->timeline_semaphore); +#endif #if defined (VK_KHR_video_maintenance1) priv->videomaintenance1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR; - priv->features13.pNext = &priv->videomaintenance1; -#endif + vk_link_struct (&priv->features12, &priv->videomaintenance1); #endif } @@ -487,16 +511,42 @@ DEBUG_BOOL_STRUCT ("support for (1.3)", features, maintenance4); /* *INDENT-ON* */ } +#endif /* defined (VK_API_VERSION_1_3) */ -#if defined(VK_KHR_video_maintenance1) static void -dump_videomaintenance1 (GstVulkanPhysicalDevice * device, - VkPhysicalDeviceVideoMaintenance1FeaturesKHR * features) +dump_extras (GstVulkanPhysicalDevice * device, VkBaseOutStructure * chain) { - DEBUG_BOOL_STRUCT ("support for (1.3)", features, videoMaintenance1); -} + GstVulkanPhysicalDevicePrivate *priv = GET_PRIV (device); + +#if defined (VK_KHR_sampler_ycbcr_conversion) + if (chain->sType == + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR) { + DEBUG_BOOL_STRUCT ("support for", &priv->sampler_ycbcr_conversion, + samplerYcbcrConversion); + } #endif -#endif /* defined (VK_API_VERSION_1_3) */ +#if defined (VK_KHR_synchronization2) + if (chain->sType == + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR) { + DEBUG_BOOL_STRUCT ("support for", &priv->synchronization2, + synchronization2); + } +#endif +#if defined (VK_KHR_timeline_semaphore) + if (chain->sType == + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR) { + DEBUG_BOOL_STRUCT ("support for", &priv->timeline_semaphore, + timelineSemaphore); + } +#endif +#if defined (VK_KHR_video_maintenance1) + if (chain->sType == + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR) { + DEBUG_BOOL_STRUCT ("support for", &priv->videomaintenance1, + videoMaintenance1); + } +#endif +} static gboolean dump_features (GstVulkanPhysicalDevice * device, GError ** error) @@ -522,14 +572,9 @@ && iter->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES) dump_features13 (device, (VkPhysicalDeviceVulkan13Features *) iter); -#if defined(VK_KHR_video_maintenance1) - else if (gst_vulkan_physical_device_check_api_version (device, 1, 3, 283) - && iter->sType == - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR) - dump_videomaintenance1 (device, - (VkPhysicalDeviceVideoMaintenance1FeaturesKHR *) iter); -#endif #endif + else + dump_extras (device, iter); } } else #endif
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/gst-plugins-bad.doap -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/gst-plugins-bad.doap
Changed
@@ -35,6 +35,16 @@ <release> <Version> + <revision>1.26.5</revision> + <branch>1.26</branch> + <name></name> + <created>2025-08-07</created> + <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-plugins-bad/gst-plugins-bad-1.26.5.tar.xz" /> + </Version> + </release> + + <release> + <Version> <revision>1.26.4</revision> <branch>1.26</branch> <name></name>
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/meson.build -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/meson.build
Changed
@@ -1,5 +1,5 @@ project('gst-plugins-bad', 'c', 'cpp', - version : '1.26.4', + version : '1.26.5', meson_version : '>= 1.4', default_options : 'warning_level=1', 'buildtype=debugoptimized' )
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/po/gst-plugins-bad-1.0.pot -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/po/gst-plugins-bad-1.0.pot
Changed
@@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: gst-plugins-bad-1.26.4\n" +"Project-Id-Version: gst-plugins-bad-1.26.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-16 15:27+0200\n" +"POT-Creation-Date: 2025-08-07 19:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/po/gst-plugins-bad.pot -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/po/gst-plugins-bad.pot
Changed
@@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: gst-plugins-bad-1.26.4\n" +"Project-Id-Version: gst-plugins-bad-1.26.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-16 15:27+0200\n" +"POT-Creation-Date: 2025-08-07 19:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/d3d12/gstd3d12graphicscapture.cpp -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/d3d12/gstd3d12graphicscapture.cpp
Changed
@@ -48,6 +48,8 @@ #include <windows.graphics.capture.interop.h> #include <windows.graphics.directx.direct3d11.h> #include <windows.graphics.directx.direct3d11.interop.h> +#include <queue> +#include <functional> GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_screen_capture_debug); #define GST_CAT_DEFAULT gst_d3d12_screen_capture_debug @@ -191,6 +193,249 @@ return g_vtable.WindowsDeleteString (class_id_hstring); } +struct QueueManger; + +static gpointer dispatcher_main_thread (QueueManger * m); + +struct EnqueueData +{ + ComPtr<IDispatcherQueueHandler> handler; + std::mutex lock; + std::condition_variable cond; + bool signalled = false; + HRESULT hr; +}; + +enum LoopState +{ + LOOP_STATE_INIT, + LOOP_STATE_RUNNING, + LOOP_STATE_STOPPED, +}; + +struct QueueManger +{ + QueueManger (const QueueManger &) = delete; + QueueManger& operator= (const QueueManger &) = delete; + + static QueueManger * GetInstance () + { + static QueueManger *ins = nullptr; + GST_D3D12_CALL_ONCE_BEGIN { + ins = new QueueManger (); + } GST_D3D12_CALL_ONCE_END; + + return ins; + } + + HRESULT RunOnDispatcherThread (IDispatcherQueueHandler * handler) + { + if (!Init ()) + return E_FAIL; + + auto item = std::make_shared<EnqueueData> (); + item->handler = handler; + + { + std::lock_guard <std::mutex> lk (loop_lock); + user_events.push (item); + } + + SetEvent (enqueue_handle); + + std::unique_lock <std::mutex> lk (item->lock); + while (!item->signalled) + item->cond.wait (lk); + + return item->hr; + } + + static void Deinit () + { + auto ins = GetInstance (); + SetEvent (ins->shutdown_handle); + if (ins->thread) + g_thread_join (ins->thread); + + delete ins; + } + +private: + QueueManger () + { + shutdown_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); + enqueue_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); + } + + ~QueueManger () + { + CloseHandle (shutdown_handle); + CloseHandle (enqueue_handle); + } + + bool Init () + { + static std::once_flag once; + std::call_once (once, { + gst_d3d12_graphics_capture_load_library (); + }); + + if (!g_vtable.loaded) + return false; + + std::unique_lock <std::mutex> lk (loop_lock); + if (!thread) { + thread = g_thread_new ("DispatcherThread", + (GThreadFunc) dispatcher_main_thread, this); + } + + while (loop_state == LOOP_STATE_INIT) + loop_cond.wait (lk); + + if (loop_state == LOOP_STATE_RUNNING) + return true; + + return false; + } + +public: + LoopState loop_state = LOOP_STATE_INIT; + IDispatcherQueueController *queue_ctrl = nullptr; + HANDLE shutdown_handle; + HANDLE enqueue_handle; + GThread *thread = nullptr; + std::mutex loop_lock; + std::condition_variable loop_cond; + std::queue<std::shared_ptr<EnqueueData>> user_events; +}; + +class AsyncWaiter : public RuntimeClass<RuntimeClassFlags<ClassicCom>, + FtmBase, IAsyncActionCompletedHandler> +{ +public: + STDMETHODIMP + RuntimeClassInitialize (HANDLE event_handle) + { + event_handle_ = event_handle; + return S_OK; + } + + STDMETHOD (Invoke) (IAsyncAction * action, AsyncStatus status) + { + SetEvent (event_handle_); + + return S_OK; + } + +private: + HANDLE event_handle_; +}; + +static void +dispatcher_main_thread_inner (QueueManger * m) +{ + DispatcherQueueOptions queue_opt; + queue_opt.dwSize = sizeof (DispatcherQueueOptions); + queue_opt.threadType = DQTYPE_THREAD_CURRENT; + queue_opt.apartmentType = DQTAT_COM_NONE; + + auto hr = g_vtable.CreateDispatcherQueueController (queue_opt, + &m->queue_ctrl); + if (FAILED (hr)) { + GST_ERROR ("Couldn't create queue ctrl"); + return; + } + + ComPtr < AsyncWaiter > async_waiter; + ComPtr < IAsyncAction > shutdown_action; + + HANDLE event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); + hr = MakeAndInitialize < AsyncWaiter > (&async_waiter, event_handle); + if (FAILED (hr)) { + GST_ERROR ("Couldn't create async waiter"); + return; + } + + { + std::lock_guard <std::mutex> lk (m->loop_lock); + GST_DEBUG ("Loop running"); + m->loop_state = LOOP_STATE_RUNNING; + m->loop_cond.notify_all (); + } + + HANDLE waitables = { m->shutdown_handle, event_handle, m->enqueue_handle }; + + while (true) { + MSG msg; + while (PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + auto wait_ret = MsgWaitForMultipleObjects (G_N_ELEMENTS (waitables), + waitables, FALSE, INFINITE, QS_ALLINPUT); + + if (wait_ret == WAIT_OBJECT_0) { + hr = m->queue_ctrl->ShutdownQueueAsync (&shutdown_action); + if (FAILED (hr)) { + GST_ERROR ("Shutdown failed"); + return; + } + + hr = shutdown_action->put_Completed (async_waiter.Get ()); + if (FAILED (hr)) { + GST_ERROR ("Couldn't put completed"); + return; + } + } else if (wait_ret == WAIT_OBJECT_0 + 1) { + GST_DEBUG ("Shutdown completed"); + if (shutdown_action) + shutdown_action->GetResults (); + + return; + } else if (wait_ret == WAIT_OBJECT_0 + 2) { + std::unique_lock <std::mutex> lk (m->loop_lock); + while (!m->user_events.empty ()) { + auto item = m->user_events.front (); + m->user_events.pop (); + lk.unlock (); + + item->hr = item->handler->Invoke (); + + { + std::lock_guard <std::mutex> item_lk (item->lock); + item->signalled = true; + item->cond.notify_all (); + } + + lk.lock (); + } + + } else if (wait_ret != WAIT_OBJECT_0 + G_N_ELEMENTS (waitables)) { + GST_ERROR ("Unexpected wait return %u", (guint) wait_ret); + return; + } + } +} + +static gpointer +dispatcher_main_thread (QueueManger * m) +{ + g_vtable.SetThreadDpiAwarenessContext + (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + g_vtable.RoInitialize (RO_INIT_MULTITHREADED); + + dispatcher_main_thread_inner (m); + + g_vtable.RoUninitialize (); + + std::lock_guard <std::mutex> lk (m->loop_lock); + m->loop_state = LOOP_STATE_STOPPED; + m->loop_cond.notify_all (); + + return nullptr; +} + class GraphicsCapture : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IFrameArrivedHandler, IItemClosedHandler> { @@ -202,7 +447,7 @@ virtual ~GraphicsCapture () { - GST_INFO_OBJECT (obj_, "Fin"); + GST_INFO_OBJECT (device12_, "Fin"); if (d3d12_pool_) { gst_buffer_pool_set_active (d3d12_pool_, FALSE); @@ -218,33 +463,109 @@ } STDMETHODIMP - RuntimeClassInitialize (GstD3D12GraphicsCapture * obj, - GstD3D12Device * device12, ID3D11Device * device11, - IDirect3DDevice * d3d_device, - IDirect3D11CaptureFramePoolStatics * pool_statics, - IGraphicsCaptureItem * item, HWND window_handle) + RuntimeClassInitialize (GstD3D12Device * device, HMONITOR monitor, HWND hwnd) { - obj_ = obj; - device12_ = (GstD3D12Device *) gst_object_ref (device12); + device12_ = (GstD3D12Device *) gst_object_ref (device); + + static const D3D_FEATURE_LEVEL feature_levels = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + }; - device11->QueryInterface (IID_PPV_ARGS (&device11_)); - if (!device11_) { - GST_ERROR_OBJECT (obj_, "ID3D11Device5 interface unavailable"); + hwnd_ = hwnd; + + auto adapter = gst_d3d12_device_get_adapter_handle (device); + ComPtr<ID3D11Device> device11; + + auto hr = D3D11CreateDevice (adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, feature_levels, + G_N_ELEMENTS (feature_levels), D3D11_SDK_VERSION, + &device11, nullptr, nullptr); + + if (FAILED (hr)) { + hr = D3D11CreateDevice (adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, &feature_levels1, + G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, + &device11, nullptr, nullptr); + } + + if (!device11) { + GST_ERROR_OBJECT (device, "Couldn't create d3d11 device"); + return E_FAIL; + } + + hr = device11.As (&device11_); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't get ID3D11Device5 interface"); + return E_FAIL; + } + + ComPtr<ID3D11DeviceContext> ctx; + device11->GetImmediateContext (&ctx); + hr = ctx.As (&context11_); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't get ID3D11DeviceContext4 interface"); + return E_FAIL; + } + + ComPtr < ID3D10Multithread > multi_thread; + device11_.As (&multi_thread); + if (multi_thread) + multi_thread->SetMultithreadProtected (TRUE); + + ComPtr < IDXGIDevice > dxgi_device; + hr = device11_.As (&dxgi_device); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't get IDXGIDevice interface"); + return E_FAIL; + } + + ComPtr < IInspectable > inspectable; + hr = g_vtable.CreateDirect3D11DeviceFromDXGIDevice (dxgi_device.Get (), + &inspectable); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "CreateDirect3D11DeviceFromDXGIDevice failed"); + return E_FAIL; + } + + hr = inspectable.As (&d3d_device_); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't get IDirect3DDevice interface"); + return E_FAIL; + } + + ComPtr < IGraphicsCaptureItemInterop > item_interop; + hr = GstGetActivationFactory < IGraphicsCaptureItemInterop, + RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem > (&item_interop); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "IGraphicsCaptureItemInterop is not available"); + return E_FAIL; + } + + if (monitor) + hr = item_interop->CreateForMonitor (monitor, IID_PPV_ARGS (&item_)); + else + hr = item_interop->CreateForWindow (hwnd, IID_PPV_ARGS (&item_)); + + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't create item"); return E_FAIL; } - ComPtr<ID3D11DeviceContext> context; - device11_->GetImmediateContext (&context); - context.As (&context_); - if (!context_) { - GST_ERROR_OBJECT (obj_, "ID3D11DeviceContext4 interface unavailable"); + ComPtr < IDirect3D11CaptureFramePoolStatics > pool_statics; + hr = GstGetActivationFactory < IDirect3D11CaptureFramePoolStatics, + RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool > + (&pool_statics); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, + "IDirect3D11CaptureFramePoolStatics is unavailable"); return E_FAIL; } - auto hr = device11_->CreateFence (0, D3D11_FENCE_FLAG_SHARED, + hr = device11_->CreateFence (0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS (&shared_fence11_)); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj_, "Couldn't create d3d11 fence"); + GST_ERROR_OBJECT (device, "Couldn't create d3d11 fence"); return E_FAIL; } @@ -252,52 +573,48 @@ hr = shared_fence11_->CreateSharedHandle (nullptr, GENERIC_ALL, nullptr, &fence_handle); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj_, "Couldn't create shared handle"); + GST_ERROR_OBJECT (device, "Couldn't create shared handle"); return E_FAIL; } - auto device12_handle = gst_d3d12_device_get_device_handle (device12); + auto device12_handle = gst_d3d12_device_get_device_handle (device12_); hr = device12_handle->OpenSharedHandle (fence_handle, IID_PPV_ARGS (&shared_fence12_)); CloseHandle (fence_handle); - if (!gst_d3d12_result (hr, device12)) { - GST_ERROR_OBJECT (obj_, "Couldn't open d3d12 fence"); + if (!gst_d3d12_result (hr, device12_)) { + GST_ERROR_OBJECT (device, "Couldn't open d3d12 fence"); return E_FAIL; } - device_ = d3d_device; - item_ = item; - hwnd_ = window_handle; - - hr = item->get_Size (&frame_size_); + hr = item_->get_Size (&frame_size_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't query item size"); + GST_ERROR_OBJECT (device, "Couldn't query item size"); return hr; } - hr = item->add_Closed (this, &closed_token_); + hr = item_->add_Closed (this, &closed_token_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't install closed callback"); + GST_ERROR_OBJECT (device, "Couldn't install closed callback"); return hr; } - hr = pool_statics->Create (d3d_device, + hr = pool_statics->Create (d3d_device_.Get (), DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized, - CAPTURE_POOL_SIZE, frame_size_, &frame_pool_); + 2, frame_size_, &frame_pool_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't create frame pool"); + GST_ERROR_OBJECT (device, "Couldn't create frame pool"); return hr; } hr = frame_pool_->add_FrameArrived (this, &arrived_token_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't install FrameArrived callback"); + GST_ERROR_OBJECT (device, "Couldn't install FrameArrived callback"); return hr; } - hr = frame_pool_->CreateCaptureSession (item, &session_); + hr = frame_pool_->CreateCaptureSession (item_.Get (), &session_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't create session"); + GST_ERROR_OBJECT (device, "Couldn't create session"); return hr; } @@ -311,7 +628,7 @@ hr = session_->StartCapture (); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj, "Couldn't start capture"); + GST_ERROR_OBJECT (device, "Couldn't start capture"); return hr; } @@ -322,24 +639,27 @@ { ComPtr < IDirect3D11CaptureFrame > frame; - GST_LOG_OBJECT (obj_, "Frame arrived"); + GST_LOG_OBJECT (device12_, "Frame arrived"); + + if (!frame_pool_) + return S_OK; pool->TryGetNextFrame (&frame); if (!frame) { - GST_WARNING_OBJECT (obj_, "No frame"); + GST_WARNING_OBJECT (device12_, "No frame"); return S_OK; } SizeInt32 frame_size; auto hr = frame->get_ContentSize (&frame_size); if (FAILED (hr)) { - GST_WARNING_OBJECT (obj_, "Couldn't get content size"); + GST_WARNING_OBJECT (device12_, "Couldn't get content size"); return S_OK; } if (frame_size.Width != frame_size_.Width || frame_size.Height != frame_size_.Height) { - GST_DEBUG_OBJECT (obj_, "Frame size changed %dx%d -> %dx%d", + GST_DEBUG_OBJECT (device12_, "Frame size changed %dx%d -> %dx%d", frame_size_.Width, frame_size_.Height, frame_size.Width, frame_size.Height); std::lock_guard <std::mutex> lk (lock_); @@ -356,7 +676,7 @@ frame_ = nullptr; texture_ = nullptr; staging_ = nullptr; - pool->Recreate (device_.Get (), + pool->Recreate (d3d_device_.Get (), DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized, CAPTURE_POOL_SIZE, frame_size); frame_size_ = frame_size; @@ -366,14 +686,14 @@ ComPtr < IDirect3DSurface > surface; frame->get_Surface (&surface); if (!surface) { - GST_WARNING_OBJECT (obj_, "IDirect3DSurface interface unavailable"); + GST_WARNING_OBJECT (device12_, "IDirect3DSurface interface unavailable"); return S_OK; } ComPtr < IDirect3DDxgiInterfaceAccess > access; surface.As (&access); if (!access) { - GST_WARNING_OBJECT (obj_, + GST_WARNING_OBJECT (device12_, "IDirect3DDxgiInterfaceAccess interface unavailable"); return S_OK; } @@ -381,7 +701,7 @@ ComPtr < ID3D11Texture2D > texture; access->GetInterface (IID_PPV_ARGS (&texture)); if (!texture) { - GST_WARNING_OBJECT (obj_, + GST_WARNING_OBJECT (device12_, "ID3D11Texture2D interface unavailable"); return S_OK; } @@ -409,7 +729,7 @@ IFACEMETHOD(Invoke) (IGraphicsCaptureItem * pool, IInspectable * arg) { - GST_INFO_OBJECT (obj_, "Item closed"); + GST_INFO_OBJECT (device12_, "Item closed"); std::lock_guard <std::mutex> lk (lock_); closed_ = true; @@ -474,7 +794,7 @@ if (!d3d12_pool_ || pool_info_.width != crop_w || pool_info_.height != crop_h) { - GST_DEBUG_OBJECT (obj_, "Size changed, recrate buffer pool"); + GST_DEBUG_OBJECT (device12_, "Size changed, recrate buffer pool"); if (d3d12_pool_) { gst_buffer_pool_set_active (d3d12_pool_, FALSE); @@ -485,7 +805,7 @@ crop_w, crop_h); d3d12_pool_ = gst_d3d12_buffer_pool_new (device12_); if (!d3d12_pool_) { - GST_ERROR_OBJECT (obj_, "Couldn't create buffer pool"); + GST_ERROR_OBJECT (device12_, "Couldn't create buffer pool"); return GST_FLOW_ERROR; } @@ -501,13 +821,13 @@ gst_buffer_pool_config_set_d3d12_allocation_params (config, params); gst_d3d12_allocation_params_free (params); if (!gst_buffer_pool_set_config (d3d12_pool_, config)) { - GST_ERROR_OBJECT (obj_, "Couldn't set buffer pool config"); + GST_ERROR_OBJECT (device12_, "Couldn't set buffer pool config"); gst_clear_object (&d3d12_pool_); return GST_FLOW_ERROR; } if (!gst_buffer_pool_set_active (d3d12_pool_, TRUE)) { - GST_ERROR_OBJECT (obj_, "Couldn't activate pool"); + GST_ERROR_OBJECT (device12_, "Couldn't activate pool"); gst_clear_object (&d3d12_pool_); return GST_FLOW_ERROR; } @@ -516,7 +836,7 @@ GstBuffer *outbuf = nullptr; gst_buffer_pool_acquire_buffer (d3d12_pool_, &outbuf, nullptr); if (!outbuf) { - GST_ERROR_OBJECT (obj_, "Couldn't acquire buffer"); + GST_ERROR_OBJECT (device12_, "Couldn't acquire buffer"); return GST_FLOW_ERROR; } @@ -529,21 +849,21 @@ auto texture11 = gst_d3d12_memory_get_d3d11_texture (dmem, device11_.Get ()); if (!texture11) { - GST_ERROR_OBJECT (obj_, "Couldn't get sharable d3d11 texture"); + GST_ERROR_OBJECT (device12_, "Couldn't get sharable d3d11 texture"); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } if (!gst_memory_map (mem, &map_info, GST_MAP_WRITE_D3D12)) { - GST_ERROR_OBJECT (obj_, "Couldn't map memory"); + GST_ERROR_OBJECT (device12_, "Couldn't map memory"); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } - context_->CopySubresourceRegion (texture11, + context11_->CopySubresourceRegion (texture11, 0, 0, 0, 0, texture_.Get (), 0, (const D3D11_BOX *) &crop_box); fence_val_++; - context_->Signal (shared_fence11_.Get (), fence_val_); + context11_->Signal (shared_fence11_.Get (), fence_val_); gst_memory_unmap (mem, &map_info); gst_d3d12_memory_set_fence (dmem, @@ -588,7 +908,7 @@ if (!video_pool_ || pool_info_.width != crop_w || pool_info_.height != crop_h) { - GST_DEBUG_OBJECT (obj_, "Size changed, recrate buffer pool"); + GST_DEBUG_OBJECT (device12_, "Size changed, recrate buffer pool"); if (video_pool_) { gst_buffer_pool_set_active (video_pool_, FALSE); @@ -609,7 +929,7 @@ auto hr = device11_->CreateTexture2D (&desc, nullptr, &staging_); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj_, "Couldn't create staging texture"); + GST_ERROR_OBJECT (device12_, "Couldn't create staging texture"); return GST_FLOW_ERROR; } @@ -617,7 +937,7 @@ crop_w, crop_h); video_pool_ = gst_video_buffer_pool_new (); if (!video_pool_) { - GST_ERROR_OBJECT (obj_, "Couldn't create buffer pool"); + GST_ERROR_OBJECT (device12_, "Couldn't create buffer pool"); return GST_FLOW_ERROR; } @@ -627,41 +947,41 @@ gst_caps_unref (caps); if (!gst_buffer_pool_set_config (video_pool_, config)) { - GST_ERROR_OBJECT (obj_, "Couldn't set buffer pool config"); + GST_ERROR_OBJECT (device12_, "Couldn't set buffer pool config"); gst_clear_object (&video_pool_); return GST_FLOW_ERROR; } if (!gst_buffer_pool_set_active (video_pool_, TRUE)) { - GST_ERROR_OBJECT (obj_, "Couldn't activate pool"); + GST_ERROR_OBJECT (device12_, "Couldn't activate pool"); gst_clear_object (&video_pool_); return GST_FLOW_ERROR; } } - context_->CopySubresourceRegion (staging_.Get (), 0, 0, 0, 0, + context11_->CopySubresourceRegion (staging_.Get (), 0, 0, 0, 0, texture_.Get (), 0, (const D3D11_BOX *) &crop_box); D3D11_MAPPED_SUBRESOURCE mapped_resource; - auto hr = context_->Map (staging_.Get (), 0, D3D11_MAP_READ, + auto hr = context11_->Map (staging_.Get (), 0, D3D11_MAP_READ, 0, &mapped_resource); if (FAILED (hr)) { - GST_ERROR_OBJECT (obj_, "Couldn't map staging texture"); + GST_ERROR_OBJECT (device12_, "Couldn't map staging texture"); return GST_FLOW_ERROR; } GstBuffer *outbuf = nullptr; gst_buffer_pool_acquire_buffer (video_pool_, &outbuf, nullptr); if (!outbuf) { - GST_ERROR_OBJECT (obj_, "Couldn't acquire buffer"); - context_->Unmap (staging_.Get (), 0); + GST_ERROR_OBJECT (device12_, "Couldn't acquire buffer"); + context11_->Unmap (staging_.Get (), 0); return GST_FLOW_ERROR; } GstVideoFrame vframe; if (!gst_video_frame_map (&vframe, &pool_info_, outbuf, GST_MAP_WRITE)) { - GST_ERROR_OBJECT (obj_, "Couldn't map video frame"); - context_->Unmap (staging_.Get (), 0); + GST_ERROR_OBJECT (device12_, "Couldn't map video frame"); + context11_->Unmap (staging_.Get (), 0); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } @@ -676,7 +996,7 @@ src += mapped_resource.RowPitch; } gst_video_frame_unmap (&vframe); - context_->Unmap (staging_.Get (), 0); + context11_->Unmap (staging_.Get (), 0); *width = crop_w; *height = crop_h; @@ -694,36 +1014,60 @@ void Close () { - if (item_) - item_->remove_Closed (closed_token_); - - if (frame_pool_) + if (frame_pool_) { frame_pool_->remove_FrameArrived (arrived_token_); - texture_ = nullptr; - frame_ = nullptr; - staging_ = nullptr; + ComPtr<IClosable> closable; + frame_pool_.As (&closable); + if (closable) + closable->Close (); + + frame_pool_ = nullptr; + } + + if (item_) { + item_->remove_Closed (closed_token_); - if (session_) { ComPtr<IClosable> closable; - session_.As (&closable); + item_.As (&closable); if (closable) closable->Close (); + + item_ = nullptr; } - if (frame_pool_) { + session3_ = nullptr; + session2_ = nullptr; + + if (session_) { ComPtr<IClosable> closable; - frame_pool_.As (&closable); + session_.As (&closable); if (closable) closable->Close (); + + session_ = nullptr; } - if (item_) { + if (d3d_device_) { ComPtr<IClosable> closable; - item_.As (&closable); + d3d_device_.As (&closable); if (closable) closable->Close (); + + d3d_device_ = nullptr; } + + texture_ = nullptr; + staging_ = nullptr; + + shared_fence11_ = nullptr; + shared_fence12_ = nullptr; + + if (context11_) + context11_->Flush (); + + context11_ = nullptr; + device11_ = nullptr; } private: @@ -790,15 +1134,14 @@ } private: - GstD3D12GraphicsCapture *obj_ = nullptr; HWND hwnd_ = nullptr; GstD3D12Device *device12_ = nullptr; GstVideoInfo pool_info_; GstBufferPool *d3d12_pool_ = nullptr; GstBufferPool *video_pool_ = nullptr; ComPtr<ID3D11Device5> device11_; - ComPtr<ID3D11DeviceContext4> context_; - ComPtr<IDirect3DDevice> device_; + ComPtr<ID3D11DeviceContext4> context11_; + ComPtr<IDirect3DDevice> d3d_device_; ComPtr<IGraphicsCaptureItem> item_; ComPtr<IDirect3D11CaptureFramePool> frame_pool_; ComPtr<IGraphicsCaptureSession> session_; @@ -821,62 +1164,47 @@ ComPtr<ID3D12Fence> shared_fence12_; }; -class AsyncWaiter : public RuntimeClass<RuntimeClassFlags<ClassicCom>, - FtmBase, IAsyncActionCompletedHandler> +template<typename Fn> +class LambdaHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, + IDispatcherQueueHandler> { public: - STDMETHODIMP - RuntimeClassInitialize (HANDLE event_handle) + LambdaHandler() = default; + + HRESULT RuntimeClassInitialize(Fn&& fn) { - event_handle_ = event_handle; + func_ = std::move(fn); return S_OK; } - STDMETHOD (Invoke) (IAsyncAction * action, AsyncStatus status) + IFACEMETHODIMP Invoke() override { - SetEvent (event_handle_); - - return S_OK; + return func_ ? func_() : S_OK; } private: - HANDLE event_handle_; -}; - -enum LoopState -{ - LOOP_STATE_INIT, - LOOP_STATE_RUNNING, - LOOP_STATE_STOPPED, + Fn func_; }; struct GstD3D12GraphicsCapturePrivate { - GstD3D12GraphicsCapturePrivate () - { - shutdown_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); - } - ~GstD3D12GraphicsCapturePrivate () { - SetEvent (shutdown_handle); - g_clear_pointer (&loop_thread, g_thread_join); - - CloseHandle (shutdown_handle); - gst_clear_object (&device); + if (capture) { + ComPtr<IDispatcherQueueHandler> handler; + auto hr = MakeAndInitialize<LambdaHandler<std::function<HRESULT()>>>(&handler, + object = capture.Detach ()() -> HRESULT { + object->Close (); + object->Release (); + return S_OK; + }); + + if (SUCCEEDED (hr)) + QueueManger::GetInstance ()->RunOnDispatcherThread (handler.Get ()); + } } - GstD3D12Device *device = nullptr; - ComPtr<ID3D11Device> d3d11_device; ComPtr<GraphicsCapture> capture; - HMONITOR monitor_handle = nullptr; - HWND window_handle = nullptr; - HWND hidden_hwnd = nullptr; - HANDLE shutdown_handle; - GThread *loop_thread = nullptr; - LoopState loop_state = LOOP_STATE_INIT; - std::mutex loop_lock; - std::condition_variable loop_cond; }; /* *INDENT-ON* */ @@ -937,231 +1265,6 @@ G_OBJECT_CLASS (parent_class)->finalize (object); } -static gboolean -open_device (GstD3D12GraphicsCapture * self, IDirect3DDevice ** d3d_device) -{ - auto priv = self->priv; - - static const D3D_FEATURE_LEVEL feature_levels = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - }; - - auto adapter = gst_d3d12_device_get_adapter_handle (priv->device); - auto hr = D3D11CreateDevice (adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, feature_levels, - G_N_ELEMENTS (feature_levels), D3D11_SDK_VERSION, - &priv->d3d11_device, nullptr, nullptr); - - if (FAILED (hr)) { - hr = D3D11CreateDevice (adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, &feature_levels1, - G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, - &priv->d3d11_device, nullptr, nullptr); - } - - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't create d3d11 device"); - return FALSE; - } - - ComPtr < ID3D10Multithread > multi_thread; - priv->d3d11_device.As (&multi_thread); - if (multi_thread) - multi_thread->SetMultithreadProtected (TRUE); - - ComPtr < IDXGIDevice > dxgi_device; - hr = priv->d3d11_device.As (&dxgi_device); - if (FAILED (hr)) { - GST_WARNING_OBJECT (self, "IDXGIDevice interface unavailable"); - return FALSE; - } - - ComPtr < IInspectable > inspectable; - hr = g_vtable.CreateDirect3D11DeviceFromDXGIDevice (dxgi_device.Get (), - &inspectable); - if (FAILED (hr)) { - GST_WARNING_OBJECT (self, "CreateDirect3D11DeviceFromDXGIDevice failed"); - return FALSE; - } - - ComPtr < IDirect3DDevice > device; - hr = inspectable.As (&device); - if (FAILED (hr)) { - GST_WARNING_OBJECT (self, "IDirect3DDevice interface unavailable"); - return FALSE; - } - - *d3d_device = device.Detach (); - - return TRUE; -} - -static gboolean -create_session (GstD3D12GraphicsCapture * self, IDirect3DDevice * d3d_device) -{ - auto priv = self->priv; - auto capture = priv->capture; - - ComPtr < IGraphicsCaptureItemInterop > interop; - auto hr = GstGetActivationFactory < IGraphicsCaptureItemInterop, - RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem > (&interop); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "IGraphicsCaptureItemInterop is not available"); - return FALSE; - } - - ComPtr < IGraphicsCaptureItem > item; - if (priv->monitor_handle) { - hr = interop->CreateForMonitor (priv->monitor_handle, IID_PPV_ARGS (&item)); - } else { - hr = interop->CreateForWindow (priv->window_handle, IID_PPV_ARGS (&item)); - } - - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't create item"); - return FALSE; - } - - ComPtr < IDirect3D11CaptureFramePoolStatics > pool_statics; - hr = GstGetActivationFactory < IDirect3D11CaptureFramePoolStatics, - RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool > - (&pool_statics); - if (FAILED (hr)) { - GST_WARNING_OBJECT (self, - "IDirect3D11CaptureFramePoolStatics is unavailable"); - return FALSE; - } - - hr = MakeAndInitialize < GraphicsCapture > (&priv->capture, - self, priv->device, priv->d3d11_device.Get (), d3d_device, - pool_statics.Get (), item.Get (), priv->window_handle); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't initialize capture object"); - return FALSE; - } - - return TRUE; -} - -static gpointer -gst_d3d11_graphics_thread_func (GstD3D12GraphicsCapture * self) -{ - HRESULT hr; - ComPtr < IDispatcherQueueController > queue_ctrl; - auto priv = self->priv; - HANDLE event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS); - ComPtr < IDirect3DDevice > d3d_device; - HANDLE waitables = { priv->shutdown_handle, event_handle }; - ComPtr < AsyncWaiter > async_waiter; - ComPtr < IAsyncAction > shutdown_action; - - GST_INFO_OBJECT (self, "Entering loop thread"); - - g_vtable.SetThreadDpiAwarenessContext - (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); - - g_vtable.RoInitialize (RO_INIT_MULTITHREADED); - - hr = MakeAndInitialize < AsyncWaiter > (&async_waiter, event_handle); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't create async waiter"); - goto out; - } - - DispatcherQueueOptions queue_opt; - queue_opt.dwSize = sizeof (DispatcherQueueOptions); - queue_opt.threadType = DQTYPE_THREAD_CURRENT; - queue_opt.apartmentType = DQTAT_COM_NONE; - - hr = g_vtable.CreateDispatcherQueueController (queue_opt, &queue_ctrl); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't create dispatcher queue controller"); - goto out; - } - - if (!open_device (self, &d3d_device)) { - GST_ERROR_OBJECT (self, "Couldn't open device"); - goto out; - } - - if (!create_session (self, d3d_device.Get ())) { - GST_ERROR_OBJECT (self, "Couldn't open session"); - goto out; - } - - { - std::lock_guard < std::mutex > lk (priv->loop_lock); - priv->loop_state = LOOP_STATE_RUNNING; - priv->loop_cond.notify_all (); - } - - while (true) { - MSG msg; - while (PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - - auto wait_ret = MsgWaitForMultipleObjects (G_N_ELEMENTS (waitables), - waitables, FALSE, INFINITE, QS_ALLINPUT); - - if (wait_ret == WAIT_OBJECT_0) { - GST_DEBUG_OBJECT (self, "Begin shutdown"); - priv->capture->Close (); - hr = queue_ctrl->ShutdownQueueAsync (&shutdown_action); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Shutdown failed"); - break; - } - - hr = shutdown_action->put_Completed (async_waiter.Get ()); - if (FAILED (hr)) { - GST_ERROR_OBJECT (self, "Couldn't install shutdown callback"); - break; - } - } else if (wait_ret == WAIT_OBJECT_0 + 1) { - GST_DEBUG_OBJECT (self, "Shutdown completed"); - break; - } else if (wait_ret != WAIT_OBJECT_0 + G_N_ELEMENTS (waitables)) { - GST_ERROR_OBJECT (self, "Unexpected wait return %u", (guint) wait_ret); - break; - } - } - -out: - { - std::lock_guard < std::mutex > lk (priv->loop_lock); - priv->loop_state = LOOP_STATE_STOPPED; - priv->loop_cond.notify_all (); - } - - priv->capture = nullptr; - queue_ctrl = nullptr; - shutdown_action = nullptr; - async_waiter = nullptr; - - if (d3d_device) { - ComPtr < IClosable > closable; - d3d_device.As (&closable); - if (closable) - closable->Close (); - - closable = nullptr; - d3d_device = nullptr; - } - - CloseHandle (event_handle); - - g_vtable.RoUninitialize (); - - GST_INFO_OBJECT (self, "Leaving loop thread"); - - return nullptr; -} - static GstFlowReturn gst_d3d12_graphics_capture_prepare (GstD3D12ScreenCapture * capture, guint flags) @@ -1200,6 +1303,7 @@ return TRUE; } +/* *INDENT-OFF* */ GstD3D12ScreenCapture * gst_d3d12_graphics_capture_new (GstD3D12Device * device, HWND window_handle, HMONITOR monitor_handle) @@ -1207,42 +1311,50 @@ g_return_val_if_fail (device, nullptr); if (!gst_d3d12_graphics_capture_load_library ()) { - GST_WARNING ("Couldn't load library"); + GST_WARNING_OBJECT (device, "Couldn't load library"); return nullptr; } if (window_handle && !IsWindow (window_handle)) { - GST_ERROR ("%p is not a valid HWND", window_handle); + GST_ERROR_OBJECT (device, "%p is not a valid HWND", window_handle); return nullptr; } - auto self = (GstD3D12GraphicsCapture *) - g_object_new (GST_TYPE_D3D12_GRAPHICS_CAPTURE, nullptr); - gst_object_ref_sink (self); - - auto priv = self->priv; - priv->device = (GstD3D12Device *) gst_object_ref (device); - priv->window_handle = window_handle; - priv->monitor_handle = monitor_handle; - - priv->loop_thread = g_thread_new ("GstD3D12GraphicsCapture", - (GThreadFunc) gst_d3d11_graphics_thread_func, self); + ComPtr<GraphicsCapture> capture; + ComPtr<IDispatcherQueueHandler> handler; + auto hr = MakeAndInitialize<LambdaHandler<std::function<HRESULT()>>>(&handler, + device, monitor_handle, window_handle, output = capture.GetAddressOf()() -> HRESULT + { + ComPtr<GraphicsCapture> capture; + HRESULT hr = MakeAndInitialize<GraphicsCapture>(&capture, + device, monitor_handle, window_handle); + if (FAILED(hr)) + return hr; + + *output = capture.Detach(); + return S_OK; + }); - bool configured = false; - { - std::unique_lock < std::mutex > lk (priv->loop_lock); - while (priv->loop_state == LOOP_STATE_INIT) - priv->loop_cond.wait (lk); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't create callback object"); + return nullptr; + } - if (priv->loop_state == LOOP_STATE_RUNNING) - configured = true; + hr = QueueManger::GetInstance ()->RunOnDispatcherThread (handler.Get ()); + if (FAILED (hr)) { + GST_ERROR_OBJECT (device, "Couldn't create capture object"); + return nullptr; } - if (!configured) - gst_clear_object (&self); + auto self = (GstD3D12GraphicsCapture *) + g_object_new (GST_TYPE_D3D12_GRAPHICS_CAPTURE, nullptr); + gst_object_ref_sink (self); + + self->priv->capture = capture; return (GstD3D12ScreenCapture *) self; } +/* *INDENT-ON* */ void gst_d3d12_graphics_capture_show_border (GstD3D12GraphicsCapture * capture, @@ -1280,3 +1392,9 @@ return priv->capture->GetVideoFrame (crop_rect, buffer, width, height); } + +void +gst_d3d12_graphics_capture_deinit (void) +{ + QueueManger::Deinit (); +}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/d3d12/gstd3d12graphicscapture.h -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/d3d12/gstd3d12graphicscapture.h
Changed
@@ -30,6 +30,8 @@ gboolean gst_d3d12_graphics_capture_load_library (void); +void gst_d3d12_graphics_capture_deinit (void); + GstD3D12ScreenCapture * gst_d3d12_graphics_capture_new (GstD3D12Device * device, HWND window_handle, HMONITOR monitor_handle);
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/d3d12/gstd3d12screencapturedevice.cpp -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/d3d12/gstd3d12screencapturedevice.cpp
Changed
@@ -72,8 +72,11 @@ GstDevice parent; HMONITOR monitor_handle; + + gchar *device_path; }; +static void gst_d3d12_screen_capture_device_finalize (GObject * object); static void gst_d3d12_screen_capture_device_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_d3d12_screen_capture_device_set_property (GObject * object, @@ -81,6 +84,7 @@ static GstElement *gst_d3d12_screen_capture_device_create_element (GstDevice * device, const gchar * name); +#define gst_d3d12_screen_capture_device_parent_class device_parent_class G_DEFINE_TYPE (GstD3D12ScreenCaptureDevice, gst_d3d12_screen_capture_device, GST_TYPE_DEVICE); @@ -91,6 +95,7 @@ auto object_class = G_OBJECT_CLASS (klass); auto dev_class = GST_DEVICE_CLASS (klass); + object_class->finalize = gst_d3d12_screen_capture_device_finalize; object_class->get_property = gst_d3d12_screen_capture_device_get_property; object_class->set_property = gst_d3d12_screen_capture_device_set_property; @@ -109,6 +114,16 @@ } static void +gst_d3d12_screen_capture_device_finalize (GObject * object) +{ + auto self = GST_D3D12_SCREEN_CAPTURE_DEVICE (object); + + g_free (self->device_path); + + G_OBJECT_CLASS (device_parent_class)->finalize (object); +} + +static void gst_d3d12_screen_capture_device_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { @@ -482,6 +497,9 @@ "Source/Monitor", "properties", props, "monitor-handle", (guint64) output_desc->Monitor, nullptr); + auto screen_dev = GST_D3D12_SCREEN_CAPTURE_DEVICE (device); + screen_dev->device_path = g_strdup (device_path.c_str ()); + gst_caps_unref (caps); return device; @@ -594,7 +612,7 @@ GList *new_devices = nullptr; GList *to_add = nullptr; GList *to_remove = nullptr; - GList *iter; + GList *iter, *walk; GST_DEBUG_OBJECT (self, "Device updated"); @@ -627,6 +645,44 @@ } } + iter = to_remove; + while (iter) { + auto prev_dev = GST_D3D12_SCREEN_CAPTURE_DEVICE (iter->data); + + if (!prev_dev->device_path) { + iter = g_list_next (iter); + continue; + } + + walk = to_add; + bool found = false; + while (walk) { + auto new_dev = GST_D3D12_SCREEN_CAPTURE_DEVICE (walk->data); + + if (!new_dev->device_path || + g_strcmp0 (prev_dev->device_path, new_dev->device_path)) { + walk = g_list_next (walk); + continue; + } + + gst_device_provider_device_changed (provider, GST_DEVICE (new_dev), + GST_DEVICE (prev_dev)); + gst_object_unref (new_dev); + to_add = g_list_delete_link (to_add, walk); + found = true; + break; + } + + if (found) { + gst_object_unref (prev_dev); + auto next = iter->next; + to_remove = g_list_delete_link (to_remove, iter); + iter = next; + } else { + iter = g_list_next (iter); + } + } + for (iter = to_remove; iter; iter = g_list_next (iter)) gst_device_provider_device_remove (provider, GST_DEVICE (iter->data));
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/d3d12/meson.build -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/d3d12/meson.build
Changed
@@ -198,7 +198,7 @@ name: 'DirectXMath support in Windows SDK') if not have_dx_math - directxmath_dep = dependency('directxmath', + directxmath_dep = dependency('DirectXMath', 'directxmath', allow_fallback: true, required: d3d12_option) if not directxmath_dep.found()
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/d3d12/plugin.cpp -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/d3d12/plugin.cpp
Changed
@@ -66,6 +66,10 @@ #include "gstd3d12upload.h" #endif +#ifdef HAVE_WGC +#include "gstd3d12graphicscapture.h" +#endif + /* *INDENT-OFF* */ using namespace Microsoft::WRL; /* *INDENT-ON* */ @@ -73,6 +77,9 @@ static void plugin_deinit (gpointer data) { +#ifdef HAVE_WGC + gst_d3d12_graphics_capture_deinit (); +#endif gst_d3d12_ipc_client_deinit (); gst_d3d12_flush_all_devices (); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvaav1dec.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvaav1dec.c
Changed
@@ -95,9 +95,12 @@ GstVaAV1Dec *self = GST_VA_AV1_DEC (decoder); GstVaBaseDec *base = GST_VA_BASE_DEC (decoder); - /* Ignore downstream renegotiation request. */ - if (!base->need_negotiation) - return TRUE; + /* Do not (re-)open the decoder in case the input state hasn't changed. */ + if (!base->need_negotiation) { + GST_DEBUG_OBJECT (decoder, + "Input state hasn't changed, no need to (re-)open the decoder"); + goto done; + } base->need_negotiation = FALSE; @@ -128,6 +131,7 @@ } self->preferred_format = GST_VIDEO_INFO_FORMAT (&base->output_state->info); +done: return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvaav1enc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvaav1enc.c
Changed
@@ -2768,7 +2768,7 @@ GstVideoCodecState *output_state; GstVideoFormat format, reconf_format = GST_VIDEO_FORMAT_UNKNOWN; VAProfile profile; - gboolean do_renegotiation = TRUE, do_reopen, need_negotiation; + gboolean do_renegotiation = TRUE, do_reopen, need_negotiation, rc_same; guint max_ref_frames, max_surfaces = 0, rt_format, depth = 0, chrome = 0, codedbuf_size, latency_num; gint width, height; @@ -2800,11 +2800,15 @@ if (profile == VAProfileNone) return FALSE; + GST_OBJECT_LOCK (self); + rc_same = (self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + GST_OBJECT_UNLOCK (self); + /* first check */ do_reopen = !(base->profile == profile && base->rt_format == rt_format && format == reconf_format && width == base->width - && height == base->height && self->prop.rc_ctrl == self->rc.rc_ctrl_mode - && depth == self->depth && chrome == self->chrome); + && height == base->height && rc_same && depth == self->depth + && chrome == self->chrome); if (do_reopen && gst_va_encoder_is_open (base->encoder)) gst_va_encoder_close (base->encoder); @@ -2869,8 +2873,8 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); max_ref_frames = GST_AV1_NUM_REF_FRAMES;
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvabasedec.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvabasedec.c
Changed
@@ -673,27 +673,39 @@ { GstVaBaseDec *base = GST_VA_BASE_DEC (decoder); - /* Ignore downstream renegotiation request. */ - if (!base->need_negotiation) - return TRUE; + /* Do not (re-)open the decoder in case the input state hasn't changed. */ + if (!base->need_negotiation) { + GST_DEBUG_OBJECT (decoder, + "Input state hasn't changed, no need to (re-)open the decoder"); + goto done; + } base->need_negotiation = FALSE; if (!gst_va_decoder_config_is_equal (base->decoder, base->profile, base->rt_format, base->width, base->height)) { if (gst_va_decoder_is_open (base->decoder) && - !gst_va_decoder_close (base->decoder)) + !gst_va_decoder_close (base->decoder)) { + GST_WARNING_OBJECT (decoder, "Failed to close decoder"); return FALSE; - if (!gst_va_decoder_open (base->decoder, base->profile, base->rt_format)) + } + if (!gst_va_decoder_open (base->decoder, base->profile, base->rt_format)) { + GST_WARNING_OBJECT (decoder, "Failed to open decoder"); return FALSE; + } if (!gst_va_decoder_set_frame_size (base->decoder, base->width, - base->height)) + base->height)) { + GST_WARNING_OBJECT (decoder, "Failed to set frame size"); return FALSE; + } } - if (!gst_va_base_dec_set_output_state (base)) + if (!gst_va_base_dec_set_output_state (base)) { + GST_WARNING_OBJECT (decoder, "Failed to set output state"); return FALSE; + } +done: return GST_VIDEO_DECODER_CLASS (GST_VA_BASE_DEC_GET_PARENT_CLASS (decoder)) ->negotiate (decoder); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvah264dec.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvah264dec.c
Changed
@@ -500,7 +500,7 @@ error: { GST_WARNING_OBJECT (self, - "Failed to allocated output buffer, return %s", + "Failed to allocate output buffer, returning %s", gst_flow_get_name (ret)); return ret; }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvah264enc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvah264enc.c
Changed
@@ -1586,7 +1586,7 @@ GstVideoCodecState *output_state = NULL; GstVideoFormat format, reconf_format = GST_VIDEO_FORMAT_UNKNOWN; VAProfile profile = VAProfileNone; - gboolean do_renegotiation = TRUE, do_reopen, need_negotiation; + gboolean do_renegotiation = TRUE, do_reopen, need_negotiation, rc_same; guint max_ref_frames, max_surfaces = 0, rt_format = 0, codedbuf_size, latency_num; gint width, height; @@ -1611,10 +1611,14 @@ if (!_decide_profile (self, &profile, &rt_format)) return FALSE; + GST_OBJECT_LOCK (self); + rc_same = (self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + GST_OBJECT_UNLOCK (self); + /* first check */ do_reopen = !(base->profile == profile && base->rt_format == rt_format && format == reconf_format && width == base->width - && height == base->height && self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + && height == base->height && rc_same); if (do_reopen && gst_va_encoder_is_open (base->encoder)) gst_va_encoder_close (base->encoder); @@ -1681,11 +1685,10 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); - max_ref_frames = self->gop.b_pyramid ? self->gop.highest_pyramid_level + 2 : self->gop.num_ref_frames; max_ref_frames += base->preferred_output_delay;
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvah265enc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvah265enc.c
Changed
@@ -4506,7 +4506,7 @@ GstVideoCodecState *output_state = NULL; GstVideoFormat format, reconf_format = GST_VIDEO_FORMAT_UNKNOWN; VAProfile profile = VAProfileNone; - gboolean do_renegotiation = TRUE, do_reopen, need_negotiation; + gboolean do_renegotiation = TRUE, do_reopen, need_negotiation, rc_same; guint max_ref_frames, max_surfaces = 0, rt_format = 0, codedbuf_size, latency_num; gint width, height; @@ -4532,10 +4532,14 @@ if (!_h265_decide_profile (self, &profile, &rt_format)) return FALSE; + GST_OBJECT_LOCK (self); + rc_same = (self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + GST_OBJECT_UNLOCK (self); + /* first check */ do_reopen = !(base->profile == profile && base->rt_format == rt_format && format == reconf_format && width == base->width - && height == base->height && self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + && height == base->height && rc_same); if (do_reopen && gst_va_encoder_is_open (base->encoder)) gst_va_encoder_close (base->encoder); @@ -4652,8 +4656,8 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); max_ref_frames = self->gop.b_pyramid ?
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvajpegdec.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvajpegdec.c
Changed
@@ -360,9 +360,12 @@ guint64 modifier; GstCapsFeatures *capsfeatures = NULL; - /* Ignore downstream renegotiation request. */ - if (!base->need_negotiation) - return TRUE; + /* Do not (re-)open the decoder in case the input state hasn't changed. */ + if (!base->need_negotiation) { + GST_DEBUG_OBJECT (decoder, + "Input state hasn't changed, no need to (re-)open the decoder"); + goto done; + } base->need_negotiation = FALSE; @@ -420,6 +423,7 @@ GST_INFO_OBJECT (self, "Negotiated caps %" GST_PTR_FORMAT, base->output_state->caps); +done: return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvajpegenc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvajpegenc.c
Changed
@@ -378,8 +378,7 @@ } /* Unknown frame rate is allowed for jpeg, such as a single still image. */ - if (GST_VIDEO_INFO_FPS_N (&base->in_info) == 0 - || GST_VIDEO_INFO_FPS_D (&base->in_info) == 0) { + if (GST_VIDEO_INFO_FPS_D (&base->in_info) == 0) { GST_DEBUG_OBJECT (self, "Unknown framerate"); GST_VIDEO_INFO_FPS_N (&base->in_info) = 0; GST_VIDEO_INFO_FPS_D (&base->in_info) = 1; @@ -395,8 +394,8 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvavp8enc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvavp8enc.c
Changed
@@ -577,7 +577,7 @@ GstVideoCodecState *output_state; GstVideoFormat format, reconf_format = GST_VIDEO_FORMAT_UNKNOWN; const GstVideoFormatInfo *format_info; - gboolean do_renegotiation = TRUE, do_reopen, need_negotiation; + gboolean do_renegotiation = TRUE, do_reopen, need_negotiation, rc_same; guint max_ref_frames, max_surfaces = 0, codedbuf_size, latency_num; gint width, height; GstClockTime latency; @@ -605,9 +605,13 @@ reconf_format = GST_VIDEO_INFO_FORMAT (&vi); } + GST_OBJECT_LOCK (self); + rc_same = (self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + GST_OBJECT_UNLOCK (self); + /* First check */ do_reopen = !(format == reconf_format && width == base->width - && height == base->height && self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + && height == base->height && rc_same); if (do_reopen && gst_va_encoder_is_open (base->encoder)) gst_va_encoder_close (base->encoder); @@ -655,8 +659,8 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); max_ref_frames = GST_VP8_MAX_REF_FRAMES;
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvavp9dec.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvavp9dec.c
Changed
@@ -555,9 +555,12 @@ GstVaVp9Dec *self = GST_VA_VP9_DEC (decoder); gboolean need_open; - /* Ignore downstream renegotiation request. */ - if (!base->need_negotiation) - return TRUE; + /* Do not (re-)open the decoder in case the input state hasn't changed. */ + if (!base->need_negotiation) { + GST_DEBUG_OBJECT (decoder, + "Input state hasn't changed, no need to (re-)open the decoder"); + goto done; + } base->need_negotiation = FALSE; @@ -601,6 +604,7 @@ if (!gst_va_base_dec_set_output_state (base)) return FALSE; +done: return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); }
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/va/gstvavp9enc.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/va/gstvavp9enc.c
Changed
@@ -2097,7 +2097,7 @@ GstVideoCodecState *output_state; GstVideoFormat format, reconf_format = GST_VIDEO_FORMAT_UNKNOWN; VAProfile profile; - gboolean do_renegotiation = TRUE, do_reopen, need_negotiation; + gboolean do_renegotiation = TRUE, do_reopen, need_negotiation, rc_same; guint max_ref_frames, max_surfaces = 0, rt_format, depth = 0, chrome = 0, codedbuf_size, latency_num; gint width, height; @@ -2129,11 +2129,15 @@ if (profile == VAProfileNone) return FALSE; + GST_OBJECT_LOCK (self); + rc_same = (self->prop.rc_ctrl == self->rc.rc_ctrl_mode); + GST_OBJECT_UNLOCK (self); + /* first check */ do_reopen = !(base->profile == profile && base->rt_format == rt_format && format == reconf_format && width == base->width - && height == base->height && self->prop.rc_ctrl == self->rc.rc_ctrl_mode - && depth == self->depth && chrome == self->chrome); + && height == base->height && rc_same && depth == self->depth + && chrome == self->chrome); if (do_reopen && gst_va_encoder_is_open (base->encoder)) gst_va_encoder_close (base->encoder); @@ -2188,8 +2192,8 @@ /* Set the latency */ latency = gst_util_uint64_scale (latency_num, - GST_VIDEO_INFO_FPS_D (&base->input_state->info) * GST_SECOND, - GST_VIDEO_INFO_FPS_N (&base->input_state->info)); + GST_VIDEO_INFO_FPS_D (&base->in_info) * GST_SECOND, + GST_VIDEO_INFO_FPS_N (&base->in_info)); gst_video_encoder_set_latency (venc, latency, latency); max_ref_frames = GST_VP9_REF_FRAMES;
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2activator.cpp
Added
@@ -0,0 +1,151 @@ +/* GStreamer + * Copyright (C) 2025 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. + */ + +#include "gstwasapi2activator.h" +#include <objidl.h> + +GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); +#define GST_CAT_DEFAULT gst_wasapi2_debug + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +void +Wasapi2ActivationHandler::CreateInstance (Wasapi2ActivationHandler ** handler, + const wchar_t * device_id, + const AUDIOCLIENT_ACTIVATION_PARAMS * params) +{ + auto self = new Wasapi2ActivationHandler (); + self->device_id_ = device_id; + + if (params) { + self->params_ = *params; + self->prop_.vt = VT_BLOB; + self->prop_.blob.cbSize = sizeof (AUDIOCLIENT_ACTIVATION_PARAMS); + self->prop_.blob.pBlobData = (BYTE *) &self->params_; + self->have_params_ = true; + } + + *handler = self; +} + +STDMETHODIMP_ (ULONG) +Wasapi2ActivationHandler::AddRef (void) +{ + return InterlockedIncrement (&refcount_); +} + +STDMETHODIMP_ (ULONG) +Wasapi2ActivationHandler::Release (void) +{ + ULONG ref_count; + + ref_count = InterlockedDecrement (&refcount_); + + if (ref_count == 0) + delete this; + + return ref_count; +} + +STDMETHODIMP +Wasapi2ActivationHandler::QueryInterface (REFIID riid, void ** object) +{ + if (riid == __uuidof(IUnknown) || riid == __uuidof(IAgileObject)) { + *object = static_cast<IUnknown *>(static_cast<Wasapi2ActivationHandler*>(this)); + } else if (riid == __uuidof(IActivateAudioInterfaceCompletionHandler)) { + *object = static_cast<IActivateAudioInterfaceCompletionHandler *>( + static_cast<Wasapi2ActivationHandler*>(this)); + } else if (riid == IID_Wasapi2ActivationHandler) { + *object = this; + } else { + *object = nullptr; + return E_NOINTERFACE; + } + + AddRef (); + + return S_OK; +} + +STDMETHODIMP +Wasapi2ActivationHandler::ActivateCompleted (IActivateAudioInterfaceAsyncOperation * op) +{ + ComPtr<IUnknown> iface; + HRESULT hr = S_OK; + hr = op->GetActivateResult (&hr, &iface); + if (FAILED (hr)) { + GST_ERROR ("Couldn't get activate result, hr: 0x%x", (guint) hr); + activate_hr_ = hr; + SetEvent (event_); + return hr; + } + + hr = iface.As (&client_); + activate_hr_ = hr; + + GST_LOG ("Activation result 0x%x", (guint) hr); + + SetEvent (event_); + + return hr; +} + +HRESULT +Wasapi2ActivationHandler::ActivateAsync () +{ + ComPtr<IActivateAudioInterfaceAsyncOperation> async_op; + auto hr = ActivateAudioInterfaceAsync (device_id_.c_str (), + __uuidof (IAudioClient), have_params_ ? &prop_ : nullptr, + this, &async_op); + if (!gst_wasapi2_result (hr)) { + activate_hr_ = hr; + SetEvent (event_); + } + + return hr; +} + +HRESULT +Wasapi2ActivationHandler::GetClient (IAudioClient ** client, DWORD timeout) +{ + WaitForSingleObject (event_, timeout); + auto hr = activate_hr_.load (); + if (!gst_wasapi2_result (hr)) + return hr; + + if (!client) + return S_OK; + + *client = client_.Get (); + (*client)->AddRef (); + + return S_OK; +} + +Wasapi2ActivationHandler::Wasapi2ActivationHandler () +{ + event_ = CreateEvent (nullptr, TRUE, FALSE, nullptr); +} + +Wasapi2ActivationHandler::~Wasapi2ActivationHandler () +{ + CloseHandle (event_); +} +/* *INDENT-ON* */
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2activator.h
Added
@@ -0,0 +1,94 @@ +/* GStreamer + * Copyright (C) 2025 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. + */ + +#pragma once + +#include <gst/gst.h> +#include "gstwasapi2util.h" +#include <wrl.h> +#include <atomic> +#include <string> + +/* Copy of audioclientactivationparams.h since those types are defined only for + * NTDDI_VERSION >= NTDDI_WIN10_FE */ +#define VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" +typedef enum +{ + PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE = 0, + PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE = 1 +} PROCESS_LOOPBACK_MODE; + +typedef struct +{ + DWORD TargetProcessId; + PROCESS_LOOPBACK_MODE ProcessLoopbackMode; +} AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; + +typedef enum +{ + AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT = 0, + AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK = 1 +} AUDIOCLIENT_ACTIVATION_TYPE; + +typedef struct +{ + AUDIOCLIENT_ACTIVATION_TYPE ActivationType; + union + { + AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; + } DUMMYUNIONNAME; +} AUDIOCLIENT_ACTIVATION_PARAMS; +/* End of audioclientactivationparams.h */ + +DEFINE_GUID (IID_Wasapi2ActivationHandler, 0xaa7e8f85, 0x211e, + 0x42cc, 0x8c, 0x86, 0x99, 0x83, 0x5b, 0xef, 0x54, 0x86); +class Wasapi2ActivationHandler : + public IActivateAudioInterfaceCompletionHandler +{ +public: + static void CreateInstance (Wasapi2ActivationHandler ** handler, + const wchar_t * device_id, + const AUDIOCLIENT_ACTIVATION_PARAMS * params); + + STDMETHODIMP_ (ULONG) AddRef (void); + + STDMETHODIMP_ (ULONG) Release (void); + + STDMETHODIMP QueryInterface (REFIID riid, void ** object); + + STDMETHODIMP ActivateCompleted (IActivateAudioInterfaceAsyncOperation * op); + + HRESULT ActivateAsync (void); + + HRESULT GetClient (IAudioClient ** client, DWORD timeout); + +private: + Wasapi2ActivationHandler (); + virtual ~Wasapi2ActivationHandler (); + +private: + Microsoft::WRL::ComPtr<IAudioClient> client_; + std::atomic<HRESULT> activate_hr_ = { E_FAIL }; + HANDLE event_; + PROPVARIANT prop_ = { }; + AUDIOCLIENT_ACTIVATION_PARAMS params_ = { }; + bool have_params_ = false; + std::wstring device_id_; + ULONG refcount_ = 1; +};
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2device.cpp
Added
@@ -0,0 +1,395 @@ +/* GStreamer + * Copyright (C) 2018 Nirbheek Chauhan <nirbheek@centricular.com> + * 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 "gstwasapi2device.h" +#include "gstwasapi2util.h" +#include "gstwasapi2enumerator.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); +#define GST_CAT_DEFAULT gst_wasapi2_debug + +enum +{ + PROP_0, + PROP_DEVICE, +}; + +struct _GstWasapi2Device +{ + GstDevice parent; + + gchar *device_id; + const gchar *factory_name; + GstWasapi2EndpointClass device_class; +}; + +G_DEFINE_TYPE (GstWasapi2Device, gst_wasapi2_device, GST_TYPE_DEVICE); + +static void gst_wasapi2_device_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_wasapi2_device_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_wasapi2_device_finalize (GObject * object); +static GstElement *gst_wasapi2_device_create_element (GstDevice * device, + const gchar * name); + +static void +gst_wasapi2_device_class_init (GstWasapi2DeviceClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); + + dev_class->create_element = gst_wasapi2_device_create_element; + + gobject_class->get_property = gst_wasapi2_device_get_property; + gobject_class->set_property = gst_wasapi2_device_set_property; + gobject_class->finalize = gst_wasapi2_device_finalize; + + g_object_class_install_property (gobject_class, PROP_DEVICE, + g_param_spec_string ("device", "Device", + "Audio device ID as provided by " + "Windows.Devices.Enumeration.DeviceInformation.Id", nullptr, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +static void +gst_wasapi2_device_init (GstWasapi2Device * self) +{ +} + +static void +gst_wasapi2_device_finalize (GObject * object) +{ + GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); + + g_free (self->device_id); + + G_OBJECT_CLASS (gst_wasapi2_device_parent_class)->finalize (object); +} + +static GstElement * +gst_wasapi2_device_create_element (GstDevice * device, const gchar * name) +{ + GstWasapi2Device *self = GST_WASAPI2_DEVICE (device); + GstElement *elem; + + elem = gst_element_factory_make (self->factory_name, name); + + g_object_set (elem, "device", self->device_id, nullptr); + + if (self->device_class == GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE) + g_object_set (elem, "loopback", TRUE, nullptr); + + return elem; +} + +static void +gst_wasapi2_device_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string (value, self->device_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_wasapi2_device_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWasapi2Device *self = GST_WASAPI2_DEVICE (object); + + switch (prop_id) { + case PROP_DEVICE: + /* G_PARAM_CONSTRUCT_ONLY */ + self->device_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +struct _GstWasapi2DeviceProvider +{ + GstDeviceProvider parent; + + GstWasapi2Enumerator *enumerator; + GPtrArray *device_list; +}; + +G_DEFINE_TYPE (GstWasapi2DeviceProvider, gst_wasapi2_device_provider, + GST_TYPE_DEVICE_PROVIDER); + +static void gst_wasapi2_device_provider_dispose (GObject * object); + +static GList *gst_wasapi2_device_provider_probe (GstDeviceProvider * provider); +static gboolean +gst_wasapi2_device_provider_start (GstDeviceProvider * provider); +static void gst_wasapi2_device_provider_stop (GstDeviceProvider * provider); +static void gst_wasapi2_device_provider_on_updated (GstWasapi2Enumerator * + object, GstWasapi2DeviceProvider * self); + +static void +gst_wasapi2_device_provider_class_init (GstWasapi2DeviceProviderClass * klass) +{ + auto gobject_class = G_OBJECT_CLASS (klass); + auto provider_class = GST_DEVICE_PROVIDER_CLASS (klass); + + gobject_class->dispose = gst_wasapi2_device_provider_dispose; + + provider_class->probe = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_probe); + provider_class->start = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_start); + provider_class->stop = GST_DEBUG_FUNCPTR (gst_wasapi2_device_provider_stop); + + gst_device_provider_class_set_static_metadata (provider_class, + "WASAPI (Windows Audio Session API) Device Provider", + "Source/Sink/Audio", "List WASAPI source devices", + "Nirbheek Chauhan <nirbheek@centricular.com>, " + "Seungha Yang <seungha@centricular.com>"); +} + +static void +gst_wasapi2_device_provider_init (GstWasapi2DeviceProvider * self) +{ + self->enumerator = gst_wasapi2_enumerator_new (); + g_signal_connect (self->enumerator, "updated", + G_CALLBACK (gst_wasapi2_device_provider_on_updated), self); + + self->device_list = g_ptr_array_new_with_free_func ((GDestroyNotify) + gst_wasapi2_enumerator_entry_free); +} + +static void +gst_wasapi2_device_provider_dispose (GObject * object) +{ + auto self = GST_WASAPI2_DEVICE_PROVIDER (object); + + gst_clear_object (&self->enumerator); + + g_clear_pointer (&self->device_list, g_ptr_array_unref); + + G_OBJECT_CLASS (gst_wasapi2_device_provider_parent_class)->dispose (object); +} + +static GList * +gst_wasapi2_device_provider_probe (GstDeviceProvider * provider) +{ + auto self = GST_WASAPI2_DEVICE_PROVIDER (provider); + GList *devices = nullptr; + + g_ptr_array_set_size (self->device_list, 0); + gst_wasapi2_enumerator_enumerate_devices (self->enumerator, + self->device_list); + + for (guint i = 0; i < self->device_list->len; i++) { + auto entry = (GstWasapi2EnumeratorEntry *) + g_ptr_array_index (self->device_list, i); + + auto props = gst_structure_new ("wasapi2-proplist", + "device.api", G_TYPE_STRING, "wasapi2", + "device.id", G_TYPE_STRING, entry->device_id, + "device.default", G_TYPE_BOOLEAN, entry->is_default, + "wasapi2.device.description", G_TYPE_STRING, entry->device_name, + nullptr); + + if (entry->flow == eCapture) { + gst_structure_set (props, + "wasapi2.device.loopback", G_TYPE_BOOLEAN, FALSE, nullptr); + + auto device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE, + "device", entry->device_id, + "display-name", entry->device_name, "caps", entry->caps, + "device-class", "Audio/Source", "properties", props, nullptr); + gst_structure_free (props); + + GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2src"; + GST_WASAPI2_DEVICE (device)->device_class = + GST_WASAPI2_ENDPOINT_CLASS_CAPTURE; + + devices = g_list_append (devices, device); + } else { + auto prop_copy = gst_structure_copy (props); + gst_structure_set (prop_copy, + "wasapi2.device.loopback", G_TYPE_BOOLEAN, TRUE, nullptr); + + auto device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE, + "device", entry->device_id, + "display-name", entry->device_name, "caps", entry->caps, + "device-class", "Audio/Sink", "properties", props, nullptr); + gst_structure_free (props); + + GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2sink"; + GST_WASAPI2_DEVICE (device)->device_class = + GST_WASAPI2_ENDPOINT_CLASS_RENDER; + + devices = g_list_append (devices, device); + + device = (GstDevice *) g_object_new (GST_TYPE_WASAPI2_DEVICE, + "device", entry->device_id, + "display-name", entry->device_name, "caps", entry->caps, + "device-class", "Audio/Source", "properties", prop_copy, nullptr); + gst_structure_free (prop_copy); + + GST_WASAPI2_DEVICE (device)->factory_name = "wasapi2src"; + GST_WASAPI2_DEVICE (device)->device_class = + GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE; + + devices = g_list_append (devices, device); + } + } + + g_ptr_array_set_size (self->device_list, 0); + + return devices; +} + +static gboolean +gst_wasapi2_device_provider_start (GstDeviceProvider * provider) +{ + auto self = GST_WASAPI2_DEVICE_PROVIDER (provider); + GList *devices = nullptr; + GList *iter; + + if (!self->enumerator) { + GST_ERROR_OBJECT (self, "Enumerator object wasn't configured"); + return FALSE; + } + + devices = gst_wasapi2_device_provider_probe (provider); + if (devices) { + for (iter = devices; iter; iter = g_list_next (iter)) + gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); + + g_list_free (devices); + } + + gst_wasapi2_enumerator_activate_notification (self->enumerator, TRUE); + + return TRUE; +} + +static void +gst_wasapi2_device_provider_stop (GstDeviceProvider * provider) +{ + auto self = GST_WASAPI2_DEVICE_PROVIDER (provider); + + if (self->enumerator) + gst_wasapi2_enumerator_activate_notification (self->enumerator, FALSE); +} + +static gboolean +gst_wasapi2_device_is_in_list (GList * list, GstDevice * device) +{ + GList *iter; + GstStructure *s; + gboolean found = FALSE; + + s = gst_device_get_properties (device); + g_assert (s); + + for (iter = list; iter; iter = g_list_next (iter)) { + GstStructure *other_s; + + other_s = gst_device_get_properties (GST_DEVICE (iter->data)); + g_assert (other_s); + + found = gst_structure_is_equal (s, other_s); + + gst_structure_free (other_s); + if (found) + break; + } + + gst_structure_free (s); + + return found; +} + +static void +gst_wasapi2_device_provider_update_devices (GstWasapi2DeviceProvider * self) +{ + GstDeviceProvider *provider = GST_DEVICE_PROVIDER_CAST (self); + GList *prev_devices = nullptr; + GList *new_devices = nullptr; + GList *to_add = nullptr; + GList *to_remove = nullptr; + GList *iter; + + GST_OBJECT_LOCK (self); + prev_devices = g_list_copy_deep (provider->devices, + (GCopyFunc) gst_object_ref, nullptr); + GST_OBJECT_UNLOCK (self); + + new_devices = gst_wasapi2_device_provider_probe (provider); + + /* Ownership of GstDevice for gst_device_provider_device_add() + * and gst_device_provider_device_remove() is a bit complicated. + * Remove floating reference here for things to be clear */ + for (iter = new_devices; iter; iter = g_list_next (iter)) + gst_object_ref_sink (iter->data); + + /* Check newly added devices */ + for (iter = new_devices; iter; iter = g_list_next (iter)) { + if (!gst_wasapi2_device_is_in_list (prev_devices, GST_DEVICE (iter->data))) { + to_add = g_list_prepend (to_add, gst_object_ref (iter->data)); + } + } + + /* Check removed device */ + for (iter = prev_devices; iter; iter = g_list_next (iter)) { + if (!gst_wasapi2_device_is_in_list (new_devices, GST_DEVICE (iter->data))) { + to_remove = g_list_prepend (to_remove, gst_object_ref (iter->data)); + } + } + + for (iter = to_remove; iter; iter = g_list_next (iter)) + gst_device_provider_device_remove (provider, GST_DEVICE (iter->data)); + + for (iter = to_add; iter; iter = g_list_next (iter)) + gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); + + if (prev_devices) + g_list_free_full (prev_devices, (GDestroyNotify) gst_object_unref); + + if (to_add) + g_list_free_full (to_add, (GDestroyNotify) gst_object_unref); + + if (to_remove) + g_list_free_full (to_remove, (GDestroyNotify) gst_object_unref); +} + +static void +gst_wasapi2_device_provider_on_updated (GstWasapi2Enumerator * object, + GstWasapi2DeviceProvider * self) +{ + gst_wasapi2_device_provider_update_devices (self); +}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2device.h -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2device.h
Changed
@@ -28,7 +28,9 @@ G_DECLARE_FINAL_TYPE (GstWasapi2Device, gst_wasapi2_device, GST, WASAPI2_DEVICE, GstDevice); -void gst_wasapi2_device_provider_register (GstPlugin * plugin, guint rank); +#define GST_TYPE_WASAPI2_DEVICE_PROVIDER (gst_wasapi2_device_provider_get_type()) +G_DECLARE_FINAL_TYPE (GstWasapi2DeviceProvider, gst_wasapi2_device_provider, + GST, WASAPI2_DEVICE_PROVIDER, GstDeviceProvider); G_END_DECLS
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2enumerator.cpp
Added
@@ -0,0 +1,620 @@ +/* GStreamer + * Copyright (C) 2025 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 "gstwasapi2enumerator.h" +#include "gstwasapi2activator.h" +#include <mutex> +#include <condition_variable> +#include <wrl.h> +#include <functiondiscoverykeys_devpkey.h> +#include <string> + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); +#define GST_CAT_DEFAULT gst_wasapi2_debug + +static GstStaticCaps template_caps = GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS); + +static void gst_wasapi2_on_device_updated (GstWasapi2Enumerator * object); + +/* IMMNotificationClient implementation */ +class IWasapi2NotificationClient : public IMMNotificationClient +{ +public: + static void + CreateInstance (GstWasapi2Enumerator * object, IMMNotificationClient ** client) + { + auto self = new IWasapi2NotificationClient (); + + g_weak_ref_set (&self->obj_, object); + + *client = (IMMNotificationClient *) self; + } + + /* IUnknown */ + STDMETHODIMP + QueryInterface (REFIID riid, void ** object) + { + if (!object) + return E_POINTER; + + if (riid == IID_IUnknown) { + *object = static_cast<IUnknown *> (this); + } else if (riid == __uuidof(IMMNotificationClient)) { + *object = static_cast<IMMNotificationClient *> (this); + } else { + *object = nullptr; + return E_NOINTERFACE; + } + + AddRef (); + + return S_OK; + } + + STDMETHODIMP_ (ULONG) + AddRef (void) + { + GST_TRACE ("%p, %d", this, (guint) ref_count_); + return InterlockedIncrement (&ref_count_); + } + + STDMETHODIMP_ (ULONG) + Release (void) + { + ULONG ref_count; + + GST_TRACE ("%p, %d", this, (guint) ref_count_); + ref_count = InterlockedDecrement (&ref_count_); + + if (ref_count == 0) { + GST_TRACE ("Delete instance %p", this); + delete this; + } + + return ref_count; + } + + /* IMMNotificationClient */ + STDMETHODIMP + OnDeviceStateChanged (LPCWSTR device_id, DWORD new_state) + { + auto object = (GstWasapi2Enumerator *) g_weak_ref_get (&obj_); + if (!object) + return S_OK; + + gst_wasapi2_on_device_updated (object); + gst_object_unref (object); + + return S_OK; + } + + STDMETHODIMP + OnDeviceAdded (LPCWSTR device_id) + { + auto object = (GstWasapi2Enumerator *) g_weak_ref_get (&obj_); + if (!object) + return S_OK; + + gst_wasapi2_on_device_updated (object); + gst_object_unref (object); + + return S_OK; + } + + STDMETHODIMP + OnDeviceRemoved (LPCWSTR device_id) + { + auto object = (GstWasapi2Enumerator *) g_weak_ref_get (&obj_); + if (!object) + return S_OK; + + gst_wasapi2_on_device_updated (object); + gst_object_unref (object); + + return S_OK; + } + + STDMETHODIMP + OnDefaultDeviceChanged (EDataFlow flow, ERole role, LPCWSTR default_device_id) + { + auto object = (GstWasapi2Enumerator *) g_weak_ref_get (&obj_); + if (!object) + return S_OK; + + gst_wasapi2_on_device_updated (object); + gst_object_unref (object); + + return S_OK; + } + + STDMETHODIMP + OnPropertyValueChanged (LPCWSTR device_id, const PROPERTYKEY key) + { + return S_OK; + } + +private: + IWasapi2NotificationClient () + { + g_weak_ref_init (&obj_, nullptr); + } + + virtual ~IWasapi2NotificationClient () + { + g_weak_ref_clear (&obj_); + } + +private: + ULONG ref_count_ = 1; + GWeakRef obj_; +}; + +enum +{ + PROP_0, + PROP_ENUMERATOR, +}; + +enum +{ + SIGNAL_UPDATED, + SIGNAL_LAST, +}; + +static guint wasapi2_device_signalsSIGNAL_LAST = { }; + +struct GstWasapi2EnumeratorPrivate +{ + ComPtr<IMMDeviceEnumerator> handle; + std::mutex lock; + std::condition_variable cond; + + ComPtr<IMMNotificationClient> client; + Wasapi2ActivationHandler *capture_activator = nullptr; + Wasapi2ActivationHandler *render_activator = nullptr; + + void ClearCOM () + { + if (capture_activator) { + capture_activator->GetClient (nullptr, INFINITE); + capture_activator->Release (); + } + + if (render_activator) { + render_activator->GetClient (nullptr, INFINITE); + render_activator->Release (); + } + + if (client && handle) + handle->UnregisterEndpointNotificationCallback (client.Get ()); + + client = nullptr; + handle = nullptr; + } +}; +/* *INDENT-ON* */ + +struct _GstWasapi2Enumerator +{ + GstObject parent; + + GstWasapi2EnumeratorPrivate *priv; + + GThread *thread; + GMainContext *context; + GMainLoop *loop; +}; + +static void gst_wasapi2_enumerator_finalize (GObject * object); + +#define gst_wasapi2_enumerator_parent_class parent_class +G_DEFINE_TYPE (GstWasapi2Enumerator, gst_wasapi2_enumerator, GST_TYPE_OBJECT); + +static void +gst_wasapi2_enumerator_class_init (GstWasapi2EnumeratorClass * klass) +{ + auto object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gst_wasapi2_enumerator_finalize; + + wasapi2_device_signalsSIGNAL_UPDATED = + g_signal_new_class_handler ("updated", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr, G_TYPE_NONE, 0); +} + +static void +gst_wasapi2_enumerator_init (GstWasapi2Enumerator * self) +{ + self->priv = new GstWasapi2EnumeratorPrivate (); + self->context = g_main_context_new (); + self->loop = g_main_loop_new (self->context, FALSE); +} + +static void +gst_wasapi2_enumerator_finalize (GObject * object) +{ + auto self = GST_WASAPI2_ENUMERATOR (object); + + g_main_loop_quit (self->loop); + g_thread_join (self->thread); + g_main_loop_unref (self->loop); + g_main_context_unref (self->context); + + delete self->priv; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_wasapi2_on_device_updated (GstWasapi2Enumerator * object) +{ + /* *INDENT-OFF* */ + g_main_context_invoke_full (object->context, G_PRIORITY_DEFAULT, + (gpointer obj) -> gboolean { + g_signal_emit (obj, wasapi2_device_signalsSIGNAL_UPDATED, 0); + return G_SOURCE_REMOVE; + }, + gst_object_ref (object), (GDestroyNotify) gst_object_unref); + /* *INDENT-ON* */ +} + +static gpointer +gst_wasapi2_enumerator_thread_func (GstWasapi2Enumerator * self) +{ + auto priv = self->priv; + + CoInitializeEx (nullptr, COINIT_MULTITHREADED); + + g_main_context_push_thread_default (self->context); + + auto idle_source = g_idle_source_new (); + /* *INDENT-OFF* */ + g_source_set_callback (idle_source, + (gpointer user_data) -> gboolean { + auto self = (GstWasapi2Enumerator *) user_data; + auto priv = self->priv; + std::lock_guard < std::mutex > lk (priv->lock); + priv->cond.notify_all (); + return G_SOURCE_REMOVE; + }, + self, nullptr); + /* *INDENT-ON* */ + g_source_attach (idle_source, self->context); + g_source_unref (idle_source); + + auto hr = CoCreateInstance (__uuidof (MMDeviceEnumerator), + nullptr, CLSCTX_ALL, IID_PPV_ARGS (&priv->handle)); + if (FAILED (hr)) { + GST_ERROR_OBJECT (self, "Failed to create IMMDeviceEnumerator instance"); + goto run_loop; + } + + if (gst_wasapi2_can_automatic_stream_routing ()) { + Wasapi2ActivationHandler::CreateInstance (&priv->capture_activator, + gst_wasapi2_get_default_device_id_wide (eCapture), nullptr); + priv->capture_activator->ActivateAsync (); + + Wasapi2ActivationHandler::CreateInstance (&priv->render_activator, + gst_wasapi2_get_default_device_id_wide (eRender), nullptr); + priv->render_activator->ActivateAsync (); + } + +run_loop: + GST_INFO_OBJECT (self, "Starting loop"); + g_main_loop_run (self->loop); + GST_INFO_OBJECT (self, "Stopped loop"); + + priv->ClearCOM (); + + g_main_context_pop_thread_default (self->context); + + CoUninitialize (); + + return nullptr; +} + +GstWasapi2Enumerator * +gst_wasapi2_enumerator_new (void) +{ + auto self = (GstWasapi2Enumerator *) + g_object_new (GST_TYPE_WASAPI2_ENUMERATOR, nullptr); + gst_object_ref_sink (self); + + auto priv = self->priv; + + { + std::unique_lock < std::mutex > lk (priv->lock); + self->thread = g_thread_new ("GstWasapi2Enumerator", + (GThreadFunc) gst_wasapi2_enumerator_thread_func, self); + while (!g_main_loop_is_running (self->loop)) + priv->cond.wait (lk); + } + + if (!priv->handle) { + gst_object_unref (self); + return nullptr; + } + + return self; +} + +/* *INDENT-OFF* */ +struct ActivateNotificationData +{ + ActivateNotificationData () + { + event = CreateEvent (nullptr, FALSE, FALSE, nullptr); + } + + ~ActivateNotificationData () + { + CloseHandle (event); + } + + GstWasapi2Enumerator *self; + gboolean active; + HANDLE event; +}; +/* *INDENT-ON* */ + +static gboolean +set_notification_callback (ActivateNotificationData * data) +{ + auto self = data->self; + auto priv = self->priv; + + if (data->active) { + if (!priv->client) { + ComPtr < IMMNotificationClient > client; + IWasapi2NotificationClient::CreateInstance (self, &client); + + auto hr = + priv->handle->RegisterEndpointNotificationCallback (client.Get ()); + if (FAILED (hr)) { + GST_ERROR_OBJECT (self, "Couldn't register callback"); + } else { + GST_LOG_OBJECT (self, "Registered notification"); + priv->client = client; + } + } + } else if (priv->client) { + priv->handle->UnregisterEndpointNotificationCallback (priv->client.Get ()); + priv->client = nullptr; + GST_LOG_OBJECT (self, "Unregistered notification"); + } + + SetEvent (data->event); + + return G_SOURCE_REMOVE; +} + +void +gst_wasapi2_enumerator_activate_notification (GstWasapi2Enumerator * object, + gboolean active) +{ + auto priv = object->priv; + + if (!priv->handle) + return; + + ActivateNotificationData data; + data.self = object; + data.active = active; + + g_main_context_invoke (object->context, + (GSourceFunc) set_notification_callback, &data); + + WaitForSingleObject (data.event, INFINITE); +} + +void +gst_wasapi2_enumerator_entry_free (GstWasapi2EnumeratorEntry * entry) +{ + g_free (entry->device_id); + g_free (entry->device_name); + gst_clear_caps (&entry->caps); + g_free (entry); +} + +/* *INDENT-OFF* */ +struct EnumerateData +{ + EnumerateData () + { + event = CreateEvent (nullptr, FALSE, FALSE, nullptr); + } + + ~EnumerateData () + { + CloseHandle (event); + } + + GstWasapi2Enumerator *self; + GPtrArray *device_list; + HANDLE event; +}; +/* *INDENT-ON* */ + +static void +gst_wasapi2_enumerator_add_entry (GstWasapi2Enumerator * self, + IAudioClient * client, + GstCaps * static_caps, EDataFlow flow, gboolean is_default, + gchar * device_id, gchar * device_name, GPtrArray * device_list) +{ + WAVEFORMATEX *mix_format = nullptr; + GstCaps *supported_caps = nullptr; + + client->GetMixFormat (&mix_format); + if (!mix_format) { + g_free (device_id); + g_free (device_name); + return; + } + + gst_wasapi2_util_parse_waveformatex (mix_format, + static_caps, &supported_caps, nullptr); + CoTaskMemFree (mix_format); + + if (!supported_caps) { + g_free (device_id); + g_free (device_name); + return; + } + + auto entry = g_new0 (GstWasapi2EnumeratorEntry, 1); + + entry->device_id = device_id; + entry->device_name = device_name; + entry->caps = supported_caps; + entry->flow = flow; + entry->is_default = is_default; + + GST_LOG_OBJECT (self, "Adding entry %s (%s), flow %d, caps %" GST_PTR_FORMAT, + device_id, device_name, flow, supported_caps); + + g_ptr_array_add (device_list, entry); +} + +static gboolean +gst_wasapi2_enumerator_enumerate_internal (EnumerateData * data) +{ + auto self = data->self; + auto priv = self->priv; + ComPtr < IMMDeviceCollection > collection; + + auto hr = priv->handle->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, + &collection); + if (!gst_wasapi2_result (hr)) { + SetEvent (data->event); + return G_SOURCE_REMOVE; + } + + UINT count = 0; + hr = collection->GetCount (&count); + if (!gst_wasapi2_result (hr) || count == 0) { + SetEvent (data->event); + return G_SOURCE_REMOVE; + } + + auto scaps = gst_static_caps_get (&template_caps); + + ComPtr < IAudioClient > default_capture_client; + ComPtr < IAudioClient > default_render_client; + if (priv->capture_activator) + priv->capture_activator->GetClient (&default_capture_client, 10000); + if (priv->render_activator) + priv->render_activator->GetClient (&default_render_client, 10000); + + if (default_capture_client) { + gst_wasapi2_enumerator_add_entry (self, default_capture_client.Get (), + scaps, eCapture, TRUE, + g_strdup (gst_wasapi2_get_default_device_id (eCapture)), + g_strdup ("Default Audio Capture Device"), data->device_list); + } + + if (default_render_client) { + gst_wasapi2_enumerator_add_entry (self, default_render_client.Get (), + scaps, eRender, TRUE, + g_strdup (gst_wasapi2_get_default_device_id (eRender)), + g_strdup ("Default Audio Render Device"), data->device_list); + } + + for (UINT i = 0; i < count; i++) { + ComPtr < IMMDevice > device; + ComPtr < IMMEndpoint > endpoint; + EDataFlow flow; + + hr = collection->Item (i, &device); + if (!gst_wasapi2_result (hr)) + continue; + + hr = device.As (&endpoint); + if (!gst_wasapi2_result (hr)) + continue; + + hr = endpoint->GetDataFlow (&flow); + if (!gst_wasapi2_result (hr)) + continue; + + ComPtr < IPropertyStore > prop; + hr = device->OpenPropertyStore (STGM_READ, &prop); + if (!gst_wasapi2_result (hr)) + continue; + + PROPVARIANT var; + PropVariantInit (&var); + hr = prop->GetValue (PKEY_Device_FriendlyName, &var); + if (!gst_wasapi2_result (hr)) + continue; + + auto desc = g_utf16_to_utf8 ((gunichar2 *) var.pwszVal, + -1, nullptr, nullptr, nullptr); + PropVariantClear (&var); + + LPWSTR wid = nullptr; + hr = device->GetId (&wid); + if (!gst_wasapi2_result (hr)) { + g_free (desc); + continue; + } + + auto device_id = g_utf16_to_utf8 ((gunichar2 *) wid, + -1, nullptr, nullptr, nullptr); + CoTaskMemFree (wid); + + ComPtr < IAudioClient > client; + hr = device->Activate (__uuidof (IAudioClient), CLSCTX_ALL, nullptr, + &client); + if (!gst_wasapi2_result (hr)) { + g_free (device_id); + g_free (desc); + continue; + } + + gst_wasapi2_enumerator_add_entry (self, client.Get (), scaps, flow, FALSE, + device_id, desc, data->device_list); + } + + gst_caps_unref (scaps); + + SetEvent (data->event); + return G_SOURCE_REMOVE; +} + +void +gst_wasapi2_enumerator_enumerate_devices (GstWasapi2Enumerator * object, + GPtrArray * device_list) +{ + EnumerateData data; + + data.self = object; + data.device_list = device_list; + + g_main_context_invoke (object->context, + (GSourceFunc) gst_wasapi2_enumerator_enumerate_internal, &data); + + WaitForSingleObject (data.event, INFINITE); +}
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2enumerator.h
Added
@@ -0,0 +1,51 @@ +/* GStreamer + * Copyright (C) 2025 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. + */ + +#pragma once + +#include <gst/gst.h> +#include "gstwasapi2util.h" + +G_BEGIN_DECLS + +#define GST_TYPE_WASAPI2_ENUMERATOR (gst_wasapi2_enumerator_get_type ()) +G_DECLARE_FINAL_TYPE (GstWasapi2Enumerator, gst_wasapi2_enumerator, + GST, WASAPI2_ENUMERATOR, GstObject); + +typedef struct _GstWasapi2EnumeratorEntry +{ + gchar *device_id; + gchar *device_name; + gboolean is_default; + GstCaps *caps; + EDataFlow flow; +} GstWasapi2EnumeratorEntry; + +GstWasapi2Enumerator * gst_wasapi2_enumerator_new (void); + +void gst_wasapi2_enumerator_activate_notification (GstWasapi2Enumerator * object, + gboolean active); + +void gst_wasapi2_enumerator_entry_free (GstWasapi2EnumeratorEntry * entry); + +void gst_wasapi2_enumerator_enumerate_devices (GstWasapi2Enumerator * object, + GPtrArray * entry); + +G_END_DECLS +
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2object.cpp
Added
@@ -0,0 +1,461 @@ +/* GStreamer + * Copyright (C) 2025 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 "gstwasapi2object.h" +#include "gstwasapi2activator.h" +#include <endpointvolume.h> +#include <mutex> +#include <condition_variable> +#include <wrl.h> +#include <string> +#include <atomic> +#include <string.h> + +/* *INDENT-OFF* */ +using namespace Microsoft::WRL; + +GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); +#define GST_CAT_DEFAULT gst_wasapi2_debug + +static GstStaticCaps template_caps = GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS); + +static void gst_wasapi2_object_set_endpoint_muted (GstWasapi2Object * object, + bool muted); + +DEFINE_GUID (IID_Wasapi2EndpointVolumeCallback, 0x21ba991f, 0x4d78, + 0x418c, 0xa1, 0xea, 0x8a, 0xc7, 0xdd, 0xa2, 0xdc, 0x39); +class Wasapi2EndpointVolumeCallback : public IAudioEndpointVolumeCallback +{ +public: + static void CreateInstance (Wasapi2EndpointVolumeCallback ** iface, + GstWasapi2Object * client) + { + auto self = new Wasapi2EndpointVolumeCallback (); + g_weak_ref_set (&self->client_, client); + *iface = self; + } + + STDMETHODIMP_ (ULONG) + AddRef (void) + { + return InterlockedIncrement (&refcount_); + } + + STDMETHODIMP_ (ULONG) + Release (void) + { + ULONG ref_count; + + ref_count = InterlockedDecrement (&refcount_); + + if (ref_count == 0) + delete this; + + return ref_count; + } + + STDMETHODIMP + QueryInterface (REFIID riid, void ** object) + { + if (riid == __uuidof(IUnknown) || riid == __uuidof(IAgileObject)) { + *object = static_cast<IUnknown *>( + static_cast<Wasapi2EndpointVolumeCallback*>(this)); + } else if (riid == __uuidof(IAudioEndpointVolumeCallback)) { + *object = static_cast<IAudioEndpointVolumeCallback *>( + static_cast<Wasapi2EndpointVolumeCallback*>(this)); + } else if (riid == IID_Wasapi2EndpointVolumeCallback) { + *object = static_cast<Wasapi2EndpointVolumeCallback *> (this); + } else { + *object = nullptr; + return E_NOINTERFACE; + } + + AddRef (); + + return S_OK; + } + + STDMETHODIMP + OnNotify (AUDIO_VOLUME_NOTIFICATION_DATA * notify) + { + auto client = (GstWasapi2Object *) g_weak_ref_get (&client_); + + if (client) { + gst_wasapi2_object_set_endpoint_muted (client, notify->bMuted); + gst_object_unref (client); + } + + return S_OK; + } + +private: + Wasapi2EndpointVolumeCallback () + { + g_weak_ref_init (&client_, nullptr); + } + + virtual ~Wasapi2EndpointVolumeCallback () + { + g_weak_ref_set (&client_, nullptr); + } + +private: + ULONG refcount_ = 1; + GWeakRef client_; +}; + +struct GstWasapi2ObjectPrivate +{ + ComPtr<IMMDeviceEnumerator> enumerator; + ComPtr<IMMDevice> device; + ComPtr<IAudioClient> client; + ComPtr<IAudioEndpointVolume> endpoint_volume; + std::atomic<bool> endpoint_muted = { false }; + Wasapi2EndpointVolumeCallback *volume_callback = nullptr; + Wasapi2ActivationHandler *activator = nullptr; + std::mutex lock; + std::condition_variable cond; + std::string device_id; + GstWasapi2EndpointClass device_class; + guint target_pid; + gboolean is_default_device = FALSE; + + void ClearCOM () + { + if (volume_callback && endpoint_volume) + endpoint_volume->UnregisterControlChangeNotify (volume_callback); + if (activator) + activator->Release (); + client = nullptr; + if (volume_callback) + volume_callback->Release (); + endpoint_volume = nullptr; + device = nullptr; + enumerator = nullptr; + } +}; +/* *INDENT-ON* */ + +struct _GstWasapi2Object +{ + GstObject parent; + + GstWasapi2ObjectPrivate *priv; + + GThread *thread; + GMainContext *context; + GMainLoop *loop; + GstCaps *caps; +}; + +static void gst_wasapi2_object_finalize (GObject * object); + +#define gst_wasapi2_object_parent_class parent_class +G_DEFINE_TYPE (GstWasapi2Object, gst_wasapi2_object, GST_TYPE_OBJECT); + +static void +gst_wasapi2_object_class_init (GstWasapi2ObjectClass * klass) +{ + auto object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gst_wasapi2_object_finalize; +} + +static void +gst_wasapi2_object_init (GstWasapi2Object * self) +{ + self->priv = new GstWasapi2ObjectPrivate (); + self->context = g_main_context_new (); + self->loop = g_main_loop_new (self->context, FALSE); +} + +static void +gst_wasapi2_object_finalize (GObject * object) +{ + auto self = GST_WASAPI2_OBJECT (object); + + g_main_loop_quit (self->loop); + g_thread_join (self->thread); + g_main_loop_unref (self->loop); + g_main_context_unref (self->context); + gst_clear_caps (&self->caps); + + delete self->priv; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_wasapi2_object_set_endpoint_muted (GstWasapi2Object * object, bool muted) +{ + auto priv = object->priv; + priv->endpoint_muted.store (muted, std::memory_order_release); +} + +static gboolean +is_equal_device_id (const gchar * a, const gchar * b) +{ + auto len_a = strlen (a); + auto len_b = strlen (b); + + if (len_a != len_b) + return FALSE; + +#ifdef _MSC_VER + return _strnicmp (a, b, len_a) == 0; +#else + return strncasecmp (a, b, len_a) == 0; +#endif +} + +static gpointer +gst_wasapi2_object_thread_func (GstWasapi2Object * self) +{ + auto priv = self->priv; + + CoInitializeEx (nullptr, COINIT_MULTITHREADED); + + g_main_context_push_thread_default (self->context); + + auto idle_source = g_idle_source_new (); + /* *INDENT-OFF* */ + g_source_set_callback (idle_source, + (gpointer user_data) -> gboolean { + auto self = (GstWasapi2Object *) user_data; + auto priv = self->priv; + std::lock_guard < std::mutex > lk (priv->lock); + priv->cond.notify_all (); + return G_SOURCE_REMOVE; + }, + self, nullptr); + /* *INDENT-ON* */ + g_source_attach (idle_source, self->context); + g_source_unref (idle_source); + + auto hr = CoCreateInstance (__uuidof (MMDeviceEnumerator), + nullptr, CLSCTX_ALL, IID_PPV_ARGS (&priv->enumerator)); + if (FAILED (hr)) { + GST_ERROR_OBJECT (self, "Failed to create IMMDeviceEnumerator instance"); + goto run_loop; + } + + switch (priv->device_class) { + case GST_WASAPI2_ENDPOINT_CLASS_CAPTURE: + if (priv->device_id.empty () || + is_equal_device_id (priv->device_id.c_str (), + gst_wasapi2_get_default_device_id (eCapture))) { + if (gst_wasapi2_can_automatic_stream_routing ()) { + Wasapi2ActivationHandler::CreateInstance (&priv->activator, + gst_wasapi2_get_default_device_id_wide (eCapture), nullptr); + GST_DEBUG_OBJECT (self, "Creating default capture device"); + priv->is_default_device = TRUE; + } else { + GST_DEBUG_OBJECT (self, "Creating default capture MMdevice"); + hr = priv->enumerator->GetDefaultAudioEndpoint (eCapture, + eConsole, &priv->device); + } + } else { + auto wstr = g_utf8_to_utf16 (priv->device_id.c_str (), + -1, nullptr, nullptr, nullptr); + hr = priv->enumerator->GetDevice ((LPCWSTR) wstr, &priv->device); + g_free (wstr); + } + break; + case GST_WASAPI2_ENDPOINT_CLASS_RENDER: + case GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE: + if (priv->device_id.empty () || + is_equal_device_id (priv->device_id.c_str (), + gst_wasapi2_get_default_device_id (eRender))) { + if (gst_wasapi2_can_automatic_stream_routing ()) { + Wasapi2ActivationHandler::CreateInstance (&priv->activator, + gst_wasapi2_get_default_device_id_wide (eRender), nullptr); + GST_DEBUG_OBJECT (self, "Creating default render device"); + priv->is_default_device = TRUE; + } else { + GST_DEBUG_OBJECT (self, "Creating default render MMdevice"); + hr = priv->enumerator->GetDefaultAudioEndpoint (eRender, + eConsole, &priv->device); + } + } else { + auto wstr = g_utf8_to_utf16 (priv->device_id.c_str (), + -1, nullptr, nullptr, nullptr); + hr = priv->enumerator->GetDevice ((LPCWSTR) wstr, &priv->device); + g_free (wstr); + } + break; + case GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE: + { + AUDIOCLIENT_ACTIVATION_PARAMS params = { }; + params.ActivationType = AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + params.ProcessLoopbackParams.TargetProcessId = priv->target_pid; + if (priv->device_class == + GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE) { + params.ProcessLoopbackParams.ProcessLoopbackMode = + PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; + } else { + params.ProcessLoopbackParams.ProcessLoopbackMode = + PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE; + } + + GST_DEBUG_OBJECT (self, "Creating process loopback capture device"); + + Wasapi2ActivationHandler::CreateInstance (&priv->activator, + VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, ¶ms); + break; + } + default: + g_assert_not_reached (); + break; + } + + if (priv->activator || priv->device) { + if (priv->activator) { + hr = priv->activator->ActivateAsync (); + if (gst_wasapi2_result (hr)) + hr = priv->activator->GetClient (&priv->client, INFINITE); + } else { + hr = priv->device->Activate (__uuidof (IAudioClient), CLSCTX_ALL, + nullptr, &priv->client); + } + + if (!gst_wasapi2_result (hr)) { + GST_WARNING_OBJECT (self, "Couldn't activate device"); + } else if (priv->device && + priv->device_class == GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE) { + hr = priv->device->Activate (__uuidof (IAudioEndpointVolume), + CLSCTX_ALL, nullptr, &priv->endpoint_volume); + if (gst_wasapi2_result (hr)) { + Wasapi2EndpointVolumeCallback::CreateInstance (&priv->volume_callback, + self); + hr = priv->endpoint_volume-> + RegisterControlChangeNotify (priv->volume_callback); + if (!gst_wasapi2_result (hr)) { + priv->volume_callback->Release (); + priv->volume_callback = nullptr; + } else { + BOOL muted = FALSE; + priv->endpoint_volume->GetMute (&muted); + if (gst_wasapi2_result (hr)) + gst_wasapi2_object_set_endpoint_muted (self, muted); + } + } + } + } else { + GST_WARNING_OBJECT (self, "No device created"); + } + + if (priv->client) { + WAVEFORMATEX *mix_format = nullptr; + hr = priv->client->GetMixFormat (&mix_format); + if (!gst_wasapi2_result (hr)) { + if (gst_wasapi2_is_process_loopback_class (priv->device_class)) + mix_format = gst_wasapi2_get_default_mix_format (); + } + + if (mix_format) { + auto scaps = gst_static_caps_get (&template_caps); + gst_wasapi2_util_parse_waveformatex (mix_format, + scaps, &self->caps, nullptr); + gst_caps_unref (scaps); + + CoTaskMemFree (mix_format); + } + } + +run_loop: + GST_INFO_OBJECT (self, "Starting loop"); + g_main_loop_run (self->loop); + GST_INFO_OBJECT (self, "Stopped loop"); + + priv->ClearCOM (); + + g_main_context_pop_thread_default (self->context); + + CoUninitialize (); + + return nullptr; +} + +GstWasapi2Object * +gst_wasapi2_object_new (GstWasapi2EndpointClass device_class, + const gchar * device_id, guint target_pid) +{ + auto self = (GstWasapi2Object *) + g_object_new (GST_TYPE_WASAPI2_OBJECT, nullptr); + gst_object_ref_sink (self); + + auto priv = self->priv; + priv->device_class = device_class; + if (device_id) + priv->device_id = device_id; + priv->target_pid = target_pid; + + if (gst_wasapi2_is_process_loopback_class (device_class) && !target_pid) { + GST_ERROR_OBJECT (self, "Unspecified target PID"); + gst_object_unref (self); + return nullptr; + } + + { + std::unique_lock < std::mutex > lk (priv->lock); + self->thread = g_thread_new ("GstWasapi2Object", + (GThreadFunc) gst_wasapi2_object_thread_func, self); + while (!g_main_loop_is_running (self->loop)) + priv->cond.wait (lk); + } + + if (!priv->client) { + gst_object_unref (self); + return nullptr; + } + + return self; +} + +GstCaps * +gst_wasapi2_object_get_caps (GstWasapi2Object * object) +{ + if (object->caps) + return gst_caps_ref (object->caps); + + return nullptr; +} + +IAudioClient * +gst_wasapi2_object_get_handle (GstWasapi2Object * object) +{ + return object->priv->client.Get (); +} + +gboolean +gst_wasapi2_object_is_endpoint_muted (GstWasapi2Object * object) +{ + return object->priv->endpoint_muted.load (std::memory_order_acquire); +} + +gboolean +gst_wasapi2_object_auto_routing_supported (GstWasapi2Object * object) +{ + return object->priv->is_default_device; +}
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2object.h
Added
@@ -0,0 +1,44 @@ +/* GStreamer + * Copyright (C) 2025 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. + */ + +#pragma once + +#include <gst/gst.h> +#include "gstwasapi2util.h" + +G_BEGIN_DECLS + +#define GST_TYPE_WASAPI2_OBJECT (gst_wasapi2_object_get_type ()) +G_DECLARE_FINAL_TYPE (GstWasapi2Object, gst_wasapi2_object, + GST, WASAPI2_OBJECT, GstObject); + +GstWasapi2Object * gst_wasapi2_object_new (GstWasapi2EndpointClass device_class, + const gchar * device_id, + guint target_pid); + +GstCaps * gst_wasapi2_object_get_caps (GstWasapi2Object * object); + +IAudioClient * gst_wasapi2_object_get_handle (GstWasapi2Object * object); + +gboolean gst_wasapi2_object_is_endpoint_muted (GstWasapi2Object * object); + +gboolean gst_wasapi2_object_auto_routing_supported (GstWasapi2Object * object); + +G_END_DECLS +
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2ringbuffer.cpp -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2ringbuffer.cpp
Changed
@@ -18,6 +18,7 @@ */ #include "gstwasapi2ringbuffer.h" +#include "gstwasapi2object.h" #include <string.h> #include <mfapi.h> #include <wrl.h> @@ -59,11 +60,13 @@ { } + virtual ~GstWasapiAsyncCallback () { } + /* IUnknown */ STDMETHODIMP_ (ULONG) AddRef (void) { - GST_TRACE ("%p, %d", this, ref_count_); + GST_TRACE ("%p, %u", this, (guint) ref_count_); return InterlockedIncrement (&ref_count_); } STDMETHODIMP_ (ULONG) @@ -71,7 +74,7 @@ { ULONG ref_count; - GST_TRACE ("%p, %d", this, ref_count_); + GST_TRACE ("%p, %u", this, (guint) ref_count_); ref_count = InterlockedDecrement (&ref_count_); if (ref_count == 0) { @@ -151,7 +154,7 @@ { GstAudioRingBuffer parent; - GstWasapi2ClientDeviceClass device_class; + GstWasapi2EndpointClass device_class; gchar *device_id; gboolean low_latency; gboolean mute; @@ -160,8 +163,8 @@ gboolean can_auto_routing; guint loopback_target_pid; - GstWasapi2Client *client; - GstWasapi2Client *loopback_client; + GstWasapi2Object *client; + GstWasapi2Object *loopback_client; IAudioCaptureClient *capture_client; IAudioRenderClient *render_client; IAudioStreamVolume *volume_object; @@ -344,7 +347,7 @@ return; } - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) { + if (self->device_class == GST_WASAPI2_ENDPOINT_CLASS_RENDER) { GST_ELEMENT_ERROR (parent, RESOURCE, OPEN_WRITE, (nullptr), ("Failed to open device")); } else { @@ -380,13 +383,14 @@ error_msg = gst_wasapi2_util_get_error_message (hr); - GST_ERROR_OBJECT (self, "Posting I/O error %s (hr: 0x%x)", error_msg, hr); - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) { + GST_ERROR_OBJECT (self, "Posting I/O error %s (hr: 0x%x)", error_msg, + (guint) hr); + if (self->device_class == GST_WASAPI2_ENDPOINT_CLASS_RENDER) { GST_ELEMENT_ERROR (parent, RESOURCE, WRITE, - ("Failed to write to device"), ("%s, hr: 0x%x", error_msg, hr)); + ("Failed to write to device"), ("%s, hr: 0x%x", error_msg, (guint) hr)); } else { GST_ELEMENT_ERROR (parent, RESOURCE, READ, - ("Failed to read from device"), ("%s hr: 0x%x", error_msg, hr)); + ("Failed to read from device"), ("%s hr: 0x%x", error_msg, (guint) hr)); } g_free (error_msg); @@ -404,20 +408,21 @@ return TRUE; } - self->client = gst_wasapi2_client_new (self->device_class, - -1, self->device_id, self->loopback_target_pid, self->dispatcher); + self->client = gst_wasapi2_object_new (self->device_class, + self->device_id, self->loopback_target_pid); if (!self->client) { gst_wasapi2_ring_buffer_post_open_error (self); return FALSE; } - g_object_get (self->client, "auto-routing", &self->can_auto_routing, nullptr); + self->can_auto_routing = + gst_wasapi2_object_auto_routing_supported (self->client); /* Open another render client to feed silence */ - if (gst_wasapi2_device_class_is_loopback (self->device_class)) { + if (gst_wasapi2_is_loopback_class (self->device_class)) { self->loopback_client = - gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, - -1, self->device_id, 0, self->dispatcher); + gst_wasapi2_object_new (GST_WASAPI2_ENDPOINT_CLASS_RENDER, + self->device_id, 0); if (!self->loopback_client) { gst_wasapi2_ring_buffer_post_open_error (self); @@ -500,7 +505,7 @@ is_device_muted = self->priv->monitor_device_mute.load (std::memory_order_acquire) && - gst_wasapi2_client_is_endpoint_muted (self->client); + gst_wasapi2_object_is_endpoint_muted (self->client); to_read_bytes = to_read * GST_AUDIO_INFO_BPF (info); @@ -509,7 +514,7 @@ self->expected_position); /* XXX: position might not be increased in case of process loopback */ - if (!gst_wasapi2_device_class_is_process_loopback (self->device_class)) { + if (!gst_wasapi2_is_process_loopback_class (self->device_class)) { if (self->is_first) { self->expected_position = position + to_read; self->is_first = FALSE; @@ -540,7 +545,7 @@ g_assert (self->segoffset >= 0); len -= self->segoffset; - if (len > gap_size) + if (len > (gint) gap_size) len = gap_size; gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo, @@ -563,7 +568,7 @@ } len -= self->segoffset; - if (len > to_read_bytes) + if (len > (gint) to_read_bytes) len = to_read_bytes; if (((flags & AUDCLNT_BUFFERFLAGS_SILENT) == AUDCLNT_BUFFERFLAGS_SILENT) || @@ -607,7 +612,7 @@ gint len; BYTE *data = nullptr; - client_handle = gst_wasapi2_client_get_handle (self->client); + client_handle = gst_wasapi2_object_get_handle (self->client); if (!client_handle) { GST_ERROR_OBJECT (self, "IAudioClient is not available"); return E_FAIL; @@ -667,7 +672,7 @@ len -= self->segoffset; - if (len > can_write_bytes) + if (len > (gint) can_write_bytes) len = can_write_bytes; can_write = len / GST_AUDIO_INFO_BPF (&ringbuffer->spec.info); @@ -712,13 +717,13 @@ } switch (self->device_class) { - case GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE: - case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE: - case GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE: - case GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE: hr = gst_wasapi2_ring_buffer_read (self); break; - case GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER: + case GST_WASAPI2_ENDPOINT_CLASS_RENDER: hr = gst_wasapi2_ring_buffer_write (self, FALSE); break; default: @@ -731,8 +736,8 @@ * loopback capture client doesn't seem to be able to recover status from this * situation */ if (self->can_auto_routing && - !gst_wasapi2_device_class_is_loopback (self->device_class) && - !gst_wasapi2_device_class_is_process_loopback (self->device_class) && + !gst_wasapi2_is_loopback_class (self->device_class) && + !gst_wasapi2_is_process_loopback_class (self->device_class) && (hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED || hr == AUDCLNT_E_DEVICE_INVALIDATED)) { GST_WARNING_OBJECT (self, @@ -744,7 +749,7 @@ if (gst_wasapi2_result (hr) && /* In case of normal loopback capture, this method is called from * silence feeding thread. Don't schedule again in that case */ - self->device_class != GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE) { + self->device_class != GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE) { hr = MFPutWaitingWorkItem (self->event_handle, 0, self->callback_result, &self->callback_key); @@ -776,7 +781,7 @@ guint32 can_write; BYTE *data = nullptr; - client_handle = gst_wasapi2_client_get_handle (self->loopback_client); + client_handle = gst_wasapi2_object_get_handle (self->loopback_client); if (!client_handle) { GST_ERROR_OBJECT (self, "IAudioClient is not available"); return E_FAIL; @@ -817,7 +822,7 @@ HRESULT hr = E_FAIL; g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (self), E_FAIL); - g_return_val_if_fail (gst_wasapi2_device_class_is_loopback + g_return_val_if_fail (gst_wasapi2_is_loopback_class (self->device_class), E_FAIL); if (!self->running) { @@ -898,7 +903,7 @@ static HRESULT gst_wasapi2_ring_buffer_initialize_audio_client (GstWasapi2RingBuffer * self, IAudioClient * client_handle, WAVEFORMATEX * mix_format, guint * period, - DWORD extra_flags, GstWasapi2ClientDeviceClass device_class, + DWORD extra_flags, GstWasapi2EndpointClass device_class, GstAudioRingBufferSpec * spec, gboolean low_latency) { GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self); @@ -910,7 +915,7 @@ stream_flags |= extra_flags; - if (!gst_wasapi2_device_class_is_process_loopback (device_class)) { + if (!gst_wasapi2_is_process_loopback_class (device_class)) { hr = client_handle->GetDevicePeriod (&default_period, &min_period); if (!gst_wasapi2_result (hr)) { GST_WARNING_OBJECT (self, "Couldn't get device period info"); @@ -982,12 +987,7 @@ return FALSE; } - if (!gst_wasapi2_client_ensure_activation (self->loopback_client)) { - GST_ERROR_OBJECT (self, "Failed to activate audio client"); - return FALSE; - } - - client_handle = gst_wasapi2_client_get_handle (self->loopback_client); + client_handle = gst_wasapi2_object_get_handle (self->loopback_client); if (!client_handle) { GST_ERROR_OBJECT (self, "IAudioClient handle is not available"); return FALSE; @@ -1000,7 +1000,7 @@ } hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle, - mix_format, &period, 0, GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, + mix_format, &period, 0, GST_WASAPI2_ENDPOINT_CLASS_RENDER, nullptr, FALSE); CoTaskMemFree (mix_format); @@ -1073,19 +1073,14 @@ if (!self->client && !gst_wasapi2_ring_buffer_open_device (buf)) return FALSE; - if (gst_wasapi2_device_class_is_loopback (self->device_class)) { + if (gst_wasapi2_is_loopback_class (self->device_class)) { if (!gst_wasapi2_ring_buffer_prepare_loopback_client (self)) { GST_ERROR_OBJECT (self, "Failed to prepare loopback client"); goto error; } } - if (!gst_wasapi2_client_ensure_activation (self->client)) { - GST_ERROR_OBJECT (self, "Failed to activate audio client"); - goto error; - } - - client_handle = gst_wasapi2_client_get_handle (self->client); + client_handle = gst_wasapi2_object_get_handle (self->client); if (!client_handle) { GST_ERROR_OBJECT (self, "IAudioClient handle is not available"); goto error; @@ -1094,7 +1089,7 @@ /* TODO: convert given caps to mix format */ hr = client_handle->GetMixFormat (&mix_format); if (!gst_wasapi2_result (hr)) { - if (gst_wasapi2_device_class_is_process_loopback (self->device_class)) { + if (gst_wasapi2_is_process_loopback_class (self->device_class)) { mix_format = gst_wasapi2_get_default_mix_format (); } else { GST_ERROR_OBJECT (self, "Failed to get mix format"); @@ -1109,8 +1104,8 @@ if (self->low_latency && /* AUDCLNT_STREAMFLAGS_LOOPBACK is not allowed for * InitializeSharedAudioStream */ - !gst_wasapi2_device_class_is_loopback (self->device_class) && - !gst_wasapi2_device_class_is_process_loopback (self->device_class)) { + !gst_wasapi2_is_loopback_class (self->device_class) && + !gst_wasapi2_is_process_loopback_class (self->device_class)) { hr = gst_wasapi2_ring_buffer_initialize_audio_client3 (self, client_handle, mix_format, &period); } @@ -1123,7 +1118,7 @@ */ if (FAILED (hr)) { DWORD extra_flags = 0; - if (gst_wasapi2_device_class_is_loopback (self->device_class)) + if (gst_wasapi2_is_loopback_class (self->device_class)) extra_flags = AUDCLNT_STREAMFLAGS_LOOPBACK; hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle, @@ -1168,7 +1163,7 @@ "Buffer size: %d frames, period: %d frames, segsize: %d bytes, " "segtotal: %d", self->buffer_size, period, spec->segsize, spec->segtotal); - if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) { + if (self->device_class == GST_WASAPI2_ENDPOINT_CLASS_RENDER) { ComPtr < IAudioRenderClient > render_client; hr = client_handle->GetService (IID_PPV_ARGS (&render_client)); @@ -1251,14 +1246,14 @@ return TRUE; } - client_handle = gst_wasapi2_client_get_handle (self->client); + client_handle = gst_wasapi2_object_get_handle (self->client); self->is_first = TRUE; self->running = TRUE; self->segoffset = 0; self->write_frame_offset = 0; switch (self->device_class) { - case GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER: + case GST_WASAPI2_ENDPOINT_CLASS_RENDER: /* render client might read data from buffer immediately once it's prepared. * Pre-fill with silence in order to start-up glitch */ hr = gst_wasapi2_ring_buffer_write (self, TRUE); @@ -1267,13 +1262,13 @@ goto error; } break; - case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE: { IAudioClient *loopback_client_handle; /* Start silence feed client first */ loopback_client_handle = - gst_wasapi2_client_get_handle (self->loopback_client); + gst_wasapi2_object_get_handle (self->loopback_client); hr = loopback_client_handle->Start (); if (!gst_wasapi2_result (hr)) { @@ -1303,7 +1298,7 @@ goto error; } - if (self->device_class != GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE) { + if (self->device_class != GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE) { hr = MFPutWaitingWorkItem (self->event_handle, 0, self->callback_result, &self->callback_key); if (!gst_wasapi2_result (hr)) { @@ -1357,7 +1352,7 @@ return TRUE; } - client_handle = gst_wasapi2_client_get_handle (self->client); + client_handle = gst_wasapi2_object_get_handle (self->client); self->running = FALSE; MFCancelWorkItem (self->callback_key); @@ -1371,7 +1366,7 @@ self->write_frame_offset = 0; if (self->loopback_client) { - client_handle = gst_wasapi2_client_get_handle (self->loopback_client); + client_handle = gst_wasapi2_object_get_handle (self->loopback_client); MFCancelWorkItem (self->loopback_callback_key); @@ -1427,7 +1422,7 @@ } GstAudioRingBuffer * -gst_wasapi2_ring_buffer_new (GstWasapi2ClientDeviceClass device_class, +gst_wasapi2_ring_buffer_new (GstWasapi2EndpointClass device_class, gboolean low_latency, const gchar * device_id, gpointer dispatcher, const gchar * name, guint loopback_target_pid) { @@ -1461,12 +1456,7 @@ if (!buf->client) return nullptr; - if (!gst_wasapi2_client_ensure_activation (buf->client)) { - GST_ERROR_OBJECT (buf, "Failed to activate audio client"); - return nullptr; - } - - buf->supported_caps = gst_wasapi2_client_get_caps (buf->client); + buf->supported_caps = gst_wasapi2_object_get_caps (buf->client); if (buf->supported_caps) return gst_caps_ref (buf->supported_caps); @@ -1511,7 +1501,7 @@ HRESULT gst_wasapi2_ring_buffer_set_volume (GstWasapi2RingBuffer * buf, gfloat volume) { - HRESULT hr; + HRESULT hr = S_OK; g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG); g_return_val_if_fail (volume >= 0 && volume <= 1.0, E_INVALIDARG);
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2ringbuffer.h -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2ringbuffer.h
Changed
@@ -22,7 +22,7 @@ #include <gst/gst.h> #include <gst/audio/audio.h> -#include "gstwasapi2client.h" +#include "gstwasapi2util.h" G_BEGIN_DECLS @@ -30,7 +30,7 @@ G_DECLARE_FINAL_TYPE (GstWasapi2RingBuffer, gst_wasapi2_ring_buffer, GST, WASAPI2_RING_BUFFER, GstAudioRingBuffer); -GstAudioRingBuffer * gst_wasapi2_ring_buffer_new (GstWasapi2ClientDeviceClass device_class, +GstAudioRingBuffer * gst_wasapi2_ring_buffer_new (GstWasapi2EndpointClass device_class, gboolean low_latency, const gchar *device_id, gpointer dispatcher,
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2sink.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2sink.c
Changed
@@ -124,7 +124,7 @@ g_object_class_install_property (gobject_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "Audio device ID as provided by " - "Windows.Devices.Enumeration.DeviceInformation.Id", + "WASAPI device endpoint ID as provided by IMMDevice::GetId", NULL, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -333,7 +333,7 @@ name = g_strdup_printf ("%s-ringbuffer", GST_OBJECT_NAME (sink)); ringbuffer = - gst_wasapi2_ring_buffer_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, + gst_wasapi2_ring_buffer_new (GST_WASAPI2_ENDPOINT_CLASS_RENDER, self->low_latency, self->device_id, self->dispatcher, name, 0); g_free (name);
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2src.c -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2src.c
Changed
@@ -194,7 +194,7 @@ g_object_class_install_property (gobject_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "Audio device ID as provided by " - "Windows.Devices.Enumeration.DeviceInformation.Id", + "WASAPI device endpoint ID as provided by IMMDevice::GetId", NULL, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -493,20 +493,19 @@ GstWasapi2Src *self = GST_WASAPI2_SRC (src); GstAudioRingBuffer *ringbuffer; gchar *name; - GstWasapi2ClientDeviceClass device_class = - GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE; + GstWasapi2EndpointClass device_class = GST_WASAPI2_ENDPOINT_CLASS_CAPTURE; if (self->loopback_pid) { if (self->loopback_mode == GST_WASAPI2_SRC_LOOPBACK_INCLUDE_PROCESS_TREE) { device_class = - GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE; + GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE; } else if (self->loopback_mode == GST_WASAPI2_SRC_LOOPBACK_EXCLUDE_PROCESS_TREE) { device_class = - GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE; + GST_WASAPI2_ENDPOINT_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE; } } else if (self->loopback) { - device_class = GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE; + device_class = GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE; } GST_DEBUG_OBJECT (self, "Device class %d", device_class);
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2util.cpp
Added
@@ -0,0 +1,594 @@ +/* + * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> + * Copyright (C) 2018 Centricular Ltd. + * Author: Nirbheek Chauhan <nirbheek@centricular.com> + * 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 "gstwasapi2util.h" +#include <audioclient.h> +#include <mmdeviceapi.h> +#include <winternl.h> +#include <mutex> +#include <string.h> + +GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_debug); +#define GST_CAT_DEFAULT gst_wasapi2_debug + +/* Desktop only defines */ +#ifndef KSAUDIO_SPEAKER_MONO +#define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER) +#endif +#ifndef KSAUDIO_SPEAKER_1POINT1 +#define KSAUDIO_SPEAKER_1POINT1 (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) +#endif +#ifndef KSAUDIO_SPEAKER_STEREO +#define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) +#endif +#ifndef KSAUDIO_SPEAKER_2POINT1 +#define KSAUDIO_SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY) +#endif +#ifndef KSAUDIO_SPEAKER_3POINT0 +#define KSAUDIO_SPEAKER_3POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER) +#endif +#ifndef KSAUDIO_SPEAKER_3POINT1 +#define KSAUDIO_SPEAKER_3POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY) +#endif +#ifndef KSAUDIO_SPEAKER_QUAD +#define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +#endif +#define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) +#ifndef KSAUDIO_SPEAKER_5POINT0 +#define KSAUDIO_SPEAKER_5POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) +#endif +#define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +#ifndef KSAUDIO_SPEAKER_7POINT0 +#define KSAUDIO_SPEAKER_7POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) +#endif +#ifndef KSAUDIO_SPEAKER_7POINT1 +#define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ + SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) +#endif + +/* *INDENT-OFF* */ +static struct +{ + guint64 wasapi_pos; + GstAudioChannelPosition gst_pos; +} wasapi_to_gst_pos = { + {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, + {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, + {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1}, + {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, + {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + {SPEAKER_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, + {SPEAKER_FRONT_RIGHT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, + /* Enum values diverge from this point onwards */ + {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, + {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, + {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, + {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, + {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, + {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, + {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, + {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT} +}; + +static DWORD default_ch_masks = { + 0, + KSAUDIO_SPEAKER_MONO, + /* 2ch */ + KSAUDIO_SPEAKER_STEREO, + /* 2.1ch */ + /* KSAUDIO_SPEAKER_3POINT0 ? */ + KSAUDIO_SPEAKER_2POINT1, + /* 4ch */ + /* KSAUDIO_SPEAKER_3POINT1 or KSAUDIO_SPEAKER_SURROUND ? */ + KSAUDIO_SPEAKER_QUAD, + /* 5ch */ + KSAUDIO_SPEAKER_5POINT0, + /* 5.1ch */ + KSAUDIO_SPEAKER_5POINT1, + /* 7ch */ + KSAUDIO_SPEAKER_7POINT0, + /* 7.1ch */ + KSAUDIO_SPEAKER_7POINT1, +}; +/* *INDENT-ON* */ + +static const gchar * +hresult_to_string_fallback (HRESULT hr) +{ + const gchar *s = "unknown error"; + + switch (hr) { + case AUDCLNT_E_NOT_INITIALIZED: + s = "AUDCLNT_E_NOT_INITIALIZED"; + break; + case AUDCLNT_E_ALREADY_INITIALIZED: + s = "AUDCLNT_E_ALREADY_INITIALIZED"; + break; + case AUDCLNT_E_WRONG_ENDPOINT_TYPE: + s = "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; + break; + case AUDCLNT_E_DEVICE_INVALIDATED: + s = "AUDCLNT_E_DEVICE_INVALIDATED"; + break; + case AUDCLNT_E_NOT_STOPPED: + s = "AUDCLNT_E_NOT_STOPPED"; + break; + case AUDCLNT_E_BUFFER_TOO_LARGE: + s = "AUDCLNT_E_BUFFER_TOO_LARGE"; + break; + case AUDCLNT_E_OUT_OF_ORDER: + s = "AUDCLNT_E_OUT_OF_ORDER"; + break; + case AUDCLNT_E_UNSUPPORTED_FORMAT: + s = "AUDCLNT_E_UNSUPPORTED_FORMAT"; + break; + case AUDCLNT_E_INVALID_DEVICE_PERIOD: + s = "AUDCLNT_E_INVALID_DEVICE_PERIOD"; + break; + case AUDCLNT_E_INVALID_SIZE: + s = "AUDCLNT_E_INVALID_SIZE"; + break; + case AUDCLNT_E_DEVICE_IN_USE: + s = "AUDCLNT_E_DEVICE_IN_USE"; + break; + case AUDCLNT_E_BUFFER_OPERATION_PENDING: + s = "AUDCLNT_E_BUFFER_OPERATION_PENDING"; + break; + case AUDCLNT_E_BUFFER_SIZE_ERROR: + s = "AUDCLNT_E_BUFFER_SIZE_ERROR"; + break; + case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: + s = "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; + break; + case AUDCLNT_E_THREAD_NOT_REGISTERED: + s = "AUDCLNT_E_THREAD_NOT_REGISTERED"; + break; + case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: + s = "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; + break; + case AUDCLNT_E_ENDPOINT_CREATE_FAILED: + s = "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; + break; + case AUDCLNT_E_SERVICE_NOT_RUNNING: + s = "AUDCLNT_E_SERVICE_NOT_RUNNING"; + break; + case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: + s = "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; + break; + case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: + s = "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; + break; + case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: + s = "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; + break; + case AUDCLNT_E_EVENTHANDLE_NOT_SET: + s = "AUDCLNT_E_EVENTHANDLE_NOT_SET"; + break; + case AUDCLNT_E_INCORRECT_BUFFER_SIZE: + s = "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; + break; + case AUDCLNT_E_CPUUSAGE_EXCEEDED: + s = "AUDCLNT_E_CPUUSAGE_EXCEEDED"; + break; + case AUDCLNT_S_BUFFER_EMPTY: + s = "AUDCLNT_S_BUFFER_EMPTY"; + break; + case AUDCLNT_S_THREAD_ALREADY_REGISTERED: + s = "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; + break; + case AUDCLNT_S_POSITION_STALLED: + s = "AUDCLNT_S_POSITION_STALLED"; + break; + case E_POINTER: + s = "E_POINTER"; + break; + case E_INVALIDARG: + s = "E_INVALIDARG"; + break; + } + + return s; +} + +gchar * +gst_wasapi2_util_get_error_message (HRESULT hr) +{ + gchar *error_text = NULL; + + error_text = g_win32_error_message ((gint) hr); + if (!error_text || strlen (error_text) == 0) { + g_free (error_text); + error_text = g_strdup (hresult_to_string_fallback (hr)); + } + + return error_text; +} + +gboolean +_gst_wasapi2_result (HRESULT hr, GstDebugCategory * cat, const gchar * file, + const gchar * function, gint line) +{ +#ifndef GST_DISABLE_GST_DEBUG + gboolean ret = TRUE; + + if (FAILED (hr)) { + gchar *error_text = NULL; + gboolean free_string = TRUE; + + error_text = g_win32_error_message ((gint) hr); + /* g_win32_error_message() seems to be returning empty string for + * AUDCLNT_* cases */ + if (!error_text || strlen (error_text) == 0) { + g_free (error_text); + error_text = (gchar *) hresult_to_string_fallback (hr); + + free_string = FALSE; + } + + gst_debug_log (cat, GST_LEVEL_WARNING, file, function, line, + NULL, "WASAPI call failed: 0x%x, %s", (guint) hr, error_text); + + if (free_string) + g_free (error_text); + + ret = FALSE; + } + + return ret; +#else + return SUCCEEDED (hr); +#endif +} + +static void +gst_wasapi_util_channel_position_all_none (guint channels, + GstAudioChannelPosition * position) +{ + guint i; + + for (i = 0; i < channels; i++) + positioni = GST_AUDIO_CHANNEL_POSITION_NONE; +} + +guint64 +gst_wasapi2_util_waveformatex_to_channel_mask (WAVEFORMATEX * format, + GstAudioChannelPosition ** out_position) +{ + guint i, ch; + guint64 mask = 0; + GstAudioChannelPosition *pos = NULL; + WORD nChannels = 0; + DWORD dwChannelMask = 0; + + nChannels = format->nChannels; + if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + WAVEFORMATEXTENSIBLE *extensible = (WAVEFORMATEXTENSIBLE *) format; + dwChannelMask = extensible->dwChannelMask; + } + + if (out_position) + *out_position = NULL; + + if (nChannels > 2 && !dwChannelMask) { + GST_WARNING ("Unknown channel mask value for %d channel stream", nChannels); + + if (nChannels >= G_N_ELEMENTS (default_ch_masks)) { + GST_ERROR ("To may channels %d", nChannels); + return 0; + } + + dwChannelMask = default_ch_masksnChannels; + } + + pos = g_new (GstAudioChannelPosition, nChannels); + gst_wasapi_util_channel_position_all_none (nChannels, pos); + + /* Too many channels, have to assume that they are all non-positional */ + if (nChannels > G_N_ELEMENTS (wasapi_to_gst_pos)) { + GST_LOG ("Got too many (%i) channels, assuming non-positional", nChannels); + goto out; + } + + /* Too many bits in the channel mask, and the bits don't match nChannels */ + if (dwChannelMask >> (G_N_ELEMENTS (wasapi_to_gst_pos) + 1) != 0) { + GST_WARNING ("Too many bits in channel mask (%lu), assuming " + "non-positional", dwChannelMask); + goto out; + } + + /* Map WASAPI's channel mask to Gstreamer's channel mask and positions. + * If the no. of bits in the mask > nChannels, we will ignore the extra. */ + for (i = 0, ch = 0; i < G_N_ELEMENTS (wasapi_to_gst_pos) && ch < nChannels; + i++) { + if (!(dwChannelMask & wasapi_to_gst_posi.wasapi_pos)) + /* no match, try next */ + continue; + mask |= G_GUINT64_CONSTANT (1) << wasapi_to_gst_posi.gst_pos; + posch++ = wasapi_to_gst_posi.gst_pos; + } + + /* XXX: Warn if some channel masks couldn't be mapped? */ + + GST_TRACE ("Converted WASAPI mask 0x%" G_GINT64_MODIFIER "x -> 0x%" + G_GINT64_MODIFIER "x", (guint64) dwChannelMask, (guint64) mask); + +out: + if (out_position) { + *out_position = pos; + } else { + g_free (pos); + } + + return mask; +} + +const gchar * +gst_wasapi2_util_waveformatex_to_audio_format (WAVEFORMATEX * format) +{ + const gchar *fmt_str = NULL; + GstAudioFormat fmt = GST_AUDIO_FORMAT_UNKNOWN; + + switch (format->wFormatTag) { + case WAVE_FORMAT_PCM: + fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, + format->wBitsPerSample, format->wBitsPerSample); + break; + case WAVE_FORMAT_IEEE_FLOAT: + if (format->wBitsPerSample == 32) + fmt = GST_AUDIO_FORMAT_F32LE; + else if (format->wBitsPerSample == 64) + fmt = GST_AUDIO_FORMAT_F64LE; + break; + case WAVE_FORMAT_EXTENSIBLE: + { + WAVEFORMATEXTENSIBLE *ex = (WAVEFORMATEXTENSIBLE *) format; + if (IsEqualGUID (ex->SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) { + fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, + format->wBitsPerSample, ex->Samples.wValidBitsPerSample); + } else if (IsEqualGUID (ex->SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (format->wBitsPerSample == 32 + && ex->Samples.wValidBitsPerSample == 32) + fmt = GST_AUDIO_FORMAT_F32LE; + else if (format->wBitsPerSample == 64 && + ex->Samples.wValidBitsPerSample == 64) + fmt = GST_AUDIO_FORMAT_F64LE; + } + break; + } + default: + break; + } + + if (fmt != GST_AUDIO_FORMAT_UNKNOWN) + fmt_str = gst_audio_format_to_string (fmt); + + return fmt_str; +} + +gboolean +gst_wasapi2_util_parse_waveformatex (WAVEFORMATEX * format, + GstCaps * template_caps, GstCaps ** out_caps, + GstAudioChannelPosition ** out_positions) +{ + const gchar *afmt; + guint64 channel_mask; + + *out_caps = NULL; + + /* TODO: handle SPDIF and other encoded formats */ + + /* 1 or 2 channels <= 16 bits sample size OR + * 1 or 2 channels > 16 bits sample size or >2 channels */ + if (format->wFormatTag != WAVE_FORMAT_PCM && + format->wFormatTag != WAVE_FORMAT_IEEE_FLOAT && + format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + /* Unhandled format tag */ + return FALSE; + + /* WASAPI can only tell us one canonical mix format that it will accept. The + * alternative is calling IsFormatSupported on all combinations of formats. + * Instead, it's simpler and faster to require conversion inside gstreamer */ + afmt = gst_wasapi2_util_waveformatex_to_audio_format (format); + if (afmt == NULL) + return FALSE; + + *out_caps = gst_caps_copy (template_caps); + + channel_mask = gst_wasapi2_util_waveformatex_to_channel_mask (format, + out_positions); + + gst_caps_set_simple (*out_caps, + "format", G_TYPE_STRING, afmt, + "channels", G_TYPE_INT, format->nChannels, + "rate", G_TYPE_INT, format->nSamplesPerSec, NULL); + + if (channel_mask) { + gst_caps_set_simple (*out_caps, + "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); + } + + return TRUE; +} + +gboolean +gst_wasapi2_can_automatic_stream_routing (void) +{ + static gboolean ret = FALSE; + + GST_WASAPI2_CALL_ONCE_BEGIN { + OSVERSIONINFOEXW osverinfo; + typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW); + fRtlGetVersion *RtlGetVersion = NULL; + HMODULE hmodule = NULL; + + memset (&osverinfo, 0, sizeof (OSVERSIONINFOEXW)); + osverinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW); + + hmodule = LoadLibraryW (L"ntdll.dll"); + if (hmodule) + RtlGetVersion = + (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion (&osverinfo); + + /* automatic stream routing requires Windows 10 + * Anniversary Update (version 1607, build number 14393.0) */ + if (osverinfo.dwMajorVersion > 10 || + (osverinfo.dwMajorVersion == 10 && osverinfo.dwBuildNumber >= 14393)) + ret = TRUE; + } + + if (hmodule) + FreeLibrary (hmodule); + } + GST_WASAPI2_CALL_ONCE_END; + + GST_TRACE ("Automatic stream routing support: %d", ret); + + return ret; +} + +gboolean +gst_wasapi2_can_process_loopback (void) +{ + static gboolean ret = FALSE; + + GST_WASAPI2_CALL_ONCE_BEGIN { + OSVERSIONINFOEXW osverinfo; + typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW); + fRtlGetVersion *RtlGetVersion = NULL; + HMODULE hmodule = NULL; + + memset (&osverinfo, 0, sizeof (OSVERSIONINFOEXW)); + osverinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW); + + hmodule = LoadLibraryW (L"ntdll.dll"); + if (hmodule) + RtlGetVersion = + (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion"); + + if (RtlGetVersion) { + RtlGetVersion (&osverinfo); + + /* Process loopback requires Windows 10 build 20348 + * https://learn.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params + * + * Note: "Windows 10 build 20348" would mean "Windows server 2022" or + * "Windows 11", since build number of "Windows 10 version 21H2" is + * still 19044.XXX + */ + + /* But other software enables this for build number 19041 or higher... */ + if (osverinfo.dwMajorVersion > 10 || + (osverinfo.dwMajorVersion == 10 && osverinfo.dwBuildNumber >= 19041)) + ret = TRUE; + } + + if (hmodule) + FreeLibrary (hmodule); + } + GST_WASAPI2_CALL_ONCE_END; + + GST_INFO ("Process loopback support: %d", ret); + + return ret; +} + +WAVEFORMATEX * +gst_wasapi2_get_default_mix_format (void) +{ + WAVEFORMATEX *format; + + /* virtual loopback device might not provide mix format. Create our default + * mix format */ + format = (WAVEFORMATEX *) CoTaskMemAlloc (sizeof (WAVEFORMATEX)); + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = 2; + format->nSamplesPerSec = 44100; + format->wBitsPerSample = 16; + format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8; + format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; + + return format; +} + +const wchar_t * +gst_wasapi2_get_default_device_id_wide (EDataFlow flow) +{ + static wchar_t *capture = nullptr; + static wchar_t *render = nullptr; + + GST_WASAPI2_CALL_ONCE_BEGIN { + StringFromIID (DEVINTERFACE_AUDIO_CAPTURE, &capture); + StringFromIID (DEVINTERFACE_AUDIO_RENDER, &render); + } GST_WASAPI2_CALL_ONCE_END; + + if (flow == eCapture) + return (const wchar_t *) capture; + + return (const wchar_t *) render; +} + +const char * +gst_wasapi2_get_default_device_id (EDataFlow flow) +{ + static char *capture = nullptr; + static char *render = nullptr; + + GST_WASAPI2_CALL_ONCE_BEGIN { + auto wstr = gst_wasapi2_get_default_device_id_wide (eCapture); + if (wstr) { + capture = g_utf16_to_utf8 ((const gunichar2 *) wstr, + -1, nullptr, nullptr, nullptr); + } + + wstr = gst_wasapi2_get_default_device_id_wide (eRender); + if (wstr) { + render = g_utf16_to_utf8 ((const gunichar2 *) wstr, + -1, nullptr, nullptr, nullptr); + } + } GST_WASAPI2_CALL_ONCE_END; + + if (flow == eCapture) + return (const char *) capture; + + return (const char *) render; +}
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/gstwasapi2util.h -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/gstwasapi2util.h
Changed
@@ -26,6 +26,8 @@ #include <initguid.h> #include <audioclient.h> #include <endpointvolume.h> +#include <string.h> +#include <mmdeviceapi.h> G_BEGIN_DECLS @@ -57,6 +59,42 @@ _gst_wasapi2_result (result, NULL, __FILE__, GST_FUNCTION, __LINE__) #endif +typedef enum +{ + GST_WASAPI2_ENDPOINT_CLASS_CAPTURE = 0, + GST_WASAPI2_ENDPOINT_CLASS_RENDER, + GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE, + GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE, + GST_WASAPI2_ENDPOINT_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE, +} GstWasapi2EndpointClass; + +static inline gboolean +gst_wasapi2_is_loopback_class (GstWasapi2EndpointClass device_class) +{ + switch (device_class) { + case GST_WASAPI2_ENDPOINT_CLASS_LOOPBACK_CAPTURE: + return TRUE; + default: + break; + } + + return FALSE; +} + +static inline gboolean +gst_wasapi2_is_process_loopback_class (GstWasapi2EndpointClass device_class) +{ + switch (device_class) { + case GST_WASAPI2_ENDPOINT_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE: + case GST_WASAPI2_ENDPOINT_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE: + return TRUE; + default: + break; + } + + return FALSE; +} + guint64 gst_wasapi2_util_waveformatex_to_channel_mask (WAVEFORMATEX * format, GstAudioChannelPosition ** out_position); @@ -75,6 +113,20 @@ WAVEFORMATEX * gst_wasapi2_get_default_mix_format (void); +const wchar_t * gst_wasapi2_get_default_device_id_wide (EDataFlow flow); + +const char * gst_wasapi2_get_default_device_id (EDataFlow flow); + G_END_DECLS +#ifdef __cplusplus +#include <mutex> + +#define GST_WASAPI2_CALL_ONCE_BEGIN \ + static std::once_flag __once_flag; \ + std::call_once (__once_flag, &() + +#define GST_WASAPI2_CALL_ONCE_END ) +#endif + #endif /* __GST_WASAPI_UTIL_H__ */
View file
_service:download_url:gst-plugins-bad-1.26.4.tar.xz/sys/wasapi2/meson.build -> _service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/meson.build
Changed
@@ -1,11 +1,13 @@ wasapi2_sources = 'gstwasapi2src.c', 'gstwasapi2sink.c', - 'gstwasapi2util.c', - 'gstwasapi2client.cpp', - 'gstwasapi2device.c', + 'gstwasapi2util.cpp', + 'gstwasapi2device.cpp', 'gstwasapi2ringbuffer.cpp', - 'plugin.c', + 'gstwasapi2activator.cpp', + 'gstwasapi2enumerator.cpp', + 'gstwasapi2object.cpp', + 'plugin.cpp', wasapi2_headers = @@ -14,8 +16,7 @@ 'gstwasapi2util.h', 'gstwasapi2src.h', 'gstwasapi2sink.h', - 'gstwasapi2client.h', - 'AsyncOperations.h', + 'gstwasapi2object.h', mmdeviceapi_symbols = @@ -44,11 +45,10 @@ ole32_dep = cc.find_library('ole32', required : get_option('wasapi2')) ksuser_dep = cc.find_library('ksuser', required : get_option('wasapi2')) -runtimeobject_dep = cc.find_library('runtimeobject', required : get_option('wasapi2')) mmdeviceapi_dep = cc.find_library('mmdevapi', required : get_option('wasapi2')) mfplat_dep = cc.find_library('mfplat', required : get_option('wasapi2')) -wasapi2_dep = ole32_dep, ksuser_dep, runtimeobject_dep, mmdeviceapi_dep, mfplat_dep -extra_args = '-DGST_USE_UNSTABLE_API' +wasapi2_dep = ole32_dep, ksuser_dep, mmdeviceapi_dep, mfplat_dep +extra_args = foreach dep: wasapi2_dep if not dep.found() @@ -78,40 +78,6 @@ endif endforeach -winapi_app = cxx.compiles('''#include <winapifamily.h> - #include <windows.applicationmodel.core.h> - #include <wrl.h> - #include <wrl/wrappers/corewrappers.h> - #include <audioclient.h> - #include <mmdeviceapi.h> - #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) - #error "not winrt" - #endif - int main (int argc, char ** argv) { - IAudioClient3 *client = NULL; - return 0; - } ''', - dependencies: wasapi2_dep, - name: 'building for WINAPI_PARTITION_APP') - -if not winapi_app - if wasapi2_option.enabled() - error('wasapi2 plugin was enabled explicitly, but build target is not include WINAPI_PARTITION_APP') - else - subdir_done() - endif -endif - -winapi_desktop = cxx.compiles('''#include <winapifamily.h> - #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - #error "not win32" - #endif''', - name: 'building for WINAPI_PARTITION_DESKTOP') - -if winapi_app and not winapi_desktop - extra_args += '-DGST_WASAPI2_WINAPI_ONLY_APP' -endif - win10_sdk = cxx.compiles('''#include <windows.h> #ifndef WDK_NTDDI_VERSION #error "unknown Windows SDK version" @@ -145,24 +111,13 @@ extra_args += '-U_WIN32_WINNT', '-UWINVER', '-DWINVER=0x0A00', '-D_WIN32_WINNT=0x0A00', '-DNTDDI_VERSION=WDK_NTDDI_VERSION' endif -if not gstwinrt_dep.found() - if wasapi2_option.enabled() - error('wasapi2 plugin was enabled explicitly, but GstWinRt library is unavailable') - else - subdir_done() - endif -endif - -# Work around for Windows SDK header issue -# https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance?view=msvc-160#windows-header-issues -extra_cpp_args = cxx.get_supported_arguments('/Zc:twoPhase-') - gstwasapi2 = library('gstwasapi2', wasapi2_sources, - c_args : gst_plugins_bad_args + '-DCOBJMACROS' + extra_args, - cpp_args : gst_plugins_bad_args + extra_args + extra_cpp_args, + c_args : gst_plugins_bad_args + extra_args, + cpp_args : gst_plugins_bad_args + extra_args, include_directories : configinc, - dependencies : gstaudio_dep, gstwinrt_dep + wasapi2_dep, + dependencies : gstaudio_dep + wasapi2_dep, + override_options : 'cpp_std=c++14', install : true, install_dir : plugins_install_dir) plugins += gstwasapi2
View file
_service:download_url:gst-plugins-bad-1.26.5.tar.xz/sys/wasapi2/plugin.cpp
Added
@@ -0,0 +1,80 @@ +/* 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 <winapifamily.h> + +#include "gstwasapi2sink.h" +#include "gstwasapi2src.h" +#include "gstwasapi2device.h" +#include "gstwasapi2util.h" +#include <mfapi.h> + +GST_DEBUG_CATEGORY (gst_wasapi2_debug); +GST_DEBUG_CATEGORY (gst_wasapi2_client_debug); + +static void +plugin_deinit (gpointer data) +{ + MFShutdown (); +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + guint rank = GST_RANK_PRIMARY + 1; + HRESULT hr; + + /** + * plugin-wasapi2: + * + * Since: 1.18 + */ + + hr = MFStartup (MF_VERSION, MFSTARTUP_NOSOCKET); + if (!gst_wasapi2_result (hr)) { + GST_WARNING ("MFStartup failure, hr: 0x%x", (guint) hr); + return TRUE; + } + + GST_DEBUG_CATEGORY_INIT (gst_wasapi2_debug, "wasapi2", 0, "wasapi2"); + GST_DEBUG_CATEGORY_INIT (gst_wasapi2_client_debug, "wasapi2client", + 0, "wasapi2client"); + + gst_element_register (plugin, "wasapi2sink", rank, GST_TYPE_WASAPI2_SINK); + gst_element_register (plugin, "wasapi2src", rank, GST_TYPE_WASAPI2_SRC); + + gst_device_provider_register (plugin, "wasapi2deviceprovider", rank, + GST_TYPE_WASAPI2_DEVICE_PROVIDER); + + g_object_set_data_full (G_OBJECT (plugin), + "plugin-wasapi2-shutdown", (gpointer) "shutdown-data", + (GDestroyNotify) plugin_deinit); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + wasapi2, + "Windows audio session API plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.