diff options
Diffstat (limited to 'modules/webm')
-rw-r--r-- | modules/webm/config.py | 5 | ||||
-rw-r--r-- | modules/webm/doc_classes/VideoStreamWebm.xml | 3 | ||||
-rw-r--r-- | modules/webm/libvpx/SCsub | 4 | ||||
-rw-r--r-- | modules/webm/register_types.cpp | 2 | ||||
-rw-r--r-- | modules/webm/video_stream_webm.cpp | 150 | ||||
-rw-r--r-- | modules/webm/video_stream_webm.h | 66 |
6 files changed, 92 insertions, 138 deletions
diff --git a/modules/webm/config.py b/modules/webm/config.py index 93b49d177a..99f8ace114 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -1,5 +1,8 @@ def can_build(env, platform): - return platform not in ["iphone"] + if platform in ["iphone"]: + return False + + return env.module_check_dependencies("webm", ["ogg", "opus", "vorbis"]) def configure(env): diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index dfa04720cf..2edbc08cc8 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -4,7 +4,8 @@ [VideoStream] resource for WebM videos. </brief_description> <description> - [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. + [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU. + [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora] </description> <tutorials> </tutorials> diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index dd6866ad0e..d0744fa313 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -238,6 +238,7 @@ else: is_x11_or_server_arm = (env["platform"] == "linuxbsd" or env["platform"] == "server") and ( platform.machine().startswith("arm") or platform.machine().startswith("aarch") ) + is_macos_x86 = env["platform"] == "osx" and ("arch" in env and (env["arch"] != "arm64")) is_ios_x86 = env["platform"] == "iphone" and ("arch" in env and env["arch"].startswith("x86")) is_android_x86 = env["platform"] == "android" and env["android_arch"].startswith("x86") if is_android_x86: @@ -248,14 +249,15 @@ else: and ( env["platform"] == "windows" or env["platform"] == "linuxbsd" - or env["platform"] == "osx" or env["platform"] == "haiku" + or is_macos_x86 or is_android_x86 or is_ios_x86 ) ) webm_cpu_arm = ( is_x11_or_server_arm + or (not is_macos_x86 and env["platform"] == "osx") or (not is_ios_x86 and env["platform"] == "iphone") or (not is_android_x86 and env["platform"] == "android") ) diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp index 5449dd458c..6248787879 100644 --- a/modules/webm/register_types.cpp +++ b/modules/webm/register_types.cpp @@ -35,7 +35,6 @@ static Ref<ResourceFormatLoaderWebm> resource_loader_webm; void register_webm_types() { - resource_loader_webm.instance(); ResourceLoader::add_resource_format_loader(resource_loader_webm, true); @@ -43,7 +42,6 @@ void register_webm_types() { } void unregister_webm_types() { - ResourceLoader::remove_resource_format_loader(resource_loader_webm); resource_loader_webm.unref(); } diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index a2d0f78f5f..2128b82e3f 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -30,9 +30,9 @@ #include "video_stream_webm.h" +#include "core/config/project_settings.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/project_settings.h" #include "servers/audio_server.h" #include "thirdparty/misc/yuv2rgb.h" @@ -48,41 +48,39 @@ #include <mkvparser/mkvparser.h> class MkvReader : public mkvparser::IMkvReader { - public: MkvReader(const String &p_file) { - file = FileAccess::open(p_file, FileAccess::READ); ERR_FAIL_COND_MSG(!file, "Failed loading resource: '" + p_file + "'."); } ~MkvReader() { - - if (file) + if (file) { memdelete(file); + } } virtual int Read(long long pos, long len, unsigned char *buf) { - if (file) { - - if (file->get_position() != (size_t)pos) + if (file->get_position() != (size_t)pos) { file->seek(pos); - if (file->get_buffer(buf, len) == len) + } + if (file->get_buffer(buf, len) == len) { return 0; + } } return -1; } virtual int Length(long long *total, long long *available) { - if (file) { - const size_t len = file->get_len(); - if (total) + if (total) { *total = len; - if (available) + } + if (available) { *available = len; + } return 0; } return -1; @@ -95,47 +93,23 @@ private: /**/ VideoStreamPlaybackWebm::VideoStreamPlaybackWebm() : - audio_track(0), - webm(nullptr), - video(nullptr), - audio(nullptr), - video_frames(nullptr), - audio_frame(nullptr), - video_frames_pos(0), - video_frames_capacity(0), - num_decoded_samples(0), - samples_offset(-1), - mix_callback(nullptr), - mix_udata(nullptr), - playing(false), - paused(false), - delay_compensation(0.0), - time(0.0), - video_frame_delay(0.0), - video_pos(0.0), - texture(memnew(ImageTexture)), - pcm(nullptr) {} -VideoStreamPlaybackWebm::~VideoStreamPlaybackWebm() { + texture(memnew(ImageTexture)) {} +VideoStreamPlaybackWebm::~VideoStreamPlaybackWebm() { delete_pointers(); } bool VideoStreamPlaybackWebm::open_file(const String &p_file) { - file_name = p_file; webm = memnew(WebMDemuxer(new MkvReader(file_name), 0, audio_track)); if (webm->isOpen()) { - video = memnew(VPXDecoder(*webm, OS::get_singleton()->get_processor_count())); if (video->isOpen()) { - audio = memnew(OpusVorbisDecoder(*webm)); if (audio->isOpen()) { - audio_frame = memnew(WebMFrame); pcm = (float *)memalloc(sizeof(float) * audio->getBufferSamples() * webm->getChannels()); } else { - memdelete(audio); audio = nullptr; } @@ -157,9 +131,7 @@ bool VideoStreamPlaybackWebm::open_file(const String &p_file) { } void VideoStreamPlaybackWebm::stop() { - if (playing) { - delete_pointers(); pcm = nullptr; @@ -180,8 +152,8 @@ void VideoStreamPlaybackWebm::stop() { time = 0.0; playing = false; } -void VideoStreamPlaybackWebm::play() { +void VideoStreamPlaybackWebm::play() { stop(); delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms"); @@ -191,58 +163,52 @@ void VideoStreamPlaybackWebm::play() { } bool VideoStreamPlaybackWebm::is_playing() const { - return playing; } void VideoStreamPlaybackWebm::set_paused(bool p_paused) { - paused = p_paused; } -bool VideoStreamPlaybackWebm::is_paused() const { +bool VideoStreamPlaybackWebm::is_paused() const { return paused; } void VideoStreamPlaybackWebm::set_loop(bool p_enable) { - //Empty } -bool VideoStreamPlaybackWebm::has_loop() const { +bool VideoStreamPlaybackWebm::has_loop() const { return false; } float VideoStreamPlaybackWebm::get_length() const { - - if (webm) + if (webm) { return webm->getLength(); + } return 0.0f; } float VideoStreamPlaybackWebm::get_playback_position() const { - return video_pos; } -void VideoStreamPlaybackWebm::seek(float p_time) { +void VideoStreamPlaybackWebm::seek(float p_time) { //Not implemented } void VideoStreamPlaybackWebm::set_audio_track(int p_idx) { - audio_track = p_idx; } Ref<Texture2D> VideoStreamPlaybackWebm::get_texture() const { - return texture; } void VideoStreamPlaybackWebm::update(float p_delta) { - - if ((!playing || paused) || !video) + if ((!playing || paused) || !video) { return; + } time += p_delta; @@ -253,16 +219,13 @@ void VideoStreamPlaybackWebm::update(float p_delta) { bool audio_buffer_full = false; if (samples_offset > -1) { - //Mix remaining samples const int to_read = num_decoded_samples - samples_offset; const int mixed = mix_callback(mix_udata, pcm + samples_offset * webm->getChannels(), to_read); if (mixed != to_read) { - samples_offset += mixed; audio_buffer_full = true; } else { - samples_offset = -1; } } @@ -270,10 +233,8 @@ void VideoStreamPlaybackWebm::update(float p_delta) { const bool hasAudio = (audio && mix_callback); while ((hasAudio && !audio_buffer_full && !has_enough_video_frames()) || (!hasAudio && video_frames_pos == 0)) { - if (hasAudio && !audio_buffer_full && audio_frame->isValid() && audio->getPCMF(*audio_frame, pcm, num_decoded_samples) && num_decoded_samples > 0) { - const int mixed = mix_callback(mix_udata, pcm, num_decoded_samples); if (mixed != num_decoded_samples) { @@ -284,42 +245,37 @@ void VideoStreamPlaybackWebm::update(float p_delta) { WebMFrame *video_frame; if (video_frames_pos >= video_frames_capacity) { - WebMFrame **video_frames_new = (WebMFrame **)memrealloc(video_frames, ++video_frames_capacity * sizeof(void *)); ERR_FAIL_COND(!video_frames_new); //Out of memory (video_frames = video_frames_new)[video_frames_capacity - 1] = memnew(WebMFrame); } video_frame = video_frames[video_frames_pos]; - if (!webm->readFrame(video_frame, audio_frame)) //This will invalidate frames + if (!webm->readFrame(video_frame, audio_frame)) { //This will invalidate frames break; //Can't demux, EOS? + } - if (video_frame->isValid()) + if (video_frame->isValid()) { ++video_frames_pos; + } }; bool video_frame_done = false; while (video_frames_pos > 0 && !video_frame_done) { - WebMFrame *video_frame = video_frames[0]; // It seems VPXDecoder::decode has to be executed even though we might skip this frame if (video->decode(*video_frame)) { - VPXDecoder::IMAGE_ERROR err; VPXDecoder::Image image; if (should_process(*video_frame)) { - if ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) { - if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) { - uint8_t *w = frame_data.ptrw(); bool converted = false; if (image.chromaShiftW == 0 && image.chromaShiftH == 0 && image.cs == VPX_CS_SRGB) { - uint8_t *wp = w; unsigned char *rRow = image.planes[2]; unsigned char *gRow = image.planes[0]; @@ -337,22 +293,18 @@ void VideoStreamPlaybackWebm::update(float p_delta) { } converted = true; } else if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { - yuv420_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); //libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { - yuv422_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); //libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { - yuv444_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); //libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { - //libyuv::I411ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2] image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); //converted = true; } @@ -372,25 +324,27 @@ void VideoStreamPlaybackWebm::update(float p_delta) { video_frames[video_frames_pos] = video_frame; } - if (video_frames_pos == 0 && webm->isEOS()) + if (video_frames_pos == 0 && webm->isEOS()) { stop(); + } } void VideoStreamPlaybackWebm::set_mix_callback(VideoStreamPlayback::AudioMixCallback p_callback, void *p_userdata) { - mix_callback = p_callback; mix_udata = p_userdata; } -int VideoStreamPlaybackWebm::get_channels() const { - if (audio) +int VideoStreamPlaybackWebm::get_channels() const { + if (audio) { return webm->getChannels(); + } return 0; } -int VideoStreamPlaybackWebm::get_mix_rate() const { - if (audio) +int VideoStreamPlaybackWebm::get_mix_rate() const { + if (audio) { return webm->getSampleRate(); + } return 0; } @@ -415,52 +369,54 @@ bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) { } void VideoStreamPlaybackWebm::delete_pointers() { - - if (pcm) + if (pcm) { memfree(pcm); + } - if (audio_frame) + if (audio_frame) { memdelete(audio_frame); + } if (video_frames) { - for (int i = 0; i < video_frames_capacity; ++i) + for (int i = 0; i < video_frames_capacity; ++i) { memdelete(video_frames[i]); + } memfree(video_frames); } - if (video) + if (video) { memdelete(video); - if (audio) + } + if (audio) { memdelete(audio); + } - if (webm) + if (webm) { memdelete(webm); + } } /**/ -VideoStreamWebm::VideoStreamWebm() : - audio_track(0) {} +VideoStreamWebm::VideoStreamWebm() {} Ref<VideoStreamPlayback> VideoStreamWebm::instance_playback() { - Ref<VideoStreamPlaybackWebm> pb = memnew(VideoStreamPlaybackWebm); pb->set_audio_track(audio_track); - if (pb->open_file(file)) + if (pb->open_file(file)) { return pb; + } return nullptr; } void VideoStreamWebm::set_file(const String &p_file) { - file = p_file; } -String VideoStreamWebm::get_file() { +String VideoStreamWebm::get_file() { return file; } void VideoStreamWebm::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamWebm::set_file); ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamWebm::get_file); @@ -468,14 +424,12 @@ void VideoStreamWebm::_bind_methods() { } void VideoStreamWebm::set_audio_track(int p_track) { - audio_track = p_track; } //////////// RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { if (r_error) { @@ -499,19 +453,17 @@ RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_origina } void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("webm"); } bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const { - return ClassDB::is_parent_class(p_type, "VideoStream"); } String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "webm") + if (el == "webm") { return "VideoStreamWebm"; + } return ""; } diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index 6677fb85aa..25675cb248 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -40,31 +40,30 @@ class VPXDecoder; class OpusVorbisDecoder; class VideoStreamPlaybackWebm : public VideoStreamPlayback { - GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback); String file_name; - int audio_track; + int audio_track = 0; - WebMDemuxer *webm; - VPXDecoder *video; - OpusVorbisDecoder *audio; + WebMDemuxer *webm = nullptr; + VPXDecoder *video = nullptr; + OpusVorbisDecoder *audio = nullptr; - WebMFrame **video_frames, *audio_frame; - int video_frames_pos, video_frames_capacity; + WebMFrame **video_frames = nullptr, *audio_frame = nullptr; + int video_frames_pos = 0, video_frames_capacity = 0; - int num_decoded_samples, samples_offset; - AudioMixCallback mix_callback; - void *mix_udata; + int num_decoded_samples = 0, samples_offset = -1; + AudioMixCallback mix_callback = nullptr; + void *mix_udata = nullptr; - bool playing, paused; - double delay_compensation; - double time, video_frame_delay, video_pos; + bool playing = false, paused = false; + double delay_compensation = 0.0; + double time = 0.0, video_frame_delay = 0.0, video_pos = 0.0; Vector<uint8_t> frame_data; Ref<ImageTexture> texture; - float *pcm; + float *pcm = nullptr; public: VideoStreamPlaybackWebm(); @@ -72,30 +71,30 @@ public: bool open_file(const String &p_file); - virtual void stop(); - virtual void play(); + virtual void stop() override; + virtual void play() override; - virtual bool is_playing() const; + virtual bool is_playing() const override; - virtual void set_paused(bool p_paused); - virtual bool is_paused() const; + virtual void set_paused(bool p_paused) override; + virtual bool is_paused() const override; - virtual void set_loop(bool p_enable); - virtual bool has_loop() const; + virtual void set_loop(bool p_enable) override; + virtual bool has_loop() const override; - virtual float get_length() const; + virtual float get_length() const override; - virtual float get_playback_position() const; - virtual void seek(float p_time); + virtual float get_playback_position() const override; + virtual void seek(float p_time) override; - virtual void set_audio_track(int p_idx); + virtual void set_audio_track(int p_idx) override; - virtual Ref<Texture2D> get_texture() const; - virtual void update(float p_delta); + virtual Ref<Texture2D> get_texture() const override; + virtual void update(float p_delta) override; - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); - virtual int get_channels() const; - virtual int get_mix_rate() const; + virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override; + virtual int get_channels() const override; + virtual int get_mix_rate() const override; private: inline bool has_enough_video_frames() const; @@ -107,11 +106,10 @@ private: /**/ class VideoStreamWebm : public VideoStream { - GDCLASS(VideoStreamWebm, VideoStream); String file; - int audio_track; + int audio_track = 0; protected: static void _bind_methods(); @@ -119,11 +117,11 @@ protected: public: VideoStreamWebm(); - virtual Ref<VideoStreamPlayback> instance_playback(); + virtual Ref<VideoStreamPlayback> instance_playback() override; virtual void set_file(const String &p_file); String get_file(); - virtual void set_audio_track(int p_track); + virtual void set_audio_track(int p_track) override; }; class ResourceFormatLoaderWebm : public ResourceFormatLoader { |