Ensure all MovieWriter frames have the same resolution as the first frame
This fixes issues with certain video formats (like OGV) and video players not supporting resolution changes during playback. Resizing the viewport during recording is still not recommended (as cropping and resizing has a performance cost and can impact visuals), but it shouldn't cause crashes or broken files anymore.
This commit is contained in:
@@ -4704,7 +4704,21 @@ int Main::start() {
|
||||
}
|
||||
|
||||
if (movie_writer) {
|
||||
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
|
||||
Size2i movie_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
|
||||
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
|
||||
if (stretch_mode != "viewport") {
|
||||
// `canvas_items` and `disabled` modes use the window size override instead,
|
||||
// which allows for higher resolution recording with 2D elements designed for a lower resolution.
|
||||
const int window_width_override = GLOBAL_GET("display/window/size/window_width_override");
|
||||
if (window_width_override > 0) {
|
||||
movie_size.width = window_width_override;
|
||||
}
|
||||
const int window_height_override = GLOBAL_GET("display/window/size/window_height_override");
|
||||
if (window_height_override > 0) {
|
||||
movie_size.height = window_height_override;
|
||||
}
|
||||
}
|
||||
movie_writer->begin(movie_size, fixed_fps, Engine::get_singleton()->get_write_movie_path());
|
||||
}
|
||||
|
||||
GDExtensionManager::get_singleton()->startup();
|
||||
|
||||
@@ -97,20 +97,9 @@ void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
|
||||
|
||||
void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
|
||||
project_name = GLOBAL_GET("application/config/name");
|
||||
movie_size = p_movie_size;
|
||||
|
||||
print_line(vformat("Movie Maker mode enabled, recording movie at %d FPS...", p_fps));
|
||||
|
||||
// When using Display/Window/Stretch/Mode = Viewport, use the project's
|
||||
// configured viewport size instead of the size of the window in the OS
|
||||
Size2i actual_movie_size = p_movie_size;
|
||||
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
|
||||
if (stretch_mode == "viewport") {
|
||||
actual_movie_size.width = GLOBAL_GET("display/window/size/viewport_width");
|
||||
actual_movie_size.height = GLOBAL_GET("display/window/size/viewport_height");
|
||||
|
||||
print_line(vformat("Movie Maker mode using project viewport size: %dx%d",
|
||||
actual_movie_size.width, actual_movie_size.height));
|
||||
}
|
||||
print_line(vformat(U"Movie Maker mode enabled, recording movie in %s×%s @ %d FPS...", movie_size.width, movie_size.height, p_fps));
|
||||
|
||||
// Check for available disk space and warn the user if needed.
|
||||
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
@@ -139,7 +128,7 @@ void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String
|
||||
audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels();
|
||||
audio_mix_buffer.resize(mix_rate * audio_channels / fps);
|
||||
|
||||
write_begin(actual_movie_size, p_fps, p_base_path);
|
||||
write_begin(movie_size, p_fps, p_base_path);
|
||||
}
|
||||
|
||||
void MovieWriter::_bind_methods() {
|
||||
@@ -206,6 +195,37 @@ void MovieWriter::add_frame() {
|
||||
RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
|
||||
RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
|
||||
Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
|
||||
|
||||
if (vp_tex->get_size() != movie_size) {
|
||||
// Resize the texture to the output resolution if it differs from the current viewport size.
|
||||
// This ensures all frames have the same resolution, as not all video formats and players
|
||||
// support resolution changes during playback.
|
||||
|
||||
const float src_aspect = vp_tex->get_size().aspect();
|
||||
const float dst_aspect = movie_size.aspect();
|
||||
|
||||
int crop_width = vp_tex->get_size().width;
|
||||
int crop_height = vp_tex->get_size().height;
|
||||
int crop_x = 0;
|
||||
int crop_y = 0;
|
||||
|
||||
// If the aspect ratio differs, crop the image to cover the base resolution's aspect ratio
|
||||
// in a way similar to `TextureRect.STRETCH_KEEP_ASPECT_COVERED`.
|
||||
if (src_aspect > dst_aspect) {
|
||||
// Source is wider, crop horizontally.
|
||||
crop_width = int(vp_tex->get_size().height * dst_aspect);
|
||||
crop_x = (vp_tex->get_size().width - crop_width) / 2;
|
||||
vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height);
|
||||
} else if (src_aspect < dst_aspect) {
|
||||
// Source is taller, crop vertically.
|
||||
crop_height = int(vp_tex->get_size().width / dst_aspect);
|
||||
crop_y = (vp_tex->get_size().height - crop_height) / 2;
|
||||
vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height);
|
||||
}
|
||||
|
||||
vp_tex->resize(movie_size.width, movie_size.height, Image::INTERPOLATE_BILINEAR);
|
||||
}
|
||||
|
||||
if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) {
|
||||
vp_tex->convert(Image::FORMAT_RGBA8);
|
||||
vp_tex->linear_to_srgb();
|
||||
|
||||
@@ -41,6 +41,10 @@ class MovieWriter : public Object {
|
||||
uint64_t mix_rate = 0;
|
||||
uint32_t audio_channels = 0;
|
||||
|
||||
// The output resolution, which can differ from the window size.
|
||||
// Used as a base for resizing all subsequent frames if their resolution differs.
|
||||
Vector2i movie_size;
|
||||
|
||||
float cpu_time = 0.0f;
|
||||
float gpu_time = 0.0f;
|
||||
uint64_t encoding_time_usec = 0;
|
||||
|
||||
Reference in New Issue
Block a user