diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d83557933..ca423562ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog +[0.13.0] - 2025-03-24 + +* [All] feat: add getBufferedAmount for DataChannel. (#1796) +* [Windows] fix: fixed non-platform thread call error. (#1795) + +[0.12.12+hotfix.1] - 2025-03-12 + +* [Android] fix: fixed video not rendered after resume from background. + [0.12.12] - 2025-03-09 * [Android] feat: Migrate to the new Surface API. (#1726) diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java b/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java index 60931b0116..b5c3df69ef 100644 --- a/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java @@ -482,6 +482,12 @@ public void onMethodCall(MethodCall call, @NonNull Result notSafeResult) { createDataChannel(peerConnectionId, label, new ConstraintsMap(dataChannelDict), result); break; } + case "dataChannelGetBufferedAmount": { + String peerConnectionId = call.argument("peerConnectionId"); + String dataChannelId = call.argument("dataChannelId"); + dataChannelGetBufferedAmount(peerConnectionId, dataChannelId, result); + break; + } case "dataChannelSend": { String peerConnectionId = call.argument("peerConnectionId"); String dataChannelId = call.argument("dataChannelId"); @@ -2039,6 +2045,17 @@ public void dataChannelSend(String peerConnectionId, String dataChannelId, ByteB } } + public void dataChannelGetBufferedAmount(String peerConnectionId, String dataChannelId, Result result) { + PeerConnectionObserver pco + = mPeerConnectionObservers.get(peerConnectionId); + if (pco == null || pco.getPeerConnection() == null) { + Log.d(TAG, "dataChannelGetBufferedAmount() peerConnection is null"); + resultError("dataChannelGetBufferedAmount", "peerConnection is null", result); + } else { + pco.dataChannelGetBufferedAmount(dataChannelId, result); + } + } + public void dataChannelClose(String peerConnectionId, String dataChannelId) { // Forward to PeerConnectionObserver which deals with DataChannels // because DataChannel is owned by PeerConnection. diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/PeerConnectionObserver.java b/android/src/main/java/com/cloudwebrtc/webrtc/PeerConnectionObserver.java index a4e8736268..9c36dce354 100755 --- a/android/src/main/java/com/cloudwebrtc/webrtc/PeerConnectionObserver.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/PeerConnectionObserver.java @@ -168,6 +168,18 @@ void dataChannelSend(String dataChannelId, ByteBuffer byteBuffer, Boolean isBina } } + void dataChannelGetBufferedAmount(String dataChannelId, Result result) { + DataChannel dataChannel = dataChannels.get(dataChannelId); + if (dataChannel != null) { + ConstraintsMap params = new ConstraintsMap(); + params.putLong("bufferedAmount", dataChannel.bufferedAmount()); + result.success(params.toMap()); + } else { + Log.d(TAG, "dataChannelGetBufferedAmount() dataChannel is null"); + resultError("dataChannelGetBufferedAmount", "DataChannel is null", result); + } + } + RtpTransceiver getRtpTransceiverById(String id) { RtpTransceiver transceiver = transceivers.get(id); if (null == transceiver) { diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/SurfaceTextureRenderer.java b/android/src/main/java/com/cloudwebrtc/webrtc/SurfaceTextureRenderer.java index 60cbd6de68..c4daa8acdd 100755 --- a/android/src/main/java/com/cloudwebrtc/webrtc/SurfaceTextureRenderer.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/SurfaceTextureRenderer.java @@ -134,6 +134,7 @@ public void surfaceDestroyed() { final CountDownLatch completionLatch = new CountDownLatch(1); releaseEglSurface(completionLatch::countDown); ThreadUtils.awaitUninterruptibly(completionLatch); + surface = null; } // Update frame dimensions and report any changes to |rendererEvents|. diff --git a/common/cpp/include/flutter_common.h b/common/cpp/include/flutter_common.h index f82054d68d..50e6097bf9 100644 --- a/common/cpp/include/flutter_common.h +++ b/common/cpp/include/flutter_common.h @@ -12,8 +12,10 @@ #include #include -#include +#include #include +#include +#include typedef flutter::EncodableValue EncodableValue; typedef flutter::EncodableMap EncodableMap; @@ -27,6 +29,8 @@ typedef flutter::EventSink EventSink; typedef flutter::MethodCall MethodCall; typedef flutter::MethodResult MethodResult; +class TaskRunner; + // foo.StringValue() becomes std::get(foo) // foo.IsString() becomes std::holds_alternative(foo) @@ -90,7 +94,8 @@ inline double findDouble(const EncodableMap& map, const std::string& key) { return 0.0; } -inline std::optional maybeFindDouble(const EncodableMap& map, const std::string& key) { +inline std::optional maybeFindDouble(const EncodableMap& map, + const std::string& key) { auto it = map.find(EncodableValue(key)); if (it != map.end() && TypeIs(it->second)) return GetValue(it->second); @@ -171,6 +176,7 @@ class EventChannelProxy { public: static std::unique_ptr Create( BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channelName); virtual ~EventChannelProxy() = default; diff --git a/common/cpp/include/flutter_data_channel.h b/common/cpp/include/flutter_data_channel.h index ccffa511cd..1e5bfd1584 100644 --- a/common/cpp/include/flutter_data_channel.h +++ b/common/cpp/include/flutter_data_channel.h @@ -10,6 +10,7 @@ class FlutterRTCDataChannelObserver : public RTCDataChannelObserver { public: FlutterRTCDataChannelObserver(scoped_refptr data_channel, BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channel_name); virtual ~FlutterRTCDataChannelObserver(); @@ -39,6 +40,9 @@ class FlutterDataChannel { const EncodableValue& data, std::unique_ptr); + void DataChannelGetBufferedAmount(RTCDataChannel* data_channel, + std::unique_ptr result); + void DataChannelClose(RTCDataChannel* data_channel, const std::string& data_channel_uuid, std::unique_ptr); diff --git a/common/cpp/include/flutter_frame_cryptor.h b/common/cpp/include/flutter_frame_cryptor.h index 111b2f6abf..36756272f9 100644 --- a/common/cpp/include/flutter_frame_cryptor.h +++ b/common/cpp/include/flutter_frame_cryptor.h @@ -10,8 +10,8 @@ namespace flutter_webrtc_plugin { class FlutterFrameCryptorObserver : public libwebrtc::RTCFrameCryptorObserver { public: - FlutterFrameCryptorObserver(BinaryMessenger* messenger,const std::string& channelName) - : event_channel_(EventChannelProxy::Create(messenger, channelName)) {} + FlutterFrameCryptorObserver(BinaryMessenger* messenger, TaskRunner* task_runner, const std::string& channelName) + : event_channel_(EventChannelProxy::Create(messenger, task_runner, channelName)) {} void OnFrameCryptionStateChanged( const string participant_id, libwebrtc::RTCFrameCryptionState state); diff --git a/common/cpp/include/flutter_peerconnection.h b/common/cpp/include/flutter_peerconnection.h index 5efd1e5a45..699823dfdc 100644 --- a/common/cpp/include/flutter_peerconnection.h +++ b/common/cpp/include/flutter_peerconnection.h @@ -11,6 +11,7 @@ class FlutterPeerConnectionObserver : public RTCPeerConnectionObserver { FlutterPeerConnectionObserver(FlutterWebRTCBase* base, scoped_refptr peerconnection, BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channel_name, std::string& peerConnectionId); diff --git a/common/cpp/include/flutter_video_renderer.h b/common/cpp/include/flutter_video_renderer.h index 41bec0c4de..b2454f8458 100644 --- a/common/cpp/include/flutter_video_renderer.h +++ b/common/cpp/include/flutter_video_renderer.h @@ -22,6 +22,7 @@ class FlutterVideoRenderer void initialize(TextureRegistrar* registrar, BinaryMessenger* messenger, + TaskRunner* task_runner, std::unique_ptr texture, int64_t texture_id); diff --git a/common/cpp/include/flutter_webrtc.h b/common/cpp/include/flutter_webrtc.h index 5886a67dff..573956b9aa 100644 --- a/common/cpp/include/flutter_webrtc.h +++ b/common/cpp/include/flutter_webrtc.h @@ -21,6 +21,8 @@ class FlutterWebRTCPlugin : public flutter::Plugin { virtual BinaryMessenger* messenger() = 0; virtual TextureRegistrar* textures() = 0; + + virtual TaskRunner* task_runner() = 0; }; class FlutterWebRTC : public FlutterWebRTCBase, diff --git a/common/cpp/include/flutter_webrtc_base.h b/common/cpp/include/flutter_webrtc_base.h index 9d24455df7..9edabc7680 100644 --- a/common/cpp/include/flutter_webrtc_base.h +++ b/common/cpp/include/flutter_webrtc_base.h @@ -43,7 +43,7 @@ class FlutterWebRTCBase { enum ParseConstraintType { kMandatory, kOptional }; public: - FlutterWebRTCBase(BinaryMessenger* messenger, TextureRegistrar* textures); + FlutterWebRTCBase(BinaryMessenger* messenger, TextureRegistrar* textures, TaskRunner* task_runner); ~FlutterWebRTCBase(); std::string GenerateUUID(); @@ -122,6 +122,7 @@ class FlutterWebRTCBase { protected: BinaryMessenger* messenger_; + TaskRunner *task_runner_; TextureRegistrar* textures_; std::unique_ptr event_channel_; }; diff --git a/common/cpp/include/task_runner.h b/common/cpp/include/task_runner.h new file mode 100644 index 0000000000..74c510c581 --- /dev/null +++ b/common/cpp/include/task_runner.h @@ -0,0 +1,17 @@ +// Copyright 2024 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #ifndef PACKAGES_FLUTTER_WEBRTC_TASK_RUNNER_H_ + #define PACKAGES_FLUTTER_WEBRTC_TASK_RUNNER_H_ + + #include + + using TaskClosure = std::function; + + class TaskRunner { + public: + virtual void EnqueueTask(TaskClosure task) = 0; + virtual ~TaskRunner() = default; + }; + + #endif // PACKAGES_FLUTTER_WEBRTC_TASK_RUNNER_H_ \ No newline at end of file diff --git a/common/cpp/src/flutter_common.cc b/common/cpp/src/flutter_common.cc index c967f2eb12..1daa606a17 100644 --- a/common/cpp/src/flutter_common.cc +++ b/common/cpp/src/flutter_common.cc @@ -1,4 +1,7 @@ #include "flutter_common.h" +#include "task_runner.h" + +#include class MethodCallProxyImpl : public MethodCallProxy { public: @@ -66,56 +69,75 @@ std::unique_ptr MethodResultProxy::Create( } class EventChannelProxyImpl : public EventChannelProxy { - public: - EventChannelProxyImpl(BinaryMessenger* messenger, - const std::string& channelName) - : channel_(std::make_unique( - messenger, - channelName, - &flutter::StandardMethodCodec::GetInstance())) { - auto handler = std::make_unique< - flutter::StreamHandlerFunctions>( - [&](const EncodableValue* arguments, - std::unique_ptr>&& events) - -> std::unique_ptr> { - sink_ = std::move(events); - for (auto& event : event_queue_) { - sink_->Success(event); - } - event_queue_.clear(); - on_listen_called_ = true; - return nullptr; - }, - [&](const EncodableValue* arguments) - -> std::unique_ptr> { - on_listen_called_ = false; - return nullptr; - }); - - channel_->SetStreamHandler(std::move(handler)); - } - - virtual ~EventChannelProxyImpl() {} - - void Success(const EncodableValue& event, bool cache_event = true) override { - if (on_listen_called_) { + public: + EventChannelProxyImpl(BinaryMessenger* messenger, + TaskRunner* task_runner, + const std::string& channelName) + : channel_(std::make_unique( + messenger, + channelName, + &flutter::StandardMethodCodec::GetInstance())), + task_runner_(task_runner) { + auto handler = std::make_unique< + flutter::StreamHandlerFunctions>( + [&](const EncodableValue* arguments, + std::unique_ptr>&& events) + -> std::unique_ptr> { + sink_ = std::move(events); + std::weak_ptr weak_sink = sink_; + for (auto& event : event_queue_) { + PostEvent(event); + } + event_queue_.clear(); + on_listen_called_ = true; + return nullptr; + }, + [&](const EncodableValue* arguments) + -> std::unique_ptr> { + on_listen_called_ = false; + return nullptr; + }); + + channel_->SetStreamHandler(std::move(handler)); + } + + virtual ~EventChannelProxyImpl() {} + + void Success(const EncodableValue& event, bool cache_event = true) override { + if (on_listen_called_) { + PostEvent(event); + } else { + if (cache_event) { + event_queue_.push_back(event); + } + } + } + + void PostEvent(const EncodableValue& event) { + if(task_runner_) { + std::weak_ptr weak_sink = sink_; + task_runner_->EnqueueTask([weak_sink, event]() { + auto sink = weak_sink.lock(); + if (sink) { + sink->Success(event); + } + }); + } else { sink_->Success(event); - } else { - if (cache_event) { - event_queue_.push_back(event); - } - } - } - - private: - std::unique_ptr channel_; - std::unique_ptr sink_; - std::list event_queue_; - bool on_listen_called_ = false; -}; + } + } + + private: + std::unique_ptr channel_; + std::shared_ptr> sink_; + std::list event_queue_; + bool on_listen_called_ = false; + TaskRunner* task_runner_; + }; std::unique_ptr EventChannelProxy::Create( BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channelName) { - return std::make_unique(messenger, channelName); -} + return std::make_unique(messenger, task_runner, channelName); +} \ No newline at end of file diff --git a/common/cpp/src/flutter_data_channel.cc b/common/cpp/src/flutter_data_channel.cc index f333d42a6f..37afd12b54 100644 --- a/common/cpp/src/flutter_data_channel.cc +++ b/common/cpp/src/flutter_data_channel.cc @@ -7,8 +7,9 @@ namespace flutter_webrtc_plugin { FlutterRTCDataChannelObserver::FlutterRTCDataChannelObserver( scoped_refptr data_channel, BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channelName) - : event_channel_(EventChannelProxy::Create(messenger, channelName)), + : event_channel_(EventChannelProxy::Create(messenger, task_runner, channelName)), data_channel_(data_channel) { data_channel_->RegisterObserver(this); } @@ -53,7 +54,7 @@ void FlutterDataChannel::CreateDataChannel( "FlutterWebRTC/dataChannelEvent" + peerConnectionId + uuid; std::unique_ptr observer( - new FlutterRTCDataChannelObserver(data_channel, base_->messenger_, + new FlutterRTCDataChannelObserver(data_channel, base_->messenger_, base_->task_runner_, event_channel)); base_->lock(); @@ -86,6 +87,13 @@ void FlutterDataChannel::DataChannelSend( result->Success(); } +void FlutterDataChannel::DataChannelGetBufferedAmount(RTCDataChannel* data_channel, + std::unique_ptr result) { + EncodableMap params; + params[EncodableValue("bufferedAmount")] = EncodableValue((int64_t)data_channel->buffered_amount()); + result->Success(EncodableValue(params)); +} + void FlutterDataChannel::DataChannelClose( RTCDataChannel* data_channel, const std::string& data_channel_uuid, diff --git a/common/cpp/src/flutter_frame_cryptor.cc b/common/cpp/src/flutter_frame_cryptor.cc index 975f400751..a9e44e9bd6 100644 --- a/common/cpp/src/flutter_frame_cryptor.cc +++ b/common/cpp/src/flutter_frame_cryptor.cc @@ -166,7 +166,7 @@ void FlutterFrameCryptor::FrameCryptorFactoryCreateFrameCryptor( keyProvider); std::string event_channel = "FlutterWebRTC/frameCryptorEvent" + uuid; - scoped_refptr observer(new RefCountedObject(base_->messenger_, event_channel)); + scoped_refptr observer(new RefCountedObject(base_->messenger_, base_->task_runner_, event_channel)); frameCryptor->RegisterRTCFrameCryptorObserver(observer); @@ -192,7 +192,7 @@ void FlutterFrameCryptor::FrameCryptorFactoryCreateFrameCryptor( std::string event_channel = "FlutterWebRTC/frameCryptorEvent" + uuid; - scoped_refptr observer(new RefCountedObject(base_->messenger_, event_channel)); + scoped_refptr observer(new RefCountedObject(base_->messenger_, base_->task_runner_, event_channel)); frameCryptor->RegisterRTCFrameCryptorObserver(observer.get()); diff --git a/common/cpp/src/flutter_peerconnection.cc b/common/cpp/src/flutter_peerconnection.cc index d77af9c5f2..691ec29f19 100644 --- a/common/cpp/src/flutter_peerconnection.cc +++ b/common/cpp/src/flutter_peerconnection.cc @@ -338,6 +338,7 @@ void FlutterPeerConnection::CreateRTCPeerConnection( std::unique_ptr observer( new FlutterPeerConnectionObserver(base_, pc, base_->messenger_, + base_->task_runner_, event_channel, uuid)); base_->peerconnection_observers_[uuid] = std::move(observer); @@ -1118,9 +1119,10 @@ FlutterPeerConnectionObserver::FlutterPeerConnectionObserver( FlutterWebRTCBase* base, scoped_refptr peerconnection, BinaryMessenger* messenger, + TaskRunner* task_runner, const std::string& channel_name, std::string& peerConnectionId) - : event_channel_(EventChannelProxy::Create(messenger, channel_name)), + : event_channel_(EventChannelProxy::Create(messenger, task_runner, channel_name)), peerconnection_(peerconnection), base_(base), id_(peerConnectionId) { @@ -1326,6 +1328,7 @@ void FlutterPeerConnectionObserver::OnDataChannel( std::unique_ptr observer( new FlutterRTCDataChannelObserver(data_channel, base_->messenger_, + base_->task_runner_, event_channel)); base_->lock(); diff --git a/common/cpp/src/flutter_video_renderer.cc b/common/cpp/src/flutter_video_renderer.cc index e7e1774a39..77c5dd12c6 100644 --- a/common/cpp/src/flutter_video_renderer.cc +++ b/common/cpp/src/flutter_video_renderer.cc @@ -7,6 +7,7 @@ FlutterVideoRenderer::~FlutterVideoRenderer() {} void FlutterVideoRenderer::initialize( TextureRegistrar* registrar, BinaryMessenger* messenger, + TaskRunner* task_runner, std::unique_ptr texture, int64_t trxture_id) { registrar_ = registrar; @@ -14,7 +15,7 @@ void FlutterVideoRenderer::initialize( texture_id_ = trxture_id; std::string channel_name = "FlutterWebRTC/Texture" + std::to_string(texture_id_); - event_channel_ = EventChannelProxy::Create(messenger, channel_name); + event_channel_ = EventChannelProxy::Create(messenger, task_runner, channel_name); } const FlutterDesktopPixelBuffer* FlutterVideoRenderer::CopyPixelBuffer( @@ -121,7 +122,7 @@ void FlutterVideoRendererManager::CreateVideoRendererTexture( })); auto texture_id = base_->textures_->RegisterTexture(textureVariant.get()); - texture->initialize(base_->textures_, base_->messenger_, + texture->initialize(base_->textures_, base_->messenger_, base_->task_runner_, std::move(textureVariant), texture_id); renderers_[texture_id] = texture; EncodableMap params; diff --git a/common/cpp/src/flutter_webrtc.cc b/common/cpp/src/flutter_webrtc.cc index 77cddc1bd4..a402e47ed9 100644 --- a/common/cpp/src/flutter_webrtc.cc +++ b/common/cpp/src/flutter_webrtc.cc @@ -6,7 +6,8 @@ namespace flutter_webrtc_plugin { FlutterWebRTC::FlutterWebRTC(FlutterWebRTCPlugin* plugin) : FlutterWebRTCBase::FlutterWebRTCBase(plugin->messenger(), - plugin->textures()), + plugin->textures(), + plugin->task_runner()), FlutterVideoRendererManager::FlutterVideoRendererManager(this), FlutterMediaStream::FlutterMediaStream(this), FlutterPeerConnection::FlutterPeerConnection(this), @@ -351,6 +352,29 @@ void FlutterWebRTC::HandleMethodCall( return; } DataChannelSend(data_channel, type, data, std::move(result)); + } else if (method_call.method_name().compare("dataChannelGetBufferedAmount") == 0) { + if (!method_call.arguments()) { + result->Error("Bad Arguments", "Null constraints arguments received"); + return; + } + const EncodableMap params = + GetValue(*method_call.arguments()); + const std::string peerConnectionId = findString(params, "peerConnectionId"); + RTCPeerConnection* pc = PeerConnectionForId(peerConnectionId); + if (pc == nullptr) { + result->Error("dataChannelGetBufferedAmountFailed", + "dataChannelGetBufferedAmount() peerConnection is null"); + return; + } + + const std::string dataChannelId = findString(params, "dataChannelId"); + RTCDataChannel* data_channel = DataChannelForId(dataChannelId); + if (data_channel == nullptr) { + result->Error("dataChannelGetBufferedAmountFailed", + "dataChannelGetBufferedAmount() data_channel is null"); + return; + } + DataChannelGetBufferedAmount(data_channel, std::move(result)); } else if (method_call.method_name().compare("dataChannelClose") == 0) { if (!method_call.arguments()) { result->Error("Bad Arguments", "Null constraints arguments received"); diff --git a/common/cpp/src/flutter_webrtc_base.cc b/common/cpp/src/flutter_webrtc_base.cc index 901ba384eb..a8c184ba15 100644 --- a/common/cpp/src/flutter_webrtc_base.cc +++ b/common/cpp/src/flutter_webrtc_base.cc @@ -8,14 +8,15 @@ namespace flutter_webrtc_plugin { const char* kEventChannelName = "FlutterWebRTC.Event"; FlutterWebRTCBase::FlutterWebRTCBase(BinaryMessenger* messenger, - TextureRegistrar* textures) - : messenger_(messenger), textures_(textures) { + TextureRegistrar* textures, + TaskRunner *task_runner) + : messenger_(messenger), task_runner_(task_runner), textures_(textures) { LibWebRTC::Initialize(); factory_ = LibWebRTC::CreateRTCPeerConnectionFactory(); audio_device_ = factory_->GetAudioDevice(); video_device_ = factory_->GetVideoDevice(); desktop_device_ = factory_->GetDesktopDevice(); - event_channel_ = EventChannelProxy::Create(messenger_, kEventChannelName); + event_channel_ = EventChannelProxy::Create(messenger_, task_runner_, kEventChannelName); } FlutterWebRTCBase::~FlutterWebRTCBase() { diff --git a/common/darwin/Classes/FlutterRTCDataChannel.h b/common/darwin/Classes/FlutterRTCDataChannel.h index 96f4b5b86a..2b1d685274 100644 --- a/common/darwin/Classes/FlutterRTCDataChannel.h +++ b/common/darwin/Classes/FlutterRTCDataChannel.h @@ -24,4 +24,7 @@ data:(nonnull NSString*)data type:(nonnull NSString*)type; +- (void)dataChannelGetBufferedAmount:(nonnull NSString*)peerConnectionId + dataChannelId:(nonnull NSString*)dataChannelId + result:(nonnull FlutterResult)result; @end diff --git a/common/darwin/Classes/FlutterRTCDataChannel.m b/common/darwin/Classes/FlutterRTCDataChannel.m index 03003fcca5..67e1083b2f 100644 --- a/common/darwin/Classes/FlutterRTCDataChannel.m +++ b/common/darwin/Classes/FlutterRTCDataChannel.m @@ -118,6 +118,21 @@ - (void)dataChannelClose:(nonnull NSString*)peerConnectionId } } +- (void)dataChannelGetBufferedAmount:(nonnull NSString*)peerConnectionId + dataChannelId:(nonnull NSString*)dataChannelId + result:(nonnull FlutterResult)result { + RTCPeerConnection* peerConnection = self.peerConnections[peerConnectionId]; + RTCDataChannel* dataChannel = peerConnection.dataChannels[dataChannelId]; + if(dataChannel == NULL || dataChannel.readyState != RTCDataChannelStateOpen) { + result([FlutterError + errorWithCode:[NSString stringWithFormat:@"%@Failed", @"dataChannelGetBufferedAmount"] + message:[NSString stringWithFormat:@"Error: dataChannel not found or not opened!"] + details:nil]); + } else { + result(@{@"bufferedAmount": @(dataChannel.bufferedAmount)}); + } +} + - (void)dataChannelSend:(nonnull NSString*)peerConnectionId dataChannelId:(nonnull NSString*)dataChannelId data:(id)data diff --git a/common/darwin/Classes/FlutterWebRTCPlugin.m b/common/darwin/Classes/FlutterWebRTCPlugin.m index 7e4867417e..4c3cf8726f 100644 --- a/common/darwin/Classes/FlutterWebRTCPlugin.m +++ b/common/darwin/Classes/FlutterWebRTCPlugin.m @@ -570,7 +570,14 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self dataChannelSend:peerConnectionId dataChannelId:dataChannelId data:data type:type]; result(nil); - } else if ([@"dataChannelClose" isEqualToString:call.method]) { + } else if ([@"dataChannelGetBufferedAmount" isEqualToString:call.method]) { + NSDictionary* argsMap = call.arguments; + NSString* peerConnectionId = argsMap[@"peerConnectionId"]; + NSString* dataChannelId = argsMap[@"dataChannelId"]; + + [self dataChannelGetBufferedAmount:peerConnectionId dataChannelId:dataChannelId result:result]; + } + else if ([@"dataChannelClose" isEqualToString:call.method]) { NSDictionary* argsMap = call.arguments; NSString* peerConnectionId = argsMap[@"peerConnectionId"]; NSString* dataChannelId = argsMap[@"dataChannelId"]; diff --git a/elinux/flutter_webrtc_plugin.cc b/elinux/flutter_webrtc_plugin.cc index 9c2c5fd2b6..4e8a36656c 100644 --- a/elinux/flutter_webrtc_plugin.cc +++ b/elinux/flutter_webrtc_plugin.cc @@ -37,6 +37,8 @@ class FlutterWebRTCPluginImpl : public FlutterWebRTCPlugin { TextureRegistrar* textures() { return textures_; } + TaskRunner* task_runner() { return nullptr; } + private: // Creates a plugin that communicates on the given channel. FlutterWebRTCPluginImpl(PluginRegistrar* registrar, diff --git a/example/lib/src/loopback_data_channel_sample.dart b/example/lib/src/loopback_data_channel_sample.dart index 4e94b3030f..ba02873324 100644 --- a/example/lib/src/loopback_data_channel_sample.dart +++ b/example/lib/src/loopback_data_channel_sample.dart @@ -49,12 +49,14 @@ class _DataChannelLoopBackSampleState extends State { _dataChannel2Status += '\ndataChannel2: state: ${state.toString()}'; }); }; - _dataChannel2!.onMessage = (data) { + _dataChannel2!.onMessage = (data) async { + var bufferedAmount = await _dataChannel2!.getBufferedAmount(); setState(() { _dataChannel2Status += - '\ndataChannel2: Received message: ${data.text}'; + '\ndataChannel2: Received message: ${data.text}, bufferedAmount: $bufferedAmount'; }); - _dataChannel2!.send(RTCDataChannelMessage( + + await _dataChannel2!.send(RTCDataChannelMessage( '(dataChannel2 ==> dataChannel1) Hello from dataChannel2 echo !!!')); }; }; @@ -69,10 +71,12 @@ class _DataChannelLoopBackSampleState extends State { } }; - _dataChannel1!.onMessage = (data) => setState(() { - _dataChannel1Status += - '\ndataChannel1: Received message: ${data.text}'; - }); + _dataChannel1!.onMessage = (data) async { + var bufferedAmount = await _dataChannel2!.getBufferedAmount(); + _dataChannel1Status += + '\ndataChannel1: Received message: ${data.text}, bufferedAmount: $bufferedAmount'; + setState(() {}); + }; var offer = await _peerConnection1!.createOffer({}); print('peerConnection1 offer: ${offer.sdp}'); diff --git a/lib/src/native/rtc_data_channel_impl.dart b/lib/src/native/rtc_data_channel_impl.dart index 2aca9819da..2c3c84b676 100644 --- a/lib/src/native/rtc_data_channel_impl.dart +++ b/lib/src/native/rtc_data_channel_impl.dart @@ -109,6 +109,17 @@ class RTCDataChannelNative extends RTCDataChannel { } } + @override + Future getBufferedAmount() async { + final Map response = await WebRTC.invokeMethod( + 'dataChannelGetBufferedAmount', { + 'peerConnectionId': _peerConnectionId, + 'dataChannelId': _flutterId + }); + _bufferedAmount = response['bufferedAmount']; + return _bufferedAmount; + } + @override Future send(RTCDataChannelMessage message) async { await WebRTC.invokeMethod('dataChannelSend', { diff --git a/linux/flutter_webrtc_plugin.cc b/linux/flutter_webrtc_plugin.cc index 3cd15f8c1a..79ce73d0f3 100644 --- a/linux/flutter_webrtc_plugin.cc +++ b/linux/flutter_webrtc_plugin.cc @@ -37,6 +37,8 @@ class FlutterWebRTCPluginImpl : public FlutterWebRTCPlugin { TextureRegistrar* textures() { return textures_; } + TaskRunner* task_runner() { return nullptr; } + private: // Creates a plugin that communicates on the given channel. FlutterWebRTCPluginImpl(PluginRegistrar* registrar, diff --git a/pubspec.yaml b/pubspec.yaml index 56f8f8219b..5359459f74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_webrtc description: Flutter WebRTC plugin for iOS/Android/Destkop/Web, based on GoogleWebRTC. -version: 0.12.12 +version: 0.13.0 homepage: https://github.com/cloudwebrtc/flutter-webrtc environment: sdk: '>=3.3.0 <4.0.0' @@ -8,12 +8,12 @@ environment: dependencies: collection: ^1.17.0 - dart_webrtc: ^1.5.2+hotfix.1 + dart_webrtc: ^1.5.3 flutter: sdk: flutter path_provider: ^2.0.2 web: ^1.0.0 - webrtc_interface: ^1.2.1+hotfix.1 + webrtc_interface: ^1.2.2+hotfix.1 dev_dependencies: flutter_test: @@ -38,4 +38,4 @@ flutter: linux: pluginClass: FlutterWebRTCPlugin elinux: - pluginClass: FlutterWebRTCPlugin \ No newline at end of file + pluginClass: FlutterWebRTCPlugin diff --git a/third_party/libwebrtc/include/rtc_data_channel.h b/third_party/libwebrtc/include/rtc_data_channel.h index 183d5da6df..e1351959f2 100644 --- a/third_party/libwebrtc/include/rtc_data_channel.h +++ b/third_party/libwebrtc/include/rtc_data_channel.h @@ -103,6 +103,13 @@ class RTCDataChannel : public RefCountInterface { */ virtual int id() const = 0; + /** + * Returns the amount of data buffered in the data channel. + * + * @return uint64_t + */ + virtual uint64_t buffered_amount() const = 0; + /** * Returns the state of the data channel. */ diff --git a/third_party/libwebrtc/include/rtc_ice_transport.h b/third_party/libwebrtc/include/rtc_ice_transport.h index b6c1ee5a68..645f305b2d 100644 --- a/third_party/libwebrtc/include/rtc_ice_transport.h +++ b/third_party/libwebrtc/include/rtc_ice_transport.h @@ -21,7 +21,8 @@ #include "rtc_base/ref_count.h" namespace libwebrtc { -class IceTransport : public rtc::RefCountInterface { + +class IceTransport : public RefCountInterface { public: virtual IceTransport* internal() = 0; }; diff --git a/third_party/libwebrtc/lib/linux-arm64/libwebrtc.so b/third_party/libwebrtc/lib/linux-arm64/libwebrtc.so index 2b7c1251f9..22c3bde89f 100755 Binary files a/third_party/libwebrtc/lib/linux-arm64/libwebrtc.so and b/third_party/libwebrtc/lib/linux-arm64/libwebrtc.so differ diff --git a/third_party/libwebrtc/lib/linux-x64/libwebrtc.so b/third_party/libwebrtc/lib/linux-x64/libwebrtc.so index 08ce7d1c60..ae9c4eec39 100755 Binary files a/third_party/libwebrtc/lib/linux-x64/libwebrtc.so and b/third_party/libwebrtc/lib/linux-x64/libwebrtc.so differ diff --git a/third_party/libwebrtc/lib/win64/libwebrtc.dll b/third_party/libwebrtc/lib/win64/libwebrtc.dll index 04a82c0bd8..e62437621a 100644 Binary files a/third_party/libwebrtc/lib/win64/libwebrtc.dll and b/third_party/libwebrtc/lib/win64/libwebrtc.dll differ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index ffa61d788c..39ee5943ac 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(${PLUGIN_NAME} SHARED "../common/cpp/src/flutter_webrtc_base.cc" "../third_party/uuidxx/uuidxx.cc" "flutter_webrtc_plugin.cc" + "task_runner_windows.cc" ) include_directories( diff --git a/windows/flutter_webrtc_plugin.cc b/windows/flutter_webrtc_plugin.cc index f22d06efaa..bfe08328dd 100644 --- a/windows/flutter_webrtc_plugin.cc +++ b/windows/flutter_webrtc_plugin.cc @@ -2,6 +2,7 @@ #include "flutter_common.h" #include "flutter_webrtc.h" +#include "task_runner_windows.h" const char* kChannelName = "FlutterWebRTC.Method"; @@ -35,13 +36,16 @@ class FlutterWebRTCPluginImpl : public FlutterWebRTCPlugin { TextureRegistrar* textures() { return textures_; } + TaskRunner* task_runner() { return task_runner_.get(); } + private: // Creates a plugin that communicates on the given channel. FlutterWebRTCPluginImpl(PluginRegistrar* registrar, std::unique_ptr channel) : channel_(std::move(channel)), messenger_(registrar->messenger()), - textures_(registrar->texture_registrar()) { + textures_(registrar->texture_registrar()), + task_runner_(std::make_unique()) { webrtc_ = std::make_unique(this); } @@ -59,6 +63,7 @@ class FlutterWebRTCPluginImpl : public FlutterWebRTCPlugin { std::unique_ptr webrtc_; BinaryMessenger* messenger_; TextureRegistrar* textures_; + std::unique_ptr task_runner_; }; } // namespace flutter_webrtc_plugin @@ -66,7 +71,7 @@ class FlutterWebRTCPluginImpl : public FlutterWebRTCPlugin { void FlutterWebRTCPluginRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { - static auto* plugin_registrar = new flutter::PluginRegistrar(registrar); - flutter_webrtc_plugin::FlutterWebRTCPluginImpl::RegisterWithRegistrar( - plugin_registrar); + static auto* plugin_registrar = new flutter::PluginRegistrar(registrar); + flutter_webrtc_plugin::FlutterWebRTCPluginImpl::RegisterWithRegistrar( + plugin_registrar); } \ No newline at end of file diff --git a/windows/task_runner_windows.cc b/windows/task_runner_windows.cc new file mode 100644 index 0000000000..0c47ab519e --- /dev/null +++ b/windows/task_runner_windows.cc @@ -0,0 +1,106 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "task_runner_windows.h" + + #include + #include + + namespace flutter_webrtc_plugin { + + TaskRunnerWindows::TaskRunnerWindows() { + WNDCLASS window_class = RegisterWindowClass(); + window_handle_ = + CreateWindowEx(0, window_class.lpszClassName, L"", 0, 0, 0, 0, 0, + HWND_MESSAGE, nullptr, window_class.hInstance, nullptr); + + if (window_handle_) { + SetWindowLongPtr(window_handle_, GWLP_USERDATA, + reinterpret_cast(this)); + } else { + auto error = GetLastError(); + LPWSTR message = nullptr; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message), 0, NULL); + OutputDebugString(message); + LocalFree(message); + } + } + + TaskRunnerWindows::~TaskRunnerWindows() { + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + UnregisterClass(window_class_name_.c_str(), nullptr); + } + + void TaskRunnerWindows::EnqueueTask(TaskClosure task) { + { + std::lock_guard lock(tasks_mutex_); + tasks_.push(task); + } + if (!PostMessage(window_handle_, WM_NULL, 0, 0)) { + DWORD error_code = GetLastError(); + std::cerr << "Failed to post message to main thread; error_code: " + << error_code << std::endl; + } + } + + void TaskRunnerWindows::ProcessTasks() { + // Even though it would usually be sufficient to process only a single task + // whenever we receive the message, if the message queue happens to be full, + // we might not receive a message for each individual task. + for (;;) { + std::lock_guard lock(tasks_mutex_); + if (tasks_.empty()) break; + TaskClosure task = tasks_.front(); + tasks_.pop(); + task(); + } + } + + WNDCLASS TaskRunnerWindows::RegisterWindowClass() { + window_class_name_ = L"FlutterWebRTCWindowsTaskRunnerWindow"; + + WNDCLASS window_class{}; + window_class.hCursor = nullptr; + window_class.lpszClassName = window_class_name_.c_str(); + window_class.style = 0; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = nullptr; + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = WndProc; + RegisterClass(&window_class); + return window_class; + } + + LRESULT + TaskRunnerWindows::HandleMessage(UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_NULL: + ProcessTasks(); + return 0; + } + return DefWindowProcW(window_handle_, message, wparam, lparam); + } + + LRESULT TaskRunnerWindows::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (auto* that = reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA))) { + return that->HandleMessage(message, wparam, lparam); + } else { + return DefWindowProc(window, message, wparam, lparam); + } + } + + } // namespace flutter_webrtc_plugin \ No newline at end of file diff --git a/windows/task_runner_windows.h b/windows/task_runner_windows.h new file mode 100644 index 0000000000..f86c99d3f8 --- /dev/null +++ b/windows/task_runner_windows.h @@ -0,0 +1,55 @@ +// Copyright 2024 The Flutter Authors. All rights reserved. + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + #ifndef PACKAGES_FLUTTER_WEBRTC_WINDOWS_TASK_RUNNER_WINDOW_H_ + #define PACKAGES_FLUTTER_WEBRTC_WINDOWS_TASK_RUNNER_WINDOW_H_ + + #include + + #include + #include + #include + #include + #include + + #include "task_runner.h" + + namespace flutter_webrtc_plugin { + + // Hidden HWND responsible for processing camera tasks on main thread + // Adapted from Flutter Engine, see: + // https://github.com/flutter/flutter/issues/134346#issuecomment-2141023146 + // and: + // https://github.com/flutter/engine/blob/d7c0bcfe7a30408b0722c9d47d8b0b1e4cdb9c81/shell/platform/windows/task_runner_window.h + class TaskRunnerWindows : public TaskRunner { + public: + virtual void EnqueueTask(TaskClosure task); + + TaskRunnerWindows(); + ~TaskRunnerWindows(); + + private: + void ProcessTasks(); + + WNDCLASS RegisterWindowClass(); + + LRESULT + HandleMessage(UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept; + + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + HWND window_handle_; + std::wstring window_class_name_; + std::mutex tasks_mutex_; + std::queue tasks_; + + // Prevent copying. + TaskRunnerWindows(TaskRunnerWindows const&) = delete; + TaskRunnerWindows& operator=(TaskRunnerWindows const&) = delete; + }; + } // namespace flutter_webrtc_plugin + + #endif // PACKAGES_FLUTTER_WEBRTC_WINDOWS_TASK_RUNNER_WINDOW_H_ \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy