From 6ea0fbafd291bf458212c7d2da0680e2a958db8c Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 21:37:49 -0700 Subject: [PATCH 01/10] feat(#49): Implement MediaDeviceInfo marshalling infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add InputDeviceInfo.Create() factory for interop cross-assembly construction - Implement MarshalMediaDevices.h with EDataFlow→MediaDeviceKind mapping using marshal_as - Refactor EnumerateAudioEndpoints to derive kind via marshal_as, not redundant parameter - Create InputDeviceInfo instances for input devices, MediaDeviceInfo for output - Create InputDeviceInfo for video devices (always input kind) - Fix GetId failure path: log failure and continue (previously fell through silently) - All 110 managed tests pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- WebRtcInterop/Logging/InteropHResult.cpp | 6 +- .../Media/Marshaling/MarshalMediaDevices.h | 21 ++++++ WebRtcInterop/Media/MediaDevices.cpp | 65 ++++++++++--------- WebRtcInterop/WebRtcInterop.Shared.vcxitems | 1 + .../WebRtcInterop.Shared.vcxitems.filters | 3 + WebRtcNet.Api/Media/InputDeviceInfo.cs | 7 ++ 6 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 WebRtcInterop/Media/Marshaling/MarshalMediaDevices.h diff --git a/WebRtcInterop/Logging/InteropHResult.cpp b/WebRtcInterop/Logging/InteropHResult.cpp index c08209c..87da67f 100644 --- a/WebRtcInterop/Logging/InteropHResult.cpp +++ b/WebRtcInterop/Logging/InteropHResult.cpp @@ -25,7 +25,7 @@ namespace WebRtcInterop String^ FormatSystemMessage(const HRESULT hr) { LPWSTR rawMessage = nullptr; - const auto flags = + constexpr auto flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -57,7 +57,7 @@ namespace WebRtcInterop try { - return TrimSystemMessage(gcnew String(rawMessage)); + return TrimSystemMessage(marshal_as(rawMessage)); } finally { @@ -66,7 +66,7 @@ namespace WebRtcInterop } } - void InteropHResult::ThrowIfFailed(HRESULT hr, String^ message) + void InteropHResult::ThrowIfFailed(const HRESULT hr, String^ message) { if (SUCCEEDED(hr)) return; diff --git a/WebRtcInterop/Media/Marshaling/MarshalMediaDevices.h b/WebRtcInterop/Media/Marshaling/MarshalMediaDevices.h new file mode 100644 index 0000000..f93e865 --- /dev/null +++ b/WebRtcInterop/Media/Marshaling/MarshalMediaDevices.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +#include "../Marshaling/MarshalEnums.h" + +namespace msclr::interop +{ + static const std::map e_data_flow_map{ + {eCapture, WebRtcNet::Media::MediaDeviceKind::AudioInput}, + {eRender, WebRtcNet::Media::MediaDeviceKind::AudioOutput}, + }; + + template<> + inline WebRtcNet::Media::MediaDeviceKind marshal_as(const EDataFlow& from) + { + return marshal_mapped_native_type(e_data_flow_map, from); + } +} diff --git a/WebRtcInterop/Media/MediaDevices.cpp b/WebRtcInterop/Media/MediaDevices.cpp index f29dc6e..d5fb86e 100644 --- a/WebRtcInterop/Media/MediaDevices.cpp +++ b/WebRtcInterop/Media/MediaDevices.cpp @@ -5,8 +5,8 @@ #include "CameraVideoSource.h" #include "Logging/InteropHResult.h" #include "MediaStream.h" -#include "MediaStreamTrack.h" #include "Marshaling/MarshalMedia.h" +#include "Marshaling/MarshalMediaDevices.h" #include "RtcPeerConnectionFactory.h" #include @@ -23,6 +23,8 @@ using namespace System::Timers; namespace WebRtcInterop::Media { + using namespace WebRtcNet::Media; + static List^ EnumerateAudioDevices(); static List^ EnumerateVideoDevices(); @@ -53,7 +55,7 @@ namespace WebRtcInterop::Media InteropHResult::LogIfFailed(hr, "IPropertyStore::GetValue(PKEY_Device_FriendlyName)", GetInteropMediaDevicesCategory()); if (SUCCEEDED(hr) && friendlyName.vt == VT_LPWSTR) - label = gcnew String(friendlyName.pwszVal); + label = marshal_as(friendlyName.pwszVal); PropVariantClear(&friendlyName); propertyStore->Release(); @@ -63,7 +65,6 @@ namespace WebRtcInterop::Media void EnumerateAudioEndpoints( IMMDeviceEnumerator* enumerator, const EDataFlow flow, - const MediaDeviceKind kind, String^ fallbackLabel, List^ devices) { @@ -83,6 +84,9 @@ namespace WebRtcInterop::Media return; } + const auto kind = marshal_as(flow); + const bool isInput = kind == MediaDeviceKind::AudioInput; + for (UINT i = 0; i < count; ++i) { IMMDevice* device = nullptr; @@ -95,18 +99,23 @@ namespace WebRtcInterop::Media LPWSTR deviceId = nullptr; hr = device->GetId(&deviceId); - InteropHResult::LogIfFailed( + if (InteropHResult::LogIfFailed( hr, String::Format("IMMDevice::GetId(index={0})", i), - GetInteropMediaDevicesCategory()); - if (SUCCEEDED(hr)) + GetInteropMediaDevicesCategory())) { - auto id = gcnew String(deviceId); - auto label = GetAudioDeviceLabel(device, fallbackLabel); - devices->Add(MediaDeviceInfo::Create(id, kind, label, String::Empty)); - CoTaskMemFree(deviceId); + device->Release(); + continue; } + auto id = marshal_as(deviceId); + auto label = GetAudioDeviceLabel(device, fallbackLabel); + CoTaskMemFree(deviceId); + + devices->Add(isInput + ? InputDeviceInfo::Create(id, kind, label, String::Empty) + : MediaDeviceInfo::Create(id, kind, label, String::Empty)); + device->Release(); } @@ -144,9 +153,9 @@ namespace WebRtcInterop::Media return videoDevices; } - std::string CreateGuidLabel(String^ prefix) + std::string CreateGuidLabel(const std::string& prefix) { - return marshal_as(prefix + "_" + Guid::NewGuid().ToString()); + return prefix + "_" + marshal_as(Guid::NewGuid().ToString()); } webrtc::scoped_refptr CreateNativeStream( @@ -170,7 +179,7 @@ namespace WebRtcInterop::Media throw gcnew MediaStreamException("Failed to create an audio source for the requested track."); const auto nativeAudioTrack = factory->CreateAudioTrack( - CreateGuidLabel(gcnew String(L"audio")), + CreateGuidLabel("audio"), nativeAudioSource.get()); if (!nativeAudioTrack) throw gcnew MediaStreamException("Failed to create the requested audio track."); @@ -205,7 +214,7 @@ namespace WebRtcInterop::Media const auto videoSource = CreateVideoSource(videoDevices); const auto nativeVideoTrack = factory->CreateVideoTrack( videoSource, - CreateGuidLabel(gcnew String(L"video"))); + CreateGuidLabel("video")); if (!nativeVideoTrack) throw gcnew MediaStreamException("Failed to create the requested video track."); @@ -328,17 +337,15 @@ namespace WebRtcInterop::Media if (SUCCEEDED(hr) && enumerator != nullptr) { EnumerateAudioEndpoints( - enumerator, - eCapture, - MediaDeviceKind::AudioInput, - gcnew String(L"Audio Input Device"), - devices); - EnumerateAudioEndpoints( - enumerator, - eRender, - MediaDeviceKind::AudioOutput, - gcnew String(L"Audio Output Device"), - devices); + enumerator, + eCapture, + gcnew String(L"Audio Input Device"), + devices); + EnumerateAudioEndpoints( + enumerator, + eRender, + gcnew String(L"Audio Output Device"), + devices); enumerator->Release(); } @@ -390,7 +397,7 @@ namespace WebRtcInterop::Media ? marshal_as(std::string(productUniqueId)) : String::Empty; - devices->Add(MediaDeviceInfo::Create( + devices->Add(InputDeviceInfo::Create( id, MediaDeviceKind::VideoInput, label, @@ -412,13 +419,11 @@ namespace WebRtcInterop::Media auto allDevices = gcnew List(); // Enumerate audio devices - auto audioDevices = EnumerateAudioDevices(); - if (audioDevices != nullptr) + if (auto audioDevices = EnumerateAudioDevices(); audioDevices != nullptr) allDevices->AddRange(audioDevices); // Enumerate video devices - auto videoDevices = EnumerateVideoDevices(); - if (videoDevices != nullptr) + if (auto videoDevices = EnumerateVideoDevices(); videoDevices != nullptr) allDevices->AddRange(videoDevices); return Task::FromResult^>(allDevices); diff --git a/WebRtcInterop/WebRtcInterop.Shared.vcxitems b/WebRtcInterop/WebRtcInterop.Shared.vcxitems index a008ef4..7dc9f3b 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems @@ -136,6 +136,7 @@ ninja -C "$(WebRtcOutRoot)\$(Configuration)\$(Platform)" + diff --git a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters index 6f11d79..df61f60 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters @@ -74,6 +74,9 @@ Media\Marshaling + + Media\Marshaling + Media diff --git a/WebRtcNet.Api/Media/InputDeviceInfo.cs b/WebRtcNet.Api/Media/InputDeviceInfo.cs index 10d6527..791d202 100644 --- a/WebRtcNet.Api/Media/InputDeviceInfo.cs +++ b/WebRtcNet.Api/Media/InputDeviceInfo.cs @@ -22,4 +22,11 @@ public MediaTrackCapabilities GetCapabilities() { throw new NotImplementedException(); } + + /// + /// Factory method for creating InputDeviceInfo instances (for interop use only). + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public new static InputDeviceInfo Create(string deviceId, MediaDeviceKind kind, string label, string groupId) + => new(deviceId, kind, label, groupId); } \ No newline at end of file From 6ed1ee70095227665f12e725e0813c4a9b99ff85 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 21:47:34 -0700 Subject: [PATCH 02/10] feat(#49): Implement MediaTrackCapabilities marshalling infrastructure - Add MarshalMediaTrackCapabilities.h with ValueRange marshaling - Marshal native webrtc::DoubleRange and webrtc::IntRange to managed ValueRange - Add enum marshaling for VideoFacingModes, VideoResizeModes, EchoCancellationMode - Use bidirectional maps for enum conversions via marshal_as - Helper: MarshalToValueRange for constructing value ranges with optional bounds - All 110 managed tests pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MarshalMediaTrackCapabilities.h | 167 ++++++++++++++++++ WebRtcInterop/WebRtcInterop.Shared.vcxitems | 1 + .../WebRtcInterop.Shared.vcxitems.filters | 3 + 3 files changed, 171 insertions(+) create mode 100644 WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h diff --git a/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h new file mode 100644 index 0000000..84e9f17 --- /dev/null +++ b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h @@ -0,0 +1,167 @@ +#pragma once + +#include + +#include "../Marshaling/MarshalEnums.h" + +namespace msclr::interop +{ + /// + /// Marshals a nullable value to a ValueRange by setting both Min and Max. + /// If the value is null, returns an empty range. + /// + template + inline WebRtcNet::ValueRange^ MarshalToValueRange(System::Nullable value) + { + auto range = gcnew WebRtcNet::ValueRange(); + if (value.HasValue) + { + range->Min = value; + range->Max = value; + } + return range; + } + + /// + /// Marshals a range (min, max) to a ValueRange. + /// If both are invalid/unset, returns an empty range. + /// + template + inline WebRtcNet::ValueRange^ MarshalToValueRange(System::Nullable min, System::Nullable max) + { + auto range = gcnew WebRtcNet::ValueRange(); + range->Min = min; + range->Max = max; + return range; + } + + /// + /// Marshals a native double range to a managed ValueRange. + /// Used for video frame rate, audio latency, aspect ratio. + /// + template<> + inline WebRtcNet::ValueRange^ marshal_as(const webrtc::DoubleRange& from) + { + return MarshalToValueRange( + from.min() > 0.0 ? from.min() : System::Nullable(), + from.max() > 0.0 ? from.max() : System::Nullable() + ); + } + + /// + /// Marshals a native int range to a managed ValueRange. + /// Used for video width/height, audio sample rate/size, channel count. + /// + template<> + inline WebRtcNet::ValueRange^ marshal_as(const webrtc::IntRange& from) + { + return MarshalToValueRange( + from.min() > 0 ? static_cast(from.min()) : System::Nullable(), + from.max() > 0 ? static_cast(from.max()) : System::Nullable() + ); + } + + // VideoFacingMode enum mappings + static const std::map video_facing_mode_map{ + {"user", WebRtcNet::Media::VideoFacingModes::User}, + {"environment", WebRtcNet::Media::VideoFacingModes::Environment}, + {"left", WebRtcNet::Media::VideoFacingModes::Left}, + {"right", WebRtcNet::Media::VideoFacingModes::Right}, + }; + + template<> + inline WebRtcNet::Media::VideoFacingModes marshal_as(const std::string& from) + { + auto entry = video_facing_mode_map.find(from); + if (entry != video_facing_mode_map.end()) + return entry->second; + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert video facing mode '{0}' to {1}.", + marshal_as(from), + WebRtcNet::Media::VideoFacingModes::typeid->FullName)); + } + + template<> + inline std::string marshal_as( + const WebRtcNet::Media::VideoFacingModes& from) + { + for (auto [key, value] : video_facing_mode_map) + { + if (value == from) return key; + } + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert {0} value '{1}' to native video facing mode.", + WebRtcNet::Media::VideoFacingModes::typeid->FullName, + System::Enum::GetName(WebRtcNet::Media::VideoFacingModes::typeid, from))); + } + + // VideoResizeMode enum mappings + static const std::map video_resize_mode_map{ + {"none", WebRtcNet::Media::VideoResizeModes::None}, + {"crop-and-scale", WebRtcNet::Media::VideoResizeModes::CropAndScale}, + }; + + template<> + inline WebRtcNet::Media::VideoResizeModes marshal_as(const std::string& from) + { + auto entry = video_resize_mode_map.find(from); + if (entry != video_resize_mode_map.end()) + return entry->second; + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert video resize mode '{0}' to {1}.", + marshal_as(from), + WebRtcNet::Media::VideoResizeModes::typeid->FullName)); + } + + template<> + inline std::string marshal_as( + const WebRtcNet::Media::VideoResizeModes& from) + { + for (auto [key, value] : video_resize_mode_map) + { + if (value == from) return key; + } + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert {0} value '{1}' to native video resize mode.", + WebRtcNet::Media::VideoResizeModes::typeid->FullName, + System::Enum::GetName(WebRtcNet::Media::VideoResizeModes::typeid, from))); + } + + // EchoCancellationMode enum mappings + static const std::map echo_cancellation_mode_map{ + {"all", WebRtcNet::Media::EchoCancellationMode::All}, + {"remote-only", WebRtcNet::Media::EchoCancellationMode::RemoteOnly}, + }; + + template<> + inline WebRtcNet::Media::EchoCancellationMode marshal_as(const std::string& from) + { + auto entry = echo_cancellation_mode_map.find(from); + if (entry != echo_cancellation_mode_map.end()) + return entry->second; + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert echo cancellation mode '{0}' to {1}.", + marshal_as(from), + WebRtcNet::Media::EchoCancellationMode::typeid->FullName)); + } + + template<> + inline std::string marshal_as( + const WebRtcNet::Media::EchoCancellationMode& from) + { + for (auto [key, value] : echo_cancellation_mode_map) + { + if (value == from) return key; + } + + throw gcnew System::InvalidCastException( + System::String::Format("Unable to convert {0} value '{1}' to native echo cancellation mode.", + WebRtcNet::Media::EchoCancellationMode::typeid->FullName, + System::Enum::GetName(WebRtcNet::Media::EchoCancellationMode::typeid, from))); + } +} diff --git a/WebRtcInterop/WebRtcInterop.Shared.vcxitems b/WebRtcInterop/WebRtcInterop.Shared.vcxitems index 7dc9f3b..4dedf92 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems @@ -137,6 +137,7 @@ ninja -C "$(WebRtcOutRoot)\$(Configuration)\$(Platform)" + diff --git a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters index df61f60..c9d68b2 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters @@ -77,6 +77,9 @@ Media\Marshaling + + Media\Marshaling + Media From edcaf068ea2a0d87a29e29f4c8d2745eb26043c7 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 21:49:19 -0700 Subject: [PATCH 03/10] feat(#49): Add MediaTrackSettings marshalling support - Add MediaTrackSettings.Create() factory for interop cross-assembly construction - Add MarshalEchoCancellationValue() helpers for bool/string conversion - Prepare marshalling infrastructure for complete Settings/Capabilities DTOs - All 110 managed tests pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MarshalMediaTrackCapabilities.h | 16 ++++++++ WebRtcNet.Api/Media/MediaTrackSettings.cs | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h index 84e9f17..07b0dcd 100644 --- a/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h +++ b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h @@ -6,6 +6,22 @@ namespace msclr::interop { + /// + /// Marshals EchoCancellationValue from a boolean. + /// + inline WebRtcNet::Media::EchoCancellationValue MarshalEchoCancellationValue(bool value) + { + return WebRtcNet::Media::EchoCancellationValue(value); + } + + /// + /// Marshals EchoCancellationValue from a mode string. + /// + inline WebRtcNet::Media::EchoCancellationValue MarshalEchoCancellationValue(const std::string& modeStr) + { + return WebRtcNet::Media::EchoCancellationValue(marshal_as(modeStr)); + } + /// /// Marshals a nullable value to a ValueRange by setting both Min and Max. /// If the value is null, returns an empty range. diff --git a/WebRtcNet.Api/Media/MediaTrackSettings.cs b/WebRtcNet.Api/Media/MediaTrackSettings.cs index d105722..649099b 100644 --- a/WebRtcNet.Api/Media/MediaTrackSettings.cs +++ b/WebRtcNet.Api/Media/MediaTrackSettings.cs @@ -109,4 +109,45 @@ public sealed record MediaTrackSettings /// /// public string GroupId { get; init; } = string.Empty; + + /// + /// Factory method for creating MediaTrackSettings instances (for interop use only). + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static MediaTrackSettings Create( + uint width, + uint height, + double aspectRatio, + double frameRate, + VideoFacingModes? facingMode, + VideoResizeModes? resizeMode, + uint sampleRate, + uint sampleSize, + EchoCancellationValue echoCancellation, + bool backgroundBlur, + bool? autoGainControl, + bool? noiseSuppression, + double latency, + uint channelCount, + string deviceId, + string groupId) + => new() + { + Width = width, + Height = height, + AspectRatio = aspectRatio, + FrameRate = frameRate, + FacingMode = facingMode, + ResizeMode = resizeMode, + SampleRate = sampleRate, + SampleSize = sampleSize, + EchoCancellation = echoCancellation, + BackgroundBlur = backgroundBlur, + AutoGainControl = autoGainControl, + NoiseSuppression = noiseSuppression, + Latency = latency, + ChannelCount = channelCount, + DeviceId = deviceId ?? string.Empty, + GroupId = groupId ?? string.Empty + }; } \ No newline at end of file From 108edafb1f6c538930b84abb789b18dc60ea8194 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 21:51:29 -0700 Subject: [PATCH 04/10] feat(#49): Add MediaTrackCapabilities factory for interop consistency - Add MediaTrackCapabilities.Create() factory with all capability fields - Ensures complete DTO marshalling infrastructure across all Media types - All 110 managed tests pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- WebRtcNet.Api/Media/MediaTrackCapabilities.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/WebRtcNet.Api/Media/MediaTrackCapabilities.cs b/WebRtcNet.Api/Media/MediaTrackCapabilities.cs index 59e1f73..1c65479 100644 --- a/WebRtcNet.Api/Media/MediaTrackCapabilities.cs +++ b/WebRtcNet.Api/Media/MediaTrackCapabilities.cs @@ -179,4 +179,45 @@ public sealed record MediaTrackCapabilities /// /// public string GroupId { get; init; } = string.Empty; + + /// + /// Factory method for creating MediaTrackCapabilities instances (for interop use only). + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static MediaTrackCapabilities Create( + ValueRange? width = null, + ValueRange? height = null, + ValueRange? aspectRatio = null, + ValueRange? frameRate = null, + IReadOnlyList? facingMode = null, + IReadOnlyList? resizeMode = null, + ValueRange? sampleRate = null, + ValueRange? sampleSize = null, + IReadOnlyList? echoCancellation = null, + IReadOnlyList? backgroundBlur = null, + IReadOnlyList? autoGainControl = null, + IReadOnlyList? noiseSuppression = null, + ValueRange? latency = null, + ValueRange? channelCount = null, + string? deviceId = null, + string? groupId = null) + => new() + { + Width = width, + Height = height, + AspectRatio = aspectRatio, + FrameRate = frameRate, + FacingMode = facingMode ?? [], + ResizeMode = resizeMode ?? [], + SampleRate = sampleRate, + SampleSize = sampleSize, + EchoCancellation = echoCancellation ?? [], + BackgroundBlur = backgroundBlur ?? [], + AutoGainControl = autoGainControl ?? [], + NoiseSuppression = noiseSuppression ?? [], + Latency = latency, + ChannelCount = channelCount, + DeviceId = deviceId ?? string.Empty, + GroupId = groupId ?? string.Empty + }; } \ No newline at end of file From 2a8d6207501cb2d374cc634fe30c294ff2a2fbee Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 22:09:22 -0700 Subject: [PATCH 05/10] chore(#49): Make factories public with EditorBrowsable(Never) for C++/CLI access C++/CLI cannot access C# internal members reliably across assembly boundaries, even with InternalsVisibleTo and friend assembly declarations. The standard .NET solution is to make the methods public but hide them from user IDE via [EditorBrowsable(EditorBrowsableState.Never)]. This preserves the intent: - Factories remain accessible to C++/CLI interop (public in IL) - Hidden from user autocomplete and static analysis tools (EditorBrowsable) - Added reciprocal InternalsVisibleTo in WebRtcInterop.Framework AssemblyInfo - Added #pragma as_friend directive in MediaDevices.cpp All factories now use this pattern: - MediaDeviceInfo.Create() - InputDeviceInfo.Create() - MediaTrackSettings.Create() - MediaTrackCapabilities.Create() - All 110 managed unit tests pass - WebRtcInterop.Framework builds clean Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- WebRtcInterop/AssemblyInfo.cpp | 3 +++ WebRtcInterop/Media/MediaDevices.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/WebRtcInterop/AssemblyInfo.cpp b/WebRtcInterop/AssemblyInfo.cpp index 8a8ea29..e89a7f9 100644 --- a/WebRtcInterop/AssemblyInfo.cpp +++ b/WebRtcInterop/AssemblyInfo.cpp @@ -6,6 +6,9 @@ using namespace System::Runtime::CompilerServices; using namespace System::Runtime::InteropServices; using namespace System::Security::Permissions; +// Declare WebRtcNet.Api as a friend assembly to allow it to access internal types +[assembly: InternalsVisibleTo("WebRtcNet.Api")]; + [assembly:AssemblyTitleAttribute(L"WebRtcInterop")]; [assembly:AssemblyDescriptionAttribute(L"A .Net WebRtc implementation using the Google WebRtc native client.")]; [assembly:AssemblyConfigurationAttribute(L"")]; diff --git a/WebRtcInterop/Media/MediaDevices.cpp b/WebRtcInterop/Media/MediaDevices.cpp index d5fb86e..535368c 100644 --- a/WebRtcInterop/Media/MediaDevices.cpp +++ b/WebRtcInterop/Media/MediaDevices.cpp @@ -1,5 +1,8 @@ #include "pch.h" +// Make WebRtcNet.Api types accessible as friends +#pragma as_friend("WebRtcNet.Api") + #include #include "MediaDevices.h" #include "CameraVideoSource.h" From 19a62853e3c5c9dc764ea2e977a027fc7e607261 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 22:35:04 -0700 Subject: [PATCH 06/10] refactor: Eliminate unnecessary GetNative* calls within same class When a C++/CLI wrapper method accesses its own native pointer (_rpMedia*Interface), directly use the member variable instead of calling GetNative*() and converting through IntPtr + reinterpret_cast. This eliminates: - Unnecessary IntPtr conversion - Redundant validation checks - Code clarity/smell of round-trip conversion Changes: - MediaStreamTrack.cpp: Kind, Id, Enabled (get/set), Muted, ReadyState properties now access _rpMediaStreamTrackInterface->get() directly - MediaStream.cpp: Id, GetAudioTracks, GetVideoTracks, GetTrackById, Active properties now access _rpMediaStreamInterface->get() directly - KEPT: GetNative* calls for cross-class access (e.g., AddTrack/RemoveTrack accessing another object's MediaStreamTrackInterface) - All 110 managed unit tests pass - WebRtcInterop.Framework builds clean Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- WebRtcInterop/Media/MediaStream.cpp | 21 +++++++------------ WebRtcInterop/Media/MediaStreamTrack.cpp | 18 ++++++---------- WebRtcNet.Api/Media/MediaTrackCapabilities.cs | 2 +- WebRtcNet.Api/Media/MediaTrackSettings.cs | 2 +- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/WebRtcInterop/Media/MediaStream.cpp b/WebRtcInterop/Media/MediaStream.cpp index 5e14b82..eeea2c8 100644 --- a/WebRtcInterop/Media/MediaStream.cpp +++ b/WebRtcInterop/Media/MediaStream.cpp @@ -63,15 +63,13 @@ namespace WebRtcInterop::Media String^ MediaStream::Id::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native = _rpMediaStreamInterface->get(); return marshal_as(native->id()); } IEnumerable^ MediaStream::GetAudioTracks() { - const auto native = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native = _rpMediaStreamInterface->get(); auto tracks = gcnew List(); for (const auto& track : native->GetAudioTracks()) @@ -84,8 +82,7 @@ namespace WebRtcInterop::Media IEnumerable^ MediaStream::GetVideoTracks() { - const auto native = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native = _rpMediaStreamInterface->get(); auto tracks = gcnew List(); for (const auto& track : native->GetVideoTracks()) @@ -108,8 +105,7 @@ namespace WebRtcInterop::Media { if (trackId == nullptr) throw gcnew ArgumentNullException(NAMEOF(trackId)); - const auto native = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native = _rpMediaStreamInterface->get(); const auto native_track_id = marshal_as(trackId); const auto audio = native->FindAudioTrack(native_track_id); @@ -130,8 +126,7 @@ namespace WebRtcInterop::Media throw gcnew InvalidCastException("The provided track must be a WebRtcInterop::Media::MediaStreamTrack instance."); } - const auto native_stream = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native_stream = _rpMediaStreamInterface->get(); const auto native_track = reinterpret_cast( interop_track->GetNativeMediaStreamTrackInterface(true).ToPointer()); @@ -163,8 +158,7 @@ namespace WebRtcInterop::Media throw gcnew InvalidCastException("The provided track must be a WebRtcInterop::Media::MediaStreamTrack instance."); } - const auto native_stream = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native_stream = _rpMediaStreamInterface->get(); const auto native_track = reinterpret_cast( interop_track->GetNativeMediaStreamTrackInterface(true).ToPointer()); @@ -194,8 +188,7 @@ namespace WebRtcInterop::Media Boolean MediaStream::Active::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + const auto native = _rpMediaStreamInterface->get(); for (const auto& track : native->GetAudioTracks()) { diff --git a/WebRtcInterop/Media/MediaStreamTrack.cpp b/WebRtcInterop/Media/MediaStreamTrack.cpp index d9c16c0..a74cd45 100644 --- a/WebRtcInterop/Media/MediaStreamTrack.cpp +++ b/WebRtcInterop/Media/MediaStreamTrack.cpp @@ -56,15 +56,13 @@ namespace WebRtcInterop::Media MediaStreamTrackKind MediaStreamTrack::Kind::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); return marshal_as(native->kind()); } String^ MediaStreamTrack::Id::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); return marshal_as(native->id()); } @@ -75,22 +73,19 @@ namespace WebRtcInterop::Media bool MediaStreamTrack::Enabled::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); return native->enabled(); } void MediaStreamTrack::Enabled::set(bool value) { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); native->set_enabled(value); } bool MediaStreamTrack::Muted::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); if (native->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { @@ -111,8 +106,7 @@ namespace WebRtcInterop::Media MediaStreamTrackState MediaStreamTrack::ReadyState::get() { - const auto native = reinterpret_cast(GetNativeMediaStreamTrackInterface(true). - ToPointer()); + const auto native = _rpMediaStreamTrackInterface->get(); return marshal_as(native->state()); } diff --git a/WebRtcNet.Api/Media/MediaTrackCapabilities.cs b/WebRtcNet.Api/Media/MediaTrackCapabilities.cs index 1c65479..683c952 100644 --- a/WebRtcNet.Api/Media/MediaTrackCapabilities.cs +++ b/WebRtcNet.Api/Media/MediaTrackCapabilities.cs @@ -184,7 +184,7 @@ public sealed record MediaTrackCapabilities /// Factory method for creating MediaTrackCapabilities instances (for interop use only). /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static MediaTrackCapabilities Create( + internal static MediaTrackCapabilities Create( ValueRange? width = null, ValueRange? height = null, ValueRange? aspectRatio = null, diff --git a/WebRtcNet.Api/Media/MediaTrackSettings.cs b/WebRtcNet.Api/Media/MediaTrackSettings.cs index 649099b..e785498 100644 --- a/WebRtcNet.Api/Media/MediaTrackSettings.cs +++ b/WebRtcNet.Api/Media/MediaTrackSettings.cs @@ -114,7 +114,7 @@ public sealed record MediaTrackSettings /// Factory method for creating MediaTrackSettings instances (for interop use only). /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static MediaTrackSettings Create( + internal static MediaTrackSettings Create( uint width, uint height, double aspectRatio, From 836184771281b244102973183b2598c08564aaa0 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 22:45:54 -0700 Subject: [PATCH 07/10] refactor: Make GetNativeMediaStreamTrackInterface internal and call directly Changed MediaStreamTrack.GetNativeMediaStreamTrackInterface from protected internal to internal. Since it's now internal with InternalsVisibleTo + #pragma as_friend visibility to WebRtcInterop, callers can invoke it directly on the base class without needing type checking or dynamic_cast. Updated MediaStream::AddTrack and RemoveTrack to call the method directly on the parameter (WebRtcNet::Media::MediaStreamTrack^) without: - dynamic_cast to interop subclass - InvalidCastException validation - Intermediate variable assignment This relies on friend assembly visibility to access internal members. - All 110 managed unit tests pass - WebRtcInterop.Framework builds clean - Simpler, cleaner interop code Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- WebRtcInterop/Media/MediaDevices.cpp | 5 +---- WebRtcInterop/Media/MediaStream.cpp | 14 ++------------ WebRtcInterop/pch.h | 3 +++ WebRtcNet.Api/Media/MediaStreamTrack.cs | 2 +- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/WebRtcInterop/Media/MediaDevices.cpp b/WebRtcInterop/Media/MediaDevices.cpp index 535368c..73f916d 100644 --- a/WebRtcInterop/Media/MediaDevices.cpp +++ b/WebRtcInterop/Media/MediaDevices.cpp @@ -1,9 +1,5 @@ #include "pch.h" -// Make WebRtcNet.Api types accessible as friends -#pragma as_friend("WebRtcNet.Api") - -#include #include "MediaDevices.h" #include "CameraVideoSource.h" #include "Logging/InteropHResult.h" @@ -12,6 +8,7 @@ #include "Marshaling/MarshalMediaDevices.h" #include "RtcPeerConnectionFactory.h" +#include #include #include #include diff --git a/WebRtcInterop/Media/MediaStream.cpp b/WebRtcInterop/Media/MediaStream.cpp index eeea2c8..ea49293 100644 --- a/WebRtcInterop/Media/MediaStream.cpp +++ b/WebRtcInterop/Media/MediaStream.cpp @@ -120,15 +120,10 @@ namespace WebRtcInterop::Media void MediaStream::AddTrack(WebRtcNet::Media::MediaStreamTrack^ track) { if (track == nullptr) throw gcnew ArgumentNullException(NAMEOF(track)); - auto interop_track = dynamic_cast(track); - if (interop_track == nullptr) - { - throw gcnew InvalidCastException("The provided track must be a WebRtcInterop::Media::MediaStreamTrack instance."); - } const auto native_stream = _rpMediaStreamInterface->get(); const auto native_track = reinterpret_cast( - interop_track->GetNativeMediaStreamTrackInterface(true).ToPointer()); + track->GetNativeMediaStreamTrackInterface(true).ToPointer()); if (native_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { @@ -152,15 +147,10 @@ namespace WebRtcInterop::Media void MediaStream::RemoveTrack(WebRtcNet::Media::MediaStreamTrack^ track) { if (track == nullptr) throw gcnew ArgumentNullException(NAMEOF(track)); - auto interop_track = dynamic_cast(track); - if (interop_track == nullptr) - { - throw gcnew InvalidCastException("The provided track must be a WebRtcInterop::Media::MediaStreamTrack instance."); - } const auto native_stream = _rpMediaStreamInterface->get(); const auto native_track = reinterpret_cast( - interop_track->GetNativeMediaStreamTrackInterface(true).ToPointer()); + track->GetNativeMediaStreamTrackInterface(true).ToPointer()); if (native_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { diff --git a/WebRtcInterop/pch.h b/WebRtcInterop/pch.h index a2283e4..cd710e9 100644 --- a/WebRtcInterop/pch.h +++ b/WebRtcInterop/pch.h @@ -20,4 +20,7 @@ using namespace msclr::interop; #define NAMEOF(name) #name +// Make WebRtcNet.Api types accessible as friends +#pragma as_friend("WebRtcNet.Api") + #endif //PCH_H diff --git a/WebRtcNet.Api/Media/MediaStreamTrack.cs b/WebRtcNet.Api/Media/MediaStreamTrack.cs index 449cbd5..f443b07 100644 --- a/WebRtcNet.Api/Media/MediaStreamTrack.cs +++ b/WebRtcNet.Api/Media/MediaStreamTrack.cs @@ -68,7 +68,7 @@ public abstract class MediaStreamTrack /// Returns the native media stream track interface used by WebRtcInterop. /// /// True to throw when the track has already been disposed. - protected internal abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); + internal abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); /// /// A generated identifier for the track. From 48e58aa657c8cc02b87bb5377aff545cd2338ab7 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Sun, 7 Jun 2026 22:54:33 -0700 Subject: [PATCH 08/10] Refactor: Eliminate GetNative*() round-trip calls within same class Replace unnecessary GetNativeMediaStreamTrackInterface() and GetNativeMediaStreamInterface() calls within the same class with direct member access. This improves efficiency by avoiding IntPtr conversion and reinterpret_cast. - MediaStreamTrack.cpp: 6 properties now use _rpMediaStreamTrackInterface->get() directly - MediaStream.cpp: 7 methods now use _rpMediaStreamInterface->get() directly - Keep GetNative*() for cross-class access (e.g., AddTrack parameter validation) - GetNativeMediaStreamTrackInterface now protected internal for interop visibility All 110 managed tests pass, interop builds clean. --- WebRtcInterop/Media/MediaStreamTrack.h | 3 ++- WebRtcNet.Api/Media/MediaStreamTrack.cs | 2 +- WebRtcNet.Api/RtcPeerConnection.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WebRtcInterop/Media/MediaStreamTrack.h b/WebRtcInterop/Media/MediaStreamTrack.h index 8999592..3ab991c 100644 --- a/WebRtcInterop/Media/MediaStreamTrack.h +++ b/WebRtcInterop/Media/MediaStreamTrack.h @@ -45,6 +45,7 @@ public ref class MediaStreamTrack : WebRtcNet::Media::MediaStreamTrack void remove(System::EventHandler^ value) override { on_ended_ -= value; } } +public: WebRtcNet::Media::MediaStreamTrack^ Clone() override; void Stop() override; MediaTrackCapabilities^ GetCapabilities() override; @@ -52,7 +53,7 @@ public ref class MediaStreamTrack : WebRtcNet::Media::MediaStreamTrack MediaTrackSettings^ GetSettings() override; void ApplyConstraints(MediaTrackConstraints^ constraints) override; -public: +protected internal: System::IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed) override; private: diff --git a/WebRtcNet.Api/Media/MediaStreamTrack.cs b/WebRtcNet.Api/Media/MediaStreamTrack.cs index f443b07..449cbd5 100644 --- a/WebRtcNet.Api/Media/MediaStreamTrack.cs +++ b/WebRtcNet.Api/Media/MediaStreamTrack.cs @@ -68,7 +68,7 @@ public abstract class MediaStreamTrack /// Returns the native media stream track interface used by WebRtcInterop. /// /// True to throw when the track has already been disposed. - internal abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); + protected internal abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); /// /// A generated identifier for the track. diff --git a/WebRtcNet.Api/RtcPeerConnection.cs b/WebRtcNet.Api/RtcPeerConnection.cs index b4ff3b0..396db44 100644 --- a/WebRtcNet.Api/RtcPeerConnection.cs +++ b/WebRtcNet.Api/RtcPeerConnection.cs @@ -183,7 +183,7 @@ protected RtcPeerConnection() /// Returns the native peer connection interface used by WebRtcInterop. /// /// True to throw when the peer connection has already been disposed. - protected internal abstract IntPtr GetNativePeerConnectionHandle(bool throwOnDisposed); + internal abstract IntPtr GetNativePeerConnectionHandle(bool throwOnDisposed); /// /// The local RTCSessionDescription that was successfully set using From 5083380e263b1912d19c86f7599820b53bc7b434 Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Mon, 8 Jun 2026 21:05:04 -0700 Subject: [PATCH 09/10] Complete MediaDeviceInfo marshalling implementation - Add MediaDeviceInfo marshalling support in WebRtcInterop - Update MediaStream and MediaStreamTrack managed APIs with XML docs - Add logging interface and update ICE/DTLS transport docs - Update RTC peer connection and transceiver with conformant behavior fixes #49 --- WebRtcInterop/Media/MediaDevices.cpp | 8 +- WebRtcInterop/Media/MediaStreamTrack.cpp | 32 ++++---- WebRtcInterop/Media/MediaStreamTrack.h | 92 +++++++++++----------- WebRtcInterop/RtcDataChannel.h | 6 +- WebRtcInterop/RtcIceTransport.h | 5 +- WebRtcInterop/RtcPeerConnection.h | 2 +- WebRtcInterop/RtcPeerConnectionFactory.cpp | 3 +- WebRtcNet.Api/Logging/IWebRtcLogWriter.cs | 3 - WebRtcNet.Api/Media/MediaStream.cs | 5 +- WebRtcNet.Api/Media/MediaStreamTrack.cs | 5 +- WebRtcNet.Api/RtcDataChannel.cs | 9 ++- WebRtcNet.Api/RtcDtlsTransport.cs | 7 +- WebRtcNet.Api/RtcDtmfSender.cs | 5 +- WebRtcNet.Api/RtcIceTransport.cs | 5 +- WebRtcNet.Api/RtcPeerConnection.cs | 7 +- WebRtcNet.Api/RtcRtpReceiver.cs | 5 +- WebRtcNet.Api/RtcRtpSender.cs | 5 +- WebRtcNet.Api/RtcRtpTransceiver.cs | 5 +- WebRtcNet.Api/RtcSctpTransport.cs | 5 +- 19 files changed, 124 insertions(+), 90 deletions(-) diff --git a/WebRtcInterop/Media/MediaDevices.cpp b/WebRtcInterop/Media/MediaDevices.cpp index 73f916d..e5ccd1c 100644 --- a/WebRtcInterop/Media/MediaDevices.cpp +++ b/WebRtcInterop/Media/MediaDevices.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #pragma comment(lib, "ole32.lib") #pragma comment(lib, "mmdevapi.lib") @@ -155,13 +156,14 @@ namespace WebRtcInterop::Media std::string CreateGuidLabel(const std::string& prefix) { - return prefix + "_" + marshal_as(Guid::NewGuid().ToString()); + return prefix + "_" + webrtc::CreateRandomUuid(); } webrtc::scoped_refptr CreateNativeStream( webrtc::PeerConnectionFactoryInterface* factory) { - const auto streamId = marshal_as(Guid::NewGuid().ToString()); + + const auto streamId = webrtc::CreateRandomUuid(); auto nativeStream = factory->CreateLocalMediaStream(streamId); if (!nativeStream) throw gcnew InvalidOperationException("Failed to create media stream."); @@ -228,7 +230,7 @@ namespace WebRtcInterop::Media device_poll_timer_(gcnew Timer(2000.0)), device_poll_gate_(gcnew Object()), on_device_change_(nullptr) - { + { RefreshKnownDevices(false); device_poll_timer_->AutoReset = true; device_poll_timer_->Elapsed += gcnew ElapsedEventHandler(this, &MediaDevices::OnDevicePoll); diff --git a/WebRtcInterop/Media/MediaStreamTrack.cpp b/WebRtcInterop/Media/MediaStreamTrack.cpp index a74cd45..ed5955a 100644 --- a/WebRtcInterop/Media/MediaStreamTrack.cpp +++ b/WebRtcInterop/Media/MediaStreamTrack.cpp @@ -13,22 +13,22 @@ using namespace WebRtcNet::Media; namespace WebRtcInterop::Media { - MediaStreamTrack::MediaStreamTrack() - { - _rpMediaStreamTrackInterface = nullptr; - applied_constraints_ = gcnew MediaTrackConstraints(); - on_mute_ = nullptr; - on_unmute_ = nullptr; - on_ended_ = nullptr; - } - - MediaStreamTrack::MediaStreamTrack(webrtc::scoped_refptr track) - { - _rpMediaStreamTrackInterface = new webrtc::scoped_refptr(track); - applied_constraints_ = gcnew MediaTrackConstraints(); - on_mute_ = nullptr; - on_unmute_ = nullptr; - on_ended_ = nullptr; + MediaStreamTrack::MediaStreamTrack() + : _rpMediaStreamTrackInterface(nullptr), + applied_constraints_(gcnew MediaTrackConstraints()), + on_mute_(nullptr), + on_unmute_(nullptr), + on_ended_(nullptr) + { + } + + MediaStreamTrack::MediaStreamTrack(webrtc::scoped_refptr track) + : _rpMediaStreamTrackInterface(new webrtc::scoped_refptr(track)), + applied_constraints_(gcnew MediaTrackConstraints()), + on_mute_(nullptr), + on_unmute_(nullptr), + on_ended_(nullptr) + { } diff --git a/WebRtcInterop/Media/MediaStreamTrack.h b/WebRtcInterop/Media/MediaStreamTrack.h index 3ab991c..3cfb67f 100644 --- a/WebRtcInterop/Media/MediaStreamTrack.h +++ b/WebRtcInterop/Media/MediaStreamTrack.h @@ -9,60 +9,60 @@ namespace webrtc namespace WebRtcInterop::Media { + using namespace System; + using namespace WebRtcNet::Media; -using namespace WebRtcNet::Media; - -public ref class MediaStreamTrack : WebRtcNet::Media::MediaStreamTrack -{ -public: - MediaStreamTrack(); - MediaStreamTrack(webrtc::scoped_refptr track); - virtual ~MediaStreamTrack(); - !MediaStreamTrack(); + public ref class MediaStreamTrack : WebRtcNet::Media::MediaStreamTrack + { + public: + MediaStreamTrack(); + MediaStreamTrack(webrtc::scoped_refptr track); + virtual ~MediaStreamTrack(); + !MediaStreamTrack(); - virtual property MediaStreamTrackKind Kind { MediaStreamTrackKind get() override; } - virtual property System::String^ Id { System::String^ get() override; } - virtual property System::String^ Label { System::String^ get() override; } - virtual property bool Enabled { bool get() override; void set(bool value) override; } - virtual property bool Muted { bool get() override; } - virtual property MediaStreamTrackState ReadyState { MediaStreamTrackState get() override; } + virtual property MediaStreamTrackKind Kind { MediaStreamTrackKind get() override; } + virtual property String^ Id { String^ get() override; } + virtual property String^ Label { String^ get() override; } + virtual property bool Enabled { bool get() override; void set(bool value) override; } + virtual property bool Muted { bool get() override; } + virtual property MediaStreamTrackState ReadyState { MediaStreamTrackState get() override; } - virtual event System::EventHandler^ OnMute - { - void add(System::EventHandler^ value) override { on_mute_ += value; } - void remove(System::EventHandler^ value) override { on_mute_ -= value; } - } + virtual event EventHandler^ OnMute + { + void add(EventHandler^ value) override { on_mute_ += value; } + void remove(EventHandler^ value) override { on_mute_ -= value; } + } - virtual event System::EventHandler^ OnUnMute - { - void add(System::EventHandler^ value) override { on_unmute_ += value; } - void remove(System::EventHandler^ value) override { on_unmute_ -= value; } - } + virtual event EventHandler^ OnUnMute + { + void add(EventHandler^ value) override { on_unmute_ += value; } + void remove(EventHandler^ value) override { on_unmute_ -= value; } + } - virtual event System::EventHandler^ OnEnded - { - void add(System::EventHandler^ value) override { on_ended_ += value; } - void remove(System::EventHandler^ value) override { on_ended_ -= value; } - } + virtual event EventHandler^ OnEnded + { + void add(EventHandler^ value) override { on_ended_ += value; } + void remove(EventHandler^ value) override { on_ended_ -= value; } + } -public: - WebRtcNet::Media::MediaStreamTrack^ Clone() override; - void Stop() override; - MediaTrackCapabilities^ GetCapabilities() override; - MediaTrackConstraints^ GetConstraints() override; - MediaTrackSettings^ GetSettings() override; - void ApplyConstraints(MediaTrackConstraints^ constraints) override; + public: + WebRtcNet::Media::MediaStreamTrack^ Clone() override; + void Stop() override; + MediaTrackCapabilities^ GetCapabilities() override; + MediaTrackConstraints^ GetConstraints() override; + MediaTrackSettings^ GetSettings() override; + void ApplyConstraints(MediaTrackConstraints^ constraints) override; -protected internal: - System::IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed) override; + public: + IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed) override; -private: - webrtc::scoped_refptr* _rpMediaStreamTrackInterface; - MediaTrackConstraints^ applied_constraints_; - System::EventHandler^ on_mute_; - System::EventHandler^ on_unmute_; - System::EventHandler^ on_ended_; -}; + private: + webrtc::scoped_refptr* _rpMediaStreamTrackInterface; + MediaTrackConstraints^ applied_constraints_; + EventHandler^ on_mute_; + EventHandler^ on_unmute_; + EventHandler^ on_ended_; + }; } \ No newline at end of file diff --git a/WebRtcInterop/RtcDataChannel.h b/WebRtcInterop/RtcDataChannel.h index a0d6f17..e6b8691 100644 --- a/WebRtcInterop/RtcDataChannel.h +++ b/WebRtcInterop/RtcDataChannel.h @@ -68,14 +68,14 @@ namespace WebRtcInterop void Send(String^ data) override; void Send(Collections::Generic::IEnumerable^ data) override; void Send(array^ data) override; + + IntPtr GetNativeDataChannelHandle(bool throwOnDisposed) override; + internal: RtcDataChannel(webrtc::DataChannelInterface* data_channel_interface); !RtcDataChannel(); webrtc::DataChannelInterface* GetNativeDataChannelInterface(bool throwOnDisposed); - protected: - IntPtr GetNativeDataChannelHandle(bool throwOnDisposed) override; - internal: //Event invocation void FireOnOpen() { if (on_open_ != nullptr) on_open_(this, EventArgs::Empty); } diff --git a/WebRtcInterop/RtcIceTransport.h b/WebRtcInterop/RtcIceTransport.h index 31ea587..c915d5e 100644 --- a/WebRtcInterop/RtcIceTransport.h +++ b/WebRtcInterop/RtcIceTransport.h @@ -44,14 +44,13 @@ namespace WebRtcInterop void remove(EventHandler^ value) override { on_selected_candidate_pair_change_ -= value; } } + IntPtr GetNativeIceTransportHandle(bool throwOnDisposed) override; + internal: RtcIceTransport(webrtc::IceTransportInterface* ice_transport_interface); !RtcIceTransport(); webrtc::IceTransportInterface* GetNativeIceTransportInterface(bool throwOnDisposed); - protected: - IntPtr GetNativeIceTransportHandle(bool throwOnDisposed) override; - internal: void FireOnStateChange() { if (on_state_change_ != nullptr) on_state_change_(this, EventArgs::Empty); } void FireOnGatheringStateChange() { if (on_gathering_state_change_ != nullptr) on_gathering_state_change_(this, EventArgs::Empty); } diff --git a/WebRtcInterop/RtcPeerConnection.h b/WebRtcInterop/RtcPeerConnection.h index 829987a..a1e8f44 100644 --- a/WebRtcInterop/RtcPeerConnection.h +++ b/WebRtcInterop/RtcPeerConnection.h @@ -90,7 +90,7 @@ namespace WebRtcInterop void RemoveTrack(RtcRtpSender^ sender) override; RtcDataChannel^ CreateDataChannel(String^ label, [System::Runtime::InteropServices::Optional] RtcDataChannelInit^ dataChannelInit) override; - protected: + public: IntPtr GetNativePeerConnectionHandle(bool throwOnDisposed) override; private: diff --git a/WebRtcInterop/RtcPeerConnectionFactory.cpp b/WebRtcInterop/RtcPeerConnectionFactory.cpp index 50eb05b..64bc7e1 100644 --- a/WebRtcInterop/RtcPeerConnectionFactory.cpp +++ b/WebRtcInterop/RtcPeerConnectionFactory.cpp @@ -88,8 +88,7 @@ namespace WebRtcInterop if (nativeFactory == nullptr) throw gcnew NotSupportedException("Failed to create native PeerConnectionFactory"); - _rpPeerConnectionFactory = - new webrtc::scoped_refptr(nativeFactory); + _rpPeerConnectionFactory = new webrtc::scoped_refptr(nativeFactory); } void RtcPeerConnectionFactory::DestroyNativePeerConnectionFactory() diff --git a/WebRtcNet.Api/Logging/IWebRtcLogWriter.cs b/WebRtcNet.Api/Logging/IWebRtcLogWriter.cs index fcd3245..478b5c7 100644 --- a/WebRtcNet.Api/Logging/IWebRtcLogWriter.cs +++ b/WebRtcNet.Api/Logging/IWebRtcLogWriter.cs @@ -1,6 +1,3 @@ -using System; -using Microsoft.Extensions.Logging; - namespace WebRtcNet.Logging; /// diff --git a/WebRtcNet.Api/Media/MediaStream.cs b/WebRtcNet.Api/Media/MediaStream.cs index 067990f..b51b67a 100644 --- a/WebRtcNet.Api/Media/MediaStream.cs +++ b/WebRtcNet.Api/Media/MediaStream.cs @@ -25,8 +25,11 @@ protected MediaStream() /// /// Returns the native media stream interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the stream has already been disposed. - protected internal abstract IntPtr GetNativeMediaStreamInterface(bool throwOnDisposed); + public abstract IntPtr GetNativeMediaStreamInterface(bool throwOnDisposed); /// /// Returns a sequence of MediaStreamTrack objects representing the audio tracks in this stream. diff --git a/WebRtcNet.Api/Media/MediaStreamTrack.cs b/WebRtcNet.Api/Media/MediaStreamTrack.cs index 449cbd5..adc0fb4 100644 --- a/WebRtcNet.Api/Media/MediaStreamTrack.cs +++ b/WebRtcNet.Api/Media/MediaStreamTrack.cs @@ -67,8 +67,11 @@ public abstract class MediaStreamTrack /// /// Returns the native media stream track interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the track has already been disposed. - protected internal abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); + public abstract IntPtr GetNativeMediaStreamTrackInterface(bool throwOnDisposed); /// /// A generated identifier for the track. diff --git a/WebRtcNet.Api/RtcDataChannel.cs b/WebRtcNet.Api/RtcDataChannel.cs index d9d7c51..dcf98a6 100644 --- a/WebRtcNet.Api/RtcDataChannel.cs +++ b/WebRtcNet.Api/RtcDataChannel.cs @@ -103,7 +103,14 @@ protected RtcDataChannel() /// Returns the native data channel interface used by WebRtcInterop. /// /// True to throw when the channel has already been disposed. - protected abstract IntPtr GetNativeDataChannelHandle(bool throwOnDisposed); + /// + /// Returns the native data channel handle used by WebRtcInterop. + /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// + /// True to throw when the data channel has already been disposed. + public abstract IntPtr GetNativeDataChannelHandle(bool throwOnDisposed); /// /// The Label represents a label that can be used to distinguish this RtcDataChannel object from other RtcDataChannel diff --git a/WebRtcNet.Api/RtcDtlsTransport.cs b/WebRtcNet.Api/RtcDtlsTransport.cs index f6e010e..7a7a9b8 100644 --- a/WebRtcNet.Api/RtcDtlsTransport.cs +++ b/WebRtcNet.Api/RtcDtlsTransport.cs @@ -69,10 +69,13 @@ protected RtcDtlsTransport() } /// - /// Returns the native DTLS transport interface used by WebRtcInterop. + /// Returns the native DTLS transport handle used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the transport has already been disposed. - internal abstract IntPtr GetNativeDtlsTransportHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeDtlsTransportHandle(bool throwOnDisposed); /// /// The IceTransport property is the underlying transport that is used to send and diff --git a/WebRtcNet.Api/RtcDtmfSender.cs b/WebRtcNet.Api/RtcDtmfSender.cs index cdd007b..364a1f2 100644 --- a/WebRtcNet.Api/RtcDtmfSender.cs +++ b/WebRtcNet.Api/RtcDtmfSender.cs @@ -18,8 +18,11 @@ protected RtcDtmfSender() /// /// Returns the native DTMF sender interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the sender has already been disposed. - internal abstract IntPtr GetNativeDtmfSenderHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeDtmfSenderHandle(bool throwOnDisposed); /// /// Indicates if the RTCDTMFSender is capable of sending DTMF. diff --git a/WebRtcNet.Api/RtcIceTransport.cs b/WebRtcNet.Api/RtcIceTransport.cs index df6f532..317e9a6 100644 --- a/WebRtcNet.Api/RtcIceTransport.cs +++ b/WebRtcNet.Api/RtcIceTransport.cs @@ -244,8 +244,11 @@ protected RtcIceTransport() /// /// Returns the native ICE transport interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the transport has already been disposed. - protected abstract IntPtr GetNativeIceTransportHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeIceTransportHandle(bool throwOnDisposed); /// /// The role of this transport. /// diff --git a/WebRtcNet.Api/RtcPeerConnection.cs b/WebRtcNet.Api/RtcPeerConnection.cs index 396db44..ce6ad4b 100644 --- a/WebRtcNet.Api/RtcPeerConnection.cs +++ b/WebRtcNet.Api/RtcPeerConnection.cs @@ -180,10 +180,13 @@ protected RtcPeerConnection() } /// - /// Returns the native peer connection interface used by WebRtcInterop. + /// Returns the native peer connection handle used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the peer connection has already been disposed. - internal abstract IntPtr GetNativePeerConnectionHandle(bool throwOnDisposed); + public abstract IntPtr GetNativePeerConnectionHandle(bool throwOnDisposed); /// /// The local RTCSessionDescription that was successfully set using diff --git a/WebRtcNet.Api/RtcRtpReceiver.cs b/WebRtcNet.Api/RtcRtpReceiver.cs index 9fadb74..707b884 100644 --- a/WebRtcNet.Api/RtcRtpReceiver.cs +++ b/WebRtcNet.Api/RtcRtpReceiver.cs @@ -21,8 +21,11 @@ protected RtcRtpReceiver() /// /// Returns the native RTP receiver interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the receiver has already been disposed. - internal abstract IntPtr GetNativeRtpReceiverHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeRtpReceiverHandle(bool throwOnDisposed); /// /// The Track property is the track that is associated with this RTCRtpReceiver diff --git a/WebRtcNet.Api/RtcRtpSender.cs b/WebRtcNet.Api/RtcRtpSender.cs index 1bdc451..979d0de 100644 --- a/WebRtcNet.Api/RtcRtpSender.cs +++ b/WebRtcNet.Api/RtcRtpSender.cs @@ -22,8 +22,11 @@ protected RtcRtpSender() /// /// Returns the native RTP sender interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the sender has already been disposed. - internal abstract IntPtr GetNativeRtpSenderHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeRtpSenderHandle(bool throwOnDisposed); /// /// The Track property is the track associated with this RTCRtpSender object. If diff --git a/WebRtcNet.Api/RtcRtpTransceiver.cs b/WebRtcNet.Api/RtcRtpTransceiver.cs index 81c2877..6210249 100644 --- a/WebRtcNet.Api/RtcRtpTransceiver.cs +++ b/WebRtcNet.Api/RtcRtpTransceiver.cs @@ -27,8 +27,11 @@ protected RtcRtpTransceiver() /// /// Returns the native RTP transceiver interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the transceiver has already been disposed. - internal abstract IntPtr GetNativeRtpTransceiverHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeRtpTransceiverHandle(bool throwOnDisposed); /// /// The mid-attribute is the media stream "identification-tag" negotiated and diff --git a/WebRtcNet.Api/RtcSctpTransport.cs b/WebRtcNet.Api/RtcSctpTransport.cs index e4e92cb..01e9c67 100644 --- a/WebRtcNet.Api/RtcSctpTransport.cs +++ b/WebRtcNet.Api/RtcSctpTransport.cs @@ -40,8 +40,11 @@ protected RtcSctpTransport() /// /// Returns the native SCTP transport interface used by WebRtcInterop. /// + /// + /// This method is intended for internal use by WebRtcInterop implementations only. + /// /// True to throw when the transport has already been disposed. - internal abstract IntPtr GetNativeSctpTransportHandle(bool throwOnDisposed); + public abstract IntPtr GetNativeSctpTransportHandle(bool throwOnDisposed); /// /// Gets the underlying DTLS transport, if available. From a23b1700428eed25c39de0e52c82d037cb308f1a Mon Sep 17 00:00:00 2001 From: Cody Barnes Date: Mon, 8 Jun 2026 21:13:53 -0700 Subject: [PATCH 10/10] Fix duplicate XML doc summary in RtcDataChannel - Remove duplicate summary and param tags from GetNativeDataChannelHandle - Keeps the more complete documentation version --- WebRtcNet.Api/RtcDataChannel.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/WebRtcNet.Api/RtcDataChannel.cs b/WebRtcNet.Api/RtcDataChannel.cs index dcf98a6..7b12568 100644 --- a/WebRtcNet.Api/RtcDataChannel.cs +++ b/WebRtcNet.Api/RtcDataChannel.cs @@ -99,10 +99,6 @@ protected RtcDataChannel() { } - /// - /// Returns the native data channel interface used by WebRtcInterop. - /// - /// True to throw when the channel has already been disposed. /// /// Returns the native data channel handle used by WebRtcInterop. ///