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/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/Marshaling/MarshalMediaTrackCapabilities.h b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h new file mode 100644 index 0000000..07b0dcd --- /dev/null +++ b/WebRtcInterop/Media/Marshaling/MarshalMediaTrackCapabilities.h @@ -0,0 +1,183 @@ +#pragma once + +#include + +#include "../Marshaling/MarshalEnums.h" + +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. + /// + 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/Media/MediaDevices.cpp b/WebRtcInterop/Media/MediaDevices.cpp index f29dc6e..e5ccd1c 100644 --- a/WebRtcInterop/Media/MediaDevices.cpp +++ b/WebRtcInterop/Media/MediaDevices.cpp @@ -1,18 +1,19 @@ #include "pch.h" -#include #include "MediaDevices.h" #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 #include #include #include #include +#include #pragma comment(lib, "ole32.lib") #pragma comment(lib, "mmdevapi.lib") @@ -23,6 +24,8 @@ using namespace System::Timers; namespace WebRtcInterop::Media { + using namespace WebRtcNet::Media; + static List^ EnumerateAudioDevices(); static List^ EnumerateVideoDevices(); @@ -53,7 +56,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 +66,6 @@ namespace WebRtcInterop::Media void EnumerateAudioEndpoints( IMMDeviceEnumerator* enumerator, const EDataFlow flow, - const MediaDeviceKind kind, String^ fallbackLabel, List^ devices) { @@ -83,6 +85,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 +100,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,15 +154,16 @@ 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 + "_" + 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."); @@ -170,7 +181,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 +216,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."); @@ -219,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); @@ -328,17 +339,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 +399,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 +421,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/Media/MediaStream.cpp b/WebRtcInterop/Media/MediaStream.cpp index 5e14b82..ea49293 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); @@ -124,16 +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 = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + 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) { @@ -157,16 +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 = reinterpret_cast(GetNativeMediaStreamInterface(true). - ToPointer()); + 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) { @@ -194,8 +178,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..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() + MediaStreamTrack::MediaStreamTrack() + : _rpMediaStreamTrackInterface(nullptr), + applied_constraints_(gcnew MediaTrackConstraints()), + on_mute_(nullptr), + on_unmute_(nullptr), + on_ended_(nullptr) { - _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(webrtc::scoped_refptr track) + : _rpMediaStreamTrackInterface(new webrtc::scoped_refptr(track)), + applied_constraints_(gcnew MediaTrackConstraints()), + on_mute_(nullptr), + on_unmute_(nullptr), + on_ended_(nullptr) + { } @@ -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/WebRtcInterop/Media/MediaStreamTrack.h b/WebRtcInterop/Media/MediaStreamTrack.h index 8999592..3cfb67f 100644 --- a/WebRtcInterop/Media/MediaStreamTrack.h +++ b/WebRtcInterop/Media/MediaStreamTrack.h @@ -9,59 +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; } + } - 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; -public: - 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/WebRtcInterop/WebRtcInterop.Shared.vcxitems b/WebRtcInterop/WebRtcInterop.Shared.vcxitems index a008ef4..4dedf92 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems @@ -136,6 +136,8 @@ ninja -C "$(WebRtcOutRoot)\$(Configuration)\$(Platform)" + + diff --git a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters index 6f11d79..c9d68b2 100644 --- a/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters +++ b/WebRtcInterop/WebRtcInterop.Shared.vcxitems.filters @@ -74,6 +74,12 @@ Media\Marshaling + + Media\Marshaling + + + Media\Marshaling + Media 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/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/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 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/Media/MediaTrackCapabilities.cs b/WebRtcNet.Api/Media/MediaTrackCapabilities.cs index 59e1f73..683c952 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)] + internal 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 diff --git a/WebRtcNet.Api/Media/MediaTrackSettings.cs b/WebRtcNet.Api/Media/MediaTrackSettings.cs index d105722..e785498 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)] + internal 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 diff --git a/WebRtcNet.Api/RtcDataChannel.cs b/WebRtcNet.Api/RtcDataChannel.cs index d9d7c51..7b12568 100644 --- a/WebRtcNet.Api/RtcDataChannel.cs +++ b/WebRtcNet.Api/RtcDataChannel.cs @@ -100,10 +100,13 @@ protected RtcDataChannel() } /// - /// Returns the native data channel interface used by WebRtcInterop. + /// Returns the native data channel handle used by WebRtcInterop. /// - /// True to throw when the channel has already been disposed. - protected abstract IntPtr GetNativeDataChannelHandle(bool throwOnDisposed); + /// + /// 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 b4ff3b0..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. - protected 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.