From b7cc070fae1efa72ae0cc6102e118c669efeec5b Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Fri, 13 Oct 2023 22:01:54 -0300 Subject: [PATCH 1/7] feat: add audio stream and fix only current parcel id can plays audio --- .../audio_streaming.gd | 6 + godot/src/decentraland_components/avatar.gd | 6 +- .../src/av/video_stream.rs | 44 ++++---- .../scene_runner/components/audio_stream.rs | 104 ++++++++++++++++++ .../src/scene_runner/components/material.rs | 2 +- .../src/scene_runner/components/mod.rs | 1 + .../scene_runner/components/video_player.rs | 33 +++++- .../src/scene_runner/deleted_entities.rs | 2 + .../src/scene_runner/godot_dcl_scene.rs | 2 + .../src/scene_runner/scene.rs | 14 ++- .../src/scene_runner/scene_manager.rs | 12 ++ .../src/scene_runner/update_scene.rs | 21 ++-- 12 files changed, 211 insertions(+), 36 deletions(-) create mode 100644 rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs diff --git a/godot/src/decentraland_components/audio_streaming.gd b/godot/src/decentraland_components/audio_streaming.gd index cdc5cc74..72621e66 100644 --- a/godot/src/decentraland_components/audio_streaming.gd +++ b/godot/src/decentraland_components/audio_streaming.gd @@ -7,6 +7,12 @@ func stream_buffer(data: PackedVector2Array): self.get_stream_playback().push_buffer(data) +func set_mute(value: bool): + if value: + self.volume_db = 0 + else: + self.volume_db = -80 + func init(frame_rate, frames, length, format, bit_rate, frame_size, channels): print( diff --git a/godot/src/decentraland_components/avatar.gd b/godot/src/decentraland_components/avatar.gd index 4d1eb06e..23f5fc82 100644 --- a/godot/src/decentraland_components/avatar.gd +++ b/godot/src/decentraland_components/avatar.gd @@ -172,9 +172,13 @@ func try_to_set_body_shape(body_shape_hash): var skeleton = body_shape.find_child("Skeleton3D") if skeleton == null: return + + + var animation_player_parent = animation_player.get_parent() + if animation_player_parent != null: + animation_player_parent.remove_child(animation_player) if body_shape_root != null: - body_shape_root.remove_child(animation_player) remove_child(body_shape_root) _free_old_skeleton.call_deferred(body_shape_root) diff --git a/rust/decentraland-godot-lib/src/av/video_stream.rs b/rust/decentraland-godot-lib/src/av/video_stream.rs index b5d5ab1f..0788ae6d 100644 --- a/rust/decentraland-godot-lib/src/av/video_stream.rs +++ b/rust/decentraland-godot-lib/src/av/video_stream.rs @@ -21,7 +21,7 @@ pub enum AudioDecoderError { pub struct VideoSink { pub source: String, pub command_sender: tokio::sync::mpsc::Sender, - pub tex: Gd, + pub texture: Gd, pub size: (u32, u32), pub current_time: f64, pub length: Option, @@ -30,20 +30,18 @@ pub struct VideoSink { pub fn av_sinks( source: String, - tex: Gd, + texture: Option>, audio_stream_player: Gd, volume: f32, playing: bool, repeat: bool, -) -> (VideoSink, AudioSink) { +) -> (Option, AudioSink) { let (command_sender, command_receiver) = tokio::sync::mpsc::channel(10); spawn_av_thread( command_receiver, - // video_sender, - // audio_sender, source.clone(), - tex.clone(), + texture.clone(), audio_stream_player, ); @@ -55,15 +53,15 @@ pub fn av_sinks( .unwrap(); ( - VideoSink { + texture.map(|texture| VideoSink { source, command_sender: command_sender.clone(), size: (0, 0), - tex, + texture, current_time: 0.0, length: None, rate: None, - }, + }), AudioSink { volume, command_sender, @@ -74,10 +72,10 @@ pub fn av_sinks( pub fn spawn_av_thread( commands: tokio::sync::mpsc::Receiver, path: String, - tex: Gd, + tex: Option>, audio_stream_player: Gd, ) { - let video_instance_id = tex.instance_id(); + let video_instance_id = tex.map(|value| value.instance_id()); let audio_stream_player_instance_id = audio_stream_player.instance_id(); std::thread::Builder::new() .name("av thread".to_string()) @@ -97,10 +95,10 @@ fn av_thread( // frames: tokio::sync::mpsc::Sender, // audio: tokio::sync::mpsc::Sender>, path: String, - tex: InstanceId, + tex: Option, audio_stream: InstanceId, ) { - let tex = Gd::from_instance_id(tex); + let tex = tex.map(|tex| Gd::from_instance_id(tex)); let audio_stream_player: Gd = Gd::from_instance_id(audio_stream); if let Err(error) = av_thread_inner(commands, path, tex, audio_stream_player) { warn!("av error: {error}"); @@ -112,21 +110,25 @@ fn av_thread( pub fn av_thread_inner( commands: tokio::sync::mpsc::Receiver, path: String, - tex: Gd, + texture: Option>, audio_stream_player: Gd, ) -> Result<(), String> { let input_context = input(&path).map_err(|e| format!("{:?} on line {}", e, line!()))?; // try and get a video context let video_context: Option = { - match VideoContext::init(&input_context, tex) { - Ok(vc) => Some(vc), - Err(VideoError::BadPixelFormat) => { - return Err("bad pixel format".to_string()); + if let Some(texture) = texture { + match VideoContext::init(&input_context, texture) { + Ok(vc) => Some(vc), + Err(VideoError::BadPixelFormat) => { + return Err("bad pixel format".to_string()); + } + Err(VideoError::NoStream) => None, + Err(VideoError::Failed(ffmpeg_err)) => return Err(ffmpeg_err.to_string()), + Err(VideoError::ChannelClosed) => return Ok(()), } - Err(VideoError::NoStream) => None, - Err(VideoError::Failed(ffmpeg_err)) => return Err(ffmpeg_err.to_string()), - Err(VideoError::ChannelClosed) => return Ok(()), + } else { + None } }; diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs new file mode 100644 index 00000000..c0db8842 --- /dev/null +++ b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs @@ -0,0 +1,104 @@ +use crate::{ + av::{stream_processor::AVCommand, video_stream::av_sinks}, + dcl::{ + components::SceneComponentId, + crdt::{ + last_write_wins::LastWriteWinsComponentOperation, SceneCrdtState, + SceneCrdtStateProtoComponents, + }, + SceneId, + }, + scene_runner::scene::{Scene, SceneType}, +}; +use godot::{engine::AudioStreamGenerator, prelude::*}; + +pub fn update_audio_stream( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + current_parcel_scene_id: &SceneId, +) { + let godot_dcl_scene = &mut scene.godot_dcl_scene; + let dirty_lww_components = &scene.current_dirty.lww_components; + let audio_stream_component = SceneCrdtStateProtoComponents::get_audio_stream(crdt_state); + + if let Some(audio_stream_dirty) = dirty_lww_components.get(&SceneComponentId::AUDIO_STREAM) { + for entity in audio_stream_dirty { + let new_value = audio_stream_component.get(*entity); + if new_value.is_none() { + continue; + } + + let new_value = new_value.unwrap(); + let node = godot_dcl_scene.ensure_node_mut(entity); + + let new_value = new_value.value.clone(); + // let existing = node + // .base + // .try_get_node_as::(NodePath::from("MeshCollider")); + + if new_value.is_none() { + if let Some(audio_stream_data) = node.audio_stream.as_ref() { + let _ = audio_stream_data + .command_sender + .blocking_send(AVCommand::Dispose); + } + } else if let Some(new_value) = new_value { + if let Some(audio_stream_data) = node.audio_stream.as_ref() { + new_value.volume.unwrap_or(1.0); + + new_value.playing.unwrap_or(true); + + if new_value.playing.unwrap_or(true) { + let _ = audio_stream_data + .command_sender + .blocking_send(AVCommand::Play); + } else { + let _ = audio_stream_data + .command_sender + .blocking_send(AVCommand::Pause); + } + // let _ = audio_stream_data + // .command_sender + // .blocking_send(AVCommand::Repeat(new_value..unwrap_or(false))); + } else { + let mut audio_stream_player = godot::engine::load::( + "res://src/decentraland_components/audio_streaming.tscn", + ) + .instantiate() + .unwrap() + .cast::(); + let audio_stream_generator = AudioStreamGenerator::new(); + + audio_stream_player.set_stream(audio_stream_generator.upcast()); + node.base.add_child(audio_stream_player.clone().upcast()); + audio_stream_player.play(); + + let start_muted = if let SceneType::Parcel = scene.scene_type { + &scene.scene_id == current_parcel_scene_id + } else { + true + }; + + if start_muted { + audio_stream_player.set_volume_db(0.0); + } + + let (_, audio_sink) = av_sinks( + new_value.url.clone(), + None, + audio_stream_player.clone(), + new_value.volume.unwrap_or(1.0), + new_value.playing.unwrap_or(true), + true, + ); + + node.audio_stream = Some(audio_sink); + + scene + .audio_video_players + .insert(*entity, audio_stream_player.clone()); + } + } + } + } +} diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/material.rs b/rust/decentraland-godot-lib/src/scene_runner/components/material.rs index 6bdd8c72..0c37330b 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/material.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/material.rs @@ -283,7 +283,7 @@ fn check_texture( DclSourceTex::VideoTexture(video_entity_id) => { if let Some(node) = scene.godot_dcl_scene.get_node(video_entity_id) { if let Some(data) = &node.video_player_data { - material.set_texture(param, data.video_sink.tex.clone().upcast()); + material.set_texture(param, data.video_sink.texture.clone().upcast()); return true; } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/mod.rs b/rust/decentraland-godot-lib/src/scene_runner/components/mod.rs index 3f085612..36770f6e 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/mod.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/mod.rs @@ -1,5 +1,6 @@ pub mod animator; pub mod audio_source; +pub mod audio_stream; pub mod avatar_attach; pub mod avatar_shape; pub mod billboard; diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index 5b8c0202..c16cb7e9 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -6,15 +6,23 @@ use crate::{ last_write_wins::LastWriteWinsComponentOperation, SceneCrdtState, SceneCrdtStateProtoComponents, }, + SceneId, + }, + scene_runner::{ + godot_dcl_scene::VideoPlayerData, + scene::{Scene, SceneType}, }, - scene_runner::{godot_dcl_scene::VideoPlayerData, scene::Scene}, }; use godot::{ engine::{image::Format, AudioStreamGenerator, Image, ImageTexture}, prelude::*, }; -pub fn update_video_player(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { +pub fn update_video_player( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + current_parcel_scene_id: &SceneId, +) { let godot_dcl_scene = &mut scene.godot_dcl_scene; let dirty_lww_components = &scene.current_dirty.lww_components; let video_player_component = SceneCrdtStateProtoComponents::get_video_player(crdt_state); @@ -80,18 +88,37 @@ pub fn update_video_player(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { node.base.add_child(audio_stream_player.clone().upcast()); audio_stream_player.play(); + let start_muted = if let SceneType::Parcel = scene.scene_type { + &scene.scene_id == current_parcel_scene_id + } else { + true + }; + + if start_muted { + audio_stream_player.set_volume_db(0.0); + } + let (video_sink, audio_sink) = av_sinks( new_value.src.clone(), - texture, + Some(texture), audio_stream_player.clone(), new_value.volume.unwrap_or(1.0), new_value.playing.unwrap_or(true), new_value.r#loop.unwrap_or(false), ); + + let Some(video_sink) = video_sink else { + tracing::error!("couldn't create an video sink"); + continue; + }; + node.video_player_data = Some(VideoPlayerData { video_sink, audio_sink, }); + scene + .audio_video_players + .insert(*entity, audio_stream_player.clone()); } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs b/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs index 661f2991..fbfbd1dc 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs @@ -27,5 +27,7 @@ pub fn update_deleted_entities(scene: &mut Scene) { node.base.clone().free(); godot_dcl_scene.entities.remove(deleted_entity); scene.audio_sources.remove(deleted_entity); + scene.audio_streams.remove(deleted_entity); + scene.audio_video_players.remove(deleted_entity); } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs b/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs index 6497d905..8233a85a 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs @@ -32,6 +32,7 @@ pub struct Node3DEntity { pub material: Option, pub pointer_events: Option, pub video_player_data: Option, + pub audio_stream: Option, } impl SceneDefinition { @@ -95,6 +96,7 @@ impl Node3DEntity { material: None, pointer_events: None, video_player_data: None, + audio_stream: None, } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene.rs b/rust/decentraland-godot-lib/src/scene_runner/scene.rs index 6d26cb0f..ce7d9a34 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene.rs @@ -3,7 +3,7 @@ use std::{ time::Instant, }; -use godot::prelude::{Dictionary, Gd}; +use godot::prelude::{AudioStreamPlayer, Dictionary, Gd}; use crate::{ dcl::{ @@ -69,6 +69,7 @@ pub enum SceneUpdateState { Raycasts, AvatarAttach, VideoPlayer, + AudioStream, CameraModeArea, AudioSource, ComputeCrdtState, @@ -94,7 +95,8 @@ impl SceneUpdateState { Self::Animator => Self::AvatarShape, Self::AvatarShape => Self::Raycasts, Self::Raycasts => Self::VideoPlayer, - Self::VideoPlayer => Self::CameraModeArea, + Self::VideoPlayer => Self::AudioStream, + Self::AudioStream => Self::CameraModeArea, Self::CameraModeArea => Self::AudioSource, Self::AudioSource => Self::AvatarAttach, Self::AvatarAttach => Self::ComputeCrdtState, @@ -138,6 +140,10 @@ pub struct Scene { pub scene_type: SceneType, pub audio_sources: HashMap>, + + // Used by VideoPlayer and AudioStream + pub audio_streams: HashMap>, + pub audio_video_players: HashMap>, } #[derive(Debug)] @@ -216,6 +222,8 @@ impl Scene { materials: HashMap::new(), dirty_materials: false, audio_sources: HashMap::new(), + audio_streams: HashMap::new(), + audio_video_players: HashMap::new(), scene_type, } } @@ -265,6 +273,8 @@ impl Scene { dirty_materials: false, scene_type: SceneType::Parcel, audio_sources: HashMap::new(), + audio_streams: HashMap::new(), + audio_video_players: HashMap::new(), } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs index 5410acd4..245ce793 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs @@ -243,6 +243,12 @@ impl SceneManager { audio_source_node.bind_mut().set_dcl_enable(false); audio_source_node.call("apply_audio_props".into(), &[false.to_variant()]); } + for (_, audio_stream_node) in scene.audio_streams.iter_mut() { + audio_stream_node.set_volume_db(-80.0); + } + for (_, audio_stream_node) in scene.audio_video_players.iter_mut() { + audio_stream_node.set_volume_db(-80.0); + } } if let Some(scene) = self.scenes.get_mut(&self.current_parcel_scene_id) { @@ -251,6 +257,12 @@ impl SceneManager { audio_source_node.bind_mut().set_dcl_enable(true); audio_source_node.call("apply_audio_props".into(), &[false.to_variant()]); } + for (_, audio_stream_node) in scene.audio_streams.iter_mut() { + audio_stream_node.set_volume_db(0.0); + } + for (_, audio_stream_node) in scene.audio_video_players.iter_mut() { + audio_stream_node.set_volume_db(0.0); + } } self.last_current_parcel_scene_id = self.current_parcel_scene_id; diff --git a/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs b/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs index 8576aef4..92ea5e79 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs @@ -5,13 +5,14 @@ use godot::prelude::{Callable, GodotString, ToVariant, Transform3D, VariantArray use super::{ components::{ animator::update_animator, audio_source::update_audio_source, - avatar_attach::update_avatar_attach, avatar_shape::update_avatar_shape, - billboard::update_billboard, camera_mode_area::update_camera_mode_area, - gltf_container::update_gltf_container, material::update_material, - mesh_collider::update_mesh_collider, mesh_renderer::update_mesh_renderer, - pointer_events::update_scene_pointer_events, raycast::update_raycasts, - text_shape::update_text_shape, transform_and_parent::update_transform_and_parent, - video_player::update_video_player, visibility::update_visibility, + audio_stream::update_audio_stream, avatar_attach::update_avatar_attach, + avatar_shape::update_avatar_shape, billboard::update_billboard, + camera_mode_area::update_camera_mode_area, gltf_container::update_gltf_container, + material::update_material, mesh_collider::update_mesh_collider, + mesh_renderer::update_mesh_renderer, pointer_events::update_scene_pointer_events, + raycast::update_raycasts, text_shape::update_text_shape, + transform_and_parent::update_transform_and_parent, video_player::update_video_player, + visibility::update_visibility, }, deleted_entities::update_deleted_entities, scene::{Dirty, Scene, SceneUpdateState}, @@ -143,7 +144,11 @@ pub fn _process_scene( false } SceneUpdateState::VideoPlayer => { - update_video_player(scene, crdt_state); + update_video_player(scene, crdt_state, current_parcel_scene_id); + false + } + SceneUpdateState::AudioStream => { + update_audio_stream(scene, crdt_state, current_parcel_scene_id); false } SceneUpdateState::CameraModeArea => { From 09e82d03561634f9891bf7a2a10f250b50f1f3e6 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Fri, 13 Oct 2023 22:16:45 -0300 Subject: [PATCH 2/7] fix format --- godot/src/decentraland_components/audio_streaming.gd | 1 + godot/src/decentraland_components/avatar.gd | 3 +-- rust/decentraland-godot-lib/src/av/video_stream.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/godot/src/decentraland_components/audio_streaming.gd b/godot/src/decentraland_components/audio_streaming.gd index 72621e66..63c7f824 100644 --- a/godot/src/decentraland_components/audio_streaming.gd +++ b/godot/src/decentraland_components/audio_streaming.gd @@ -7,6 +7,7 @@ func stream_buffer(data: PackedVector2Array): self.get_stream_playback().push_buffer(data) + func set_mute(value: bool): if value: self.volume_db = 0 diff --git a/godot/src/decentraland_components/avatar.gd b/godot/src/decentraland_components/avatar.gd index 23f5fc82..7afb2d5e 100644 --- a/godot/src/decentraland_components/avatar.gd +++ b/godot/src/decentraland_components/avatar.gd @@ -172,8 +172,7 @@ func try_to_set_body_shape(body_shape_hash): var skeleton = body_shape.find_child("Skeleton3D") if skeleton == null: return - - + var animation_player_parent = animation_player.get_parent() if animation_player_parent != null: animation_player_parent.remove_child(animation_player) diff --git a/rust/decentraland-godot-lib/src/av/video_stream.rs b/rust/decentraland-godot-lib/src/av/video_stream.rs index 0788ae6d..bd24f4b3 100644 --- a/rust/decentraland-godot-lib/src/av/video_stream.rs +++ b/rust/decentraland-godot-lib/src/av/video_stream.rs @@ -98,7 +98,7 @@ fn av_thread( tex: Option, audio_stream: InstanceId, ) { - let tex = tex.map(|tex| Gd::from_instance_id(tex)); + let tex = tex.map(Gd::from_instance_id); let audio_stream_player: Gd = Gd::from_instance_id(audio_stream); if let Err(error) = av_thread_inner(commands, path, tex, audio_stream_player) { warn!("av error: {error}"); From 9d17c29b514620c1d6c662064ece06b1ec50e5e9 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Tue, 17 Oct 2023 17:22:52 -0300 Subject: [PATCH 3/7] fix audio stream and video player, set the same logic --- .../{audio_streaming.gd => audio_stream.gd} | 7 +- ...audio_streaming.tscn => audio_stream.tscn} | 4 +- .../decentraland_components/video_player.gd | 33 +++ .../decentraland_components/video_player.tscn | 6 + godot/src/ui/explorer.tscn | 4 +- .../src/av/audio_context.rs | 3 +- .../src/av/video_stream.rs | 9 +- .../src/dcl/js/js_modules/Runtime.js | 12 +- .../src/godot_classes/dcl_audio_stream.rs | 19 ++ .../src/godot_classes/dcl_video_player.rs | 22 ++ .../src/godot_classes/mod.rs | 2 + .../scene_runner/components/audio_stream.rs | 189 +++++++++----- .../scene_runner/components/video_player.rs | 243 ++++++++++++------ .../src/scene_runner/deleted_entities.rs | 7 +- .../src/scene_runner/godot_dcl_scene.rs | 2 +- .../src/scene_runner/scene.rs | 13 +- .../src/scene_runner/scene_manager.rs | 10 +- 17 files changed, 416 insertions(+), 169 deletions(-) rename godot/src/decentraland_components/{audio_streaming.gd => audio_stream.gd} (72%) rename godot/src/decentraland_components/{audio_streaming.tscn => audio_stream.tscn} (62%) create mode 100644 godot/src/decentraland_components/video_player.gd create mode 100644 godot/src/decentraland_components/video_player.tscn create mode 100644 rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs create mode 100644 rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs diff --git a/godot/src/decentraland_components/audio_streaming.gd b/godot/src/decentraland_components/audio_stream.gd similarity index 72% rename from godot/src/decentraland_components/audio_streaming.gd rename to godot/src/decentraland_components/audio_stream.gd index 63c7f824..d8910b79 100644 --- a/godot/src/decentraland_components/audio_streaming.gd +++ b/godot/src/decentraland_components/audio_stream.gd @@ -1,5 +1,4 @@ -extends AudioStreamPlayer - +extends DclAudioStream func stream_buffer(data: PackedVector2Array): if not self.playing: @@ -15,9 +14,9 @@ func set_mute(value: bool): self.volume_db = -80 -func init(frame_rate, frames, length, format, bit_rate, frame_size, channels): +func init_audio(frame_rate, frames, length, format, bit_rate, frame_size, channels): print( - "audio_streaming debug init ", + "audio_stream debug init ", frame_rate, " - ", frames, diff --git a/godot/src/decentraland_components/audio_streaming.tscn b/godot/src/decentraland_components/audio_stream.tscn similarity index 62% rename from godot/src/decentraland_components/audio_streaming.tscn rename to godot/src/decentraland_components/audio_stream.tscn index aacb6b29..73cec5c1 100644 --- a/godot/src/decentraland_components/audio_streaming.tscn +++ b/godot/src/decentraland_components/audio_stream.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://de7563ke1agyk"] -[ext_resource type="Script" path="res://src/decentraland_components/audio_streaming.gd" id="1_oqq6w"] +[ext_resource type="Script" path="res://src/decentraland_components/audio_stream.gd" id="1_oqq6w"] -[node name="audio_streaming" type="AudioStreamPlayer"] +[node name="audio_stream" type="DclAudioStream"] script = ExtResource("1_oqq6w") diff --git a/godot/src/decentraland_components/video_player.gd b/godot/src/decentraland_components/video_player.gd new file mode 100644 index 00000000..e3d774d5 --- /dev/null +++ b/godot/src/decentraland_components/video_player.gd @@ -0,0 +1,33 @@ +extends DclVideoPlayer + +func stream_buffer(data: PackedVector2Array): + if not self.playing: + self.play() + + self.get_stream_playback().push_buffer(data) + + +func set_mute(value: bool): + if value: + self.volume_db = 0 + else: + self.volume_db = -80 + + +func init_audio(frame_rate, frames, length, format, bit_rate, frame_size, channels): + print( + "audio_stream debug init ", + frame_rate, + " - ", + frames, + " - ", + length, + " - ", + format, + " - ", + bit_rate, + " - ", + frame_size, + " - ", + channels + ) diff --git a/godot/src/decentraland_components/video_player.tscn b/godot/src/decentraland_components/video_player.tscn new file mode 100644 index 00000000..6d90ee9a --- /dev/null +++ b/godot/src/decentraland_components/video_player.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://de7563ke1agyk"] + +[ext_resource type="Script" path="res://src/decentraland_components/video_player.gd" id="1_oqq6w"] + +[node name="video_player" type="DclVideoPlayer"] +script = ExtResource("1_oqq6w") diff --git a/godot/src/ui/explorer.tscn b/godot/src/ui/explorer.tscn index 09a946eb..e8fa89fe 100644 --- a/godot/src/ui/explorer.tscn +++ b/godot/src/ui/explorer.tscn @@ -27,7 +27,7 @@ texture_filter = 0 [sub_resource type="Theme" id="Theme_1ufu0"] -[sub_resource type="ButtonGroup" id="ButtonGroup_gkn7s"] +[sub_resource type="ButtonGroup" id="ButtonGroup_b41u8"] resource_name = "Tabs" [node name="explorer" type="Node3D"] @@ -172,7 +172,7 @@ layout_mode = 2 [node name="Control_Menu" parent="UI" instance=ExtResource("5_mso44")] visible = false layout_mode = 1 -group = SubResource("ButtonGroup_gkn7s") +group = SubResource("ButtonGroup_b41u8") [node name="Timer_BroadcastPosition" type="Timer" parent="."] wait_time = 0.1 diff --git a/rust/decentraland-godot-lib/src/av/audio_context.rs b/rust/decentraland-godot-lib/src/av/audio_context.rs index 73bc9f2b..e720a934 100644 --- a/rust/decentraland-godot-lib/src/av/audio_context.rs +++ b/rust/decentraland-godot-lib/src/av/audio_context.rs @@ -10,7 +10,6 @@ use super::stream_processor::AVCommand; use super::stream_processor::FfmpegContext; pub struct AudioSink { - pub volume: f32, pub command_sender: tokio::sync::mpsc::Sender, } @@ -160,7 +159,7 @@ impl AudioContext { ); audio_stream_player.call_deferred( - "init".into(), + "init_audio".into(), &[ frame_rate.to_variant(), input_stream.frames().to_variant(), diff --git a/rust/decentraland-godot-lib/src/av/video_stream.rs b/rust/decentraland-godot-lib/src/av/video_stream.rs index bd24f4b3..e302d768 100644 --- a/rust/decentraland-godot-lib/src/av/video_stream.rs +++ b/rust/decentraland-godot-lib/src/av/video_stream.rs @@ -17,7 +17,6 @@ pub enum AudioDecoderError { StreamClosed, Other(String), } - pub struct VideoSink { pub source: String, pub command_sender: tokio::sync::mpsc::Sender, @@ -32,7 +31,6 @@ pub fn av_sinks( source: String, texture: Option>, audio_stream_player: Gd, - volume: f32, playing: bool, repeat: bool, ) -> (Option, AudioSink) { @@ -62,10 +60,7 @@ pub fn av_sinks( length: None, rate: None, }), - AudioSink { - volume, - command_sender, - }, + AudioSink { command_sender }, ) } @@ -92,8 +87,6 @@ pub fn spawn_av_thread( fn av_thread( commands: tokio::sync::mpsc::Receiver, - // frames: tokio::sync::mpsc::Sender, - // audio: tokio::sync::mpsc::Sender>, path: String, tex: Option, audio_stream: InstanceId, diff --git a/rust/decentraland-godot-lib/src/dcl/js/js_modules/Runtime.js b/rust/decentraland-godot-lib/src/dcl/js/js_modules/Runtime.js index 9e50a401..a6e48b8c 100644 --- a/rust/decentraland-godot-lib/src/dcl/js/js_modules/Runtime.js +++ b/rust/decentraland-godot-lib/src/dcl/js/js_modules/Runtime.js @@ -1,4 +1,14 @@ -module.exports.getRealm = async function (body) { return {} } +module.exports.getRealm = async function (body) { + return { + realmInfo: { + baseUrl: "https://localhost:8000", + realName: "LocalPreview", + networkId: 1, + commsAdapter: "offline", + isPreview: true, + } + } +} module.exports.getWorldTime = async function (body) { return {} } // sync implementation diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs new file mode 100644 index 00000000..b4b6c48e --- /dev/null +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs @@ -0,0 +1,19 @@ +use godot::engine::AudioStreamPlayer; +use godot::prelude::*; + +#[derive(GodotClass)] +#[class(init, base=AudioStreamPlayer)] +pub struct DclAudioStream { + // Used to mute and restore the volume + #[export] + dcl_volume: f32, + + #[export] + dcl_url: GodotString, + + #[base] + _base: Base, +} + +#[godot_api] +impl DclAudioStream {} diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs new file mode 100644 index 00000000..d6e788b7 --- /dev/null +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs @@ -0,0 +1,22 @@ +use godot::engine::{AudioStreamPlayer, ImageTexture}; +use godot::prelude::*; + +#[derive(GodotClass)] +#[class(init, base=AudioStreamPlayer)] +pub struct DclVideoPlayer { + // Used to mute and restore the volume + #[export] + dcl_volume: f32, + + #[export] + dcl_source: GodotString, + + #[export] + dcl_texture: Option>, + + #[base] + _base: Base, +} + +#[godot_api] +impl DclVideoPlayer {} diff --git a/rust/decentraland-godot-lib/src/godot_classes/mod.rs b/rust/decentraland-godot-lib/src/godot_classes/mod.rs index b803d6a7..648be67d 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/mod.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/mod.rs @@ -1,3 +1,5 @@ pub mod dcl_audio_source; +pub mod dcl_audio_stream; pub mod dcl_camera_3d; pub mod dcl_camera_mode_area_3d; +pub mod dcl_video_player; diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs index c0db8842..65a36dfa 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs @@ -8,9 +8,15 @@ use crate::{ }, SceneId, }, + godot_classes::dcl_audio_stream::DclAudioStream, scene_runner::scene::{Scene, SceneType}, }; use godot::{engine::AudioStreamGenerator, prelude::*}; +enum AudioUpdateMode { + OnlyChangeValues, + ChangeAudio, + FirstSpawnAudio, +} pub fn update_audio_stream( scene: &mut Scene, @@ -23,81 +29,142 @@ pub fn update_audio_stream( if let Some(audio_stream_dirty) = dirty_lww_components.get(&SceneComponentId::AUDIO_STREAM) { for entity in audio_stream_dirty { - let new_value = audio_stream_component.get(*entity); - if new_value.is_none() { - continue; - } + let exist_current_node = godot_dcl_scene.get_node(entity).is_some(); - let new_value = new_value.unwrap(); - let node = godot_dcl_scene.ensure_node_mut(entity); + let next_value = if let Some(new_value) = audio_stream_component.get(*entity) { + new_value.value.as_ref() + } else { + None + }; - let new_value = new_value.value.clone(); - // let existing = node - // .base - // .try_get_node_as::(NodePath::from("MeshCollider")); + if let Some(next_value) = next_value { + let start_muted = if let SceneType::Parcel = scene.scene_type { + scene.scene_id != *current_parcel_scene_id + } else { + true + }; - if new_value.is_none() { - if let Some(audio_stream_data) = node.audio_stream.as_ref() { - let _ = audio_stream_data - .command_sender - .blocking_send(AVCommand::Dispose); - } - } else if let Some(new_value) = new_value { - if let Some(audio_stream_data) = node.audio_stream.as_ref() { - new_value.volume.unwrap_or(1.0); + let dcl_volume = next_value.volume.unwrap_or(1.0).clamp(0.0, 1.0); + let volume_db = if start_muted { + -80.0 + } else { + 20.0 * f32::log10(dcl_volume) + }; - new_value.playing.unwrap_or(true); + let playing = next_value.playing.unwrap_or(true); - if new_value.playing.unwrap_or(true) { - let _ = audio_stream_data - .command_sender - .blocking_send(AVCommand::Play); + let node = godot_dcl_scene.ensure_node_mut(entity); + let update_mode = if let Some((url, _)) = node.audio_stream.as_ref() { + if next_value.url != *url { + AudioUpdateMode::ChangeAudio } else { - let _ = audio_stream_data - .command_sender - .blocking_send(AVCommand::Pause); + AudioUpdateMode::OnlyChangeValues } - // let _ = audio_stream_data - // .command_sender - // .blocking_send(AVCommand::Repeat(new_value..unwrap_or(false))); } else { - let mut audio_stream_player = godot::engine::load::( - "res://src/decentraland_components/audio_streaming.tscn", - ) - .instantiate() - .unwrap() - .cast::(); - let audio_stream_generator = AudioStreamGenerator::new(); - - audio_stream_player.set_stream(audio_stream_generator.upcast()); - node.base.add_child(audio_stream_player.clone().upcast()); - audio_stream_player.play(); - - let start_muted = if let SceneType::Parcel = scene.scene_type { - &scene.scene_id == current_parcel_scene_id - } else { - true - }; + AudioUpdateMode::FirstSpawnAudio + }; + + match update_mode { + AudioUpdateMode::OnlyChangeValues => { + let audio_stream_data = node + .audio_stream + .as_ref() + .expect("audio_stream_data not found in node"); - if start_muted { - audio_stream_player.set_volume_db(0.0); + let mut audio_stream_node = node + .base + .get_node("AudioStream".into()) + .expect("enters on change audio branch but a AudioStream wasn't found there") + .try_cast::() + .expect("the expected AudioStream wasn't a DclAudioStream"); + + audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); + audio_stream_node.set_volume_db(volume_db); + + if next_value.playing.unwrap_or(true) { + let _ = audio_stream_data + .1 + .command_sender + .blocking_send(AVCommand::Play); + } else { + let _ = audio_stream_data + .1 + .command_sender + .blocking_send(AVCommand::Pause); + } } + AudioUpdateMode::ChangeAudio => { + if let Some(audio_stream_data) = node.audio_stream.as_ref() { + let _ = audio_stream_data + .1 + .command_sender + .blocking_send(AVCommand::Dispose); + } - let (_, audio_sink) = av_sinks( - new_value.url.clone(), - None, - audio_stream_player.clone(), - new_value.volume.unwrap_or(1.0), - new_value.playing.unwrap_or(true), - true, - ); + let mut audio_stream_node = node.base.get_node("AudioStream".into()).expect( + "enters on change audio branch but a AudioStream wasn't found there", + ).try_cast::().expect("the expected AudioStream wasn't a DclAudioStream"); - node.audio_stream = Some(audio_sink); + audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); + audio_stream_node.set_volume_db(volume_db); - scene - .audio_video_players - .insert(*entity, audio_stream_player.clone()); + let (_, audio_sink) = av_sinks( + next_value.url.clone(), + None, + audio_stream_node.clone().upcast::(), + playing, + false, + ); + + node.audio_stream = Some((next_value.url.clone(), audio_sink)); + } + AudioUpdateMode::FirstSpawnAudio => { + let mut audio_stream_node = godot::engine::load::( + "res://src/decentraland_components/audio_stream.tscn", + ) + .instantiate() + .unwrap() + .cast::(); + + audio_stream_node.set_name("AudioStream".into()); + + let audio_stream_generator = AudioStreamGenerator::new(); + audio_stream_node.set_stream(audio_stream_generator.upcast()); + + node.base.add_child(audio_stream_node.clone().upcast()); + audio_stream_node.play(); + + audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); + audio_stream_node.set_volume_db(volume_db); + + let (_, audio_sink) = av_sinks( + next_value.url.clone(), + None, + audio_stream_node.clone().upcast::(), + playing, + false, + ); + + node.audio_stream = Some((next_value.url.clone(), audio_sink)); + + scene + .audio_streams + .insert(*entity, audio_stream_node.clone()); + } } + } else if exist_current_node { + let Some(node) = godot_dcl_scene.get_node_mut(entity) else { + continue; + }; + + if let Some(audio_stream_data) = node.audio_stream.as_ref() { + let _ = audio_stream_data + .1 + .command_sender + .blocking_send(AVCommand::Dispose); + } + + node.audio_stream = None; } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index c16cb7e9..71ca40d6 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -8,16 +8,23 @@ use crate::{ }, SceneId, }, + godot_classes::dcl_video_player::DclVideoPlayer, scene_runner::{ godot_dcl_scene::VideoPlayerData, scene::{Scene, SceneType}, }, }; use godot::{ - engine::{image::Format, AudioStreamGenerator, Image, ImageTexture}, + engine::{image::Format, AudioStreamGenerator, AudioStreamPlayer3D, Image, ImageTexture}, prelude::*, }; +enum VideoUpdateMode { + OnlyChangeValues, + ChangeVideo, + FirstSpawnVideo, +} + pub fn update_video_player( scene: &mut Scene, crdt_state: &mut SceneCrdtState, @@ -29,97 +36,177 @@ pub fn update_video_player( if let Some(video_player_dirty) = dirty_lww_components.get(&SceneComponentId::VIDEO_PLAYER) { for entity in video_player_dirty { - let new_value = video_player_component.get(*entity); - if new_value.is_none() { - continue; - } + let exist_current_node = godot_dcl_scene.get_node(entity).is_some(); - let new_value = new_value.unwrap(); - let node = godot_dcl_scene.ensure_node_mut(entity); + let next_value = if let Some(new_value) = video_player_component.get(*entity) { + new_value.value.as_ref() + } else { + None + }; - let new_value = new_value.value.clone(); - // let existing = node - // .base - // .try_get_node_as::(NodePath::from("MeshCollider")); + if let Some(next_value) = next_value { + let start_muted = if let SceneType::Parcel = scene.scene_type { + scene.scene_id != *current_parcel_scene_id + } else { + true + }; - if new_value.is_none() { - if let Some(video_player_data) = node.video_player_data.as_ref() { - let _ = video_player_data - .video_sink - .command_sender - .blocking_send(AVCommand::Dispose); - } - } else if let Some(new_value) = new_value { - if let Some(video_player_data) = node.video_player_data.as_ref() { - new_value.volume.unwrap_or(1.0); + let dcl_volume = next_value.volume.unwrap_or(1.0).clamp(0.0, 1.0); + let volume_db = if start_muted { + -80.0 + } else { + 20.0 * f32::log10(dcl_volume) + }; - new_value.playing.unwrap_or(true); + let playing = next_value.playing.unwrap_or(true); + let looping = next_value.r#loop.unwrap_or(false); - if new_value.playing.unwrap_or(true) { - let _ = video_player_data - .video_sink - .command_sender - .blocking_send(AVCommand::Play); + let node = godot_dcl_scene.ensure_node_mut(entity); + let update_mode = if let Some(video_player_data) = node.video_player_data.as_ref() { + if next_value.src != video_player_data.video_sink.source { + VideoUpdateMode::ChangeVideo } else { + VideoUpdateMode::OnlyChangeValues + } + } else { + VideoUpdateMode::FirstSpawnVideo + }; + + match update_mode { + VideoUpdateMode::OnlyChangeValues => { + let video_player_data = node + .video_player_data + .as_ref() + .expect("video_player_data not found in node"); + + let mut video_player_node = node + .base + .get_node("VideoPlayer".into()) + .expect("enters on change video branch but a VideoPlayer wasn't found there") + .try_cast::() + .expect("the expected VideoPlayer wasn't a DclVideoPlayer"); + + video_player_node.bind_mut().set_dcl_volume(dcl_volume); + video_player_node.set_volume_db(volume_db); + + if next_value.playing.unwrap_or(true) { + let _ = video_player_data + .video_sink + .command_sender + .blocking_send(AVCommand::Play); + } else { + let _ = video_player_data + .video_sink + .command_sender + .blocking_send(AVCommand::Pause); + } + let _ = video_player_data .video_sink .command_sender - .blocking_send(AVCommand::Pause); + .blocking_send(AVCommand::Repeat(next_value.r#loop.unwrap_or(false))); } - let _ = video_player_data - .video_sink - .command_sender - .blocking_send(AVCommand::Repeat(new_value.r#loop.unwrap_or(false))); - } else { - let image = Image::create(8, 8, false, Format::FORMAT_RGBA8) - .expect("couldn't create an video image"); - let texture = ImageTexture::create_from_image(image) - .expect("couldn't create an video image texture"); - - let mut audio_stream_player = godot::engine::load::( - "res://src/decentraland_components/audio_streaming.tscn", - ) - .instantiate() - .unwrap() - .cast::(); - let audio_stream_generator = AudioStreamGenerator::new(); - - audio_stream_player.set_stream(audio_stream_generator.upcast()); - node.base.add_child(audio_stream_player.clone().upcast()); - audio_stream_player.play(); - - let start_muted = if let SceneType::Parcel = scene.scene_type { - &scene.scene_id == current_parcel_scene_id - } else { - true - }; + VideoUpdateMode::ChangeVideo => { + if let Some(video_player_data) = node.video_player_data.as_ref() { + let _ = video_player_data + .video_sink + .command_sender + .blocking_send(AVCommand::Dispose); + } + + let mut video_player_node = node.base.get_node("VideoPlayer".into()).expect( + "enters on change video branch but a VideoPlayer wasn't found there", + ).try_cast::().expect("the expected VideoPlayer wasn't a DclVideoPlayer"); + + video_player_node.bind_mut().set_dcl_volume(dcl_volume); + video_player_node.set_volume_db(volume_db); + + let texture = video_player_node + .bind() + .get_dcl_texture() + .expect("there should be a texture in the VideoPlayer node"); + + let (video_sink, audio_sink) = av_sinks( + next_value.src.clone(), + Some(texture.clone()), + video_player_node.clone().upcast::(), + playing, + looping, + ); + + let Some(video_sink) = video_sink else { + tracing::error!("couldn't create an video sink"); + continue; + }; + + node.video_player_data = Some(VideoPlayerData { + video_sink, + audio_sink, + }); + } + VideoUpdateMode::FirstSpawnVideo => { + let image = Image::create(8, 8, false, Format::FORMAT_RGBA8) + .expect("couldn't create an video image"); + let texture = ImageTexture::create_from_image(image) + .expect("couldn't create an video image texture"); + + let mut video_player_node = godot::engine::load::( + "res://src/decentraland_components/video_player.tscn", + ) + .instantiate() + .unwrap() + .cast::(); + + video_player_node.set_name("VideoPlayer".into()); + + video_player_node + .bind_mut() + .set_dcl_texture(Some(texture.clone())); - if start_muted { - audio_stream_player.set_volume_db(0.0); + let audio_stream_generator = AudioStreamGenerator::new(); + video_player_node.set_stream(audio_stream_generator.upcast()); + + node.base.add_child(video_player_node.clone().upcast()); + video_player_node.play(); + + video_player_node.bind_mut().set_dcl_volume(dcl_volume); + video_player_node.set_volume_db(volume_db); + + let (video_sink, audio_sink) = av_sinks( + next_value.src.clone(), + Some(texture), + video_player_node.clone().upcast::(), + playing, + looping, + ); + + let Some(video_sink) = video_sink else { + tracing::error!("couldn't create an video sink"); + continue; + }; + + node.video_player_data = Some(VideoPlayerData { + video_sink, + audio_sink, + }); + scene + .video_players + .insert(*entity, video_player_node.clone()); } + } + } else if exist_current_node { + let Some(node) = godot_dcl_scene.get_node_mut(entity) else { + continue; + }; - let (video_sink, audio_sink) = av_sinks( - new_value.src.clone(), - Some(texture), - audio_stream_player.clone(), - new_value.volume.unwrap_or(1.0), - new_value.playing.unwrap_or(true), - new_value.r#loop.unwrap_or(false), - ); - - let Some(video_sink) = video_sink else { - tracing::error!("couldn't create an video sink"); - continue; - }; - - node.video_player_data = Some(VideoPlayerData { - video_sink, - audio_sink, - }); - scene - .audio_video_players - .insert(*entity, audio_stream_player.clone()); + if let Some(video_player_data) = node.video_player_data.as_ref() { + let _ = video_player_data + .video_sink + .command_sender + .blocking_send(AVCommand::Dispose); } + + node.video_player_data = None; } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs b/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs index fbfbd1dc..e801216d 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/deleted_entities.rs @@ -26,8 +26,13 @@ pub fn update_deleted_entities(scene: &mut Scene) { let node = godot_dcl_scene.ensure_node_mut(deleted_entity); node.base.clone().free(); godot_dcl_scene.entities.remove(deleted_entity); + scene.audio_sources.remove(deleted_entity); scene.audio_streams.remove(deleted_entity); - scene.audio_video_players.remove(deleted_entity); + scene.video_players.remove(deleted_entity); + + // TODO: clean continuos_raycast + // TODO: clean gltf_loading + // TODO: clean pointer_events_result } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs b/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs index 8233a85a..f3d0244e 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/godot_dcl_scene.rs @@ -32,7 +32,7 @@ pub struct Node3DEntity { pub material: Option, pub pointer_events: Option, pub video_player_data: Option, - pub audio_stream: Option, + pub audio_stream: Option<(String, AudioSink)>, } impl SceneDefinition { diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene.rs b/rust/decentraland-godot-lib/src/scene_runner/scene.rs index ce7d9a34..a641dfc3 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene.rs @@ -22,7 +22,10 @@ use crate::{ SceneDefinition, SceneId, }, - godot_classes::dcl_audio_source::DclAudioSource, + godot_classes::{ + dcl_audio_source::DclAudioSource, dcl_audio_stream::DclAudioStream, + dcl_video_player::DclVideoPlayer, + }, }; use super::godot_dcl_scene::GodotDclScene; @@ -142,8 +145,8 @@ pub struct Scene { pub audio_sources: HashMap>, // Used by VideoPlayer and AudioStream - pub audio_streams: HashMap>, - pub audio_video_players: HashMap>, + pub audio_streams: HashMap>, + pub video_players: HashMap>, } #[derive(Debug)] @@ -223,7 +226,7 @@ impl Scene { dirty_materials: false, audio_sources: HashMap::new(), audio_streams: HashMap::new(), - audio_video_players: HashMap::new(), + video_players: HashMap::new(), scene_type, } } @@ -274,7 +277,7 @@ impl Scene { scene_type: SceneType::Parcel, audio_sources: HashMap::new(), audio_streams: HashMap::new(), - audio_video_players: HashMap::new(), + video_players: HashMap::new(), } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs index 245ce793..8097c180 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs @@ -246,7 +246,7 @@ impl SceneManager { for (_, audio_stream_node) in scene.audio_streams.iter_mut() { audio_stream_node.set_volume_db(-80.0); } - for (_, audio_stream_node) in scene.audio_video_players.iter_mut() { + for (_, audio_stream_node) in scene.video_players.iter_mut() { audio_stream_node.set_volume_db(-80.0); } } @@ -258,10 +258,12 @@ impl SceneManager { audio_source_node.call("apply_audio_props".into(), &[false.to_variant()]); } for (_, audio_stream_node) in scene.audio_streams.iter_mut() { - audio_stream_node.set_volume_db(0.0); + let db_volume = 20.0 * f32::log10(audio_stream_node.bind().get_dcl_volume()); + audio_stream_node.set_volume_db(db_volume); } - for (_, audio_stream_node) in scene.audio_video_players.iter_mut() { - audio_stream_node.set_volume_db(0.0); + for (_, video_player_node) in scene.video_players.iter_mut() { + let db_volume = 20.0 * f32::log10(video_player_node.bind().get_dcl_volume()); + video_player_node.set_volume_db(db_volume); } } From 7b3296d402c9844954abf71c9317465cade25a23 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Tue, 17 Oct 2023 17:23:40 -0300 Subject: [PATCH 4/7] format --- godot/src/decentraland_components/audio_stream.gd | 1 + godot/src/decentraland_components/video_player.gd | 1 + .../src/scene_runner/components/video_player.rs | 2 +- rust/decentraland-godot-lib/src/scene_runner/scene.rs | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/godot/src/decentraland_components/audio_stream.gd b/godot/src/decentraland_components/audio_stream.gd index d8910b79..07075315 100644 --- a/godot/src/decentraland_components/audio_stream.gd +++ b/godot/src/decentraland_components/audio_stream.gd @@ -1,5 +1,6 @@ extends DclAudioStream + func stream_buffer(data: PackedVector2Array): if not self.playing: self.play() diff --git a/godot/src/decentraland_components/video_player.gd b/godot/src/decentraland_components/video_player.gd index e3d774d5..4beeb09b 100644 --- a/godot/src/decentraland_components/video_player.gd +++ b/godot/src/decentraland_components/video_player.gd @@ -1,5 +1,6 @@ extends DclVideoPlayer + func stream_buffer(data: PackedVector2Array): if not self.playing: self.play() diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index 71ca40d6..923ace20 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -15,7 +15,7 @@ use crate::{ }, }; use godot::{ - engine::{image::Format, AudioStreamGenerator, AudioStreamPlayer3D, Image, ImageTexture}, + engine::{image::Format, AudioStreamGenerator, Image, ImageTexture}, prelude::*, }; diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene.rs b/rust/decentraland-godot-lib/src/scene_runner/scene.rs index a641dfc3..7ec574f4 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene.rs @@ -3,7 +3,7 @@ use std::{ time::Instant, }; -use godot::prelude::{AudioStreamPlayer, Dictionary, Gd}; +use godot::prelude::{Dictionary, Gd}; use crate::{ dcl::{ From c773868f24c2215b7a1a14d6ff5cc871064acce1 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 18 Oct 2023 16:54:29 -0300 Subject: [PATCH 5/7] add local videos --- .../decentraland_components/video_player.gd | 28 +++++++ godot/src/logic/content_manager.gd | 80 ++++++++++++++++++- .../src/av/video_stream.rs | 23 +++++- .../src/godot_classes/dcl_video_player.rs | 15 +++- .../scene_runner/components/audio_stream.rs | 2 + .../scene_runner/components/video_player.rs | 64 +++++++++++++++ 6 files changed, 208 insertions(+), 4 deletions(-) diff --git a/godot/src/decentraland_components/video_player.gd b/godot/src/decentraland_components/video_player.gd index 4beeb09b..dece6496 100644 --- a/godot/src/decentraland_components/video_player.gd +++ b/godot/src/decentraland_components/video_player.gd @@ -1,5 +1,6 @@ extends DclVideoPlayer +var file_hash: String = "" func stream_buffer(data: PackedVector2Array): if not self.playing: @@ -14,6 +15,33 @@ func set_mute(value: bool): else: self.volume_db = -80 +func request_video(_file_hash): + var content_mapping = Global.scene_runner.get_scene_content_mapping(dcl_scene_id) + self.file_hash = _file_hash + + var fetching_resource = Global.content_manager.fetch_video(file_hash, content_mapping) + if not fetching_resource: + self._on_video_loaded(self.file_hash) + else: + Global.content_manager.content_loading_finished.connect( + self._content_manager_resource_loaded + ) + +func _content_manager_resource_loaded(resource_hash: String): + _on_video_loaded(resource_hash, true) + +func _on_video_loaded(resource_hash: String, from_signal: bool = false): + if resource_hash != file_hash: + return + + if from_signal: + Global.content_manager.content_loading_finished.disconnect( + self._content_manager_resource_loaded + ) + + var local_video_path = "user://content/" + file_hash + var absolute_file_path = local_video_path.replace("user:/", OS.get_user_data_dir()) + self.resolve_resource(absolute_file_path) func init_audio(frame_rate, frames, length, format, bit_rate, frame_size, channels): print( diff --git a/godot/src/logic/content_manager.gd b/godot/src/logic/content_manager.gd index b728f919..b96cdcdf 100644 --- a/godot/src/logic/content_manager.gd +++ b/godot/src/logic/content_manager.gd @@ -12,7 +12,8 @@ enum ContentType { CT_WEARABLE_EMOTE = 3, CT_MESHES_MATERIAL = 4, CT_INSTACE_GLTF = 5, - CT_AUDIO = 6 + CT_AUDIO = 6, + CT_VIDEO = 7 } var loading_content: Array[Dictionary] = [] @@ -203,6 +204,27 @@ func fetch_audio(file_path: String, content_mapping: Dictionary): return true +# Public function +# @returns true if the resource was added to queue to fetch, false if it had already been fetched +func fetch_video(file_hash: String, content_mapping: Dictionary): + var content_cached = content_cache_map.get(file_hash) + if content_cached != null: + return not content_cached.get("loaded") + + content_cache_map[file_hash] = { + "loaded": false, + } + + pending_content.push_back( + { + "content_mapping": content_mapping, + "file_hash": file_hash, + "content_type": ContentType.CT_VIDEO, + "stage": 0 + } + ) + + return true func _process(_dt: float) -> void: _th_poll() @@ -251,6 +273,10 @@ func _th_poll(): if not _process_instance_gltf(content): _th_to_delete.push_back(content) + ContentType.CT_VIDEO: + if not _process_loading_video(content, _th_finished_downloads): + _th_to_delete.push_back(content) + _: printerr("Fetching invalid content type ", _th_content_type) @@ -627,6 +653,58 @@ func _process_loading_audio( return true +func _process_loading_video( + content: Dictionary, finished_downloads: Array[RequestResponse] +) -> bool: + var content_mapping = content.get("content_mapping") + var file_hash: String = content.get("file_hash") + var base_url: String = content_mapping.get("base_url", "") + var local_video_path = "user://content/" + file_hash + var stage = content.get("stage", 0) + + match stage: + # Stage 0 => request png file + 0: + if FileAccess.file_exists(local_video_path): + content["stage"] = 2 + else: + if file_hash.is_empty() or base_url.is_empty(): + printerr("hash or base_url is empty") + return false + + var absolute_file_path = local_video_path.replace("user:/", OS.get_user_data_dir()) + content["stage"] = 1 + content["request_id"] = http_requester.request_file( + 0, base_url + file_hash, absolute_file_path + ) + + # Stage 1 => wait for the file + 1: + for item in finished_downloads: + if item.id() == content["request_id"]: + if item.is_error(): + printerr("video download is_error() == true!") + return false + else: + content["stage"] = 2 + + # Stage 2 => process texture + 2: + var file := FileAccess.open(local_video_path, FileAccess.READ) + if file == null: + printerr("video download fails") + return false + + content_cache_map[file_hash]["loaded"] = true + content_cache_map[file_hash]["stage"] = 3 + self.emit_signal.call_deferred("content_loading_finished", file_hash) + return false + _: + printerr("unknown stage ", file_hash) + return false + + return true + func split_animations(_gltf_node: Node) -> void: pass diff --git a/rust/decentraland-godot-lib/src/av/video_stream.rs b/rust/decentraland-godot-lib/src/av/video_stream.rs index e302d768..c67eaded 100644 --- a/rust/decentraland-godot-lib/src/av/video_stream.rs +++ b/rust/decentraland-godot-lib/src/av/video_stream.rs @@ -33,6 +33,7 @@ pub fn av_sinks( audio_stream_player: Gd, playing: bool, repeat: bool, + wait_for_resource: Option>, ) -> (Option, AudioSink) { let (command_sender, command_receiver) = tokio::sync::mpsc::channel(10); @@ -41,6 +42,7 @@ pub fn av_sinks( source.clone(), texture.clone(), audio_stream_player, + wait_for_resource, ); if playing { @@ -69,6 +71,7 @@ pub fn spawn_av_thread( path: String, tex: Option>, audio_stream_player: Gd, + wait_for_resource: Option>, ) { let video_instance_id = tex.map(|value| value.instance_id()); let audio_stream_player_instance_id = audio_stream_player.instance_id(); @@ -80,6 +83,7 @@ pub fn spawn_av_thread( path, video_instance_id, audio_stream_player_instance_id, + wait_for_resource, ) }) .unwrap(); @@ -90,10 +94,12 @@ fn av_thread( path: String, tex: Option, audio_stream: InstanceId, + wait_for_resource: Option>, ) { let tex = tex.map(Gd::from_instance_id); let audio_stream_player: Gd = Gd::from_instance_id(audio_stream); - if let Err(error) = av_thread_inner(commands, path, tex, audio_stream_player) { + if let Err(error) = av_thread_inner(commands, path, tex, audio_stream_player, wait_for_resource) + { warn!("av error: {error}"); } else { debug!("av closed"); @@ -102,10 +108,23 @@ fn av_thread( pub fn av_thread_inner( commands: tokio::sync::mpsc::Receiver, - path: String, + mut path: String, texture: Option>, audio_stream_player: Gd, + wait_for_resource: Option>, ) -> Result<(), String> { + if let Some(wait_for_resource_receiver) = wait_for_resource { + match wait_for_resource_receiver.blocking_recv() { + Ok(file_source) => { + if file_source.is_empty() { + return Err(format!("failed to get resource: {:?}", path)); + } + path = file_source; + } + Err(err) => return Err(format!("failed to get resource: {:?}", err)), + } + } + let input_context = input(&path).map_err(|e| format!("{:?} on line {}", e, line!()))?; // try and get a video context diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs index d6e788b7..0c8b87e2 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs @@ -16,7 +16,20 @@ pub struct DclVideoPlayer { #[base] _base: Base, + + #[var] + dcl_scene_id: u32, + + pub resolve_resource_sender: Option>, } #[godot_api] -impl DclVideoPlayer {} +impl DclVideoPlayer { + #[func] + fn resolve_resource(&mut self, file_path: GodotString) { + let Some(sender) = self.resolve_resource_sender.take() else { + return; + }; + let _ = sender.send(file_path.to_string()); + } +} diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs index 65a36dfa..a3e7d922 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs @@ -114,6 +114,7 @@ pub fn update_audio_stream( audio_stream_node.clone().upcast::(), playing, false, + None, ); node.audio_stream = Some((next_value.url.clone(), audio_sink)); @@ -143,6 +144,7 @@ pub fn update_audio_stream( audio_stream_node.clone().upcast::(), playing, false, + None, ); node.audio_stream = Some((next_value.url.clone(), audio_sink)); diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index 923ace20..756ea2cf 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -25,6 +25,23 @@ enum VideoUpdateMode { FirstSpawnVideo, } +fn get_local_file_hash_future( + content_mapping: &Dictionary, + file_path: &String, +) -> Option<( + tokio::sync::oneshot::Sender, + tokio::sync::oneshot::Receiver, + String, +)> { + let file_path = file_path.to_lowercase(); + let dict = content_mapping.get("content".to_variant())?; + let file_hash = Dictionary::from_variant(&dict) + .get(file_path.to_variant())? + .to_string(); + let (sx, rx) = tokio::sync::oneshot::channel::(); + Some((sx, rx, file_hash)) +} + pub fn update_video_player( scene: &mut Scene, crdt_state: &mut SceneCrdtState, @@ -126,12 +143,29 @@ pub fn update_video_player( .get_dcl_texture() .expect("there should be a texture in the VideoPlayer node"); + let (wait_for_resource_sender, wait_for_resource_receiver, file_hash) = + if let Some(local_scene_resource) = + get_local_file_hash_future(&scene.content_mapping, &next_value.src) + { + ( + Some(local_scene_resource.0), + Some(local_scene_resource.1), + local_scene_resource.2, + ) + } else { + (None, None, "".to_string()) + }; + + video_player_node.bind_mut().resolve_resource_sender = + wait_for_resource_sender; + let (video_sink, audio_sink) = av_sinks( next_value.src.clone(), Some(texture.clone()), video_player_node.clone().upcast::(), playing, looping, + wait_for_resource_receiver, ); let Some(video_sink) = video_sink else { @@ -143,6 +177,11 @@ pub fn update_video_player( video_sink, audio_sink, }); + + if !file_hash.is_empty() { + video_player_node + .call_deferred("request_video".into(), &[file_hash.to_variant()]); + } } VideoUpdateMode::FirstSpawnVideo => { let image = Image::create(8, 8, false, Format::FORMAT_RGBA8) @@ -157,6 +196,25 @@ pub fn update_video_player( .unwrap() .cast::(); + let (wait_for_resource_sender, wait_for_resource_receiver, file_hash) = + if let Some(local_scene_resource) = + get_local_file_hash_future(&scene.content_mapping, &next_value.src) + { + ( + Some(local_scene_resource.0), + Some(local_scene_resource.1), + local_scene_resource.2, + ) + } else { + (None, None, "".to_string()) + }; + + video_player_node + .bind_mut() + .set_dcl_scene_id(scene.scene_id.0); + video_player_node.bind_mut().resolve_resource_sender = + wait_for_resource_sender; + video_player_node.set_name("VideoPlayer".into()); video_player_node @@ -178,6 +236,7 @@ pub fn update_video_player( video_player_node.clone().upcast::(), playing, looping, + wait_for_resource_receiver, ); let Some(video_sink) = video_sink else { @@ -192,6 +251,11 @@ pub fn update_video_player( scene .video_players .insert(*entity, video_player_node.clone()); + + if !file_hash.is_empty() { + video_player_node + .call_deferred("request_video".into(), &[file_hash.to_variant()]); + } } } } else if exist_current_node { From ddceef190ca74145d2d71e658953441369187709 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 18 Oct 2023 16:55:36 -0300 Subject: [PATCH 6/7] format --- godot/src/decentraland_components/video_player.gd | 7 ++++++- godot/src/logic/content_manager.gd | 3 +++ rust/decentraland-godot-lib/src/godot_classes/mod.rs | 2 +- .../src/scene_runner/components/video_player.rs | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/godot/src/decentraland_components/video_player.gd b/godot/src/decentraland_components/video_player.gd index dece6496..71d01d41 100644 --- a/godot/src/decentraland_components/video_player.gd +++ b/godot/src/decentraland_components/video_player.gd @@ -2,6 +2,7 @@ extends DclVideoPlayer var file_hash: String = "" + func stream_buffer(data: PackedVector2Array): if not self.playing: self.play() @@ -15,6 +16,7 @@ func set_mute(value: bool): else: self.volume_db = -80 + func request_video(_file_hash): var content_mapping = Global.scene_runner.get_scene_content_mapping(dcl_scene_id) self.file_hash = _file_hash @@ -27,9 +29,11 @@ func request_video(_file_hash): self._content_manager_resource_loaded ) + func _content_manager_resource_loaded(resource_hash: String): _on_video_loaded(resource_hash, true) + func _on_video_loaded(resource_hash: String, from_signal: bool = false): if resource_hash != file_hash: return @@ -38,11 +42,12 @@ func _on_video_loaded(resource_hash: String, from_signal: bool = false): Global.content_manager.content_loading_finished.disconnect( self._content_manager_resource_loaded ) - + var local_video_path = "user://content/" + file_hash var absolute_file_path = local_video_path.replace("user:/", OS.get_user_data_dir()) self.resolve_resource(absolute_file_path) + func init_audio(frame_rate, frames, length, format, bit_rate, frame_size, channels): print( "audio_stream debug init ", diff --git a/godot/src/logic/content_manager.gd b/godot/src/logic/content_manager.gd index b96cdcdf..1eed2698 100644 --- a/godot/src/logic/content_manager.gd +++ b/godot/src/logic/content_manager.gd @@ -204,6 +204,7 @@ func fetch_audio(file_path: String, content_mapping: Dictionary): return true + # Public function # @returns true if the resource was added to queue to fetch, false if it had already been fetched func fetch_video(file_hash: String, content_mapping: Dictionary): @@ -226,6 +227,7 @@ func fetch_video(file_hash: String, content_mapping: Dictionary): return true + func _process(_dt: float) -> void: _th_poll() @@ -653,6 +655,7 @@ func _process_loading_audio( return true + func _process_loading_video( content: Dictionary, finished_downloads: Array[RequestResponse] ) -> bool: diff --git a/rust/decentraland-godot-lib/src/godot_classes/mod.rs b/rust/decentraland-godot-lib/src/godot_classes/mod.rs index 7bc8cfd6..cba75adc 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/mod.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/mod.rs @@ -2,5 +2,5 @@ pub mod dcl_audio_source; pub mod dcl_audio_stream; pub mod dcl_camera_3d; pub mod dcl_camera_mode_area_3d; -pub mod dcl_video_player; pub mod dcl_confirm_dialog; +pub mod dcl_video_player; diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index 756ea2cf..d1d7dae6 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -27,7 +27,7 @@ enum VideoUpdateMode { fn get_local_file_hash_future( content_mapping: &Dictionary, - file_path: &String, + file_path: &str, ) -> Option<( tokio::sync::oneshot::Sender, tokio::sync::oneshot::Receiver, From 3f580c8f674f83495b9208bbb7a436c75cc428fb Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 18 Oct 2023 17:40:21 -0300 Subject: [PATCH 7/7] apply review --- .../decentraland_components/audio_stream.gd | 7 ------- .../decentraland_components/video_player.gd | 7 ------- .../src/godot_classes/dcl_audio_stream.rs | 13 ++++++++++-- .../src/godot_classes/dcl_video_player.rs | 11 +++++++++- .../scene_runner/components/audio_stream.rs | 20 +++++++++---------- .../scene_runner/components/video_player.rs | 20 +++++++++---------- .../src/scene_runner/scene_manager.rs | 12 +++++------ 7 files changed, 46 insertions(+), 44 deletions(-) diff --git a/godot/src/decentraland_components/audio_stream.gd b/godot/src/decentraland_components/audio_stream.gd index 07075315..de702a2e 100644 --- a/godot/src/decentraland_components/audio_stream.gd +++ b/godot/src/decentraland_components/audio_stream.gd @@ -8,13 +8,6 @@ func stream_buffer(data: PackedVector2Array): self.get_stream_playback().push_buffer(data) -func set_mute(value: bool): - if value: - self.volume_db = 0 - else: - self.volume_db = -80 - - func init_audio(frame_rate, frames, length, format, bit_rate, frame_size, channels): print( "audio_stream debug init ", diff --git a/godot/src/decentraland_components/video_player.gd b/godot/src/decentraland_components/video_player.gd index 71d01d41..d1a1afc3 100644 --- a/godot/src/decentraland_components/video_player.gd +++ b/godot/src/decentraland_components/video_player.gd @@ -10,13 +10,6 @@ func stream_buffer(data: PackedVector2Array): self.get_stream_playback().push_buffer(data) -func set_mute(value: bool): - if value: - self.volume_db = 0 - else: - self.volume_db = -80 - - func request_video(_file_hash): var content_mapping = Global.scene_runner.get_scene_content_mapping(dcl_scene_id) self.file_hash = _file_hash diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs index b4b6c48e..30b3850b 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_audio_stream.rs @@ -12,8 +12,17 @@ pub struct DclAudioStream { dcl_url: GodotString, #[base] - _base: Base, + base: Base, } #[godot_api] -impl DclAudioStream {} +impl DclAudioStream { + pub fn set_muted(&mut self, value: bool) { + if value { + self.base.set_volume_db(-80.0); + } else { + let db_volume = 20.0 * f32::log10(self.get_dcl_volume()); + self.base.set_volume_db(db_volume); + } + } +} diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs index 0c8b87e2..9d17cc2e 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_video_player.rs @@ -15,7 +15,7 @@ pub struct DclVideoPlayer { dcl_texture: Option>, #[base] - _base: Base, + base: Base, #[var] dcl_scene_id: u32, @@ -32,4 +32,13 @@ impl DclVideoPlayer { }; let _ = sender.send(file_path.to_string()); } + + pub fn set_muted(&mut self, value: bool) { + if value { + self.base.set_volume_db(-80.0); + } else { + let db_volume = 20.0 * f32::log10(self.get_dcl_volume()); + self.base.set_volume_db(db_volume); + } + } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs index a3e7d922..f2091b23 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/audio_stream.rs @@ -38,19 +38,13 @@ pub fn update_audio_stream( }; if let Some(next_value) = next_value { - let start_muted = if let SceneType::Parcel = scene.scene_type { + let muted_by_current_scene = if let SceneType::Parcel = scene.scene_type { scene.scene_id != *current_parcel_scene_id } else { true }; let dcl_volume = next_value.volume.unwrap_or(1.0).clamp(0.0, 1.0); - let volume_db = if start_muted { - -80.0 - } else { - 20.0 * f32::log10(dcl_volume) - }; - let playing = next_value.playing.unwrap_or(true); let node = godot_dcl_scene.ensure_node_mut(entity); @@ -79,7 +73,9 @@ pub fn update_audio_stream( .expect("the expected AudioStream wasn't a DclAudioStream"); audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); - audio_stream_node.set_volume_db(volume_db); + audio_stream_node + .bind_mut() + .set_muted(muted_by_current_scene); if next_value.playing.unwrap_or(true) { let _ = audio_stream_data @@ -106,7 +102,9 @@ pub fn update_audio_stream( ).try_cast::().expect("the expected AudioStream wasn't a DclAudioStream"); audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); - audio_stream_node.set_volume_db(volume_db); + audio_stream_node + .bind_mut() + .set_muted(muted_by_current_scene); let (_, audio_sink) = av_sinks( next_value.url.clone(), @@ -136,7 +134,9 @@ pub fn update_audio_stream( audio_stream_node.play(); audio_stream_node.bind_mut().set_dcl_volume(dcl_volume); - audio_stream_node.set_volume_db(volume_db); + audio_stream_node + .bind_mut() + .set_muted(muted_by_current_scene); let (_, audio_sink) = av_sinks( next_value.url.clone(), diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs index d1d7dae6..4c419f59 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/video_player.rs @@ -62,19 +62,13 @@ pub fn update_video_player( }; if let Some(next_value) = next_value { - let start_muted = if let SceneType::Parcel = scene.scene_type { + let muted_by_current_scene = if let SceneType::Parcel = scene.scene_type { scene.scene_id != *current_parcel_scene_id } else { true }; let dcl_volume = next_value.volume.unwrap_or(1.0).clamp(0.0, 1.0); - let volume_db = if start_muted { - -80.0 - } else { - 20.0 * f32::log10(dcl_volume) - }; - let playing = next_value.playing.unwrap_or(true); let looping = next_value.r#loop.unwrap_or(false); @@ -104,7 +98,9 @@ pub fn update_video_player( .expect("the expected VideoPlayer wasn't a DclVideoPlayer"); video_player_node.bind_mut().set_dcl_volume(dcl_volume); - video_player_node.set_volume_db(volume_db); + video_player_node + .bind_mut() + .set_muted(muted_by_current_scene); if next_value.playing.unwrap_or(true) { let _ = video_player_data @@ -136,7 +132,9 @@ pub fn update_video_player( ).try_cast::().expect("the expected VideoPlayer wasn't a DclVideoPlayer"); video_player_node.bind_mut().set_dcl_volume(dcl_volume); - video_player_node.set_volume_db(volume_db); + video_player_node + .bind_mut() + .set_muted(muted_by_current_scene); let texture = video_player_node .bind() @@ -228,7 +226,9 @@ pub fn update_video_player( video_player_node.play(); video_player_node.bind_mut().set_dcl_volume(dcl_volume); - video_player_node.set_volume_db(volume_db); + video_player_node + .bind_mut() + .set_muted(muted_by_current_scene); let (video_sink, audio_sink) = av_sinks( next_value.src.clone(), diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs index 2fdb67e7..fed63ed1 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs @@ -244,10 +244,10 @@ impl SceneManager { audio_source_node.call("apply_audio_props".into(), &[false.to_variant()]); } for (_, audio_stream_node) in scene.audio_streams.iter_mut() { - audio_stream_node.set_volume_db(-80.0); + audio_stream_node.bind_mut().set_muted(true); } - for (_, audio_stream_node) in scene.video_players.iter_mut() { - audio_stream_node.set_volume_db(-80.0); + for (_, video_player_node) in scene.video_players.iter_mut() { + video_player_node.bind_mut().set_muted(true); } } @@ -258,12 +258,10 @@ impl SceneManager { audio_source_node.call("apply_audio_props".into(), &[false.to_variant()]); } for (_, audio_stream_node) in scene.audio_streams.iter_mut() { - let db_volume = 20.0 * f32::log10(audio_stream_node.bind().get_dcl_volume()); - audio_stream_node.set_volume_db(db_volume); + audio_stream_node.bind_mut().set_muted(false); } for (_, video_player_node) in scene.video_players.iter_mut() { - let db_volume = 20.0 * f32::log10(video_player_node.bind().get_dcl_volume()); - video_player_node.set_volume_db(db_volume); + video_player_node.bind_mut().set_muted(false); } }