diff --git a/common/src/lib.rs b/common/src/lib.rs index 0a123ef..068437f 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -438,6 +438,14 @@ impl Default for GyroflowPluginBaseInstance { } impl GyroflowPluginBaseInstance { + fn nle_handles_rotation(out_size: (usize, usize), video_size: (usize, usize), video_rotation: f64) -> bool { + let host_is_portrait = out_size.1 > out_size.0; + let video_is_landscape = video_size.0 > video_size.1; + let r = ((video_rotation % 360.0) + 360.0) % 360.0; + let rotation_is_90_270 = (r - 90.0).abs() < 0.01 || (r - 270.0).abs() < 0.01; + host_is_portrait && video_is_landscape && rotation_is_90_270 + } + pub fn update_loaded_state(&mut self, params: &mut dyn GyroflowPluginParams, loaded: bool) { let _ = params.set_enabled(Params::Fov, loaded); let _ = params.set_enabled(Params::Smoothness, loaded); @@ -625,8 +633,14 @@ impl GyroflowPluginBaseInstance { } if md.rotation != 0 && self.reload_values_from_project { let r = ((360 - md.rotation) % 360) as f64; - params.set_f64(Params::InputRotation, r)?; + let video_size = stab.params.read().size; stab.params.write().video_rotation = r; + params.set_f64(Params::InputRotation, r)?; + if Self::nle_handles_rotation(out_size, video_size, r) { + stab.set_frame_rotation(r); + } else { + stab.set_frame_rotation(0.0); + } } params.set_string(Params::LoadedProject, &filesystem::get_filename(&filesystem::path_to_url(&path)))?; if !stab.gyro.read().file_metadata.read().has_accurate_timestamps && open_gyroflow_if_no_data { @@ -682,8 +696,14 @@ impl GyroflowPluginBaseInstance { if let Ok(video_md) = gyroflow_core::util::get_video_metadata(file.get_file(), filesize, &url) { if video_md.rotation != 0 && self.reload_values_from_project { let r = ((360 - video_md.rotation) % 360) as f64; - params.set_f64(Params::InputRotation, r)?; + let video_size = stab.params.read().size; stab.params.write().video_rotation = r; + params.set_f64(Params::InputRotation, r)?; + if Self::nle_handles_rotation(out_size, video_size, r) { + stab.set_frame_rotation(r); + } else { + stab.set_frame_rotation(0.0); + } } } } @@ -825,6 +845,20 @@ impl GyroflowPluginBaseInstance { params.framebuffer_inverted = self.framebuffer_inverted; } + // Ensure frame_rotation is set on every load path (fresh, cached, embedded, .gyroflow). + // Read video_rotation from the OFX Rotation parameter (always restored by the host on + // project load) rather than stab.params.video_rotation, which is only set when + // reload_values_from_project is true. + { + let video_rotation = params.get_f64(Params::Rotation).unwrap_or(stab.params.read().video_rotation); + let video_size = stab.params.read().size; + if Self::nle_handles_rotation(out_size, video_size, video_rotation) { + stab.set_frame_rotation(video_rotation); + } else { + stab.set_frame_rotation(0.0); + } + } + stab.init_size(); stab.set_output_size(params.get_f64(Params::OutputWidth)? as _, params.get_f64(Params::OutputHeight)? as _); diff --git a/openfx/src/gyroflow.rs b/openfx/src/gyroflow.rs index 34ba30d..534088f 100644 --- a/openfx/src/gyroflow.rs +++ b/openfx/src/gyroflow.rs @@ -201,7 +201,14 @@ impl Execute for GyroflowPlugin { let params = stab.params.read(); let fps = params.fps; let src_fps = instance_data.source_clip.get_frame_rate().unwrap_or(fps); - let org_ratio = params.size.0 as f64 / params.size.1 as f64; + let org_ratio = { + let fr = ((params.frame_rotation % 360.0) + 360.0) % 360.0; + if (fr - 90.0).abs() < 0.01 || (fr - 270.0).abs() < 0.01 { + params.size.1 as f64 / params.size.0 as f64 // Portrait: NLE has pre-rotated + } else { + params.size.0 as f64 / params.size.1 as f64 + } + }; let (has_accurate_timestamps, has_offsets) = { let gyro = stab.gyro.read(); let md = gyro.file_metadata.read();