1/**************************************************************************/
2/* video_stream_player.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "video_stream_player.h"
32
33#include "core/os/os.h"
34#include "scene/resources/image_texture.h"
35#include "scene/scene_string_names.h"
36#include "servers/audio_server.h"
37
38int VideoStreamPlayer::sp_get_channel_count() const {
39 if (playback.is_null()) {
40 return 0;
41 }
42
43 return playback->get_channels();
44}
45
46bool VideoStreamPlayer::mix(AudioFrame *p_buffer, int p_frames) {
47 // Check the amount resampler can really handle.
48 // If it cannot, wait "wait_resampler_phase_limit" times.
49 // This mechanism contributes to smoother pause/unpause operation.
50 if (p_frames <= resampler.get_num_of_ready_frames() ||
51 wait_resampler_limit <= wait_resampler) {
52 wait_resampler = 0;
53 return resampler.mix(p_buffer, p_frames);
54 }
55 wait_resampler++;
56 return false;
57}
58
59// Called from main thread (e.g. VideoStreamPlaybackTheora::update).
60int VideoStreamPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
61 ERR_FAIL_NULL_V(p_udata, 0);
62 ERR_FAIL_NULL_V(p_data, 0);
63
64 VideoStreamPlayer *vp = static_cast<VideoStreamPlayer *>(p_udata);
65
66 int todo = MIN(vp->resampler.get_writer_space(), p_frames);
67
68 float *wb = vp->resampler.get_write_buffer();
69 int c = vp->resampler.get_channel_count();
70
71 for (int i = 0; i < todo * c; i++) {
72 wb[i] = p_data[i];
73 }
74 vp->resampler.write(todo);
75
76 return todo;
77}
78
79void VideoStreamPlayer::_mix_audios(void *p_self) {
80 ERR_FAIL_NULL(p_self);
81 static_cast<VideoStreamPlayer *>(p_self)->_mix_audio();
82}
83
84// Called from audio thread
85void VideoStreamPlayer::_mix_audio() {
86 if (!stream.is_valid()) {
87 return;
88 }
89 if (!playback.is_valid() || !playback->is_playing() || playback->is_paused()) {
90 return;
91 }
92
93 AudioFrame *buffer = mix_buffer.ptrw();
94 int buffer_size = mix_buffer.size();
95
96 // Resample
97 if (!mix(buffer, buffer_size)) {
98 return;
99 }
100
101 AudioFrame vol = AudioFrame(volume, volume);
102
103 int cc = AudioServer::get_singleton()->get_channel_count();
104
105 if (cc == 1) {
106 AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
107 ERR_FAIL_NULL(target);
108
109 for (int j = 0; j < buffer_size; j++) {
110 target[j] += buffer[j] * vol;
111 }
112
113 } else {
114 AudioFrame *targets[4];
115
116 for (int k = 0; k < cc; k++) {
117 targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, k);
118 ERR_FAIL_NULL(targets[k]);
119 }
120
121 for (int j = 0; j < buffer_size; j++) {
122 AudioFrame frame = buffer[j] * vol;
123 for (int k = 0; k < cc; k++) {
124 targets[k][j] += frame;
125 }
126 }
127 }
128}
129
130void VideoStreamPlayer::_notification(int p_notification) {
131 switch (p_notification) {
132 case NOTIFICATION_ENTER_TREE: {
133 AudioServer::get_singleton()->add_mix_callback(_mix_audios, this);
134
135 if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) {
136 play();
137 }
138 } break;
139
140 case NOTIFICATION_EXIT_TREE: {
141 AudioServer::get_singleton()->remove_mix_callback(_mix_audios, this);
142 } break;
143
144 case NOTIFICATION_INTERNAL_PROCESS: {
145 bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
146
147 if (stream.is_null() || paused || playback.is_null() || !playback->is_playing()) {
148 return;
149 }
150
151 double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
152
153 double delta = last_audio_time == 0 ? 0 : audio_time - last_audio_time;
154 last_audio_time = audio_time;
155
156 if (delta == 0) {
157 return;
158 }
159
160 playback->update(delta); // playback->is_playing() returns false in the last video frame
161
162 if (!playback->is_playing()) {
163 if (loop) {
164 play();
165 return;
166 }
167 emit_signal(SceneStringNames::get_singleton()->finished);
168 }
169 } break;
170
171 case NOTIFICATION_DRAW: {
172 if (texture.is_null()) {
173 return;
174 }
175 if (texture->get_width() == 0) {
176 return;
177 }
178
179 Size2 s = expand ? get_size() : texture->get_size();
180 draw_texture_rect(texture, Rect2(Point2(), s), false);
181 } break;
182
183 case NOTIFICATION_PAUSED: {
184 if (is_playing() && !is_paused()) {
185 paused_from_tree = true;
186 if (playback.is_valid()) {
187 playback->set_paused(true);
188 set_process_internal(false);
189 }
190 last_audio_time = 0;
191 }
192 } break;
193
194 case NOTIFICATION_UNPAUSED: {
195 if (paused_from_tree) {
196 paused_from_tree = false;
197 if (playback.is_valid()) {
198 playback->set_paused(false);
199 set_process_internal(true);
200 }
201 last_audio_time = 0;
202 }
203 } break;
204 }
205}
206
207Size2 VideoStreamPlayer::get_minimum_size() const {
208 if (!expand && !texture.is_null()) {
209 return texture->get_size();
210 } else {
211 return Size2();
212 }
213}
214
215void VideoStreamPlayer::set_expand(bool p_expand) {
216 if (expand == p_expand) {
217 return;
218 }
219
220 expand = p_expand;
221 queue_redraw();
222 update_minimum_size();
223}
224
225bool VideoStreamPlayer::has_expand() const {
226 return expand;
227}
228
229void VideoStreamPlayer::set_loop(bool p_loop) {
230 loop = p_loop;
231}
232
233bool VideoStreamPlayer::has_loop() const {
234 return loop;
235}
236
237void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
238 stop();
239
240 AudioServer::get_singleton()->lock();
241 mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
242 stream = p_stream;
243 if (stream.is_valid()) {
244 stream->set_audio_track(audio_track);
245 playback = stream->instantiate_playback();
246 } else {
247 playback = Ref<VideoStreamPlayback>();
248 }
249 AudioServer::get_singleton()->unlock();
250
251 if (!playback.is_null()) {
252 playback->set_paused(paused);
253 texture = playback->get_texture();
254
255 const int channels = playback->get_channels();
256
257 AudioServer::get_singleton()->lock();
258 if (channels > 0) {
259 resampler.setup(channels, playback->get_mix_rate(), AudioServer::get_singleton()->get_mix_rate(), buffering_ms, 0);
260 } else {
261 resampler.clear();
262 }
263 AudioServer::get_singleton()->unlock();
264
265 if (channels > 0) {
266 playback->set_mix_callback(_audio_mix_callback, this);
267 }
268
269 } else {
270 texture.unref();
271 AudioServer::get_singleton()->lock();
272 resampler.clear();
273 AudioServer::get_singleton()->unlock();
274 }
275
276 queue_redraw();
277
278 if (!expand) {
279 update_minimum_size();
280 }
281}
282
283Ref<VideoStream> VideoStreamPlayer::get_stream() const {
284 return stream;
285}
286
287void VideoStreamPlayer::play() {
288 ERR_FAIL_COND(!is_inside_tree());
289 if (playback.is_null()) {
290 return;
291 }
292 playback->stop();
293 playback->play();
294 set_process_internal(true);
295 last_audio_time = 0;
296
297 // We update the playback to render the first frame immediately.
298 playback->update(0);
299
300 if (!can_process()) {
301 _notification(NOTIFICATION_PAUSED);
302 }
303}
304
305void VideoStreamPlayer::stop() {
306 if (!is_inside_tree()) {
307 return;
308 }
309 if (playback.is_null()) {
310 return;
311 }
312
313 playback->stop();
314 resampler.flush();
315 set_process_internal(false);
316 last_audio_time = 0;
317}
318
319bool VideoStreamPlayer::is_playing() const {
320 if (playback.is_null()) {
321 return false;
322 }
323
324 return playback->is_playing();
325}
326
327void VideoStreamPlayer::set_paused(bool p_paused) {
328 if (paused == p_paused) {
329 return;
330 }
331
332 paused = p_paused;
333 if (!p_paused && !can_process()) {
334 paused_from_tree = true;
335 return;
336 } else if (p_paused && paused_from_tree) {
337 paused_from_tree = false;
338 return;
339 }
340
341 if (playback.is_valid()) {
342 playback->set_paused(p_paused);
343 set_process_internal(!p_paused);
344 }
345 last_audio_time = 0;
346}
347
348bool VideoStreamPlayer::is_paused() const {
349 return paused;
350}
351
352void VideoStreamPlayer::set_buffering_msec(int p_msec) {
353 buffering_ms = p_msec;
354}
355
356int VideoStreamPlayer::get_buffering_msec() const {
357 return buffering_ms;
358}
359
360void VideoStreamPlayer::set_audio_track(int p_track) {
361 audio_track = p_track;
362 if (stream.is_valid()) {
363 stream->set_audio_track(audio_track);
364 }
365 if (playback.is_valid()) {
366 playback->set_audio_track(audio_track);
367 }
368}
369
370int VideoStreamPlayer::get_audio_track() const {
371 return audio_track;
372}
373
374void VideoStreamPlayer::set_volume(float p_vol) {
375 volume = p_vol;
376}
377
378float VideoStreamPlayer::get_volume() const {
379 return volume;
380}
381
382void VideoStreamPlayer::set_volume_db(float p_db) {
383 if (p_db < -79) {
384 set_volume(0);
385 } else {
386 set_volume(Math::db_to_linear(p_db));
387 }
388}
389
390float VideoStreamPlayer::get_volume_db() const {
391 if (volume == 0) {
392 return -80;
393 } else {
394 return Math::linear_to_db(volume);
395 }
396}
397
398String VideoStreamPlayer::get_stream_name() const {
399 if (stream.is_null()) {
400 return "<No Stream>";
401 }
402 return stream->get_name();
403}
404
405double VideoStreamPlayer::get_stream_length() const {
406 if (playback.is_null()) {
407 return 0;
408 }
409 return playback->get_length();
410}
411
412double VideoStreamPlayer::get_stream_position() const {
413 if (playback.is_null()) {
414 return 0;
415 }
416 return playback->get_playback_position();
417}
418
419void VideoStreamPlayer::set_stream_position(double p_position) {
420 if (playback.is_valid()) {
421 playback->seek(p_position);
422 }
423}
424
425Ref<Texture2D> VideoStreamPlayer::get_video_texture() const {
426 if (playback.is_valid()) {
427 return playback->get_texture();
428 }
429
430 return Ref<Texture2D>();
431}
432
433void VideoStreamPlayer::set_autoplay(bool p_enable) {
434 autoplay = p_enable;
435}
436
437bool VideoStreamPlayer::has_autoplay() const {
438 return autoplay;
439}
440
441void VideoStreamPlayer::set_bus(const StringName &p_bus) {
442 // If audio is active, must lock this.
443 AudioServer::get_singleton()->lock();
444 bus = p_bus;
445 AudioServer::get_singleton()->unlock();
446}
447
448StringName VideoStreamPlayer::get_bus() const {
449 for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
450 if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
451 return bus;
452 }
453 }
454 return SceneStringNames::get_singleton()->Master;
455}
456
457void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const {
458 if (p_property.name == "bus") {
459 String options;
460 for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
461 if (i > 0) {
462 options += ",";
463 }
464 String name = AudioServer::get_singleton()->get_bus_name(i);
465 options += name;
466 }
467
468 p_property.hint_string = options;
469 }
470}
471
472void VideoStreamPlayer::_bind_methods() {
473 ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoStreamPlayer::set_stream);
474 ClassDB::bind_method(D_METHOD("get_stream"), &VideoStreamPlayer::get_stream);
475
476 ClassDB::bind_method(D_METHOD("play"), &VideoStreamPlayer::play);
477 ClassDB::bind_method(D_METHOD("stop"), &VideoStreamPlayer::stop);
478
479 ClassDB::bind_method(D_METHOD("is_playing"), &VideoStreamPlayer::is_playing);
480
481 ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoStreamPlayer::set_paused);
482 ClassDB::bind_method(D_METHOD("is_paused"), &VideoStreamPlayer::is_paused);
483
484 ClassDB::bind_method(D_METHOD("set_loop", "loop"), &VideoStreamPlayer::set_loop);
485 ClassDB::bind_method(D_METHOD("has_loop"), &VideoStreamPlayer::has_loop);
486
487 ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoStreamPlayer::set_volume);
488 ClassDB::bind_method(D_METHOD("get_volume"), &VideoStreamPlayer::get_volume);
489
490 ClassDB::bind_method(D_METHOD("set_volume_db", "db"), &VideoStreamPlayer::set_volume_db);
491 ClassDB::bind_method(D_METHOD("get_volume_db"), &VideoStreamPlayer::get_volume_db);
492
493 ClassDB::bind_method(D_METHOD("set_audio_track", "track"), &VideoStreamPlayer::set_audio_track);
494 ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoStreamPlayer::get_audio_track);
495
496 ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoStreamPlayer::get_stream_name);
497 ClassDB::bind_method(D_METHOD("get_stream_length"), &VideoStreamPlayer::get_stream_length);
498
499 ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoStreamPlayer::set_stream_position);
500 ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoStreamPlayer::get_stream_position);
501
502 ClassDB::bind_method(D_METHOD("set_autoplay", "enabled"), &VideoStreamPlayer::set_autoplay);
503 ClassDB::bind_method(D_METHOD("has_autoplay"), &VideoStreamPlayer::has_autoplay);
504
505 ClassDB::bind_method(D_METHOD("set_expand", "enable"), &VideoStreamPlayer::set_expand);
506 ClassDB::bind_method(D_METHOD("has_expand"), &VideoStreamPlayer::has_expand);
507
508 ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoStreamPlayer::set_buffering_msec);
509 ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoStreamPlayer::get_buffering_msec);
510
511 ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoStreamPlayer::set_bus);
512 ClassDB::bind_method(D_METHOD("get_bus"), &VideoStreamPlayer::get_bus);
513
514 ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoStreamPlayer::get_video_texture);
515
516 ADD_SIGNAL(MethodInfo("finished"));
517
518 ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
519 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
520 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01,suffix:dB"), "set_volume_db", "get_volume_db");
521 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_RANGE, "0,15,0.01,exp", PROPERTY_USAGE_NONE), "set_volume", "get_volume");
522 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay");
523 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_paused", "is_paused");
524 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
525 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
526 ADD_PROPERTY(PropertyInfo(Variant::INT, "buffering_msec", PROPERTY_HINT_RANGE, "10,1000,suffix:ms"), "set_buffering_msec", "get_buffering_msec");
527 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stream_position", PROPERTY_HINT_RANGE, "0,1280000,0.1", PROPERTY_USAGE_NONE), "set_stream_position", "get_stream_position");
528
529 ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
530}
531
532VideoStreamPlayer::VideoStreamPlayer() {}
533
534VideoStreamPlayer::~VideoStreamPlayer() {
535 resampler.clear(); // Not necessary here, but make in consistent with other "stream_player" classes.
536}
537