1 /*************************************************************************/
2 /* video_stream_theora.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
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 #include "video_stream_theora.h"
31
32 #include "globals.h"
33 #include "os/os.h"
34
35 #include "thirdparty/misc/yuv2rgb.h"
36
buffer_data()37 int VideoStreamPlaybackTheora::buffer_data() {
38
39 char *buffer = ogg_sync_buffer(&oy, 4096);
40
41 #ifdef THEORA_USE_THREAD_STREAMING
42
43 int read;
44
45 do {
46 thread_sem->post();
47 read = MIN(ring_buffer.data_left(), 4096);
48 if (read) {
49 ring_buffer.read((uint8_t *)buffer, read);
50 ogg_sync_wrote(&oy, read);
51 } else {
52 OS::get_singleton()->delay_usec(100);
53 }
54
55 } while (read == 0);
56
57 return read;
58
59 #else
60
61 int bytes = file->get_buffer((uint8_t *)buffer, 4096);
62 ogg_sync_wrote(&oy, bytes);
63 return (bytes);
64
65 #endif
66 }
67
queue_page(ogg_page * page)68 int VideoStreamPlaybackTheora::queue_page(ogg_page *page) {
69 if (theora_p) {
70 ogg_stream_pagein(&to, page);
71 if (to.e_o_s)
72 theora_eos = true;
73 }
74 if (vorbis_p) {
75 ogg_stream_pagein(&vo, page);
76 if (vo.e_o_s)
77 vorbis_eos = true;
78 }
79 return 0;
80 }
81
video_write(void)82 void VideoStreamPlaybackTheora::video_write(void) {
83 th_ycbcr_buffer yuv;
84 th_decode_ycbcr_out(td, yuv);
85
86 /*
87 int y_offset, uv_offset;
88 y_offset=(ti.pic_x&~1)+yuv[0].stride*(ti.pic_y&~1);
89
90 {
91 int pixels = size.x * size.y;
92 frame_data.resize(pixels * 4);
93 DVector<uint8_t>::Write w = frame_data.write();
94 char* dst = (char*)w.ptr();
95 int p = 0;
96 for (int i=0; i<size.y; i++) {
97
98 char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
99 char *out = dst + (int)size.x * 4 * i;
100 for (int j=0;j<size.x;j++) {
101
102 dst[p++] = in_y[j];
103 dst[p++] = in_y[j];
104 dst[p++] = in_y[j];
105 dst[p++] = 255;
106 };
107 }
108 format = Image::FORMAT_RGBA;
109 }
110 // */
111
112 //*
113
114 int pitch = 4;
115 frame_data.resize(size.x * size.y * pitch);
116 {
117 DVector<uint8_t>::Write w = frame_data.write();
118 char *dst = (char *)w.ptr();
119
120 //uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y/2);
121
122 if (px_fmt == TH_PF_444) {
123
124 yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0);
125
126 } else if (px_fmt == TH_PF_422) {
127
128 yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0);
129
130 } else if (px_fmt == TH_PF_420) {
131
132 yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[2].data, (uint8_t *)yuv[1].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0);
133 };
134
135 format = Image::FORMAT_RGBA;
136 }
137
138 Image img(size.x, size.y, 0, Image::FORMAT_RGBA, frame_data); //zero copy image creation
139
140 texture->set_data(img); //zero copy send to visual server
141
142 /*
143
144 if (px_fmt == TH_PF_444) {
145
146 int pitch = 3;
147 frame_data.resize(size.x * size.y * pitch);
148 DVector<uint8_t>::Write w = frame_data.write();
149 char* dst = (char*)w.ptr();
150
151 for(int i=0;i<size.y;i++) {
152
153 char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
154 char *out = dst + (int)size.x * pitch * i;
155 char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*i;
156 char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*i;
157 for (int j=0;j<size.x;j++) {
158
159 out[j*3+0] = in_y[j];
160 out[j*3+1] = in_u[j];
161 out[j*3+2] = in_v[j];
162 };
163 }
164
165 format = Image::FORMAT_YUV_444;
166
167 } else {
168
169 int div;
170 if (px_fmt!=TH_PF_422) {
171 div = 2;
172 }
173
174 bool rgba = true;
175 if (rgba) {
176
177 int pitch = 4;
178 frame_data.resize(size.x * size.y * pitch);
179 DVector<uint8_t>::Write w = frame_data.write();
180 char* dst = (char*)w.ptr();
181
182 uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y / div);
183 for(int i=0;i<size.y;i++) {
184 char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
185 char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*(i/div);
186 char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*(i/div);
187 uint8_t *out = (uint8_t*)dst + (int)size.x * pitch * i;
188 int ofs = 0;
189 for (int j=0;j<size.x;j++) {
190
191 uint8_t y, u, v;
192 y = in_y[j];
193 u = in_u[j/2];
194 v = in_v[j/2];
195
196 int32_t r = Math::fast_ftoi(1.164 * (y - 16) + 1.596 * (v - 128));
197 int32_t g = Math::fast_ftoi(1.164 * (y - 16) - 0.813 * (v - 128) - 0.391 * (u - 128));
198 int32_t b = Math::fast_ftoi(1.164 * (y - 16) + 2.018 * (u - 128));
199
200 out[ofs++] = CLAMP(r, 0, 255);
201 out[ofs++] = CLAMP(g, 0, 255);
202 out[ofs++] = CLAMP(b, 0, 255);
203 out[ofs++] = 255;
204 }
205 }
206
207 format = Image::FORMAT_RGBA;
208
209 } else {
210
211 int pitch = 2;
212 frame_data.resize(size.x * size.y * pitch);
213 DVector<uint8_t>::Write w = frame_data.write();
214 char* dst = (char*)w.ptr();
215
216 uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y / div);
217 for(int i=0;i<size.y;i++) {
218 char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
219 char *out = dst + (int)size.x * pitch * i;
220 for (int j=0;j<size.x;j++)
221 out[j*2] = in_y[j];
222 char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*(i/div);
223 char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*(i/div);
224 for (int j=0;j<(int)size.x>>1;j++) {
225 out[j*4+1] = in_u[j];
226 out[j*4+3] = in_v[j];
227 }
228 }
229
230 format = Image::FORMAT_YUV_422;
231 };
232 };
233 // */
234
235 frames_pending = 1;
236 }
237
clear()238 void VideoStreamPlaybackTheora::clear() {
239
240 if (!file)
241 return;
242
243 if (vorbis_p) {
244 ogg_stream_clear(&vo);
245 if (vorbis_p >= 3) {
246 vorbis_block_clear(&vb);
247 vorbis_dsp_clear(&vd);
248 };
249 vorbis_comment_clear(&vc);
250 vorbis_info_clear(&vi);
251 vorbis_p = 0;
252 }
253 if (theora_p) {
254 ogg_stream_clear(&to);
255 th_decode_free(td);
256 th_comment_clear(&tc);
257 th_info_clear(&ti);
258 theora_p = 0;
259 }
260 ogg_sync_clear(&oy);
261
262 #ifdef THEORA_USE_THREAD_STREAMING
263 thread_exit = true;
264 thread_sem->post(); //just in case
265 Thread::wait_to_finish(thread);
266 memdelete(thread);
267 thread = NULL;
268 ring_buffer.clear();
269 #endif
270 //file_name = "";
271
272 theora_p = 0;
273 vorbis_p = 0;
274 videobuf_ready = 0;
275 frames_pending = 0;
276 videobuf_time = 0;
277 theora_eos = false;
278 vorbis_eos = false;
279
280 if (file) {
281 memdelete(file);
282 }
283 file = NULL;
284 playing = false;
285 };
286
set_file(const String & p_file)287 void VideoStreamPlaybackTheora::set_file(const String &p_file) {
288
289 ERR_FAIL_COND(playing);
290 ogg_packet op;
291 th_setup_info *ts = NULL;
292
293 file_name = p_file;
294 if (file) {
295 memdelete(file);
296 }
297 file = FileAccess::open(p_file, FileAccess::READ);
298 ERR_FAIL_COND(!file);
299
300 #ifdef THEORA_USE_THREAD_STREAMING
301 thread_exit = false;
302 thread_eof = false;
303 //pre-fill buffer
304 int to_read = ring_buffer.space_left();
305 int read = file->get_buffer(read_buffer.ptr(), to_read);
306 ring_buffer.write(read_buffer.ptr(), read);
307
308 thread = Thread::create(_streaming_thread, this);
309
310 #endif
311
312 ogg_sync_init(&oy);
313
314 /* init supporting Vorbis structures needed in header parsing */
315 vorbis_info_init(&vi);
316 vorbis_comment_init(&vc);
317
318 /* init supporting Theora structures needed in header parsing */
319 th_comment_init(&tc);
320 th_info_init(&ti);
321
322 theora_eos = false;
323 vorbis_eos = false;
324
325 /* Ogg file open; parse the headers */
326 /* Only interested in Vorbis/Theora streams */
327 int stateflag = 0;
328
329 int audio_track_skip = audio_track;
330
331 while (!stateflag) {
332 int ret = buffer_data();
333 if (ret == 0) break;
334 while (ogg_sync_pageout(&oy, &og) > 0) {
335 ogg_stream_state test;
336
337 /* is this a mandated initial header? If not, stop parsing */
338 if (!ogg_page_bos(&og)) {
339 /* don't leak the page; get it into the appropriate stream */
340 queue_page(&og);
341 stateflag = 1;
342 break;
343 }
344
345 ogg_stream_init(&test, ogg_page_serialno(&og));
346 ogg_stream_pagein(&test, &og);
347 ogg_stream_packetout(&test, &op);
348
349 /* identify the codec: try theora */
350 if (!theora_p && th_decode_headerin(&ti, &tc, &ts, &op) >= 0) {
351 /* it is theora */
352 copymem(&to, &test, sizeof(test));
353 theora_p = 1;
354 } else if (!vorbis_p && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) {
355
356 /* it is vorbis */
357 if (audio_track_skip) {
358 vorbis_info_clear(&vi);
359 vorbis_comment_clear(&vc);
360 ogg_stream_clear(&test);
361 vorbis_info_init(&vi);
362 vorbis_comment_init(&vc);
363
364 audio_track_skip--;
365 } else {
366 copymem(&vo, &test, sizeof(test));
367 vorbis_p = 1;
368 }
369 } else {
370 /* whatever it is, we don't care about it */
371 ogg_stream_clear(&test);
372 }
373 }
374 /* fall through to non-bos page parsing */
375 }
376
377 /* we're expecting more header packets. */
378 while ((theora_p && theora_p < 3) || (vorbis_p && vorbis_p < 3)) {
379 int ret;
380
381 /* look for further theora headers */
382 while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) {
383 if (ret < 0) {
384 fprintf(stderr, "Error parsing Theora stream headers; "
385 "corrupt stream?\n");
386 clear();
387 return;
388 }
389 if (!th_decode_headerin(&ti, &tc, &ts, &op)) {
390 fprintf(stderr, "Error parsing Theora stream headers; "
391 "corrupt stream?\n");
392 clear();
393 return;
394 }
395 theora_p++;
396 }
397
398 /* look for more vorbis header packets */
399 while (vorbis_p && (vorbis_p < 3) && (ret = ogg_stream_packetout(&vo, &op))) {
400 if (ret < 0) {
401 fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n");
402 clear();
403 return;
404 }
405 ret = vorbis_synthesis_headerin(&vi, &vc, &op);
406 if (ret) {
407 fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n");
408 clear();
409 return;
410 }
411 vorbis_p++;
412 if (vorbis_p == 3) break;
413 }
414
415 /* The header pages/packets will arrive before anything else we
416 care about, or the stream is not obeying spec */
417
418 if (ogg_sync_pageout(&oy, &og) > 0) {
419 queue_page(&og); /* demux into the appropriate stream */
420 } else {
421 int ret = buffer_data(); /* someone needs more data */
422 if (ret == 0) {
423 fprintf(stderr, "End of file while searching for codec headers.\n");
424 clear();
425 return;
426 }
427 }
428 }
429
430 /* and now we have it all. initialize decoders */
431 if (theora_p) {
432 td = th_decode_alloc(&ti, ts);
433 printf("Ogg logical stream %lx is Theora %dx%d %.02f fps",
434 to.serialno, ti.pic_width, ti.pic_height,
435 (double)ti.fps_numerator / ti.fps_denominator);
436 px_fmt = ti.pixel_fmt;
437 switch (ti.pixel_fmt) {
438 case TH_PF_420: printf(" 4:2:0 video\n"); break;
439 case TH_PF_422: printf(" 4:2:2 video\n"); break;
440 case TH_PF_444: printf(" 4:4:4 video\n"); break;
441 case TH_PF_RSVD:
442 default:
443 printf(" video\n (UNKNOWN Chroma sampling!)\n");
444 break;
445 }
446 if (ti.pic_width != ti.frame_width || ti.pic_height != ti.frame_height)
447 printf(" Frame content is %dx%d with offset (%d,%d).\n",
448 ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y);
449 th_decode_ctl(td, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max,
450 sizeof(pp_level_max));
451 pp_level = pp_level_max;
452 pp_level = 0;
453 th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
454 pp_inc = 0;
455
456 /*{
457 int arg = 0xffff;
458 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
459 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
460 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
461 arg=10;
462 th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
463 }*/
464
465 int w;
466 int h;
467 w = (ti.pic_x + ti.frame_width + 1 & ~1) - (ti.pic_x & ~1);
468 h = (ti.pic_y + ti.frame_height + 1 & ~1) - (ti.pic_y & ~1);
469 size.x = w;
470 size.y = h;
471
472 texture->create(w, h, Image::FORMAT_RGBA, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE);
473
474 } else {
475 /* tear down the partial theora setup */
476 th_info_clear(&ti);
477 th_comment_clear(&tc);
478 }
479
480 th_setup_free(ts);
481
482 if (vorbis_p) {
483 vorbis_synthesis_init(&vd, &vi);
484 vorbis_block_init(&vd, &vb);
485 fprintf(stderr, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n",
486 vo.serialno, vi.channels, vi.rate);
487 //_setup(vi.channels, vi.rate);
488
489 } else {
490 /* tear down the partial vorbis setup */
491 vorbis_info_clear(&vi);
492 vorbis_comment_clear(&vc);
493 }
494
495 playing = false;
496 buffering = true;
497 time = 0;
498 audio_frames_wrote = 0;
499 };
500
get_time() const501 float VideoStreamPlaybackTheora::get_time() const {
502
503 //print_line("total: "+itos(get_total())+" todo: "+itos(get_todo()));
504 //return MAX(0,time-((get_total())/(float)vi.rate));
505 return time - AudioServer::get_singleton()->get_output_delay() - delay_compensation; //-((get_total())/(float)vi.rate);
506 };
507
get_texture()508 Ref<Texture> VideoStreamPlaybackTheora::get_texture() {
509
510 return texture;
511 }
512
update(float p_delta)513 void VideoStreamPlaybackTheora::update(float p_delta) {
514
515 if (!file)
516 return;
517
518 if (!playing || paused) {
519 //printf("not playing\n");
520 return;
521 };
522
523 #ifdef THEORA_USE_THREAD_STREAMING
524 thread_sem->post();
525 #endif
526
527 //double ctime =AudioServer::get_singleton()->get_mix_time();
528
529 //print_line("play "+rtos(p_delta));
530 time += p_delta;
531
532 if (videobuf_time > get_time()) {
533 return; //no new frames need to be produced
534 }
535
536 bool frame_done = false;
537 bool audio_done = !vorbis_p;
538
539 while (!frame_done || (!audio_done && !vorbis_eos)) {
540 //a frame needs to be produced
541
542 ogg_packet op;
543 bool no_theora = false;
544
545 while (vorbis_p) {
546 int ret;
547 float **pcm;
548
549 bool buffer_full = false;
550
551 /* if there's pending, decoded audio, grab it */
552 if ((ret = vorbis_synthesis_pcmout(&vd, &pcm)) > 0) {
553
554 const int AUXBUF_LEN = 4096;
555 int to_read = ret;
556 int16_t aux_buffer[AUXBUF_LEN];
557
558 while (to_read) {
559
560 int m = MIN(AUXBUF_LEN / vi.channels, to_read);
561
562 int count = 0;
563
564 for (int j = 0; j < m; j++) {
565 for (int i = 0; i < vi.channels; i++) {
566
567 int val = Math::fast_ftoi(pcm[i][j] * 32767.f);
568 if (val > 32767) val = 32767;
569 if (val < -32768) val = -32768;
570 aux_buffer[count++] = val;
571 }
572 }
573
574 if (mix_callback) {
575 int mixed = mix_callback(mix_udata, aux_buffer, m);
576 to_read -= mixed;
577 if (mixed != m) { //could mix no more
578 buffer_full = true;
579 break;
580 }
581 } else {
582 to_read -= m; //just pretend we sent the audio
583 }
584 }
585
586 int tr = vorbis_synthesis_read(&vd, ret - to_read);
587
588 if (vd.granulepos >= 0) {
589 // print_line("wrote: "+itos(audio_frames_wrote)+" gpos: "+itos(vd.granulepos));
590 }
591
592 //print_line("mix audio!");
593
594 audio_frames_wrote += ret - to_read;
595
596 //print_line("AGP: "+itos(vd.granulepos)+" added "+itos(ret-to_read));
597
598 } else {
599
600 /* no pending audio; is there a pending packet to decode? */
601 if (ogg_stream_packetout(&vo, &op) > 0) {
602 if (vorbis_synthesis(&vb, &op) == 0) { /* test for success! */
603 vorbis_synthesis_blockin(&vd, &vb);
604 }
605 } else { /* we need more data; break out to suck in another page */
606 //printf("need moar data\n");
607 break;
608 };
609 }
610
611 audio_done = videobuf_time < (audio_frames_wrote / float(vi.rate));
612
613 if (buffer_full)
614 break;
615 }
616
617 while (theora_p && !frame_done) {
618 /* theora is one in, one out... */
619 if (ogg_stream_packetout(&to, &op) > 0) {
620
621 if (false && pp_inc) {
622 pp_level += pp_inc;
623 th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level,
624 sizeof(pp_level));
625 pp_inc = 0;
626 }
627 /*HACK: This should be set after a seek or a gap, but we might not have
628 a granulepos for the first packet (we only have them for the last
629 packet on a page), so we just set it as often as we get it.
630 To do this right, we should back-track from the last packet on the
631 page and compute the correct granulepos for the first packet after
632 a seek or a gap.*/
633 if (op.granulepos >= 0) {
634 th_decode_ctl(td, TH_DECCTL_SET_GRANPOS, &op.granulepos,
635 sizeof(op.granulepos));
636 }
637 ogg_int64_t videobuf_granulepos;
638 if (th_decode_packetin(td, &op, &videobuf_granulepos) == 0) {
639 videobuf_time = th_granule_time(td, videobuf_granulepos);
640
641 //printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
642
643 /* is it already too old to be useful? This is only actually
644 useful cosmetically after a SIGSTOP. Note that we have to
645 decode the frame even if we don't show it (for now) due to
646 keyframing. Soon enough libtheora will be able to deal
647 with non-keyframe seeks. */
648
649 if (videobuf_time >= get_time()) {
650 frame_done = true;
651 } else {
652 /*If we are too slow, reduce the pp level.*/
653 pp_inc = pp_level > 0 ? -1 : 0;
654 }
655 } else {
656 }
657
658 } else {
659 no_theora = true;
660 break;
661 }
662 }
663
664 //print_line("no theora: "+itos(no_theora)+" theora eos: "+itos(theora_eos)+" frame done "+itos(frame_done));
665
666 #ifdef THEORA_USE_THREAD_STREAMING
667 if (file && thread_eof && no_theora && theora_eos && ring_buffer.data_left() == 0) {
668 #else
669 if (file && /*!videobuf_ready && */ no_theora && theora_eos) {
670 #endif
671 printf("video done, stopping\n");
672 stop();
673 return;
674 };
675 #if 0
676 if (!videobuf_ready || audio_todo > 0){
677 /* no data yet for somebody. Grab another page */
678
679 buffer_data();
680 while(ogg_sync_pageout(&oy,&og)>0){
681 queue_page(&og);
682 }
683 }
684 #else
685
686 if (!frame_done || !audio_done) {
687 //what's the point of waiting for audio to grab a page?
688
689 buffer_data();
690 while (ogg_sync_pageout(&oy, &og) > 0) {
691 queue_page(&og);
692 }
693 }
694 #endif
695 /* If playback has begun, top audio buffer off immediately. */
696 //if(stateflag) audio_write_nonblocking();
697
698 /* are we at or past time for this video frame? */
699 if (videobuf_ready && videobuf_time <= get_time()) {
700
701 //video_write();
702 //videobuf_ready=0;
703 } else {
704 //printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
705 }
706
707 float tdiff = videobuf_time - get_time();
708 /*If we have lots of extra time, increase the post-processing level.*/
709 if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) {
710 pp_inc = pp_level < pp_level_max ? 1 : 0;
711 } else if (tdiff < ti.fps_denominator * 0.05 / ti.fps_numerator) {
712 pp_inc = pp_level > 0 ? -1 : 0;
713 }
714 }
715
716 video_write();
717 };
718
719 void VideoStreamPlaybackTheora::play() {
720
721 if (!playing)
722 time = 0;
723 else {
724 stop();
725 }
726
727 playing = true;
728 delay_compensation = Globals::get_singleton()->get("audio/video_delay_compensation_ms");
729 delay_compensation /= 1000.0;
730 };
731
732 void VideoStreamPlaybackTheora::stop() {
733
734 if (playing) {
735
736 clear();
737 set_file(file_name); //reset
738 }
739 playing = false;
740 time = 0;
741 };
742
743 bool VideoStreamPlaybackTheora::is_playing() const {
744
745 return playing;
746 };
747
748 void VideoStreamPlaybackTheora::set_paused(bool p_paused) {
749
750 paused = p_paused;
751 //pau = !p_paused;
752 };
753
754 bool VideoStreamPlaybackTheora::is_paused(bool p_paused) const {
755
756 return paused;
757 };
758
759 void VideoStreamPlaybackTheora::set_loop(bool p_enable){
760
761 };
762
763 bool VideoStreamPlaybackTheora::has_loop() const {
764
765 return false;
766 };
767
768 float VideoStreamPlaybackTheora::get_length() const {
769
770 return 0;
771 };
772
773 String VideoStreamPlaybackTheora::get_stream_name() const {
774
775 return "";
776 };
777
778 int VideoStreamPlaybackTheora::get_loop_count() const {
779
780 return 0;
781 };
782
783 float VideoStreamPlaybackTheora::get_pos() const {
784
785 return get_time();
786 };
787
788 void VideoStreamPlaybackTheora::seek_pos(float p_time){
789
790 // no
791 };
792
793 void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
794
795 mix_callback = p_callback;
796 mix_udata = p_userdata;
797 }
798
799 int VideoStreamPlaybackTheora::get_channels() const {
800
801 return vi.channels;
802 }
803
804 void VideoStreamPlaybackTheora::set_audio_track(int p_idx) {
805
806 audio_track = p_idx;
807 }
808
809 int VideoStreamPlaybackTheora::get_mix_rate() const {
810
811 return vi.rate;
812 }
813
814 #ifdef THEORA_USE_THREAD_STREAMING
815
816 void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
817
818 VideoStreamPlaybackTheora *vs = (VideoStreamPlaybackTheora *)ud;
819
820 while (!vs->thread_exit) {
821
822 //just fill back the buffer
823 if (!vs->thread_eof) {
824
825 int to_read = vs->ring_buffer.space_left();
826 if (to_read) {
827 int read = vs->file->get_buffer(vs->read_buffer.ptr(), to_read);
828 vs->ring_buffer.write(vs->read_buffer.ptr(), read);
829 vs->thread_eof = vs->file->eof_reached();
830 }
831 }
832
833 vs->thread_sem->wait();
834 }
835 }
836
837 #endif
838
839 VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
840
841 file = NULL;
842 theora_p = 0;
843 vorbis_p = 0;
844 videobuf_ready = 0;
845 playing = false;
846 frames_pending = 0;
847 videobuf_time = 0;
848 paused = false;
849
850 buffering = false;
851 texture = Ref<ImageTexture>(memnew(ImageTexture));
852 mix_callback = NULL;
853 mix_udata = NULL;
854 audio_track = 0;
855 delay_compensation = 0;
856 audio_frames_wrote = 0;
857
858 #ifdef THEORA_USE_THREAD_STREAMING
859 int rb_power = nearest_shift(RB_SIZE_KB * 1024);
860 ring_buffer.resize(rb_power);
861 read_buffer.resize(RB_SIZE_KB * 1024);
862 thread_sem = Semaphore::create();
863 thread = NULL;
864 thread_exit = false;
865 thread_eof = false;
866
867 #endif
868 };
869
870 VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() {
871
872 #ifdef THEORA_USE_THREAD_STREAMING
873
874 memdelete(thread_sem);
875 #endif
876 clear();
877
878 if (file)
879 memdelete(file);
880 };
881
882 RES ResourceFormatLoaderVideoStreamTheora::load(const String &p_path, const String &p_original_path, Error *r_error) {
883 if (r_error)
884 *r_error = ERR_FILE_CANT_OPEN;
885
886 VideoStreamTheora *stream = memnew(VideoStreamTheora);
887 stream->set_file(p_path);
888
889 if (r_error)
890 *r_error = OK;
891
892 return Ref<VideoStreamTheora>(stream);
893 }
894
895 void ResourceFormatLoaderVideoStreamTheora::get_recognized_extensions(List<String> *p_extensions) const {
896
897 p_extensions->push_back("ogm");
898 p_extensions->push_back("ogv");
899 }
900 bool ResourceFormatLoaderVideoStreamTheora::handles_type(const String &p_type) const {
901 return (p_type == "VideoStream" || p_type == "VideoStreamTheora");
902 }
903
904 String ResourceFormatLoaderVideoStreamTheora::get_resource_type(const String &p_path) const {
905
906 String exl = p_path.extension().to_lower();
907 if (exl == "ogm" || exl == "ogv")
908 return "VideoStreamTheora";
909 return "";
910 }
911