30 #include "theora/theora.h"
31 #include "vorbis/codec.h"
37 static int video_inited = 0;
38 static int scale_video = 0;
39 static int playing = 1;
41 static uint g_screenWidth = 0;
42 static uint g_screenHeight = 0;
43 static int g_screenX = 0;
44 static int g_screenY = 0;
50 static GLint gl_screenYH = 0;
51 static GLint gl_screenXW = 0;
54 static GLfloat glVertices[4][4] = {{0}};
59 static int timer_started = 0;
62 static struct timeval timer_expire = { 0, 0 };
64 static int timer_expire;
67 static int audio_inited = 0;
68 static int audio_buffer_tail = 0;
69 static ALuint audio_sid = 0;
70 #define OGG_AUDIO_BUFFERS 15
74 static int audiobuf_fill = 0;
75 static short *audiobuf = NULL;
76 static longlong audiobuf_granulepos = 0;
77 static int audiofd_fragsize = 0;
80 static longlong videobuf_granulepos = -1;
81 static double videobuf_time = 0;
83 static bool use_shaders =
true;
92 int bytes =
cfread(buffer, 1, 8192, movie->
cfp);
113 static double OGG_get_time()
119 gettimeofday(&tv, NULL);
120 now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
125 return (now - base_time) * 0.001;
134 static void OGG_timer_init()
139 gettimeofday(&timer_expire, NULL);
141 timer_expire.tv_usec += (
int)((videobuf_time - OGG_get_time()) * 1000000.0);
143 if (timer_expire.tv_usec > 1000000) {
144 nsec = timer_expire.tv_usec / 1000000;
145 timer_expire.tv_sec += nsec;
146 timer_expire.tv_usec -= nsec * 1000000;
150 timer_expire += (
int)((videobuf_time - OGG_get_time()) * 1000000.0);
156 static void OGG_timer_do_wait()
163 struct timespec ts, tsRem;
166 gettimeofday(&tv, NULL);
168 if (tv.tv_sec > timer_expire.tv_sec)
171 if ( (tv.tv_sec == timer_expire.tv_sec) && (tv.tv_usec >= timer_expire.tv_usec) )
174 ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
175 ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
177 if (ts.tv_nsec < 0) {
178 ts.tv_nsec += 1000000000UL;
182 if ( (nanosleep(&ts, &tsRem) == -1) && (errno == EINTR) ) {
184 if ( (nanosleep(&tsRem, NULL) == -1) && (errno == EINTR) ) {
185 mprintf((
"MVE: Timer error! Aborting movie playback!\n"));
191 timer_expire.tv_usec += (
int)((videobuf_time - OGG_get_time()) * 1000000.0);
193 if (timer_expire.tv_usec > 1000000) {
194 nsec = timer_expire.tv_usec / 1000000;
195 timer_expire.tv_sec += nsec;
196 timer_expire.tv_usec -= nsec * 1000000;
203 if (tv > timer_expire)
206 ts = timer_expire - tv;
213 timer_expire += (
int)((videobuf_time - OGG_get_time()) * 1000000.0);
225 static void OGG_audio_init(vorbis_info *vinfo)
239 audiofd_fragsize = (((vinfo->channels * 16) / 8) * vinfo->rate);
241 audiobuf = (
short *)
vm_malloc(audiofd_fragsize);
244 static void OGG_audio_close()
260 if ( (audio_buffers[
i] != 0) && alIsBuffer(audio_buffers[
i]) ) {
266 audio_buffer_tail = 0;
269 audiobuf_granulepos = 0;
270 audiofd_fragsize = 0;
272 if (audiobuf != NULL) {
278 static void OGG_audio_write(vorbis_info *vorbis,
bool *ready)
280 ALint status, queued, processed = 0;
283 if ( !audio_inited || !(*ready) )
286 OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_PROCESSED, &processed),
return );
288 while (processed-- > 2)
294 OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_QUEUED, &queued),
return );
296 if ( audiobuf_fill && (queued < OGG_AUDIO_BUFFERS) ) {
297 if ( !audio_buffers[audio_buffer_tail] )
300 ALenum format = (vorbis->channels == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
302 OpenAL_ErrorCheck( alBufferData(audio_buffers[audio_buffer_tail], format, audiobuf, audiobuf_fill, vorbis->rate),
return );
304 OpenAL_ErrorCheck( alSourceQueueBuffers(audio_sid, 1, &audio_buffers[audio_buffer_tail]),
return );
306 if (++audio_buffer_tail == OGG_AUDIO_BUFFERS)
307 audio_buffer_tail = 0;
317 OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_QUEUED, &queued),
return );
319 if ( (status != AL_PLAYING) && (queued > 0) )
331 static void OGG_video_init(theora_info *tinfo)
333 float scale_by = 0.0f;
340 g_screenWidth = tinfo->frame_width;
341 g_screenHeight = tinfo->frame_height;
361 if ( ytex + utex + vtex == 0 ) {
362 nprintf((
"MOVIE",
"ERROR: Can't create a GL texture"));
369 if ( sdr_handle >= 0)
381 nprintf((
"MOVIE",
"ERROR: Can't create a GL texture"));
439 float movie_ratio = (
float)g_screenWidth / (
float)g_screenHeight;
441 if (screen_ratio > movie_ratio)
464 nprintf((
"MOVIE",
"ERROR: Can't allocate memory for pixelbuf"));
468 memset(
pixelbuf, 0, g_screenWidth * g_screenHeight * 3 );
483 gl_screenYH = g_screenY + g_screenHeight;
484 gl_screenXW = g_screenX + g_screenWidth;
486 gl_screenU =
i2fl(g_screenWidth) /
i2fl(wp2);
487 gl_screenV =
i2fl(g_screenHeight) /
i2fl(hp2);
490 gl_screenU =
i2fl(tinfo->frame_width-1) /
i2fl(2048) ;
491 gl_screenV =
i2fl(tinfo->frame_height-1) /
i2fl(2048);
498 glVertices[0][0] = (
GLfloat)g_screenX;
499 glVertices[0][1] = (
GLfloat)g_screenY;
500 glVertices[0][2] = 0.0f;
501 glVertices[0][3] = 0.0f;
503 glVertices[1][0] = (
GLfloat)g_screenX;
504 glVertices[1][1] = (
GLfloat)gl_screenYH;
505 glVertices[1][2] = 0.0f;
506 glVertices[1][3] = gl_screenV;
508 glVertices[2][0] = (
GLfloat)gl_screenXW;
509 glVertices[2][1] = (
GLfloat)g_screenY;
510 glVertices[2][2] = gl_screenU;
511 glVertices[2][3] = 0.0f;
513 glVertices[3][0] = (
GLfloat)gl_screenXW;
514 glVertices[3][1] = (
GLfloat)gl_screenYH;
515 glVertices[3][2] = gl_screenU;
516 glVertices[3][3] = gl_screenV;
527 if(!use_shaders && tinfo->frame_height > 450) {
528 mprintf((
"VIDEO: No shader support and hd video is beeing played this can get choppy."));
533 static void OGG_video_close()
535 if ( !video_inited ) {
559 ytex = utex = vtex = 0;
584 static void convert_YUV_to_RGB(yuv_buffer *yuv)
587 int R = 0, G = 0,
B = 0;
590 uint width_2 = g_screenWidth/2;
598 for (y = 0; y < g_screenHeight; y++) {
599 for (x = 0; x < width_2; x++) {
601 Y1 = *y_ptr; y_ptr++;
602 Y2 = *y_ptr; y_ptr++;
614 R = ((C + 409 * E + 128) >> 8);
615 G = ((C - 100 * D - 208 * E + 128) >> 8);
616 B = ((C + 516 * D + 128) >> 8);
629 R = ((C + 409 * E + 128) >> 8);
630 G = ((C - 100 * D - 208 * E + 128) >> 8);
631 B = ((C + 516 * D + 128) >> 8);
642 y_ptr += (yuv->y_stride - yuv->y_width);
646 u_ptr += yuv->uv_stride;
647 v_ptr += yuv->uv_stride;
653 static void OGG_video_draw(theora_state *tstate)
657 theora_decode_YUVout(tstate, &yuv);
659 convert_YUV_to_RGB(&yuv);
704 videobuf_granulepos = -1;
710 vorbis_block_clear(&movie->
vblock);
711 vorbis_dsp_clear(&movie->
vstate);
712 vorbis_comment_clear(&movie->
vcomment);
713 vorbis_info_clear(&movie->
vinfo);
719 theora_clear(&movie->
tstate);
720 theora_comment_clear(&movie->
tcomment);
721 theora_info_clear(&movie->
tinfo);
754 memset( lower_name, 0,
sizeof(lower_name) );
755 strncpy( lower_name, filename,
sizeof(lower_name)-1 );
758 char *p = strchr( lower_name,
'.' );
768 if (movie->
cfp == NULL)
771 if (movie->
cfp == NULL) {
772 mprintf((
"Theora ERROR: Unable to find and open movie file named '%s'\n", lower_name));
780 vorbis_info_init(&movie->
vinfo);
781 vorbis_comment_init(&movie->
vcomment);
784 theora_comment_init(&movie->
tcomment);
785 theora_info_init(&movie->
tinfo);
789 while ( !stateflag ) {
790 int ret = OGG_buffer_data(movie);
796 ogg_stream_state test;
799 if ( !ogg_page_bos(&movie->
opage) ) {
801 OGG_queue_page(movie);
806 ogg_stream_init(&test, ogg_page_serialno(&movie->
opage));
807 ogg_stream_pagein(&test, &movie->
opage);
808 ogg_stream_packetout(&test, &movie->
opacket);
813 memcpy( &movie->
t_osstate, &test,
sizeof(test) );
817 memcpy( &movie->
v_osstate, &test,
sizeof(test) );
821 ogg_stream_clear(&test);
829 mprintf((
"Theora ERROR: Unable to find video data in '%s'\n", lower_name));
834 if ( (movie->
vinfo.channels < 0) || (movie->
vinfo.channels > 2) ) {
835 mprintf((
"Theora ERROR: Unsupported number of audio channels!\n"));
841 vorbis_info_clear(&movie->
vinfo);
842 vorbis_comment_clear(&movie->
vcomment);
853 while ( (movie->
theora_p < 3) && (ret) ){
855 mprintf((
"Theora ERROR: Error parsing Theora stream headers on '%s'! Corrupt stream?\n", lower_name));
869 if ( (ret < 0) || vorbis_synthesis_headerin(&movie->
vinfo, &movie->
vcomment, &movie->
opacket) ) {
870 mprintf((
"Theora ERROR: Error parsing Vorbis stream headers on '%s'! Corrupt stream?\n", lower_name));
883 OGG_queue_page(movie);
885 ret = OGG_buffer_data(movie);
888 mprintf((
"Theora ERROR: End of file while searching for codec headers in '%s'!\n", lower_name));
898 if (movie->
tinfo.pixelformat != OC_PF_420) {
899 mprintf((
"Theora ERROR: Only the yuv420p chroma is supported!\n"));
903 if (movie->
tinfo.offset_x || movie->
tinfo.offset_y) {
904 mprintf((
"Theora ERROR: Player does not support frame offsets!\n"));
910 vorbis_synthesis_init(&movie->
vstate, &movie->
vinfo);
927 bool stateflag =
false;
928 bool videobuf_ready =
false;
929 bool audiobuf_ready =
false;
930 double last_time = 0.0;
932 if ( (movie == NULL) || !movie->
theora_p )
941 OGG_audio_init(&movie->
vinfo);
944 OGG_video_init(&movie->
tinfo);
956 while ( movie->
vorbis_p && !audiobuf_ready ) {
961 if ( (ret = vorbis_synthesis_pcmout(&movie->
vstate, &pcm)) > 0 ) {
962 int count = (audiobuf_fill / 2);
963 int maxsamples = (audiofd_fragsize - audiobuf_fill) / 2 / movie->
vinfo.channels;
965 for (i = 0; (i < ret) && (i < maxsamples); i++) {
966 for (j = 0; j < movie->
vinfo.channels; j++) {
967 float val = pcm[j][
i] * 32767.0f + 0.5f;
975 audiobuf[count++] = (short)val;
979 vorbis_synthesis_read(&movie->
vstate, i);
981 audiobuf_fill += (i * movie->
vinfo.channels * 2);
983 if (audiobuf_fill == audiofd_fragsize)
984 audiobuf_ready =
true;
986 if (movie->
vstate.granulepos >= 0)
987 audiobuf_granulepos = movie->
vstate.granulepos - ret +
i;
989 audiobuf_granulepos +=
i;
993 if ( vorbis_synthesis(&movie->
vblock, &movie->
opacket) == 0 )
994 vorbis_synthesis_blockin(&movie->
vstate, &movie->
vblock);
1003 while ( !videobuf_ready ) {
1010 videobuf_time = theora_granule_time(&movie->
tstate, movie->
tstate.granulepos);
1012 double now_time = OGG_get_time();
1013 double delay = videobuf_time - OGG_get_time();
1016 if ( (delay >= 0.0) || (now_time - last_time >= 1.0) )
1017 videobuf_ready =
true;
1020 if ( !videobuf_ready && (movie->
vorbis_p && !audiobuf_ready) &&
cfeof(movie->
cfp) )
1023 if ( !videobuf_ready || (movie->
vorbis_p && !audiobuf_ready) ) {
1025 OGG_buffer_data(movie);
1028 OGG_queue_page(movie);
1033 OGG_audio_write(&movie->
vinfo, &audiobuf_ready);
1036 if ( stateflag && videobuf_ready && (videobuf_time <= OGG_get_time()) ) {
1037 OGG_video_draw(&movie->
tstate);
1038 last_time = OGG_get_time();
1039 videobuf_ready =
false;
1043 if ( videobuf_ready && (audiobuf_ready || !movie->
vorbis_p) )
1051 OGG_timer_do_wait();
GLenum GLsizei GLenum format
#define OGG_AUDIO_BUFFERS
ogg_stream_state v_osstate
#define vm_malloc_q(size)
#define OpenAL_ErrorCheck(x, y)
WINGDIAPI void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count)
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
void Color(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha=255)
void SetTextureSource(gr_texture_source ts)
WINGDIAPI void APIENTRY glMatrixMode(GLenum mode)
int timer_get_microseconds()
void theora_close(THEORAFILE *movie)
WINGDIAPI void APIENTRY glLoadIdentity(void)
WINGDIAPI void APIENTRY glDeleteTextures(GLsizei n, const GLuint *textures)
#define gr_maybe_create_shader
void SetZbufferType(gr_zbuffer_type zt)
#define OpenAL_ErrorPrint(x)
ALuint *typedef ALuint *typedef ALenum
opengl_texture_state Texture
#define GL_TEXTURE_WRAP_S
WINGDIAPI void APIENTRY glPopMatrix(void)
#define CLAMP(x, min, max)
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
void add_vertex_component(vertex_format_data::vertex_format format_type, uint stride, void *src)
ALuint *typedef ALuint *typedef ALint
ogg_stream_state t_osstate
ogg_sync_state osyncstate
WINGDIAPI void APIENTRY glGenTextures(GLsizei n, GLuint *textures)
void SetAlphaBlendMode(gr_alpha_blend ab)
void opengl_tcache_get_adjusted_texture_size(int w_in, int h_in, int *w_out, int *h_out)
WINGDIAPI void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
#define GL_TEXTURE_MAG_FILTER
opengl_uniform_state Uniform
void SetTarget(GLenum tex_target)
GLint GLint GLint GLint GLint x
for(int idx=0;idx< i;idx++)
THEORAFILE * theora_open(char *filename)
void Enable(GLuint tex_id=0)
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
void BindArrayBuffer(GLuint id)
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
#define GL_TEXTURE_WRAP_T
void theora_play(THEORAFILE *movie)
WINGDIAPI void APIENTRY glPushMatrix(void)
#define GL_TEXTURE_MIN_FILTER
void opengl_set_texture_target(GLenum target)
void SetShaderMode(GLboolean mode)
void Delete(GLuint tex_id)
WINGDIAPI void APIENTRY glScalef(GLfloat x, GLfloat y, GLfloat z)
WINGDIAPI void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)
WINGDIAPI void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param)
bool is_minimum_GLSL_version()
void opengl_shader_set_current(opengl_shader_t *shader_obj)
int cfclose(CFILE *cfile)
int timer_get_milliseconds()
void opengl_bind_vertex_layout(vertex_layout &layout)
void SetActiveUnit(GLuint id=0)
#define GL_TRIANGLE_STRIP