diff --git a/Cargo.lock b/Cargo.lock index fe92cfad9d..eecab65c70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe" +checksum = "292b4841d939b20ba44fff686a35808b0ab31a3256e3629917d9aedd43eb7b3a" dependencies = [ "cc", "cxxbridge-flags", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ce20f6b8433da4841b1dadfb9468709868022d829d5ca1f2ffbda928455ea3" +checksum = "8e7e35cf85fd4e90dcaba251f3ee95e08fb6f9d66e5c0588816f16a6ab939b40" dependencies = [ "cc", "codespan-reporting", @@ -215,15 +215,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2" +checksum = "d7030aff1908ba2b7eb639466df50792b2a3fdf02bea9557c4ee1a531975554b" [[package]] name = "cxxbridge-macro" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" +checksum = "79418ecb0c2322a7926a5fa5a9660535432b5b3588b947e1eb484cc509edbe3c" dependencies = [ "proc-macro2", "quote", @@ -347,9 +347,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.82.1" +version = "1.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef4736aac920ca50be2c7747a46494d3740ca88bc49a6654b031d6f396282f3" +checksum = "d1ab3d175f0a09c1adb55fd98d7b6460b00af72c4e889b9eec2c5aee88273996" [[package]] name = "fnv" @@ -680,9 +680,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45786cec4d5e54a224b15cb9f06751883103a27c19c93eda09b0b4f5f08fefac" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -933,9 +933,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1013,9 +1013,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.17" +version = "0.38.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" dependencies = [ "bitflags 2.4.0", "errno", @@ -1085,9 +1085,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" @@ -1280,9 +1280,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", diff --git a/crates/native/src/api.rs b/crates/native/src/api.rs index 191a16a88a..0bdad95175 100644 --- a/crates/native/src/api.rs +++ b/crates/native/src/api.rs @@ -17,7 +17,8 @@ use crate::{ // Re-exporting since it is used in the generated code. pub use crate::{ - PeerConnection, RtpEncodingParameters, RtpTransceiver, RtpTransceiverInit, + renderer::TextureEvent, PeerConnection, RtpEncodingParameters, + RtpTransceiver, RtpTransceiverInit, }; lazy_static::lazy_static! { @@ -1939,9 +1940,9 @@ pub fn set_transceiver_init_direction( #[allow(clippy::needless_pass_by_value)] pub fn add_transceiver_init_send_encoding( init: RustOpaque>, - encoding: RustOpaque>, + enc: RustOpaque>, ) { - init.add_encoding(&encoding); + init.add_encoding(&enc); } /// Creates new [`RtpEncodingParameters`] with the provided settings. @@ -2238,11 +2239,13 @@ pub fn set_on_device_changed(cb: StreamSink<()>) -> anyhow::Result<()> { /// `callback_ptr` argument should be a pointer to an [`UniquePtr`] pointing to /// an [`OnFrameCallbackInterface`]. pub fn create_video_sink( + cb: StreamSink, sink_id: i64, track_id: String, callback_ptr: u64, + texture_id: i64, ) -> anyhow::Result<()> { - let handler = FrameHandler::new(callback_ptr as _); + let handler = FrameHandler::new(callback_ptr as _, cb.into(), texture_id); WEBRTC .lock() diff --git a/crates/native/src/bridge_generated.rs b/crates/native/src/bridge_generated.rs index 8e8757b091..f734a09c0b 100644 --- a/crates/native/src/bridge_generated.rs +++ b/crates/native/src/bridge_generated.rs @@ -1,4 +1,3 @@ -#![cfg_attr(rustfmt, rustfmt_skip)] #![allow( non_camel_case_types, unused, @@ -14,13 +13,13 @@ use crate::api::*; use core::panic::UnwindSafe; -use flutter_rust_bridge::rust2dart::IntoIntoDart; -use flutter_rust_bridge::*; -use std::ffi::c_void; -use std::sync::Arc; +use flutter_rust_bridge::{rust2dart::IntoIntoDart, *}; +use std::{ffi::c_void, sync::Arc}; // Section: imports +use crate::renderer::TextureEvent; + // Section: wire functions fn wire_enable_fake_media_impl(port_: MessagePort) { @@ -178,7 +177,7 @@ fn wire_set_transceiver_init_direction_impl( fn wire_add_transceiver_init_send_encoding_impl( port_: MessagePort, init: impl Wire2Api>> + UnwindSafe, - encoding: impl Wire2Api>> + UnwindSafe, + enc: impl Wire2Api>> + UnwindSafe, ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>( WrapInfo { @@ -188,9 +187,9 @@ fn wire_add_transceiver_init_send_encoding_impl( }, move || { let api_init = init.wire2api(); - let api_encoding = encoding.wire2api(); + let api_enc = enc.wire2api(); move |task_callback| { - Ok(add_transceiver_init_send_encoding(api_init, api_encoding)) + Ok(add_transceiver_init_send_encoding(api_init, api_enc)) } }, ) @@ -704,19 +703,27 @@ fn wire_create_video_sink_impl( sink_id: impl Wire2Api + UnwindSafe, track_id: impl Wire2Api + UnwindSafe, callback_ptr: impl Wire2Api + UnwindSafe, + texture_id: impl Wire2Api + UnwindSafe, ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>( WrapInfo { debug_name: "create_video_sink", port: Some(port_), - mode: FfiCallMode::Normal, + mode: FfiCallMode::Stream, }, move || { let api_sink_id = sink_id.wire2api(); let api_track_id = track_id.wire2api(); let api_callback_ptr = callback_ptr.wire2api(); + let api_texture_id = texture_id.wire2api(); move |task_callback| { - create_video_sink(api_sink_id, api_track_id, api_callback_ptr) + create_video_sink( + task_callback.stream_sink::<_, TextureEvent>(), + api_sink_id, + api_track_id, + api_callback_ptr, + api_texture_id, + ) } }, ) @@ -1590,6 +1597,35 @@ impl rust2dart::IntoIntoDart for SignalingState { } } +impl support::IntoDart for TextureEvent { + fn into_dart(self) -> support::DartAbi { + match self { + Self::OnTextureChange { + texture_id, + width, + height, + rotation, + } => vec![ + 0.into_dart(), + texture_id.into_into_dart().into_dart(), + width.into_into_dart().into_dart(), + height.into_into_dart().into_dart(), + rotation.into_into_dart().into_dart(), + ], + Self::OnFirstFrameRendered { texture_id } => { + vec![1.into_dart(), texture_id.into_into_dart().into_dart()] + } + } + .into_dart() + } +} +impl support::IntoDartExceptPrimitive for TextureEvent {} +impl rust2dart::IntoIntoDart for TextureEvent { + fn into_into_dart(self) -> Self { + self + } +} + impl support::IntoDart for TrackEvent { fn into_dart(self) -> support::DartAbi { match self { @@ -1624,7 +1660,8 @@ impl rust2dart::IntoIntoDart for TrackState { // Section: executor support::lazy_static! { - pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); + pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = + Default::default(); } #[cfg(not(target_family = "wasm"))] @@ -1712,9 +1749,9 @@ mod io { pub extern "C" fn wire_add_transceiver_init_send_encoding( port_: i64, init: wire_ArcRtpTransceiverInit, - encoding: wire_ArcRtpEncodingParameters, + enc: wire_ArcRtpEncodingParameters, ) { - wire_add_transceiver_init_send_encoding_impl(port_, init, encoding) + wire_add_transceiver_init_send_encoding_impl(port_, init, enc) } #[no_mangle] @@ -1966,8 +2003,15 @@ mod io { sink_id: i64, track_id: *mut wire_uint_8_list, callback_ptr: u64, + texture_id: i64, ) { - wire_create_video_sink_impl(port_, sink_id, track_id, callback_ptr) + wire_create_video_sink_impl( + port_, + sink_id, + track_id, + callback_ptr, + texture_id, + ) } #[no_mangle] diff --git a/crates/native/src/renderer.rs b/crates/native/src/renderer.rs index f3972cb4c9..f901c2ec22 100644 --- a/crates/native/src/renderer.rs +++ b/crates/native/src/renderer.rs @@ -1,7 +1,101 @@ //! Implementations and definitions of the renderers API for C and C++ APIs. +use libwebrtc_sys as sys; + +use crate::stream_sink::StreamSink; + pub use frame_handler::FrameHandler; +/// Frame change events. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum TextureEvent { + /// Height, width, or rotation have changed. + OnTextureChange { + /// ID of the texture. + texture_id: i64, + + /// Width of the last processed frame. + width: i32, + + /// Height of the last processed frame. + height: i32, + + /// Rotation of the last processed frame. + rotation: i32, + }, + + /// First frame event. + OnFirstFrameRendered { + /// ID of the texture. + texture_id: i64, + }, +} + +/// Notifier of Dart side about any [`sys::VideoFrame`] dimensions changes. +struct TextureEventNotifier { + /// Sink to send asynchronous data back to Dart. + sink: StreamSink, + + /// Indicator whether any frames were rendered for the given texture. + first_frame_rendered: bool, + + /// ID of the texture. + texture_id: i64, + + /// Width of the last processed frame. + width: i32, + + /// Height of the last processed frame. + height: i32, + + /// Rotation of the last processed frame. + rotation: sys::VideoRotation, +} + +impl TextureEventNotifier { + /// Creates a new [`TextureEventNotifier`]. + fn new(sink: StreamSink, texture_id: i64) -> Self { + Self { + sink, + first_frame_rendered: false, + width: 0, + height: 0, + rotation: sys::VideoRotation::kVideoRotation_0, + texture_id, + } + } + + /// Passes the provided [`sys::VideoFrame`] to Dart side events. + fn on_frame(&mut self, frame: &cxx::UniquePtr) { + let height = frame.height(); + let width = frame.width(); + let rotation = frame.rotation(); + + if !self.first_frame_rendered { + self.first_frame_rendered = true; + self.sink.add(TextureEvent::OnFirstFrameRendered { + texture_id: self.texture_id, + }); + } + + if self.height != height + || self.width != width + || self.rotation != rotation + { + self.height = height; + self.width = width; + self.rotation = rotation; + self.sink.add(TextureEvent::OnTextureChange { + texture_id: self.texture_id, + width, + height, + rotation: rotation.repr, + }); + } + } +} + #[cfg(not(target_os = "macos"))] /// Definitions and implementation of a handler for C++ API [`sys::VideoFrame`]s /// renderer. @@ -10,21 +104,39 @@ mod frame_handler { use derive_more::From; use libwebrtc_sys as sys; + use crate::{ + renderer::{TextureEvent, TextureEventNotifier}, + stream_sink::StreamSink, + }; + pub use cpp_api_bindings::{OnFrameCallbackInterface, VideoFrame}; /// Handler for a [`sys::VideoFrame`]s renderer. - pub struct FrameHandler(UniquePtr); + pub struct FrameHandler { + inner: UniquePtr, + event_tx: TextureEventNotifier, + } impl FrameHandler { /// Returns new [`FrameHandler`] with the provided [`sys::VideoFrame`]s /// receiver. - pub fn new(handler: *mut OnFrameCallbackInterface) -> Self { - unsafe { Self(UniquePtr::from_raw(handler)) } + pub fn new( + handler: *mut OnFrameCallbackInterface, + sink: StreamSink, + texture_id: i64, + ) -> Self { + unsafe { + Self { + inner: UniquePtr::from_raw(handler), + event_tx: TextureEventNotifier::new(sink, texture_id), + } + } } /// Passes provided [`sys::VideoFrame`] to the C++ side listener. pub fn on_frame(&mut self, frame: UniquePtr) { - self.0.pin_mut().on_frame(VideoFrame::from(frame)); + self.event_tx.on_frame(&frame); + self.inner.pin_mut().on_frame(VideoFrame::from(frame)); } } @@ -132,12 +244,20 @@ mod frame_handler { use cxx::UniquePtr; use libwebrtc_sys as sys; + use crate::{ + renderer::{TextureEvent, TextureEventNotifier}, + stream_sink::StreamSink, + }; + /// Handler for a [`sys::VideoFrame`]s renderer. - pub struct FrameHandler(*const ()); + pub struct FrameHandler { + inner: *const (), + event_tx: TextureEventNotifier, + } impl Drop for FrameHandler { fn drop(&mut self) { - unsafe { drop_handler(self.0) }; + unsafe { drop_handler(self.inner) }; } } @@ -164,23 +284,32 @@ mod frame_handler { impl FrameHandler { /// Returns new [`FrameHandler`] with the provided [`sys::VideoFrame`]s /// receiver. - pub fn new(handler: *const ()) -> Self { - Self(handler) + pub fn new( + handler: *const (), + sink: StreamSink, + texture_id: i64, + ) -> Self { + Self { + inner: handler, + event_tx: TextureEventNotifier::new(sink, texture_id), + } } /// Passes the provided [`sys::VideoFrame`] to the C side listener. - #[allow(clippy::cast_sign_loss)] - pub fn on_frame(&self, frame: UniquePtr) { + #[allow(clippy::cast_sign_loss, clippy::too_many_lines)] + pub fn on_frame(&mut self, frame: UniquePtr) { let height = frame.height(); let width = frame.width(); assert!(height >= 0, "VideoFrame has a negative height"); assert!(width >= 0, "VideoFrame has a negative width"); + self.event_tx.on_frame(&frame); + let buffer_size = width * height * 4; unsafe { on_frame_caller( - self.0, + self.inner, Frame { height: height as usize, width: width as usize, diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 1c8c39b732..1e8ab7deca 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -29,11 +29,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - instrumentisto-libwebrtc-bin: f780c8c0bfb3d4df2279c678fa6b3971917af3bf + instrumentisto-libwebrtc-bin: 5fc9b7e7a46cc626c3f264022669f11e36de2053 integration_test: 13825b8a9334a850581300559b8839134b124670 libyuv-iOS: 5a154ccc84ec754029886ecb607512731fe30640 - medea_flutter_webrtc: ba8933d74f0b720684dd1b619b963b6e70633d23 + medea_flutter_webrtc: ceb980f10dbbe7e9050ebb69a42a8f7c3e4bf8a3 PODFILE CHECKSUM: be3ab4e988bb308d23906be69146fcbef66fac55 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 39dbbd31e5..13f4b0ac6f 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -143,7 +143,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 1c8b129238..8f29a3846b 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -19,4 +19,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/example/pubspec.lock b/example/pubspec.lock index 640bae6c1c..e03b767e9c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: archive - sha256: ca12e6c9ac022f33fd89128e7007fb5e97ab6e814d4fa05dd8d4f2db1e3c69cb + sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" url: "https://pub.dev" source: hosted - version: "3.4.5" + version: "3.4.6" args: dependency: transitive description: diff --git a/lib/src/api/bridge.g.dart b/lib/src/api/bridge.g.dart index 3f505b4b20..db5d98efda 100644 --- a/lib/src/api/bridge.g.dart +++ b/lib/src/api/bridge.g.dart @@ -81,7 +81,7 @@ abstract class MedeaFlutterWebrtcNative { /// Adds the provided [`RtpEncodingParameters`] to the [`RtpTransceiverInit`]. Future addTransceiverInitSendEncoding( {required ArcRtpTransceiverInit init, - required ArcRtpEncodingParameters encoding, + required ArcRtpEncodingParameters enc, dynamic hint}); FlutterRustBridgeTaskConstMeta get kAddTransceiverInitSendEncodingConstMeta; @@ -302,10 +302,11 @@ abstract class MedeaFlutterWebrtcNative { /// /// `callback_ptr` argument should be a pointer to an [`UniquePtr`] pointing to /// an [`OnFrameCallbackInterface`]. - Future createVideoSink( + Stream createVideoSink( {required int sinkId, required String trackId, required int callbackPtr, + required int textureId, dynamic hint}); FlutterRustBridgeTaskConstMeta get kCreateVideoSinkConstMeta; @@ -1775,6 +1776,30 @@ enum SignalingState { closed, } +@freezed +sealed class TextureEvent with _$TextureEvent { + /// The height, width, or rotation have changed. + const factory TextureEvent.onTextureChange({ + /// Id of the texture. + required int textureId, + + /// Width of the last processed frame. + required int width, + + /// Height of the last processed frame. + required int height, + + /// Rotation of the last processed frame. + required int rotation, + }) = TextureEvent_OnTextureChange; + + /// First frame event. + const factory TextureEvent.onFirstFrameRendered({ + /// Id of the texture. + required int textureId, + }) = TextureEvent_OnFirstFrameRendered; +} + /// Indicator of the current state of a [`MediaStreamTrack`]. enum TrackEvent { /// Ended event of the [`MediaStreamTrack`] interface is fired when playback @@ -2014,16 +2039,16 @@ class MedeaFlutterWebrtcNativeImpl implements MedeaFlutterWebrtcNative { Future addTransceiverInitSendEncoding( {required ArcRtpTransceiverInit init, - required ArcRtpEncodingParameters encoding, + required ArcRtpEncodingParameters enc, dynamic hint}) { var arg0 = _platform.api2wire_ArcRtpTransceiverInit(init); - var arg1 = _platform.api2wire_ArcRtpEncodingParameters(encoding); + var arg1 = _platform.api2wire_ArcRtpEncodingParameters(enc); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => _platform.inner .wire_add_transceiver_init_send_encoding(port_, arg0, arg1), parseSuccessData: _wire2api_unit, constMeta: kAddTransceiverInitSendEncodingConstMeta, - argValues: [init, encoding], + argValues: [init, enc], hint: hint, )); } @@ -2031,7 +2056,7 @@ class MedeaFlutterWebrtcNativeImpl implements MedeaFlutterWebrtcNative { FlutterRustBridgeTaskConstMeta get kAddTransceiverInitSendEncodingConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "add_transceiver_init_send_encoding", - argNames: ["init", "encoding"], + argNames: ["init", "enc"], ); Future createEncodingParameters( @@ -2598,20 +2623,22 @@ class MedeaFlutterWebrtcNativeImpl implements MedeaFlutterWebrtcNative { argNames: [], ); - Future createVideoSink( + Stream createVideoSink( {required int sinkId, required String trackId, required int callbackPtr, + required int textureId, dynamic hint}) { var arg0 = _platform.api2wire_i64(sinkId); var arg1 = _platform.api2wire_String(trackId); var arg2 = _platform.api2wire_u64(callbackPtr); - return _platform.executeNormal(FlutterRustBridgeTask( + var arg3 = _platform.api2wire_i64(textureId); + return _platform.executeStream(FlutterRustBridgeTask( callFfi: (port_) => - _platform.inner.wire_create_video_sink(port_, arg0, arg1, arg2), - parseSuccessData: _wire2api_unit, + _platform.inner.wire_create_video_sink(port_, arg0, arg1, arg2, arg3), + parseSuccessData: _wire2api_texture_event, constMeta: kCreateVideoSinkConstMeta, - argValues: [sinkId, trackId, callbackPtr], + argValues: [sinkId, trackId, callbackPtr, textureId], hint: hint, )); } @@ -2619,7 +2646,7 @@ class MedeaFlutterWebrtcNativeImpl implements MedeaFlutterWebrtcNative { FlutterRustBridgeTaskConstMeta get kCreateVideoSinkConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "create_video_sink", - argNames: ["sinkId", "trackId", "callbackPtr"], + argNames: ["sinkId", "trackId", "callbackPtr", "textureId"], ); Future disposeVideoSink({required int sinkId, dynamic hint}) { @@ -3209,6 +3236,24 @@ class MedeaFlutterWebrtcNativeImpl implements MedeaFlutterWebrtcNative { return SignalingState.values[raw as int]; } + TextureEvent _wire2api_texture_event(dynamic raw) { + switch (raw[0]) { + case 0: + return TextureEvent_OnTextureChange( + textureId: _wire2api_i64(raw[1]), + width: _wire2api_i32(raw[2]), + height: _wire2api_i32(raw[3]), + rotation: _wire2api_i32(raw[4]), + ); + case 1: + return TextureEvent_OnFirstFrameRendered( + textureId: _wire2api_i64(raw[1]), + ); + default: + throw Exception("unreachable"); + } + } + TrackEvent _wire2api_track_event(dynamic raw) { return TrackEvent.values[raw as int]; } @@ -3806,12 +3851,12 @@ class MedeaFlutterWebrtcNativeWire implements FlutterRustBridgeWireBase { void wire_add_transceiver_init_send_encoding( int port_, wire_ArcRtpTransceiverInit init, - wire_ArcRtpEncodingParameters encoding, + wire_ArcRtpEncodingParameters enc, ) { return _wire_add_transceiver_init_send_encoding( port_, init, - encoding, + enc, ); } @@ -4361,21 +4406,23 @@ class MedeaFlutterWebrtcNativeWire implements FlutterRustBridgeWireBase { int sink_id, ffi.Pointer track_id, int callback_ptr, + int texture_id, ) { return _wire_create_video_sink( port_, sink_id, track_id, callback_ptr, + texture_id, ); } late final _wire_create_video_sinkPtr = _lookup< ffi.NativeFunction< ffi.Void Function(ffi.Int64, ffi.Int64, ffi.Pointer, - ffi.Uint64)>>('wire_create_video_sink'); + ffi.Uint64, ffi.Int64)>>('wire_create_video_sink'); late final _wire_create_video_sink = _wire_create_video_sinkPtr.asFunction< - void Function(int, int, ffi.Pointer, int)>(); + void Function(int, int, ffi.Pointer, int, int)>(); void wire_dispose_video_sink( int port_, diff --git a/lib/src/api/bridge.g.freezed.dart b/lib/src/api/bridge.g.freezed.dart index 294effbff5..c6a9f6dfbf 100644 --- a/lib/src/api/bridge.g.freezed.dart +++ b/lib/src/api/bridge.g.freezed.dart @@ -9314,3 +9314,453 @@ class _$RtcStatsType_UnimplementedImpl implements RtcStatsType_Unimplemented { abstract class RtcStatsType_Unimplemented implements RtcStatsType { const factory RtcStatsType_Unimplemented() = _$RtcStatsType_UnimplementedImpl; } + +/// @nodoc +mixin _$TextureEvent { + /// Id of the texture. + int get textureId => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function( + int textureId, int width, int height, int rotation) + onTextureChange, + required TResult Function(int textureId) onFirstFrameRendered, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult? Function(int textureId)? onFirstFrameRendered, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult Function(int textureId)? onFirstFrameRendered, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(TextureEvent_OnTextureChange value) + onTextureChange, + required TResult Function(TextureEvent_OnFirstFrameRendered value) + onFirstFrameRendered, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult? Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $TextureEventCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TextureEventCopyWith<$Res> { + factory $TextureEventCopyWith( + TextureEvent value, $Res Function(TextureEvent) then) = + _$TextureEventCopyWithImpl<$Res, TextureEvent>; + @useResult + $Res call({int textureId}); +} + +/// @nodoc +class _$TextureEventCopyWithImpl<$Res, $Val extends TextureEvent> + implements $TextureEventCopyWith<$Res> { + _$TextureEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? textureId = null, + }) { + return _then(_value.copyWith( + textureId: null == textureId + ? _value.textureId + : textureId // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TextureEvent_OnTextureChangeImplCopyWith<$Res> + implements $TextureEventCopyWith<$Res> { + factory _$$TextureEvent_OnTextureChangeImplCopyWith( + _$TextureEvent_OnTextureChangeImpl value, + $Res Function(_$TextureEvent_OnTextureChangeImpl) then) = + __$$TextureEvent_OnTextureChangeImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int textureId, int width, int height, int rotation}); +} + +/// @nodoc +class __$$TextureEvent_OnTextureChangeImplCopyWithImpl<$Res> + extends _$TextureEventCopyWithImpl<$Res, _$TextureEvent_OnTextureChangeImpl> + implements _$$TextureEvent_OnTextureChangeImplCopyWith<$Res> { + __$$TextureEvent_OnTextureChangeImplCopyWithImpl( + _$TextureEvent_OnTextureChangeImpl _value, + $Res Function(_$TextureEvent_OnTextureChangeImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? textureId = null, + Object? width = null, + Object? height = null, + Object? rotation = null, + }) { + return _then(_$TextureEvent_OnTextureChangeImpl( + textureId: null == textureId + ? _value.textureId + : textureId // ignore: cast_nullable_to_non_nullable + as int, + width: null == width + ? _value.width + : width // ignore: cast_nullable_to_non_nullable + as int, + height: null == height + ? _value.height + : height // ignore: cast_nullable_to_non_nullable + as int, + rotation: null == rotation + ? _value.rotation + : rotation // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$TextureEvent_OnTextureChangeImpl + implements TextureEvent_OnTextureChange { + const _$TextureEvent_OnTextureChangeImpl( + {required this.textureId, + required this.width, + required this.height, + required this.rotation}); + + /// Id of the texture. + @override + final int textureId; + + /// Width of the last processed frame. + @override + final int width; + + /// Height of the last processed frame. + @override + final int height; + + /// Rotation of the last processed frame. + @override + final int rotation; + + @override + String toString() { + return 'TextureEvent.onTextureChange(textureId: $textureId, width: $width, height: $height, rotation: $rotation)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TextureEvent_OnTextureChangeImpl && + (identical(other.textureId, textureId) || + other.textureId == textureId) && + (identical(other.width, width) || other.width == width) && + (identical(other.height, height) || other.height == height) && + (identical(other.rotation, rotation) || + other.rotation == rotation)); + } + + @override + int get hashCode => + Object.hash(runtimeType, textureId, width, height, rotation); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TextureEvent_OnTextureChangeImplCopyWith< + _$TextureEvent_OnTextureChangeImpl> + get copyWith => __$$TextureEvent_OnTextureChangeImplCopyWithImpl< + _$TextureEvent_OnTextureChangeImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + int textureId, int width, int height, int rotation) + onTextureChange, + required TResult Function(int textureId) onFirstFrameRendered, + }) { + return onTextureChange(textureId, width, height, rotation); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult? Function(int textureId)? onFirstFrameRendered, + }) { + return onTextureChange?.call(textureId, width, height, rotation); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult Function(int textureId)? onFirstFrameRendered, + required TResult orElse(), + }) { + if (onTextureChange != null) { + return onTextureChange(textureId, width, height, rotation); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(TextureEvent_OnTextureChange value) + onTextureChange, + required TResult Function(TextureEvent_OnFirstFrameRendered value) + onFirstFrameRendered, + }) { + return onTextureChange(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult? Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + }) { + return onTextureChange?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + required TResult orElse(), + }) { + if (onTextureChange != null) { + return onTextureChange(this); + } + return orElse(); + } +} + +abstract class TextureEvent_OnTextureChange implements TextureEvent { + const factory TextureEvent_OnTextureChange( + {required final int textureId, + required final int width, + required final int height, + required final int rotation}) = _$TextureEvent_OnTextureChangeImpl; + + @override + + /// Id of the texture. + int get textureId; + + /// Width of the last processed frame. + int get width; + + /// Height of the last processed frame. + int get height; + + /// Rotation of the last processed frame. + int get rotation; + @override + @JsonKey(ignore: true) + _$$TextureEvent_OnTextureChangeImplCopyWith< + _$TextureEvent_OnTextureChangeImpl> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$TextureEvent_OnFirstFrameRenderedImplCopyWith<$Res> + implements $TextureEventCopyWith<$Res> { + factory _$$TextureEvent_OnFirstFrameRenderedImplCopyWith( + _$TextureEvent_OnFirstFrameRenderedImpl value, + $Res Function(_$TextureEvent_OnFirstFrameRenderedImpl) then) = + __$$TextureEvent_OnFirstFrameRenderedImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int textureId}); +} + +/// @nodoc +class __$$TextureEvent_OnFirstFrameRenderedImplCopyWithImpl<$Res> + extends _$TextureEventCopyWithImpl<$Res, + _$TextureEvent_OnFirstFrameRenderedImpl> + implements _$$TextureEvent_OnFirstFrameRenderedImplCopyWith<$Res> { + __$$TextureEvent_OnFirstFrameRenderedImplCopyWithImpl( + _$TextureEvent_OnFirstFrameRenderedImpl _value, + $Res Function(_$TextureEvent_OnFirstFrameRenderedImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? textureId = null, + }) { + return _then(_$TextureEvent_OnFirstFrameRenderedImpl( + textureId: null == textureId + ? _value.textureId + : textureId // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$TextureEvent_OnFirstFrameRenderedImpl + implements TextureEvent_OnFirstFrameRendered { + const _$TextureEvent_OnFirstFrameRenderedImpl({required this.textureId}); + + /// Id of the texture. + @override + final int textureId; + + @override + String toString() { + return 'TextureEvent.onFirstFrameRendered(textureId: $textureId)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TextureEvent_OnFirstFrameRenderedImpl && + (identical(other.textureId, textureId) || + other.textureId == textureId)); + } + + @override + int get hashCode => Object.hash(runtimeType, textureId); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TextureEvent_OnFirstFrameRenderedImplCopyWith< + _$TextureEvent_OnFirstFrameRenderedImpl> + get copyWith => __$$TextureEvent_OnFirstFrameRenderedImplCopyWithImpl< + _$TextureEvent_OnFirstFrameRenderedImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + int textureId, int width, int height, int rotation) + onTextureChange, + required TResult Function(int textureId) onFirstFrameRendered, + }) { + return onFirstFrameRendered(textureId); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult? Function(int textureId)? onFirstFrameRendered, + }) { + return onFirstFrameRendered?.call(textureId); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(int textureId, int width, int height, int rotation)? + onTextureChange, + TResult Function(int textureId)? onFirstFrameRendered, + required TResult orElse(), + }) { + if (onFirstFrameRendered != null) { + return onFirstFrameRendered(textureId); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(TextureEvent_OnTextureChange value) + onTextureChange, + required TResult Function(TextureEvent_OnFirstFrameRendered value) + onFirstFrameRendered, + }) { + return onFirstFrameRendered(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult? Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + }) { + return onFirstFrameRendered?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(TextureEvent_OnTextureChange value)? onTextureChange, + TResult Function(TextureEvent_OnFirstFrameRendered value)? + onFirstFrameRendered, + required TResult orElse(), + }) { + if (onFirstFrameRendered != null) { + return onFirstFrameRendered(this); + } + return orElse(); + } +} + +abstract class TextureEvent_OnFirstFrameRendered implements TextureEvent { + const factory TextureEvent_OnFirstFrameRendered( + {required final int textureId}) = _$TextureEvent_OnFirstFrameRenderedImpl; + + @override + + /// Id of the texture. + int get textureId; + @override + @JsonKey(ignore: true) + _$$TextureEvent_OnFirstFrameRenderedImplCopyWith< + _$TextureEvent_OnFirstFrameRenderedImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/src/api/peer.dart b/lib/src/api/peer.dart index 045dcfe6b7..c99e8bfec6 100644 --- a/lib/src/api/peer.dart +++ b/lib/src/api/peer.dart @@ -551,7 +551,7 @@ class _PeerConnectionFFI extends PeerConnection { scaleResolutionDownBy: encoding.scaleResolutionDownBy, scalabilityMode: encoding.scalabilityMode); await api! - .addTransceiverInitSendEncoding(init: ffiInit, encoding: ffiEncoding); + .addTransceiverInitSendEncoding(init: ffiInit, enc: ffiEncoding); } var transceiver = RtpTransceiver.fromFFI(await api!.addTransceiver( diff --git a/lib/src/platform/native/video_renderer.dart b/lib/src/platform/native/video_renderer.dart index f84f854e89..7c44ba1371 100644 --- a/lib/src/platform/native/video_renderer.dart +++ b/lib/src/platform/native/video_renderer.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; +import '../../api/bridge.g.dart' as ffi; import '../../api/peer.dart'; import '/src/api/channel.dart'; import '/src/model/track.dart'; @@ -25,38 +26,18 @@ abstract class NativeVideoRenderer extends VideoRenderer { /// Unique ID for the texture on which video will be rendered. int? _textureId; - /// Unique ID of the channel for the native side `VideoRenderer`. - late int _channelId; - /// Currently rendering [MediaStreamTrack]. MediaStreamTrack? _srcObject; - /// Subscription to the events of this [NativeVideoRenderer]. - StreamSubscription? _eventChan; - /// [MethodChannel] for the [NativeVideoRenderer] used for the messaging with /// the native side. late MethodChannel _chan; @override - int get videoWidth { - if (isDesktop) { - return value.width.toInt(); - } - return value.rotation % 180 == 0 - ? value.width.toInt() - : value.height.toInt(); - } + int get videoWidth; @override - int get videoHeight { - if (isDesktop) { - return value.height.toInt(); - } - return value.rotation % 180 == 0 - ? value.height.toInt() - : value.width.toInt(); - } + int get videoHeight; @override int? get textureId => _textureId; @@ -69,37 +50,6 @@ abstract class NativeVideoRenderer extends VideoRenderer { // No-op. Mirroring is done through [VideoView]. } - /// Listener for the [NativeVideoRenderer] events received from the native - /// side. - void eventListener(dynamic event) { - final dynamic map = event; - switch (map['event']) { - case 'onTextureChange': - var rotation = map['rotation']; - var width = 0.0 + map['width']; - var height = 0.0 + map['height']; - - var newWidth = rotation % 180 == 0 ? width : height; - var newHeight = rotation % 180 == 0 ? height : width; - - width = newWidth; - height = newHeight; - - value = value.copyWith( - rotation: rotation, - width: width, - height: height, - renderVideo: renderVideo, - ); - - onResize?.call(); - break; - case 'onFirstFrameRendered': - value = value.copyWith(renderVideo: renderVideo); - break; - } - } - /// Listener for the errors of the native event channel. void errorListener(Object obj) { if (obj is Exception) { @@ -113,6 +63,26 @@ abstract class NativeVideoRenderer extends VideoRenderer { /// [MethodChannel]-based implementation of a [NativeVideoRenderer]. class _NativeVideoRendererChannel extends NativeVideoRenderer { + /// Unique ID of the channel for this [NativeVideoRenderer]. + late int _channelId; + + /// Subscription to the events of this [NativeVideoRenderer]. + StreamSubscription? _eventChan; + + @override + int get videoWidth { + return value.rotation % 180 == 0 + ? value.width.toInt() + : value.height.toInt(); + } + + @override + int get videoHeight { + return value.rotation % 180 == 0 + ? value.height.toInt() + : value.width.toInt(); + } + @override Future initialize() async { final response = await _rendererFactoryChannel.invokeMethod('create'); @@ -149,18 +119,58 @@ class _NativeVideoRendererChannel extends NativeVideoRenderer { await _chan.invokeMethod('dispose'); await super.dispose(); } + + /// Listener for this [NativeVideoRenderer]'s events received from the native + /// side. + void eventListener(dynamic event) { + final dynamic map = event; + switch (map['event']) { + case 'onTextureChange': + var rotation = map['rotation']; + var width = 0.0 + map['width']; + var height = 0.0 + map['height']; + + var newWidth = rotation % 180 == 0 ? width : height; + var newHeight = rotation % 180 == 0 ? height : width; + + width = newWidth; + height = newHeight; + + value = value.copyWith( + rotation: rotation, + width: width, + height: height, + renderVideo: renderVideo, + ); + + onResize?.call(); + break; + case 'onFirstFrameRendered': + value = value.copyWith(renderVideo: renderVideo); + break; + } + } } /// FFI-based implementation of a [NativeVideoRenderer]. class _NativeVideoRendererFFI extends NativeVideoRenderer { + /// Subscription to the events of this [NativeVideoRenderer]. + Stream? _eventStream; + + @override + int get videoWidth { + return value.width.toInt(); + } + + @override + int get videoHeight { + return value.height.toInt(); + } + @override Future initialize() async { final response = await _rendererFactoryChannel.invokeMethod('create'); _textureId = response['textureId']; - _channelId = response['channelId']; - _eventChan = eventChannel('VideoRendererEvent', _channelId) - .receiveBroadcastStream() - .listen(eventListener, onError: errorListener); _chan = methodChannel('VideoRendererFactory', 0); } @@ -174,9 +184,8 @@ class _NativeVideoRendererFFI extends NativeVideoRenderer { } _srcObject = track; - var sinkId = textureId ?? 0; if (track == null) { - api!.disposeVideoSink(sinkId: sinkId); + api!.disposeVideoSink(sinkId: textureId!); value = RTCVideoValue.empty; } else { var handler = @@ -185,20 +194,49 @@ class _NativeVideoRendererFFI extends NativeVideoRenderer { }); var trackId = track.id(); - await api! - .createVideoSink( - sinkId: sinkId, - trackId: trackId, - callbackPtr: handler['handler_ptr']) - .then((_) => {value = value.copyWith(renderVideo: renderVideo)}); + _eventStream = api!.createVideoSink( + sinkId: textureId!, + trackId: trackId, + callbackPtr: handler['handler_ptr'], + textureId: textureId!, + ); + + _eventStream!.listen(eventListener); + value = value.copyWith(renderVideo: renderVideo); } } @override Future dispose() async { - await _eventChan?.cancel(); await setSrcObject(null); await _chan.invokeMethod('dispose', {'textureId': textureId}); await super.dispose(); } + + /// Listener for this [NativeVideoRenderer]'s events received from the native + /// side. + void eventListener(ffi.TextureEvent event) { + if (event is ffi.TextureEvent_OnTextureChange) { + var rotation = event.rotation; + var width = 0.0 + event.width; + var height = 0.0 + event.height; + + var newWidth = rotation % 180 == 0 ? width : height; + var newHeight = rotation % 180 == 0 ? height : width; + + width = newWidth; + height = newHeight; + + value = value.copyWith( + rotation: rotation, + width: width, + height: height, + renderVideo: renderVideo, + ); + + onResize?.call(); + } else if (event is ffi.TextureEvent_OnFirstFrameRendered) { + value = value.copyWith(renderVideo: renderVideo); + } + } } diff --git a/linux/medea_flutter_webrtc_plugin.cc b/linux/medea_flutter_webrtc_plugin.cc index 52fd32b36f..bd103b904e 100644 --- a/linux/medea_flutter_webrtc_plugin.cc +++ b/linux/medea_flutter_webrtc_plugin.cc @@ -32,78 +32,16 @@ class TextureVideoRenderer { reinterpret_cast(FL_TEXTURE(texture_)); texture_id_ = texture_->texture_id; - - g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); - - g_autoptr(FlEventChannel) channel = fl_event_channel_new( - messenger, - ("FlutterWebRtc/VideoRendererEvent/" + std::to_string(texture_id_)) - .c_str(), - FL_METHOD_CODEC(codec)); - event_channel_ = channel; - - fl_event_channel_set_stream_handlers( - channel, - [](FlEventChannel* channel, FlValue* args, gpointer user_data) { - bool* send_events = (bool*)user_data; - *send_events = true; - FlMethodErrorResponse* res = nullptr; - return res; - }, - [](FlEventChannel* channel, FlValue* args, gpointer user_data) { - bool* send_events = (bool*)user_data; - *send_events = false; - FlMethodErrorResponse* res = nullptr; - return res; - }, - &send_events_, NULL); } void ResetRenderer() { const std::lock_guard lock(texture_->mutex); texture_->frame_.reset(); - first_frame_rendered = false; } // Called when a new `VideoFrame` is produced by the underlying source. void OnFrame(VideoFrame frame) { - if (!first_frame_rendered) { - if (send_events_) { - g_autoptr(FlValue) map = fl_value_new_map(); - - fl_value_set_string_take(map, "event", - fl_value_new_string("onFirstFrameRendered")); - fl_value_set_string_take(map, "id", fl_value_new_int(texture_id_)); - - fl_event_channel_send(event_channel_, map, nullptr, nullptr); - } - first_frame_rendered = true; - } - if (!texture_->frame_ || - height_ != frame.height || - width_ != frame.width || - rotation_ != frame.rotation) { - if (send_events_) { - g_autoptr(FlValue) map = fl_value_new_map(); - - fl_value_set_string_take( - map, "event", fl_value_new_string("onTextureChange")); - fl_value_set_string_take(map, "id", fl_value_new_int(texture_id_)); - fl_value_set_string_take(map, "rotation", - fl_value_new_int((int32_t)frame.rotation)); - fl_value_set_string_take(map, "width", - fl_value_new_int((int32_t)frame.width)); - fl_value_set_string_take(map, "height", - fl_value_new_int((int32_t)frame.height)); - - fl_event_channel_send(event_channel_, map, nullptr, nullptr); - } - width_ = frame.width; - height_ = frame.height; - rotation_ = frame.rotation; - } - texture_->mutex.lock(); texture_->frame_.emplace(std::move(frame)); texture_->mutex.unlock(); @@ -121,33 +59,14 @@ class TextureVideoRenderer { VideoTexture* texture() { return texture_; } private: - // Named channel for communicating with the Flutter application using - // asynchronous event streams. - FlEventChannel* event_channel_; - // Pointer to the `VideoTexture` that is passed to the Flutter texture. VideoTexture* texture_ = 0; - // Flag indicating Flutter events subscription. - bool send_events_ = false; - - // Indicator whether at least one `VideoFrame` has been rendered. - bool first_frame_rendered = false; - // Object keeping track of external textures. FlTextureRegistrar* registrar_ = 0; // ID of the Flutter texture. int64_t texture_id_ = -1; - - // Rotation of the current `VideoFrame`. - int32_t rotation_ = 0; - - // Height of the current `VideoFrame`. - size_t height_ = 0; - - // Width of the current `VideoFrame`. - size_t width_ = 0; }; class FrameHandler : public OnFrameCallbackInterface { @@ -182,8 +101,6 @@ class FlutterVideoRendererManager { g_autoptr(FlValue) map = fl_value_new_map(); fl_value_set_string_take(map, "textureId", fl_value_new_int((int64_t)texture_id)); - fl_value_set_string_take(map, "channelId", - fl_value_new_int((int64_t)texture_id)); (*response) = FL_METHOD_RESPONSE(fl_method_success_response_new(map)); } diff --git a/macos/Classes/VideoRenderer.h b/macos/Classes/VideoRenderer.h index 9ff63f65dc..f44cdbb978 100644 --- a/macos/Classes/VideoRenderer.h +++ b/macos/Classes/VideoRenderer.h @@ -24,21 +24,12 @@ typedef struct Frame { // `FlutterEventChannel` of this `TextureVideoRenderer`. @property(nonatomic, strong, nullable) FlutterEventChannel* eventChannel; -// Flag indicating the first frame was rendered. -@property(nonatomic) bool firstFrameRendered; - // `FlutterTextureRegistry` of this `TextureVideoRenderer`. @property(nonatomic, weak) id registry; // ID of this `TextureVideoRenderer`. @property(nonatomic, strong, nullable) NSNumber* textureId; -// Rotation of the last rendered `Frame` by this `TextureVideoRenderer`. -@property(nonatomic, strong, nullable) NSNumber* rotation; - -// `FlutterEventSink` of this `TextureVideoRenderer`. -@property(nonatomic, strong, nullable) FlutterEventSink eventSink; - // ID of the `FlutterTexture` registered in the `FlutterTextureRegistry`. @property(nonatomic) int64_t tid; @@ -49,11 +40,6 @@ typedef struct Frame { // Buffer size of the last rendered `Frame` by this `TextureVideoRenderer`. @property(nonatomic) size_t bufferSize; -// Width of the last rendered `Frame` by this `TextureVideoRenderer`. -@property(nonatomic) size_t frameWidth; - -// Height of the last rendered `Frame` by this `TextureVideoRenderer`. -@property(nonatomic) size_t frameHeight; - (instancetype)init:(id)registry messenger:(id)messenger; diff --git a/macos/Classes/VideoRenderer.m b/macos/Classes/VideoRenderer.m index bad7b50113..aec87aefb8 100644 --- a/macos/Classes/VideoRenderer.m +++ b/macos/Classes/VideoRenderer.m @@ -22,18 +22,11 @@ - (instancetype)init:(id)registry self->_pixelBufferRef = nil; self->_registry = registry; self->_bufferSize = 0; - self->_frameWidth = 0; - self->_frameHeight = 0; int64_t tid = [registry registerTexture:self]; self->_tid = tid; NSNumber* textureId = [NSNumber numberWithLong:tid]; - NSString* channelName = [NSString - stringWithFormat:@"FlutterWebRtc/VideoRendererEvent/%@", textureId]; - _eventChannel = [FlutterEventChannel eventChannelWithName:channelName - binaryMessenger:messenger]; self->_textureId = textureId; - [_eventChannel setStreamHandler:self]; __weak TextureVideoRenderer* weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ @@ -47,7 +40,6 @@ - (instancetype)init:(id)registry // Resets this `TextureVideoRenderer`. - (void)resetRenderer { - self->_firstFrameRendered = false; } // Releases `PixelBuffer` of this `TextureVideoRenderer`. @@ -81,35 +73,6 @@ - (void)onFrame:(Frame)frame { drop_frame(frame.frame); CVPixelBufferUnlockBaseAddress(_pixelBufferRef, 0); - if (!_firstFrameRendered) { - if (_eventSink != nil) { - NSDictionary* map = @{ - @"event" : @"onFirstFrameRendered", - @"id" : self->_textureId, - }; - _eventSink(map); - } - _firstFrameRendered = true; - } - NSNumber* frameRotation = [NSNumber numberWithInt:frame.rotation]; - bool isFrameWidthChanged = _frameWidth != frame.width; - bool isFrameHeightChanged = _frameHeight != frame.height; - if (isFrameWidthChanged || isFrameHeightChanged || _rotation != frameRotation) { - _frameWidth = frame.width; - _frameHeight = frame.height; - if (_eventSink != nil) { - NSDictionary* map = @{ - @"event" : @"onTextureChange", - @"id" : _textureId, - @"width" : [NSNumber numberWithLong:frame.width], - @"height" : [NSNumber numberWithLong:frame.height], - @"rotation" : frameRotation, - }; - _eventSink(map); - } - _rotation = frameRotation; - } - __weak TextureVideoRenderer* weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ __strong TextureVideoRenderer* strongSelf = weakSelf; @@ -126,7 +89,6 @@ - (NSNumber*)textureId { // Frees `EventSink` of this `TextureVideoRenderer`. - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { - _eventSink = nil; return nil; } @@ -134,7 +96,6 @@ - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink: (nonnull FlutterEventSink)sink { - _eventSink = sink; return nil; } diff --git a/windows/include/video_renderer.h b/windows/include/video_renderer.h index 816affa669..ef9f5855b1 100644 --- a/windows/include/video_renderer.h +++ b/windows/include/video_renderer.h @@ -57,29 +57,12 @@ class TextureVideoRenderer { int64_t texture_id() { return texture_id_; } private: - // Struct which describes `VideoFrame`'s dimensions. - struct FrameSize { - size_t width; - size_t height; - }; - - // `FrameSize` of the last processed `VideoFrame`. - FrameSize last_frame_size_ = {0, 0}; - // Indicates if at least one `VideoFrame` has been rendered. bool first_frame_rendered = false; // Object keeping track of external textures. TextureRegistrar* registrar_; - // Named channel for communicating with the Flutter application using - // asynchronous event streams. - std::unique_ptr> event_channel_; - - // Event callback. Events to be sent to the Flutter application act as clients - // of this interface for sending events. - std::unique_ptr> event_sink_; - // ID of the Flutter texture. int64_t texture_id_ = -1; @@ -99,9 +82,6 @@ class TextureVideoRenderer { // Protects the `frame_`, `pixel_buffer_` and `argb_buffer_` fields that are // accessed from multiple threads. std::mutex mutex_; - - // Rotation of the current `VideoFrame`. - int32_t rotation_ = 0; }; // Manager storing and managing all the `TextureVideoRenderer`s. diff --git a/windows/src/video_renderer.cc b/windows/src/video_renderer.cc index bff5afac28..e33e67210b 100644 --- a/windows/src/video_renderer.cc +++ b/windows/src/video_renderer.cc @@ -21,7 +21,6 @@ void FlutterVideoRendererManager::CreateVideoRendererTexture( renderers_[texture_id] = std::move(texture); EncodableMap params; params[EncodableValue("textureId")] = EncodableValue(texture_id); - params[EncodableValue("channelId")] = EncodableValue(texture_id); result->Success(EncodableValue(params)); } @@ -79,26 +78,6 @@ TextureVideoRenderer::TextureVideoRenderer(TextureRegistrar* registrar, })); texture_id_ = registrar_->RegisterTexture(texture_.get()); - - std::string event_channel = - "FlutterWebRtc/VideoRendererEvent/" + std::to_string(texture_id_); - event_channel_.reset(new EventChannel( - messenger, event_channel, &StandardMethodCodec::GetInstance())); - - auto handler = std::make_unique>( - [&](const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - -> std::unique_ptr> { - event_sink_ = std::move(events); - return nullptr; - }, - [&](const flutter::EncodableValue* arguments) - -> std::unique_ptr> { - event_sink_ = nullptr; - return nullptr; - }); - - event_channel_->SetStreamHandler(std::move(handler)); } // Constructs and returns `FlutterDesktopPixelBuffer` from the current @@ -131,33 +110,11 @@ FlutterDesktopPixelBuffer* TextureVideoRenderer::CopyPixelBuffer(size_t width, // about a new frame being ready for polling. void TextureVideoRenderer::OnFrame(VideoFrame frame) { if (!first_frame_rendered) { - if (event_sink_) { - EncodableMap params; - params[EncodableValue("event")] = "onFirstFrameRendered"; - params[EncodableValue("id")] = EncodableValue(texture_id_); - event_sink_->Success(EncodableValue(params)); - } pixel_buffer_.reset(new FlutterDesktopPixelBuffer()); pixel_buffer_->width = 0; pixel_buffer_->height = 0; first_frame_rendered = true; } - if (last_frame_size_.width != frame.width || - last_frame_size_.height != frame.height || rotation_ != frame.rotation) { - if (event_sink_) { - EncodableMap params; - params[EncodableValue("event")] = "onTextureChange"; - params[EncodableValue("id")] = EncodableValue(texture_id_); - params[EncodableValue("width")] = EncodableValue((int32_t) frame.width); - params[EncodableValue("height")] = - EncodableValue((int32_t) frame.height); - params[EncodableValue("rotation")] = - EncodableValue((int32_t) frame.rotation); - event_sink_->Success(EncodableValue(params)); - } - rotation_ = frame.rotation; - last_frame_size_ = {frame.width, frame.height}; - } mutex_.lock(); frame_.emplace(std::move(frame)); mutex_.unlock(); @@ -170,7 +127,6 @@ void TextureVideoRenderer::ResetRenderer() { frame_.reset(); mutex_.unlock(); frame_ = std::nullopt; - last_frame_size_ = {0, 0}; first_frame_rendered = false; }