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

Conversation

Calinou
Copy link
Member

@Calinou Calinou commented May 22, 2025

Movie Maker mode can now record files in .ogv format, which can be directly viewed in Godot's VideoStreamPlayer node along with most video players. This is a lossy format with inter-frame compression, unlike AVI + MJPEG which only performs intra-frame compression.

Thanks to @penninghlhd for the initial implementation from #98416 🙂

I've copied the code over, rebased it on top of master and tweaked the code style to match the code style guidelines. I've left some review comments accordingly where there are still open questions.

Theora information from ffprobe with the default settings:

Input #0, ogg, from 'out.ogv':
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0: Video: theora, yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 60 fps, 60 tbr, 60 tbn
  Stream #0:1: Audio: vorbis, 48000 Hz, stereo, fltp, 160 kb/s

Testing project: https://github.com/Calinou/godot-movie-maker-demo

Regarding encoding performance, I've only tested it on a debug build so far, but OGV encoding is significantly slower than AVI + MJPEG with the default quality:

AVI

1800 frames at 60 FPS (movie length: 00:00:30:00), recorded in 00:00:36 (83% of real-time speed).
CPU time: 0.50 seconds (average: 0.28 ms/frame)
GPU time: 6.49 seconds (average: 3.61 ms/frame)

OGV

1800 frames at 60 FPS (movie length: 00:00:30:00), recorded in 00:00:57 (52% of real-time speed).
CPU time: 0.70 seconds (average: 0.39 ms/frame)
GPU time: 7.01 seconds (average: 3.90 ms/frame)

This means that for quick previewing and further video editing, AVI + MJPEG is still preferred.

TODO

  • Fix files being corrupted.
    • mpv prints this when playing the video: [ffmpeg/demuxer] ogg: Broken file, keyframe not correctly marked..
  • Fix compilation when using builtin_libtheora=no builtin_libvorbis=no builtin_libogg=no (undefined references).
  • Test on Windows + MSVC and make sure the Theora files are correct once they're fixed on other platforms. MSVC uses a different set of files for x86 intrinsics in Theora.
  • Move MovieWriterOGV to the modules/theora module it depends on, like Move MovieWriterMJPEG class to jpg module it depends on #106013.

@Calinou Calinou added this to the 4.x milestone May 22, 2025
@Calinou Calinou force-pushed the moviewriter-add-theora branch 2 times, most recently from 586bf64 to ab4e1ff Compare May 22, 2025 00:41
Comment on lines +68 to +63
// Video stream keyframe frequency (one every N frames).
ogg_uint32_t keyframe_frequency = 64;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardcoded to 64 currently. Should we use a different value (to improve the quality/size ratio) or add a project setting to configure it?

cc @DeeJayLSP

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a good default, although a lower value might improve the quality in some cases, and the granularity on seeks. It could be a good candidate for a setting.

Comment on lines 76 to 69
// Sets the encoder speed level. Higher speed levels favor quicker encoding over better quality per bit. Depending on the encoding
// mode, and the internal algorithms used, quality may actually improve with higher speeds, but in this case bitrate will also
// likely increase. The maximum value, and the meaning of each value, are implementation-specific and may change depending on the
// current encoding mode.
int speed = 4;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. This could perhaps be exposed as a project setting too (I haven't benchmarked each level yet).

@Calinou Calinou force-pushed the moviewriter-add-theora branch from ab4e1ff to bc9c696 Compare May 22, 2025 00:51
@DeeJayLSP
Copy link
Contributor

DeeJayLSP commented May 22, 2025

I see I got pinged a lot on this, it's better to cc @berarma , who was responsible for #101958. I don't know anything about Theora other than how to use the encoder effectively.

Also I don't see how having support for recording in such a legacy format is beneficial. If there is something worth to be spending time on, it would be enabling MovieWriter to pipe to FFmpeg. It would close a lot of these proposals, directly or indirectly. Just my two cents.

@berarma
Copy link
Contributor

berarma commented May 22, 2025

A reminder that Windows 64bits has assembler optimizations disabled due to issues with register allocation, so it might run slower there.

ERROR: Couldn't set encoder flags for soft-target.
   at: write_begin (servers/movie_writer/movie_writer_ogv.cpp:245)
ERROR: Couldn't set rate control buffer for soft-target.
   at: write_begin (servers/movie_writer/movie_writer_ogv.cpp:256)

The reason for these errors is that there's no target bitrate set (it's zero). They are only valid when there's a target bitrate. I think these parameters can be ignored, the code removed, and the problem would be solved.

The keyframe errors have another cause. Please, see my code reviews, that might fix it.

I don't have experimented much with encoding parameters apart from the GOP and quality settings. I would leave GOP and video/audio quality as settings, and maybe speed in case it has a noticeable impact. For any other parameters use the library defaults.

@berarma
Copy link
Contributor

berarma commented May 22, 2025

I've done a couple of tests with speed and the default value 1 is actually faster than 4 on my computer, I'm not sure why. It's ~0.80ms/frame with speed=4 and ~0.60ms/frame with speed=1.

Higher values remove features. I would leave the default library setting removing the code that changes it. Unless someone gets different results, in which case I would add a setting for it.

Movie Maker mode can now record files in `.ogv` format, which can be
directly viewed in Godot's VideoStreamPlayer node along with most
video players. This is a lossy format with inter-frame compression,
unlike AVI + MJPEG which only performs intra-frame compression.

Co-authored-by: Leo de Penning <leo.depenning@illuminoo.com>
@Calinou Calinou force-pushed the moviewriter-add-theora branch from bc9c696 to 0647374 Compare May 22, 2025 21:52
@Calinou Calinou force-pushed the moviewriter-add-theora branch from 0647374 to a9a2854 Compare May 22, 2025 21:55
@Calinou
Copy link
Member Author

Calinou commented May 22, 2025

I've done a couple of tests with speed and the default value 1 is actually faster than 4 on my computer, I'm not sure why. It's ~0.80ms/frame with speed=4 and ~0.60ms/frame with speed=1.

I've tried to change speed to 1 and it seems very slightly faster, while the resulting OGV file is 3 KB smaller (for a 112 MB file). I've tested with an optimized build this time; I've updated the figures in OP.

@berarma
Copy link
Contributor

berarma commented May 23, 2025

I've done some tests using theora_encode_example with the same options used in this PR and changing speed values

I haven't found any case where speed=1 was faster than speed=4 so maybe I did something wrong.

WING IT! (cartoon with fast action and plane changes)

Speed Time Size
0 90.887 19,855,091
1 83.325 19,515,266
2 53.529 21,871,596
3 41.126 22,346,154
4 21.590 32,297,492

Cheetahs on the edge (Slow motion video)

Speed Time Size
0 95.974 35,250,402
1 92.477 35,196,586
2 61.201 37,588,152
3 49.595 40,554,313
4 27.423 91,134,777

Spring (Somewhere in between the other two)

Speed Time Size
0 81.020 24,596,271
1 74.801 24,421,160
2 48.658 29,682,777
3 37.227 30,388,304
4 20.449 64,808,056
Execution log for WING IT!
$ time theora_encoder -o test_speed_0.ogv -k 64 -v 7.5 -z 0 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 2757kbps                 
      1440 frames in 92.389 seconds: 32.559 Mpixel/s 0.62x
done.

real	1m36,127s
user	1m30,887s
sys	0m1,505s

$ time theora_encoder -o test_speed_1.ogv -k 64 -v 7.5 -z 1 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 2710kbps                 
      1440 frames in 83.966 seconds: 35.825 Mpixel/s 0.69x
done.

real	1m24,028s
user	1m23,325s
sys	0m0,644s

$ time theora_encoder -o test_speed_2.ogv -k 64 -v 7.5 -z 2 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 3037kbps                 
      1440 frames in 54.306 seconds: 55.391 Mpixel/s 1.06x
done.

real	0m54,338s
user	0m53,529s
sys	0m0,780s

$ time theora_encoder -o test_speed_3.ogv -k 64 -v 7.5 -z 3 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 3103kbps                 
      1440 frames in 41.755 seconds: 72.042 Mpixel/s 1.38x
done.

real	0m41,784s
user	0m41,126s
sys	0m0,633s

$ time theora_encoder -o test_speed_4.ogv -k 64 -v 7.5 -z 4 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 4485kbps                 
      1440 frames in 22.179 seconds: 135.628 Mpixel/s 2.60x
done.

real	0m22,844s
user	0m21,590s
sys	0m0,593s
Execution log for Cheetahs on the edge
$ time theora_encoder -o test_speed_0.ogv -k 64 -v 7.5 -z 0 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 4895kbps                 
      1440 frames in 96.599 seconds: 31.140 Mpixel/s 0.60x
done.

real	1m36,807s
user	1m35,974s
sys	0m0,629s

$ time theora_encoder -o test_speed_1.ogv -k 64 -v 7.5 -z 1 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 4888kbps                 
      1440 frames in 93.065 seconds: 32.322 Mpixel/s 0.62x
done.

real	1m33,171s
user	1m32,477s
sys	0m0,592s

$ time theora_encoder -o test_speed_2.ogv -k 64 -v 7.5 -z 2 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 5220kbps                 
      1440 frames in 61.858 seconds: 48.629 Mpixel/s 0.93x
done.

real	1m1,878s
user	1m1,201s
sys	0m0,660s

$ time theora_encoder -o test_speed_3.ogv -k 64 -v 7.5 -z 3 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 5632kbps                 
      1440 frames in 50.309 seconds: 59.793 Mpixel/s 1.14x
done.

real	0m50,360s
user	0m49,595s
sys	0m0,717s

$ time theora_encoder -o test_speed_4.ogv -k 64 -v 7.5 -z 4 test.y4m 
File test.y4m is 1920x1080 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 12657kbps                 
      1440 frames in 28.060 seconds: 107.202 Mpixel/s 2.05x
done.

real	0m29,148s
user	0m27,423s
sys	0m0,641s
Execution log for Spring
$ time theora_encoder -o test_speed_0.ogv -k 64 -v 7.5 -z 0 test.y4m 
File test.y4m is 2048x858 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 3416kbps                 
      1440 frames in 81.737 seconds: 31.174 Mpixel/s 0.70x
done.

real	1m21,984s
user	1m21,020s
sys	0m0,721s

$ time theora_encoder -o test_speed_1.ogv -k 64 -v 7.5 -z 1 test.y4m 
File test.y4m is 2048x858 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 3391kbps                 
      1440 frames in 75.794 seconds: 33.618 Mpixel/s 0.76x
done.

real	1m15,832s
user	1m14,801s
sys	0m0,997s

$ time theora_encoder -o test_speed_2.ogv -k 64 -v 7.5 -z 2 test.y4m 
File test.y4m is 2048x858 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 4122kbps                 
      1440 frames in 49.290 seconds: 51.694 Mpixel/s 1.17x
done.

real	0m49,711s
user	0m48,658s
sys	0m0,636s

$ time theora_encoder -o test_speed_3.ogv -k 64 -v 7.5 -z 3 test.y4m 
File test.y4m is 2048x858 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 4220kbps                 
      1440 frames in 37.875 seconds: 67.275 Mpixel/s 1.52x
done.

real	0m38,064s
user	0m37,227s
sys	0m0,652s

$ time theora_encoder -o test_speed_4.ogv -k 64 -v 7.5 -z 4 test.y4m 
File test.y4m is 2048x858 25.00 fps 420jpeg video.
Compressing....                                          
      0:00:57.60 audio: 0kbps video: 9001kbps                 
      1440 frames in 21.144 seconds: 120.508 Mpixel/s 2.72x
done.

real	0m23,719s
user	0m20,499s
sys	0m0,648s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants