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