diff options
author | anish bhobe <anishbhobe@hotmail.com> | 2022-07-29 00:23:11 +0200 |
---|---|---|
committer | Lyuma <xn.lyuma@gmail.com> | 2023-01-30 18:39:29 -0800 |
commit | 42a9c33fadec66f82f30e32e3d780814d64dd7c9 (patch) | |
tree | 721389fa858ff63eb61a2510972d9f7ea5931304 | |
parent | e9de988020f3d46c3e7b4fd5a8a80724996035e0 (diff) |
Updates VideoDecoder plugin API to GDExtension.
Adds VideoStream and relevant resource loaders to migrate
external GDNative plugins to GDExtension.
Adds a VideoStreamLoader as a specialization of ResourceFormatLoader
as ClassDB::is_parent_class is inaccessible from GDExtension currently.
Using Object* instead of Ref<T> in order to avoid the refcount bug
(godotengine/godot-cpp#652)
Also another bug is in ResourceLoader in use on the extension side that
requires fixing.
-rw-r--r-- | doc/classes/VideoStream.xml | 14 | ||||
-rw-r--r-- | doc/classes/VideoStreamPlayback.xml | 104 | ||||
-rw-r--r-- | modules/theora/doc_classes/VideoStreamTheora.xml | 15 | ||||
-rw-r--r-- | modules/theora/video_stream_theora.cpp | 31 | ||||
-rw-r--r-- | modules/theora/video_stream_theora.h | 15 | ||||
-rw-r--r-- | scene/gui/video_stream_player.cpp | 7 | ||||
-rw-r--r-- | scene/gui/video_stream_player.h | 1 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 3 | ||||
-rw-r--r-- | scene/resources/video_stream.cpp | 198 | ||||
-rw-r--r-- | scene/resources/video_stream.h | 76 |
10 files changed, 383 insertions, 81 deletions
diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 2797ad3513..648c3edd73 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -8,4 +8,18 @@ </description> <tutorials> </tutorials> + <methods> + <method name="_instantiate_playback" qualifiers="virtual"> + <return type="VideoStreamPlayback" /> + <description> + Called when the video starts playing, to initialize and return a subclass of [VideoStreamPlayback]. + </description> + </method> + </methods> + <members> + <member name="file" type="String" setter="set_file" getter="get_file" default=""""> + The video file path or URI that this [VideoStream] resource handles. + For [VideoStreamTheora], this filename should be an Ogg Theora video file with the [code].ogv[/code] extension. + </member> + </members> </class> diff --git a/doc/classes/VideoStreamPlayback.xml b/doc/classes/VideoStreamPlayback.xml new file mode 100644 index 0000000000..8d8b4fe5b1 --- /dev/null +++ b/doc/classes/VideoStreamPlayback.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VideoStreamPlayback" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Internal class used by [VideoStream] to manage playback state when played from a [VideoStreamPlayer]. + </brief_description> + <description> + This class is intended to be overridden by video decoder extensions with custom implementations of [VideoStream]. + </description> + <tutorials> + </tutorials> + <methods> + <method name="_get_channels" qualifiers="virtual const"> + <return type="int" /> + <description> + Returns the number of audio channels. + </description> + </method> + <method name="_get_length" qualifiers="virtual const"> + <return type="float" /> + <description> + Returns the video duration in seconds, if known, or 0 if unknown. + </description> + </method> + <method name="_get_mix_rate" qualifiers="virtual const"> + <return type="int" /> + <description> + Returns the audio sample rate used for mixing. + </description> + </method> + <method name="_get_playback_position" qualifiers="virtual const"> + <return type="float" /> + <description> + Return the current playback timestamp. Called in response to the [member VideoStreamPlayer.stream_position] getter. + </description> + </method> + <method name="_get_texture" qualifiers="virtual const"> + <return type="Texture2D" /> + <description> + Allocates a [Texture2D] in which decoded video frames will be drawn. + </description> + </method> + <method name="_is_paused" qualifiers="virtual const"> + <return type="bool" /> + <description> + Returns the paused status, as set by [method _set_paused]. + </description> + </method> + <method name="_is_playing" qualifiers="virtual const"> + <return type="bool" /> + <description> + Returns the playback state, as determined by calls to [method _play] and [method _stop]. + </description> + </method> + <method name="_play" qualifiers="virtual"> + <return type="void" /> + <description> + Called in response to [member VideoStreamPlayer.autoplay] or [method VideoStreamPlayer.play]. Note that manual playback may also invoke [method _stop] multiple times before this method is called. [method _is_playing] should return true once playing. + </description> + </method> + <method name="_seek" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="time" type="float" /> + <description> + Seeks to [code]time[/code] seconds. Called in response to the [member VideoStreamPlayer.stream_position] setter. + </description> + </method> + <method name="_set_audio_track" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="idx" type="int" /> + <description> + Select the audio track [code]idx[/code]. Called when playback starts, and in response to the [member VideoStreamPlayer.audio_track] setter. + </description> + </method> + <method name="_set_paused" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="paused" type="bool" /> + <description> + Set the paused status of video playback. [method _is_paused] must return [code]paused[/code]. Called in response to the [member VideoStreamPlayer.paused] setter. + </description> + </method> + <method name="_stop" qualifiers="virtual"> + <return type="void" /> + <description> + Stops playback. May be called multiple times before [method _play], or in response to [method VideoStreamPlayer.stop]. [method _is_playing] should return false once stopped. + </description> + </method> + <method name="_update" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="delta" type="float" /> + <description> + Ticks video playback for [code]delta[/code] seconds. Called every frame as long as [method _is_paused] and [method _is_playing] return true. + </description> + </method> + <method name="mix_audio"> + <return type="int" /> + <param index="0" name="num_frames" type="int" /> + <param index="1" name="buffer" type="PackedFloat32Array" default="PackedFloat32Array()" /> + <param index="2" name="offset" type="int" default="0" /> + <description> + Render [code]num_frames[/code] audio frames (of [method _get_channels] floats each) from [code]buffer[/code], starting from index [code]offset[/code] in the array. Returns the number of audio frames rendered, or -1 on error. + </description> + </method> + </methods> +</class> diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index e07af8f169..3ec762a880 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -9,19 +9,4 @@ </description> <tutorials> </tutorials> - <methods> - <method name="get_file"> - <return type="String" /> - <description> - Returns the Ogg Theora video file handled by this [VideoStreamTheora]. - </description> - </method> - <method name="set_file"> - <return type="void" /> - <param index="0" name="file" type="String" /> - <description> - Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension. - </description> - </method> - </methods> </class> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index c600924a3e..b38f7225a2 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -574,25 +574,10 @@ bool VideoStreamPlaybackTheora::is_paused() const { return paused; } -void VideoStreamPlaybackTheora::set_loop(bool p_enable) { -} - -bool VideoStreamPlaybackTheora::has_loop() const { - return false; -} - double VideoStreamPlaybackTheora::get_length() const { return 0; } -String VideoStreamPlaybackTheora::get_stream_name() const { - return ""; -} - -int VideoStreamPlaybackTheora::get_loop_count() const { - return 0; -} - double VideoStreamPlaybackTheora::get_playback_position() const { return get_time(); } @@ -601,11 +586,6 @@ void VideoStreamPlaybackTheora::seek(double p_time) { WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDExtension-provided video streams)."); } -void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { - mix_callback = p_callback; - mix_udata = p_userdata; -} - int VideoStreamPlaybackTheora::get_channels() const { return vi.channels; } @@ -657,16 +637,9 @@ VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() { memdelete(thread_sem); #endif clear(); -} - -void VideoStreamTheora::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file); - ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); -} +}; -//////////// +void VideoStreamTheora::_bind_methods() {} Ref<Resource> ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index f047440df7..32adc28a88 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -99,8 +99,6 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback { Ref<ImageTexture> texture; - AudioMixCallback mix_callback = nullptr; - void *mix_udata = nullptr; bool paused = false; #ifdef THEORA_USE_THREAD_STREAMING @@ -133,15 +131,8 @@ public: virtual void set_paused(bool p_paused) override; virtual bool is_paused() const override; - virtual void set_loop(bool p_enable) override; - virtual bool has_loop() const override; - virtual double get_length() const override; - virtual String get_stream_name() const; - - virtual int get_loop_count() const; - virtual double get_playback_position() const override; virtual void seek(double p_time) override; @@ -150,7 +141,6 @@ public: virtual Ref<Texture2D> get_texture() const override; virtual void update(double p_delta) override; - 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; @@ -163,9 +153,6 @@ public: class VideoStreamTheora : public VideoStream { GDCLASS(VideoStreamTheora, VideoStream); - String file; - int audio_track; - protected: static void _bind_methods(); @@ -177,8 +164,6 @@ public: return pb; } - void set_file(const String &p_file) { file = p_file; } - String get_file() { return file; } void set_audio_track(int p_track) override { audio_track = p_track; } VideoStreamTheora() { audio_track = 0; } diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 6eb25bf852..1f3bbff779 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -236,7 +236,6 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { AudioServer::get_singleton()->unlock(); if (!playback.is_null()) { - playback->set_loop(loops); playback->set_paused(paused); texture = playback->get_texture(); @@ -344,6 +343,12 @@ int VideoStreamPlayer::get_buffering_msec() const { void VideoStreamPlayer::set_audio_track(int p_track) { audio_track = p_track; + if (stream.is_valid()) { + stream->set_audio_track(audio_track); + } + if (playback.is_valid()) { + playback->set_audio_track(audio_track); + } } int VideoStreamPlayer::get_audio_track() const { diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h index 09ef272a9a..1fd599a9e1 100644 --- a/scene/gui/video_stream_player.h +++ b/scene/gui/video_stream_player.h @@ -65,7 +65,6 @@ class VideoStreamPlayer : public Control { float volume = 1.0; double last_audio_time = 0.0; bool expand = false; - bool loops = false; int buffering_ms = 500; int audio_track = 0; int bus_index = 0; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 7bebf1cfd3..39fc03f9f1 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -393,6 +393,8 @@ void register_scene_types() { GDREGISTER_CLASS(LineEdit); GDREGISTER_CLASS(VideoStreamPlayer); + GDREGISTER_VIRTUAL_CLASS(VideoStreamPlayback); + GDREGISTER_VIRTUAL_CLASS(VideoStream); #ifndef ADVANCED_GUI_DISABLED GDREGISTER_CLASS(FileDialog); @@ -906,7 +908,6 @@ void register_scene_types() { #ifndef _3D_DISABLED GDREGISTER_CLASS(AudioStreamPlayer3D); #endif - GDREGISTER_ABSTRACT_CLASS(VideoStream); GDREGISTER_CLASS(AudioStreamWAV); GDREGISTER_CLASS(AudioStreamPolyphonic); GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic); diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp new file mode 100644 index 0000000000..ee1a47c338 --- /dev/null +++ b/scene/resources/video_stream.cpp @@ -0,0 +1,198 @@ +/**************************************************************************/ +/* video_stream.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "video_stream.h" + +#include "core/config/project_settings.h" +#include "servers/audio_server.h" + +// VideoStreamPlayback starts here. + +void VideoStreamPlayback::_bind_methods() { + ClassDB::bind_method(D_METHOD("mix_audio", "num_frames", "buffer", "offset"), &VideoStreamPlayback::mix_audio, DEFVAL(PackedFloat32Array()), DEFVAL(0)); + GDVIRTUAL_BIND(_stop); + GDVIRTUAL_BIND(_play); + GDVIRTUAL_BIND(_is_playing); + GDVIRTUAL_BIND(_set_paused, "paused"); + GDVIRTUAL_BIND(_is_paused); + GDVIRTUAL_BIND(_get_length); + GDVIRTUAL_BIND(_get_playback_position); + GDVIRTUAL_BIND(_seek, "time"); + GDVIRTUAL_BIND(_set_audio_track, "idx"); + GDVIRTUAL_BIND(_get_texture); + GDVIRTUAL_BIND(_update, "delta"); + GDVIRTUAL_BIND(_get_channels); + GDVIRTUAL_BIND(_get_mix_rate); +} + +VideoStreamPlayback::VideoStreamPlayback() { +} + +VideoStreamPlayback::~VideoStreamPlayback() { +} + +void VideoStreamPlayback::stop() { + GDVIRTUAL_CALL(_stop); +} + +void VideoStreamPlayback::play() { + GDVIRTUAL_CALL(_play); +} + +bool VideoStreamPlayback::is_playing() const { + bool ret; + if (GDVIRTUAL_CALL(_is_playing, ret)) { + return ret; + } + return false; +} + +void VideoStreamPlayback::set_paused(bool p_paused) { + GDVIRTUAL_CALL(_is_playing, p_paused); +} + +bool VideoStreamPlayback::is_paused() const { + bool ret; + if (GDVIRTUAL_CALL(_is_paused, ret)) { + return ret; + } + return false; +} + +double VideoStreamPlayback::get_length() const { + double ret; + if (GDVIRTUAL_CALL(_get_length, ret)) { + return ret; + } + return 0; +} + +double VideoStreamPlayback::get_playback_position() const { + double ret; + if (GDVIRTUAL_CALL(_get_playback_position, ret)) { + return ret; + } + return 0; +} + +void VideoStreamPlayback::seek(double p_time) { + GDVIRTUAL_CALL(_seek, p_time); +} + +void VideoStreamPlayback::set_audio_track(int p_idx) { + GDVIRTUAL_CALL(_set_audio_track, p_idx); +} + +Ref<Texture2D> VideoStreamPlayback::get_texture() const { + Ref<Texture2D> ret; + if (GDVIRTUAL_CALL(_get_texture, ret)) { + return ret; + } + return nullptr; +} + +void VideoStreamPlayback::update(double p_delta) { + if (!GDVIRTUAL_CALL(_update, p_delta)) { + ERR_FAIL_MSG("VideoStreamPlayback::update unimplemented"); + } +} + +void VideoStreamPlayback::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { + mix_callback = p_callback; + mix_udata = p_userdata; +} + +int VideoStreamPlayback::get_channels() const { + int ret; + if (GDVIRTUAL_CALL(_get_channels, ret)) { + _channel_count = ret; + return ret; + } + return 0; +} + +int VideoStreamPlayback::get_mix_rate() const { + int ret; + if (GDVIRTUAL_CALL(_get_mix_rate, ret)) { + return ret; + } + return 0; +} + +int VideoStreamPlayback::mix_audio(int num_frames, PackedFloat32Array buffer, int offset) { + if (num_frames <= 0) { + return 0; + } + if (!mix_callback) { + return -1; + } + ERR_FAIL_INDEX_V(offset, buffer.size(), -1); + ERR_FAIL_INDEX_V((_channel_count < 1 ? 1 : _channel_count) * num_frames - 1, buffer.size() - offset, -1); + return mix_callback(mix_udata, buffer.ptr() + offset, num_frames); +} + +/* --- NOTE VideoStream starts here. ----- */ + +Ref<VideoStreamPlayback> VideoStream::instantiate_playback() { + Ref<VideoStreamPlayback> ret; + if (GDVIRTUAL_CALL(_instantiate_playback, ret)) { + ERR_FAIL_COND_V_MSG(ret.is_null(), nullptr, "Plugin returned null playback"); + ret->set_audio_track(audio_track); + return ret; + } + return nullptr; +} + +void VideoStream::set_file(const String &p_file) { + file = p_file; +} + +String VideoStream::get_file() { + return file; +} + +void VideoStream::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStream::set_file); + ClassDB::bind_method(D_METHOD("get_file"), &VideoStream::get_file); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file"), "set_file", "get_file"); + + GDVIRTUAL_BIND(_instantiate_playback); +} + +VideoStream::VideoStream() { +} + +VideoStream::~VideoStream() { +} + +void VideoStream::set_audio_track(int p_track) { + audio_track = p_track; +} diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index f83c621d0a..b91a7acf35 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -31,6 +31,7 @@ #ifndef VIDEO_STREAM_H #define VIDEO_STREAM_H +#include "core/io/file_access.h" #include "scene/resources/texture.h" class VideoStreamPlayback : public Resource { @@ -39,40 +40,77 @@ class VideoStreamPlayback : public Resource { public: typedef int (*AudioMixCallback)(void *p_udata, const float *p_data, int p_frames); - virtual void stop() = 0; - virtual void play() = 0; +protected: + AudioMixCallback mix_callback = nullptr; + void *mix_udata = nullptr; + mutable int _channel_count = 0; // Used only to assist with bounds checking in mix_audio. + + static void _bind_methods(); + GDVIRTUAL0(_stop); + GDVIRTUAL0(_play); + GDVIRTUAL0RC(bool, _is_playing); + GDVIRTUAL1(_set_paused, bool); + GDVIRTUAL0RC(bool, _is_paused); + GDVIRTUAL0RC(double, _get_length); + GDVIRTUAL0RC(double, _get_playback_position); + GDVIRTUAL1(_seek, double); + GDVIRTUAL1(_set_audio_track, int); + GDVIRTUAL0RC(Ref<Texture2D>, _get_texture); + GDVIRTUAL1(_update, double); + GDVIRTUAL0RC(int, _get_channels); + GDVIRTUAL0RC(int, _get_mix_rate); + + int mix_audio(int num_frames, PackedFloat32Array buffer = {}, int offset = 0); - virtual bool is_playing() const = 0; +public: + VideoStreamPlayback(); + virtual ~VideoStreamPlayback(); - virtual void set_paused(bool p_paused) = 0; - virtual bool is_paused() const = 0; + virtual void stop(); + virtual void play(); - virtual void set_loop(bool p_enable) = 0; - virtual bool has_loop() const = 0; + virtual bool is_playing() const; - virtual double get_length() const = 0; + virtual void set_paused(bool p_paused); + virtual bool is_paused() const; - virtual double get_playback_position() const = 0; - virtual void seek(double p_time) = 0; + virtual double get_length() const; - virtual void set_audio_track(int p_idx) = 0; + virtual double get_playback_position() const; + virtual void seek(double p_time); - virtual Ref<Texture2D> get_texture() const = 0; + virtual void set_audio_track(int p_idx); - virtual void update(double p_delta) = 0; + virtual Ref<Texture2D> get_texture() const; + virtual void update(double p_delta); - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) = 0; - virtual int get_channels() const = 0; - virtual int get_mix_rate() const = 0; + virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); + virtual int get_channels() const; + virtual int get_mix_rate() const; }; class VideoStream : public Resource { GDCLASS(VideoStream, Resource); - OBJ_SAVE_TYPE(VideoStream); // Saves derived classes with common type so they can be interchanged. + OBJ_SAVE_TYPE(VideoStream); + +protected: + static void + _bind_methods(); + + GDVIRTUAL0R(Ref<VideoStreamPlayback>, _instantiate_playback); + + String file; + int audio_track = 0; public: - virtual void set_audio_track(int p_track) = 0; - virtual Ref<VideoStreamPlayback> instantiate_playback() = 0; + void set_file(const String &p_file); + String get_file(); + + virtual void set_audio_track(int p_track); + virtual Ref<VideoStreamPlayback> instantiate_playback(); + + VideoStream(); + ~VideoStream(); }; #endif // VIDEO_STREAM_H |