| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2017-09-01 21:07:55 +07:00
										 |  |  | /*  video_stream_webm.cpp                                                */ | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							| 
									
										
										
										
											2017-08-27 14:16:55 +02:00
										 |  |  | /*                      https://godotengine.org                          */ | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2017-01-01 22:01:57 +01:00
										 |  |  | /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							| 
									
										
										
										
											2017-04-08 00:11:42 +02:00
										 |  |  | /* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */ | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* 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_webm.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "OpusVorbisDecoder.hpp"
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | #include "VPXDecoder.hpp"
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | #include "mkvparser/mkvparser.h"
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | #include "os/file_access.h"
 | 
					
						
							| 
									
										
										
										
											2017-07-22 17:47:04 +07:00
										 |  |  | #include "project_settings.h"
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 19:28:21 +02:00
										 |  |  | #include "thirdparty/misc/yuv2rgb.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MkvReader : public mkvparser::IMkvReader { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  | 	MkvReader(const String &p_file) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		file = FileAccess::open(p_file, FileAccess::READ); | 
					
						
							|  |  |  | 		ERR_FAIL_COND(!file); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	~MkvReader() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (file) | 
					
						
							|  |  |  | 			memdelete(file); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	virtual int Read(long long pos, long len, unsigned char *buf) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (file) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (file->get_pos() != (size_t)pos) | 
					
						
							|  |  |  | 				file->seek(pos); | 
					
						
							|  |  |  | 			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) | 
					
						
							|  |  |  | 				*total = len; | 
					
						
							|  |  |  | 			if (available) | 
					
						
							|  |  |  | 				*available = len; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  | 	FileAccess *file; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | VideoStreamPlaybackWebm::VideoStreamPlaybackWebm() | 
					
						
							|  |  |  | 	: audio_track(0), | 
					
						
							|  |  |  | 	  webm(NULL), | 
					
						
							|  |  |  | 	  video(NULL), | 
					
						
							|  |  |  | 	  audio(NULL), | 
					
						
							|  |  |  | 	  video_frames(NULL), audio_frame(NULL), | 
					
						
							|  |  |  | 	  video_frames_pos(0), video_frames_capacity(0), | 
					
						
							|  |  |  | 	  num_decoded_samples(0), samples_offset(-1), | 
					
						
							|  |  |  | 	  mix_callback(NULL), | 
					
						
							|  |  |  | 	  mix_udata(NULL), | 
					
						
							|  |  |  | 	  playing(false), paused(false), | 
					
						
							|  |  |  | 	  delay_compensation(0.0), | 
					
						
							|  |  |  | 	  time(0.0), video_frame_delay(0.0), video_pos(0.0), | 
					
						
							|  |  |  | 	  texture(memnew(ImageTexture)), | 
					
						
							|  |  |  | 	  pcm(NULL) {} | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 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, 8)); //TODO: Detect CPU threads
 | 
					
						
							|  |  |  | 		if (video->isOpen()) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			audio = memnew(OpusVorbisDecoder(*webm)); | 
					
						
							|  |  |  | 			if (audio->isOpen()) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				audio_frame = memnew(WebMFrame); | 
					
						
							|  |  |  | 				pcm = (int16_t *)memalloc(sizeof(int16_t) * audio->getBufferSamples() * webm->getChannels()); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				memdelete(audio); | 
					
						
							|  |  |  | 				audio = NULL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			frame_data.resize((webm->getWidth() * webm->getHeight()) << 2); | 
					
						
							| 
									
										
										
										
											2017-01-02 21:38:20 +01:00
										 |  |  | 			texture->create(webm->getWidth(), webm->getHeight(), Image::FORMAT_RGBA8, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE); | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		memdelete(video); | 
					
						
							|  |  |  | 		video = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	memdelete(webm); | 
					
						
							|  |  |  | 	webm = NULL; | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::stop() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (playing) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		delete_pointers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pcm = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		audio_frame = NULL; | 
					
						
							|  |  |  | 		video_frames = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		video = NULL; | 
					
						
							|  |  |  | 		audio = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		open_file(file_name); //Should not fail here...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		video_frames_capacity = video_frames_pos = 0; | 
					
						
							|  |  |  | 		num_decoded_samples = 0; | 
					
						
							|  |  |  | 		samples_offset = -1; | 
					
						
							|  |  |  | 		video_frame_delay = video_pos = 0.0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	time = 0.0; | 
					
						
							|  |  |  | 	playing = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::play() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stop(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-19 17:00:46 -03:00
										 |  |  | 	delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms"); | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 	delay_compensation /= 1000.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	playing = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool VideoStreamPlaybackWebm::is_playing() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return playing; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::set_paused(bool p_paused) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	paused = p_paused; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool VideoStreamPlaybackWebm::is_paused(bool p_paused) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return paused; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::set_loop(bool p_enable) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//Empty
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool VideoStreamPlaybackWebm::has_loop() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float VideoStreamPlaybackWebm::get_length() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (webm) | 
					
						
							|  |  |  | 		return webm->getLength(); | 
					
						
							|  |  |  | 	return 0.0f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float VideoStreamPlaybackWebm::get_pos() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return video_pos; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::seek_pos(float p_time) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//Not implemented
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::set_audio_track(int p_idx) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	audio_track = p_idx; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ref<Texture> VideoStreamPlaybackWebm::get_texture() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return texture; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::update(float p_delta) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((!playing || paused) || !video) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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->getPCMS16(*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) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				samples_offset = mixed; | 
					
						
							|  |  |  | 				audio_buffer_full = true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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
 | 
					
						
							|  |  |  | 			break; //Can't demux, EOS?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (video_frame->isValid()) | 
					
						
							|  |  |  | 			++video_frames_pos; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const double video_delay = video->getFramesDelay() * video_frame_delay; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bool want_this_frame = false; | 
					
						
							|  |  |  | 	while (video_frames_pos > 0 && !want_this_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		WebMFrame *video_frame = video_frames[0]; | 
					
						
							|  |  |  | 		if (video_frame->time <= time + video_delay) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (video->decode(*video_frame)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				VPXDecoder::IMAGE_ERROR err; | 
					
						
							|  |  |  | 				VPXDecoder::Image image; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				while ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					want_this_frame = (time - video_frame->time <= video_frame_delay); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (want_this_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-07 18:25:37 -03:00
										 |  |  | 							PoolVector<uint8_t>::Write w = frame_data.write(); | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 							bool converted = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 								// 								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);
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 								converted = true; | 
					
						
							|  |  |  | 							} else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 								// 								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);
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 								converted = true; | 
					
						
							|  |  |  | 							} else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 								// 								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);
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 								converted = true; | 
					
						
							|  |  |  | 							} else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 								// 								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;
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 							} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							if (converted) | 
					
						
							| 
									
										
										
										
											2017-01-02 21:38:20 +01:00
										 |  |  | 								texture->set_data(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data)); //Zero copy send to visual server
 | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			video_frame_delay = video_frame->time - video_pos; | 
					
						
							|  |  |  | 			video_pos = video_frame->time; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			memmove(video_frames, video_frames + 1, (--video_frames_pos) * sizeof(void *)); | 
					
						
							|  |  |  | 			video_frames[video_frames_pos] = video_frame; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	time += p_delta; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 		return webm->getChannels(); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | int VideoStreamPlaybackWebm::get_mix_rate() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (audio) | 
					
						
							|  |  |  | 		return webm->getSampleRate(); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 	if (video_frames_pos > 0) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const double audio_delay = AudioServer::get_singleton()->get_output_delay(); | 
					
						
							|  |  |  | 		const double video_time = video_frames[video_frames_pos - 1]->time; | 
					
						
							|  |  |  | 		return video_time >= time + audio_delay + delay_compensation; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamPlaybackWebm::delete_pointers() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pcm) | 
					
						
							|  |  |  | 		memfree(pcm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (audio_frame) | 
					
						
							|  |  |  | 		memdelete(audio_frame); | 
					
						
							|  |  |  | 	for (int i = 0; i < video_frames_capacity; ++i) | 
					
						
							|  |  |  | 		memdelete(video_frames[i]); | 
					
						
							|  |  |  | 	if (video_frames) | 
					
						
							|  |  |  | 		memfree(video_frames); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (video) | 
					
						
							|  |  |  | 		memdelete(video); | 
					
						
							|  |  |  | 	if (audio) | 
					
						
							|  |  |  | 		memdelete(audio); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (webm) | 
					
						
							|  |  |  | 		memdelete(webm); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RES ResourceFormatLoaderVideoStreamWebm::load(const String &p_path, const String &p_original_path, Error *r_error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Ref<VideoStreamWebm> stream = memnew(VideoStreamWebm); | 
					
						
							|  |  |  | 	stream->set_file(p_path); | 
					
						
							|  |  |  | 	if (r_error) | 
					
						
							|  |  |  | 		*r_error = OK; | 
					
						
							|  |  |  | 	return stream; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ResourceFormatLoaderVideoStreamWebm::get_recognized_extensions(List<String> *p_extensions) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p_extensions->push_back("webm"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool ResourceFormatLoaderVideoStreamWebm::handles_type(const String &p_type) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (p_type == "VideoStream" || p_type == "VideoStreamWebm"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String ResourceFormatLoaderVideoStreamWebm::get_resource_type(const String &p_path) const { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-14 00:51:09 -03:00
										 |  |  | 	const String exl = p_path.get_extension().to_lower(); | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 	if (exl == "webm") | 
					
						
							|  |  |  | 		return "VideoStreamWebm"; | 
					
						
							|  |  |  | 	return ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | VideoStreamWebm::VideoStreamWebm() | 
					
						
							|  |  |  | 	: audio_track(0) {} | 
					
						
							| 
									
										
										
										
											2016-10-17 17:14:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Ref<VideoStreamPlayback> VideoStreamWebm::instance_playback() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Ref<VideoStreamPlaybackWebm> pb = memnew(VideoStreamPlaybackWebm); | 
					
						
							|  |  |  | 	pb->set_audio_track(audio_track); | 
					
						
							|  |  |  | 	if (pb->open_file(file)) | 
					
						
							|  |  |  | 		return pb; | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void VideoStreamWebm::set_file(const String &p_file) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	file = p_file; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void VideoStreamWebm::set_audio_track(int p_track) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	audio_track = p_track; | 
					
						
							|  |  |  | } |