ソースを参照

Merge pull request #20552 from KidRigger/gsoc-peer

Added interface for GDNative Videodecoder.
Rémi Verschelde 6 年 前
コミット
fe8cdafbf4

+ 1 - 0
modules/gdnative/SCsub

@@ -19,6 +19,7 @@ Export('env_gdnative')
 SConscript("net/SCsub")
 SConscript("arvr/SCsub")
 SConscript("pluginscript/SCsub")
+SConscript("videodecoder/SCsub")
 
 
 from platform_methods import run_in_subprocess

+ 36 - 0
modules/gdnative/gdnative_api.json

@@ -6374,6 +6374,42 @@
           ]
         }
       ]
+    },
+    {
+      "name": "videodecoder",
+      "type": "VIDEODECODER",
+      "version": {
+        "major": 0,
+        "minor": 1
+      },
+      "next": null,
+      "api": [
+        {
+          "name": "godot_videodecoder_file_read",
+          "return_type": "godot_int",
+          "arguments": [
+            ["void *", "file_ptr"],
+            ["uint8_t *", "buf"],
+            ["int", "buf_size"]
+          ]
+        },
+        {
+          "name": "godot_videodecoder_file_seek",
+          "return_type": "int64_t",
+          "arguments": [
+            [ "void *", "file_ptr"],
+            ["int64_t", "pos"],
+            ["int", "whence"]
+          ]
+        },
+        {
+          "name": "godot_videodecoder_register_decoder",
+          "return_type": "void",
+          "arguments": [
+            ["const godot_videodecoder_interface_gdnative *", "p_interface"]
+          ]
+        }
+      ]
     }
   ]
 }

+ 2 - 0
modules/gdnative/gdnative_builders.py

@@ -46,6 +46,7 @@ def _build_gdnative_api_struct_header(api):
         '#include <arvr/godot_arvr.h>',
         '#include <nativescript/godot_nativescript.h>',
         '#include <pluginscript/godot_pluginscript.h>',
+        '#include <videodecoder/godot_videodecoder.h>',
         '',
         '#define GDNATIVE_API_INIT(options) do {  \\\n' + '  \\\n'.join(gdnative_api_init_macro) + '  \\\n } while (0)',
         '',
@@ -244,6 +245,7 @@ def _build_gdnative_wrapper_code(api):
         '#include <nativescript/godot_nativescript.h>',
         '#include <pluginscript/godot_pluginscript.h>',
         '#include <arvr/godot_arvr.h>',
+        '#include <videodecoder/godot_videodecoder.h>',
         '',
         '#include <gdnative_api_struct.gen.h>',
         '',

+ 75 - 0
modules/gdnative/include/videodecoder/godot_videodecoder.h

@@ -0,0 +1,75 @@
+/*************************************************************************/
+/*  godot_videodecoder.h                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef GODOT_NATIVEVIDEODECODER_H
+#define GODOT_NATIVEVIDEODECODER_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GODOTAV_API_MAJOR 0
+#define GODOTAV_API_MINOR 1
+
+typedef struct
+{
+	godot_gdnative_api_version version;
+	void *next;
+	void *(*constructor)(godot_object *);
+	void (*destructor)(void *);
+	const char *(*get_plugin_name)(void);
+	const char **(*get_supported_extensions)(int *count);
+	godot_bool (*open_file)(void *, void *); // data struct, and a FileAccess pointer
+	godot_real (*get_length)(const void *);
+	godot_real (*get_playback_position)(const void *);
+	void (*seek)(void *, godot_real);
+	void (*set_audio_track)(void *, godot_int);
+	void (*update)(void *, godot_real);
+	godot_pool_byte_array *(*get_videoframe)(void *);
+	godot_int (*get_audioframe)(void *, float *, int);
+	godot_int (*get_channels)(const void *);
+	godot_int (*get_mix_rate)(const void *);
+	godot_vector2 (*get_texture_size)(const void *);
+} godot_videodecoder_interface_gdnative;
+
+typedef int (*GDNativeAudioMixCallback)(void *, const float *, int);
+
+// FileAccess wrappers for custom FFmpeg IO
+godot_int GDAPI godot_videodecoder_file_read(void *file_ptr, uint8_t *buf, int buf_size);
+int64_t GDAPI godot_videodecoder_file_seek(void *file_ptr, int64_t pos, int whence);
+void GDAPI godot_videodecoder_register_decoder(const godot_videodecoder_interface_gdnative *p_interface);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_NATIVEVIDEODECODER_H */

+ 3 - 0
modules/gdnative/register_types.cpp

@@ -38,6 +38,7 @@
 #include "nativescript/register_types.h"
 #include "net/register_types.h"
 #include "pluginscript/register_types.h"
+#include "videodecoder/register_types.h"
 
 #include "core/engine.h"
 #include "core/io/resource_loader.h"
@@ -326,6 +327,7 @@ void register_gdnative_types() {
 	register_arvr_types();
 	register_nativescript_types();
 	register_pluginscript_types();
+	register_videodecoder_types();
 
 	// run singletons
 
@@ -378,6 +380,7 @@ void unregister_gdnative_types() {
 	}
 	singleton_gdnatives.clear();
 
+	unregister_videodecoder_types();
 	unregister_pluginscript_types();
 	unregister_nativescript_types();
 	unregister_arvr_types();

+ 9 - 0
modules/gdnative/videodecoder/SCsub

@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_vsdecoder_gdnative = env_modules.Clone()
+
+env_vsdecoder_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
+env_vsdecoder_gdnative.add_source_files(env.modules_sources, '*.cpp')

+ 50 - 0
modules/gdnative/videodecoder/register_types.cpp

@@ -0,0 +1,50 @@
+/*************************************************************************/
+/*  register_types.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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 "register_types.h"
+
+#include "core/class_db.h"
+#include "video_stream_gdnative.h"
+
+static ResourceFormatLoaderVideoStreamGDNative *resource_loader_vsgdnative = NULL;
+
+void register_videodecoder_types() {
+
+	resource_loader_vsgdnative = memnew(ResourceFormatLoaderVideoStreamGDNative);
+	ResourceLoader::add_resource_format_loader(resource_loader_vsgdnative, true);
+	ClassDB::register_class<VideoStreamGDNative>();
+}
+
+void unregister_videodecoder_types() {
+
+	if (resource_loader_vsgdnative) {
+		memdelete(resource_loader_vsgdnative);
+	}
+}

+ 32 - 0
modules/gdnative/videodecoder/register_types.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  register_types.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+void register_videodecoder_types();
+void unregister_videodecoder_types();

+ 387 - 0
modules/gdnative/videodecoder/video_stream_gdnative.cpp

@@ -0,0 +1,387 @@
+/*************************************************************************/
+/*  video_stream_gdnative.cpp                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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_gdnative.h"
+
+#include "core/project_settings.h"
+#include "servers/audio_server.h"
+
+VideoDecoderServer *VideoDecoderServer::instance = NULL;
+
+static VideoDecoderServer decoder_server;
+
+const int AUX_BUFFER_SIZE = 1024; // Buffer 1024 samples.
+
+// NOTE: Callbacks for the GDNative libraries.
+extern "C" {
+godot_int GDAPI godot_videodecoder_file_read(void *ptr, uint8_t *buf, int buf_size) {
+	// ptr is a FileAccess
+	FileAccess *file = reinterpret_cast<FileAccess *>(ptr);
+
+	// if file exists
+	if (file) {
+		long bytes_read = file->get_buffer(buf, buf_size);
+		// No bytes to read => EOF
+		if (bytes_read == 0) {
+			return 0;
+		}
+		return bytes_read;
+	}
+	return -1;
+}
+
+int64_t GDAPI godot_videodecoder_file_seek(void *ptr, int64_t pos, int whence) {
+	// file
+	FileAccess *file = reinterpret_cast<FileAccess *>(ptr);
+
+	size_t len = file->get_len();
+	if (file) {
+		switch (whence) {
+			case SEEK_SET: {
+				// Just for explicitness
+				size_t new_pos = static_cast<size_t>(pos);
+				if (new_pos > len) {
+					return -1;
+				}
+				file->seek(new_pos);
+				pos = static_cast<int64_t>(file->get_position());
+				return pos;
+			} break;
+			case SEEK_CUR: {
+				// Just in case it doesn't exist
+				if (pos < 0 && -pos > file->get_position()) {
+					return -1;
+				}
+				pos = pos + static_cast<int>(file->get_position());
+				file->seek(pos);
+				pos = static_cast<int64_t>(file->get_position());
+				return pos;
+			} break;
+			case SEEK_END: {
+				// Just in case something goes wrong
+				if (-pos > len) {
+					return -1;
+				}
+				file->seek_end(pos);
+				pos = static_cast<int64_t>(file->get_position());
+				return pos;
+			} break;
+			default: {
+				// Only 4 possible options, hence default = AVSEEK_SIZE
+				// Asks to return the length of file
+				return static_cast<int64_t>(len);
+			} break;
+		}
+	}
+	// In case nothing works out.
+	return -1;
+}
+
+void GDAPI godot_videodecoder_register_decoder(const godot_videodecoder_interface_gdnative *p_interface) {
+
+	decoder_server.register_decoder_interface(p_interface);
+}
+}
+
+// VideoStreamPlaybackGDNative starts here.
+
+bool VideoStreamPlaybackGDNative::open_file(const String &p_file) {
+	ERR_FAIL_COND_V(interface == NULL, false);
+	file = FileAccess::open(p_file, FileAccess::READ);
+	bool file_opened = interface->open_file(data_struct, file);
+
+	num_channels = interface->get_channels(data_struct);
+	mix_rate = interface->get_mix_rate(data_struct);
+
+	godot_vector2 vec = interface->get_texture_size(data_struct);
+	texture_size = *(Vector2 *)&vec;
+
+	pcm = (float *)memalloc(num_channels * AUX_BUFFER_SIZE * sizeof(float));
+	memset(pcm, 0, num_channels * AUX_BUFFER_SIZE * sizeof(float));
+	pcm_write_idx = -1;
+	samples_decoded = 0;
+
+	texture->create((int)texture_size.width, (int)texture_size.height, Image::FORMAT_RGBA8, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE);
+
+	return file_opened;
+}
+
+void VideoStreamPlaybackGDNative::update(float p_delta) {
+	if (!playing || paused) {
+		return;
+	}
+	if (!file) {
+		return;
+	}
+	time += p_delta;
+	ERR_FAIL_COND(interface == NULL);
+	interface->update(data_struct, p_delta);
+
+	if (pcm_write_idx >= 0) {
+		// Previous remains
+		int mixed = mix_callback(mix_udata, pcm, samples_decoded);
+		if (mixed == samples_decoded) {
+			pcm_write_idx = -1;
+		} else {
+			samples_decoded -= mixed;
+			pcm_write_idx += mixed;
+		}
+	}
+	if (pcm_write_idx < 0) {
+		samples_decoded = interface->get_audioframe(data_struct, pcm, AUX_BUFFER_SIZE);
+		pcm_write_idx = mix_callback(mix_udata, pcm, samples_decoded);
+		if (pcm_write_idx == samples_decoded) {
+			pcm_write_idx = -1;
+		} else {
+			samples_decoded -= pcm_write_idx;
+		}
+	}
+
+	while (interface->get_playback_position(data_struct) < time) {
+
+		update_texture();
+	}
+}
+
+void VideoStreamPlaybackGDNative::update_texture() {
+	PoolByteArray *pba = (PoolByteArray *)interface->get_videoframe(data_struct);
+
+	if (pba == NULL) {
+		playing = false;
+		return;
+	}
+
+	Ref<Image> img = memnew(Image(texture_size.width, texture_size.height, 0, Image::FORMAT_RGBA8, *pba));
+
+	texture->set_data(img);
+}
+
+// ctor and dtor
+
+VideoStreamPlaybackGDNative::VideoStreamPlaybackGDNative() :
+		texture(Ref<ImageTexture>(memnew(ImageTexture))),
+		playing(false),
+		paused(false),
+		mix_udata(NULL),
+		mix_callback(NULL),
+		num_channels(-1),
+		time(0),
+		mix_rate(0),
+		delay_compensation(0),
+		pcm(NULL),
+		pcm_write_idx(0),
+		samples_decoded(0),
+		file(NULL),
+		interface(NULL),
+		data_struct(NULL) {}
+
+VideoStreamPlaybackGDNative::~VideoStreamPlaybackGDNative() {
+	cleanup();
+}
+
+void VideoStreamPlaybackGDNative::cleanup() {
+	if (data_struct)
+		interface->destructor(data_struct);
+	if (pcm)
+		memfree(pcm);
+	pcm = NULL;
+	time = 0;
+	num_channels = -1;
+	interface = NULL;
+	data_struct = NULL;
+}
+
+void VideoStreamPlaybackGDNative::set_interface(const godot_videodecoder_interface_gdnative *p_interface) {
+	ERR_FAIL_COND(p_interface == NULL);
+	if (interface != NULL) {
+		cleanup();
+	}
+	interface = p_interface;
+	data_struct = interface->constructor((godot_object *)this);
+}
+
+// controls
+
+bool VideoStreamPlaybackGDNative::is_playing() const {
+	return playing;
+}
+
+bool VideoStreamPlaybackGDNative::is_paused() const {
+	return paused;
+}
+
+void VideoStreamPlaybackGDNative::play() {
+
+	stop();
+
+	playing = true;
+
+	delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms");
+	delay_compensation /= 1000.0;
+}
+
+void VideoStreamPlaybackGDNative::stop() {
+	if (playing) {
+		seek(0);
+	}
+	playing = false;
+}
+
+void VideoStreamPlaybackGDNative::seek(float p_time) {
+	ERR_FAIL_COND(interface == NULL);
+	interface->seek(data_struct, p_time);
+}
+
+void VideoStreamPlaybackGDNative::set_paused(bool p_paused) {
+	paused = p_paused;
+}
+
+Ref<Texture> VideoStreamPlaybackGDNative::get_texture() {
+	return texture;
+}
+
+float VideoStreamPlaybackGDNative::get_length() const {
+	ERR_FAIL_COND_V(interface == NULL, 0);
+	return interface->get_length(data_struct);
+}
+
+float VideoStreamPlaybackGDNative::get_playback_position() const {
+
+	ERR_FAIL_COND_V(interface == NULL, 0);
+	return interface->get_playback_position(data_struct);
+}
+
+bool VideoStreamPlaybackGDNative::has_loop() const {
+	// TODO: Implement looping?
+	return false;
+}
+
+void VideoStreamPlaybackGDNative::set_loop(bool p_enable) {
+	// Do nothing
+}
+
+void VideoStreamPlaybackGDNative::set_audio_track(int p_idx) {
+	ERR_FAIL_COND(interface == NULL);
+	interface->set_audio_track(data_struct, p_idx);
+}
+
+void VideoStreamPlaybackGDNative::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
+
+	mix_udata = p_userdata;
+	mix_callback = p_callback;
+}
+
+int VideoStreamPlaybackGDNative::get_channels() const {
+	ERR_FAIL_COND_V(interface == NULL, 0);
+
+	return (num_channels > 0) ? num_channels : 0;
+}
+
+int VideoStreamPlaybackGDNative::get_mix_rate() const {
+	ERR_FAIL_COND_V(interface == NULL, 0);
+
+	return mix_rate;
+}
+
+/* --- NOTE VideoStreamGDNative starts here. ----- */
+
+Ref<VideoStreamPlayback> VideoStreamGDNative::instance_playback() {
+	Ref<VideoStreamPlaybackGDNative> pb = memnew(VideoStreamPlaybackGDNative);
+	VideoDecoderGDNative *decoder = decoder_server.get_decoder(file.get_extension().to_lower());
+	if (decoder == NULL)
+		return NULL;
+	pb->set_interface(decoder->interface);
+	pb->set_audio_track(audio_track);
+	if (pb->open_file(file))
+		return pb;
+	return NULL;
+}
+
+void VideoStreamGDNative::set_file(const String &p_file) {
+
+	file = p_file;
+}
+
+String VideoStreamGDNative::get_file() {
+
+	return file;
+}
+
+void VideoStreamGDNative::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamGDNative::set_file);
+	ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamGDNative::get_file);
+
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
+}
+
+void VideoStreamGDNative::set_audio_track(int p_track) {
+
+	audio_track = p_track;
+}
+
+/* --- NOTE ResourceFormatLoaderVideoStreamGDNative starts here. ----- */
+
+RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error) {
+	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+	if (!f) {
+		if (r_error) {
+			*r_error = ERR_CANT_OPEN;
+		}
+		memdelete(f);
+		return RES();
+	}
+	VideoStreamGDNative *stream = memnew(VideoStreamGDNative);
+	stream->set_file(p_path);
+	Ref<VideoStreamGDNative> ogv_stream = Ref<VideoStreamGDNative>(stream);
+	if (r_error) {
+		*r_error = OK;
+	}
+	return ogv_stream;
+}
+
+void ResourceFormatLoaderVideoStreamGDNative::get_recognized_extensions(List<String> *p_extensions) const {
+	Map<String, int>::Element *el = VideoDecoderServer::get_instance()->get_extensions().front();
+	while (el) {
+		p_extensions->push_back(el->key());
+		el = el->next();
+	}
+}
+
+bool ResourceFormatLoaderVideoStreamGDNative::handles_type(const String &p_type) const {
+	return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderVideoStreamGDNative::get_resource_type(const String &p_path) const {
+	String el = p_path.get_extension().to_lower();
+	if (VideoDecoderServer::get_instance()->get_extensions().has(el))
+		return "VideoStreamGDNative";
+	return "";
+}

+ 207 - 0
modules/gdnative/videodecoder/video_stream_gdnative.h

@@ -0,0 +1,207 @@
+/*************************************************************************/
+/*  video_stream_gdnative.h                                              */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef VIDEO_STREAM_GDNATIVE_H
+#define VIDEO_STREAM_GDNATIVE_H
+
+#include "../gdnative.h"
+#include "core/os/file_access.h"
+#include "scene/resources/texture.h"
+#include "scene/resources/video_stream.h"
+
+struct VideoDecoderGDNative {
+	const godot_videodecoder_interface_gdnative *interface;
+	String plugin_name;
+	Vector<String> supported_extensions;
+
+	VideoDecoderGDNative() :
+			interface(NULL),
+			plugin_name("none") {}
+
+	VideoDecoderGDNative(const godot_videodecoder_interface_gdnative *p_interface) :
+			interface(p_interface),
+			plugin_name(p_interface->get_plugin_name()) {
+		_get_supported_extensions();
+	}
+
+private:
+	void _get_supported_extensions() {
+		supported_extensions.clear();
+		int num_ext;
+		const char **supported_ext = interface->get_supported_extensions(&num_ext);
+		for (int i = 0; i < num_ext; i++) {
+			supported_extensions.push_back(supported_ext[i]);
+		}
+	}
+};
+
+class VideoDecoderServer {
+private:
+	Vector<VideoDecoderGDNative *> decoders;
+	Map<String, int> extensions;
+
+	static VideoDecoderServer *instance;
+
+public:
+	static VideoDecoderServer *get_instance() {
+		return instance;
+	}
+
+	const Map<String, int> &get_extensions() {
+		return extensions;
+	}
+
+	void register_decoder_interface(const godot_videodecoder_interface_gdnative *p_interface) {
+		VideoDecoderGDNative *decoder = memnew(VideoDecoderGDNative(p_interface));
+		int index = decoders.size();
+		for (int i = 0; i < decoder->supported_extensions.size(); i++) {
+			extensions[decoder->supported_extensions[i]] = index;
+		}
+		decoders.push_back(decoder);
+	}
+
+	VideoDecoderGDNative *get_decoder(const String &extension) {
+		if (extensions.size() == 0 || !extensions.has(extension))
+			return NULL;
+		return decoders[extensions[extension]];
+	}
+
+	VideoDecoderServer() {
+		instance = this;
+	}
+
+	~VideoDecoderServer() {
+		for (int i = 0; i < decoders.size(); i++) {
+			memdelete(decoders[i]);
+		}
+		decoders.clear();
+		instance = NULL;
+	}
+};
+
+class VideoStreamPlaybackGDNative : public VideoStreamPlayback {
+
+	GDCLASS(VideoStreamPlaybackGDNative, VideoStreamPlayback);
+
+	Ref<ImageTexture> texture;
+	bool playing;
+	bool paused;
+
+	Vector2 texture_size;
+
+	void *mix_udata;
+	AudioMixCallback mix_callback;
+
+	int num_channels;
+	float time;
+	int mix_rate;
+	double delay_compensation;
+
+	float *pcm;
+	int pcm_write_idx;
+	int samples_decoded;
+
+	void cleanup();
+	void update_texture();
+
+protected:
+	String file_name;
+
+	FileAccess *file;
+
+	const godot_videodecoder_interface_gdnative *interface;
+	void *data_struct;
+
+public:
+	VideoStreamPlaybackGDNative();
+	~VideoStreamPlaybackGDNative();
+
+	void set_interface(const godot_videodecoder_interface_gdnative *p_interface);
+
+	bool open_file(const String &p_file);
+
+	virtual void stop();
+	virtual void play();
+
+	virtual bool is_playing() const;
+
+	virtual void set_paused(bool p_paused);
+	virtual bool is_paused() const;
+
+	virtual void set_loop(bool p_enable);
+	virtual bool has_loop() const;
+
+	virtual float get_length() const;
+
+	virtual float get_playback_position() const;
+	virtual void seek(float p_time);
+
+	virtual void set_audio_track(int p_idx);
+
+	//virtual int mix(int16_t* p_buffer,int p_frames)=0;
+
+	virtual Ref<Texture> get_texture();
+	virtual void update(float p_delta);
+
+	virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata);
+	virtual int get_channels() const;
+	virtual int get_mix_rate() const;
+};
+
+class VideoStreamGDNative : public VideoStream {
+
+	GDCLASS(VideoStreamGDNative, VideoStream);
+
+	String file;
+	int audio_track;
+
+protected:
+	static void
+	_bind_methods();
+
+public:
+	void set_file(const String &p_file);
+	String get_file();
+
+	virtual void set_audio_track(int p_track);
+	virtual Ref<VideoStreamPlayback> instance_playback();
+
+	VideoStreamGDNative() {}
+};
+
+class ResourceFormatLoaderVideoStreamGDNative : public ResourceFormatLoader {
+public:
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+	virtual void get_recognized_extensions(List<String> *p_extensions) const;
+	virtual bool handles_type(const String &p_type) const;
+	virtual String get_resource_type(const String &p_path) const;
+};
+
+#endif