Skip to content

Add Ogg Theora support to MovieWriter #106700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions doc/classes/MovieWriter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
</brief_description>
<description>
Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities.
Godot has 2 built-in [MovieWriter]s:
- AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most.
Godot has 3 built-in [MovieWriter]s:
- OGV container with Theora for video and Vorbis for audio ([code].ogv[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/video_quality] and [member ProjectSettings.editor/movie_writer/audio_quality]. The resulting file can be viewed in Godot with [VideoStreamPlayer] and most video players, but not web browsers as they don't support Theora.
- AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/video_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most.
- PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported, even if the root viewport is set to be transparent.
If you need to encode to a different format or pipe a stream through third-party software, you can extend the [MovieWriter] class to create your own movie writers. This should typically be done using GDExtension for performance reasons.
[b]Editor usage:[/b] A default movie file path can be specified in [member ProjectSettings.editor/movie_writer/movie_file]. Alternatively, for running single scenes, a [code]movie_file[/code] metadata can be added to the root node, specifying the path to a movie file that will be used when recording that scene. Once a path is set, click the video reel icon in the top-right corner of the editor to enable Movie Maker mode, then run any scene as usual. The engine will start recording as soon as the splash screen is finished, and it will only stop recording when the engine quits. Click the video reel icon again to disable Movie Maker mode. Note that toggling Movie Maker mode does not affect project instances that are already running.
Expand Down
16 changes: 10 additions & 6 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,10 @@
<member name="editor/import/use_multiple_threads" type="bool" setter="" getter="" default="true">
If [code]true[/code] importing of resources is run on multiple threads.
</member>
<member name="editor/movie_writer/audio_quality" type="float" setter="" getter="" default="0.5">
The audio encoding quality to use when writing Theora (Ogg Vorbis) audio to a file, between [code]0.0[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-sounding output at the cost of larger file sizes. Even at quality [code]1.0[/code], compression remains lossy.
[b]Note:[/b] This does not affect video quality.
</member>
<member name="editor/movie_writer/disable_vsync" type="bool" setter="" getter="" default="false">
If [code]true[/code], requests V-Sync to be disabled when writing a movie (similar to setting [member display/window/vsync/vsync_mode] to [b]Disabled[/b]). This can speed up video writing if the hardware is fast enough to render, encode and save the video at a framerate higher than the monitor's refresh rate.
[b]Note:[/b] [member editor/movie_writer/disable_vsync] has no effect if the operating system or graphics driver forces V-Sync with no way for applications to disable it.
Expand All @@ -1086,21 +1090,21 @@
<member name="editor/movie_writer/mix_rate" type="int" setter="" getter="" default="48000">
The audio mix rate to use in the recorded audio when writing a movie (in Hz). This can be different from [member audio/driver/mix_rate], but this value must be divisible by [member editor/movie_writer/fps] to prevent audio from desynchronizing over time.
</member>
<member name="editor/movie_writer/mjpeg_quality" type="float" setter="" getter="" default="0.75">
The JPEG quality to use when writing a video to an AVI file, between [code]0.01[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-looking output at the cost of larger file sizes. Recommended [code]quality[/code] values are between [code]0.75[/code] and [code]0.9[/code]. Even at quality [code]1.0[/code], JPEG compression remains lossy.
[b]Note:[/b] This does not affect the audio quality or writing PNG image sequences.
</member>
<member name="editor/movie_writer/movie_file" type="String" setter="" getter="" default="&quot;&quot;">
The output path for the movie. The file extension determines the [MovieWriter] that will be used.
Godot has 2 built-in [MovieWriter]s:
- AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most.
Godot has 3 built-in [MovieWriter]s:
- OGV container with Theora for video and Vorbis for audio ([code].ogv[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/video_quality] and [member ProjectSettings.editor/movie_writer/audio_quality]. The resulting file can be viewed in Godot with [VideoStreamPlayer] and most video players, but not web browsers as they don't support Theora.
- AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/video_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most.
- PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported, even if the root viewport is set to be transparent.
If you need to encode to a different format or pipe a stream through third-party software, you can extend this [MovieWriter] class to create your own movie writers.
When using PNG output, the frame number will be appended at the end of the file name. It starts from 0 and is padded with 8 digits to ensure correct sorting and easier processing. For example, if the output path is [code]/tmp/hello.png[/code], the first two frames will be [code]/tmp/hello00000000.png[/code] and [code]/tmp/hello00000001.png[/code]. The audio will be saved at [code]/tmp/hello.wav[/code].
</member>
<member name="editor/movie_writer/speaker_mode" type="int" setter="" getter="" default="0">
The speaker mode to use in the recorded audio when writing a movie. See [enum AudioServer.SpeakerMode] for possible values.
</member>
<member name="editor/movie_writer/video_quality" type="float" setter="" getter="" default="0.75">
The video encoding quality to use when writing a Theora or AVI (MJPEG) video to a file, between [code]0.0[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-looking output at the cost of larger file sizes. Recommended [code]quality[/code] values are between [code]0.75[/code] and [code]0.9[/code]. Even at quality [code]1.0[/code], compression remains lossy.
</member>
<member name="editor/naming/default_signal_callback_name" type="String" setter="" getter="" default="&quot;_on_{node_name}_{signal_name}&quot;">
The format of the default signal callback name (in the Signal Connection Dialog). The following substitutions are available: [code]{NodeName}[/code], [code]{nodeName}[/code], [code]{node_name}[/code], [code]{SignalName}[/code], [code]{signalName}[/code], and [code]{signal_name}[/code].
</member>
Expand Down
40 changes: 20 additions & 20 deletions modules/theora/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ thirdparty_obj = []
if env["builtin_libtheora"]:
thirdparty_dir = "#thirdparty/libtheora/"
thirdparty_sources = [
# "analyze.c",
"analyze.c",
# "apiwrapper.c",
"bitpack.c",
# "collect.c",
Expand All @@ -22,49 +22,49 @@ if env["builtin_libtheora"]:
"decode.c",
"dequant.c",
# "encapiwrapper.c",
# "encfrag.c",
# "encinfo.c",
# "encode.c",
"encfrag.c",
"encinfo.c",
"encode.c",
# "encoder_disabled.c",
# "enquant.c",
# "fdct.c",
"enquant.c",
"fdct.c",
"fragment.c",
"huffdec.c",
# "huffenc.c",
"huffenc.c",
"idct.c",
"info.c",
"internal.c",
# "mathops.c",
# "mcenc.c",
"mathops.c",
"mcenc.c",
"quant.c",
# "rate.c",
"rate.c",
"state.c",
# "tokenize.c",
"tokenize.c",
]

thirdparty_sources_x86 = [
# "x86/mmxencfrag.c",
# "x86/mmxfdct.c",
"x86/mmxencfrag.c",
"x86/mmxfdct.c",
"x86/mmxfrag.c",
"x86/mmxidct.c",
"x86/mmxstate.c",
# "x86/sse2encfrag.c",
# "x86/sse2fdct.c",
"x86/sse2encfrag.c",
"x86/sse2fdct.c",
"x86/sse2idct.c",
"x86/x86cpu.c",
# "x86/x86enc.c",
# "x86/x86enquant.c"
"x86/x86enc.c",
"x86/x86enquant.c",
"x86/x86state.c",
]

thirdparty_sources_x86_vc = [
# "x86_vc/mmxencfrag.c",
# "x86_vc/mmxfdct.c",
"x86_vc/mmxencfrag.c",
"x86_vc/mmxfdct.c",
"x86_vc/mmxfrag.c",
"x86_vc/mmxidct.c",
"x86_vc/mmxstate.c",
"x86_vc/x86cpu.c",
# "x86_vc/x86enc.c",
"x86_vc/x86enc.c",
"x86_vc/x86state.c",
]

Expand Down
4 changes: 2 additions & 2 deletions modules/vorbis/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ thirdparty_obj = []
if env["builtin_libvorbis"]:
thirdparty_dir = "#thirdparty/libvorbis/"
thirdparty_sources = [
# "analysis.c",
"analysis.c",
# "barkmel.c",
"bitrate.c",
"block.c",
Expand All @@ -35,7 +35,7 @@ if env["builtin_libvorbis"]:
"smallft.c",
"synthesis.c",
# "tone.c",
# "vorbisenc.c",
"vorbisenc.c",
"vorbisfile.c",
"window.c",
]
Expand Down
8 changes: 8 additions & 0 deletions servers/movie_writer/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ from misc.utility.scons_hints import *
Import("env")

env.add_source_files(env.servers_sources, "*.cpp")

# Also requires libogg, libtheora and libvorbis.
if env["builtin_libogg"]:
env.Prepend(CPPPATH=["#thirdparty/libogg"])
if env["builtin_libtheora"]:
env.Prepend(CPPPATH=["#thirdparty/libtheora", "#thirdparty/misc"])
if env["builtin_libvorbis"]:
env.Prepend(CPPPATH=["#thirdparty/libvorbis"])
4 changes: 3 additions & 1 deletion servers/movie_writer/movie_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ void MovieWriter::_bind_methods() {

GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/mix_rate", PROPERTY_HINT_RANGE, "8000,192000,1,suffix:Hz"), 48000);
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/movie_writer/speaker_mode", PROPERTY_HINT_ENUM, "Stereo,3.1,5.1,7.1"), 0);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "editor/movie_writer/mjpeg_quality", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.75);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "editor/movie_writer/video_quality", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), 0.75);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "editor/movie_writer/audio_quality", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), 0.5);

// Used by the editor.
GLOBAL_DEF_BASIC("editor/movie_writer/movie_file", "");
GLOBAL_DEF_BASIC("editor/movie_writer/disable_vsync", false);
Expand Down
2 changes: 1 addition & 1 deletion servers/movie_writer/movie_writer_mjpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,5 +259,5 @@ void MovieWriterMJPEG::write_end() {
MovieWriterMJPEG::MovieWriterMJPEG() {
mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate");
speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
quality = GLOBAL_GET("editor/movie_writer/mjpeg_quality");
quality = GLOBAL_GET("editor/movie_writer/video_quality");
}
Loading
Loading