FS2_Open
Open source remastering of the Freespace 2 engine
oggplayer.cpp
Go to the documentation of this file.
1 
2 
3 // This player code is based largely on the player example in the libtheora package
4 // and so they get a fair bit of credit for this code, even if in ideas/comments only
5 
6 
7 #ifdef SCP_UNIX
8 #include <sys/time.h>
9 #include <errno.h>
10 #endif
11 
12 #include "bmpman/bmpman.h"
13 #include "cfile/cfile.h"
14 #include "cutscene/oggplayer.h"
15 #include "globalincs/def_files.h"
16 #include "globalincs/pstypes.h"
17 #include "graphics/2d.h"
18 #include "graphics/gropengl.h"
19 #include "graphics/gropengldraw.h"
22 #include "graphics/gropenglstate.h"
24 #include "io/key.h"
25 #include "io/timer.h"
26 #include "osapi/osapi.h"
27 #include "sound/openal.h"
28 #include "sound/sound.h"
29 
30 #include "theora/theora.h"
31 #include "vorbis/codec.h"
32 
33 
34 extern int Cmdline_noscalevid;
35 
36 static int hp2, wp2;
37 static int video_inited = 0;
38 static int scale_video = 0;
39 static int playing = 1;
40 static ubyte *pixelbuf = NULL;
41 static uint g_screenWidth = 0;
42 static uint g_screenHeight = 0;
43 static int g_screenX = 0;
44 static int g_screenY = 0;
45 
46 static GLuint GLtex = 0;
47 static GLuint ytex = 0;
48 static GLuint utex = 0;
49 static GLuint vtex = 0;
50 static GLint gl_screenYH = 0;
51 static GLint gl_screenXW = 0;
52 static GLfloat gl_screenU = 0;
53 static GLfloat gl_screenV = 0;
54 static GLfloat glVertices[4][4] = {{0}};
55 
56 // video externs from API graphics functions
57 extern void opengl_tcache_get_adjusted_texture_size(int w_in, int h_in, int *w_out, int *h_out);
58 
59 static int timer_started = 0;
60 static longlong base_time = -1;
61 #ifdef SCP_UNIX
62 static struct timeval timer_expire = { 0, 0 };
63 #else
64 static int timer_expire;
65 #endif
66 
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
71 static ALuint audio_buffers[OGG_AUDIO_BUFFERS];
72 
73 // single audio fragment audio buffering
74 static int audiobuf_fill = 0;
75 static short *audiobuf = NULL;
76 static longlong audiobuf_granulepos = 0; // time position of last sample
77 static int audiofd_fragsize = 0;
78 
79 // single frame video buffering
80 static longlong videobuf_granulepos = -1;
81 static double videobuf_time = 0;
82 
83 static bool use_shaders = true;
84 // -----------------------------------------------------------------------------
85 // Utility items
86 //
87 
88 // helper; just grab some more compressed bitstream and sync it for page extraction
89 static int OGG_buffer_data(THEORAFILE *movie)
90 {
91  char *buffer = ogg_sync_buffer(&movie->osyncstate, 8192); // Doubled read size to fix choppy audio with high bitrate movies - Valathil
92  int bytes = cfread(buffer, 1, 8192, movie->cfp);
93 
94  ogg_sync_wrote(&movie->osyncstate, bytes);
95 
96  return bytes;
97 }
98 
99 
100 // helper: push a page into the appropriate steam
101 // this can be done blindly; a stream won't accept a page that doesn't belong to it
102 static void OGG_queue_page(THEORAFILE *movie)
103 {
104  Assert( movie->theora_p );
105 
106  ogg_stream_pagein(&movie->t_osstate, &movie->opage);
107 
108  if (movie->vorbis_p)
109  ogg_stream_pagein(&movie->v_osstate, &movie->opage);
110 }
111 
112 // get relative time since beginning playback, compensating for A/V drift
113 static double OGG_get_time()
114 {
115 #ifdef SCP_UNIX
116  longlong now;
117  struct timeval tv;
118 
119  gettimeofday(&tv, NULL);
120  now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
121 
122  if (base_time == -1)
123  base_time = now;
124 
125  return (now - base_time) * 0.001;
126 #else
127  if (base_time == -1)
128  base_time = timer_get_milliseconds();
129 
130  return (timer_get_milliseconds() - base_time) * 0.001;
131 #endif
132 }
133 
134 static void OGG_timer_init()
135 {
136 #if SCP_UNIX
137  int nsec = 0;
138 
139  gettimeofday(&timer_expire, NULL);
140 
141  timer_expire.tv_usec += (int)((videobuf_time - OGG_get_time()) * 1000000.0);
142 
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;
147  }
148 #else
149  timer_expire = timer_get_microseconds();
150  timer_expire += (int)((videobuf_time - OGG_get_time()) * 1000000.0);
151 #endif
152 
153  timer_started = 1;
154 }
155 
156 static void OGG_timer_do_wait()
157 {
158  if (!timer_started)
159  OGG_timer_init();
160 
161 #if SCP_UNIX
162  int nsec = 0;
163  struct timespec ts, tsRem;
164  struct timeval tv;
165 
166  gettimeofday(&tv, NULL);
167 
168  if (tv.tv_sec > timer_expire.tv_sec)
169  goto end;
170 
171  if ( (tv.tv_sec == timer_expire.tv_sec) && (tv.tv_usec >= timer_expire.tv_usec) )
172  goto end;
173 
174  ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
175  ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
176 
177  if (ts.tv_nsec < 0) {
178  ts.tv_nsec += 1000000000UL;
179  --ts.tv_sec;
180  }
181 
182  if ( (nanosleep(&ts, &tsRem) == -1) && (errno == EINTR) ) {
183  // so we got an error that was a signal interupt, try to sleep again with remainder of time
184  if ( (nanosleep(&tsRem, NULL) == -1) && (errno == EINTR) ) {
185  mprintf(("MVE: Timer error! Aborting movie playback!\n"));
186  return;
187  }
188  }
189 
190 end:
191  timer_expire.tv_usec += (int)((videobuf_time - OGG_get_time()) * 1000000.0);
192 
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;
197  }
198 #else
199  int tv, ts, ts2;
200 
201  tv = timer_get_microseconds();
202 
203  if (tv > timer_expire)
204  goto end;
205 
206  ts = timer_expire - tv;
207 
208  ts2 = ts/1000;
209 
210  Sleep(ts2);
211 
212 end:
213  timer_expire += (int)((videobuf_time - OGG_get_time()) * 1000000.0);
214 #endif
215 }
216 
217 //
218 // End Utility items
219 // -----------------------------------------------------------------------------
220 
221 // -----------------------------------------------------------------------------
222 // That wonderful Audio
223 //
224 
225 static void OGG_audio_init(vorbis_info *vinfo)
226 {
227  if (audio_inited)
228  return;
229 
230  Assert( vinfo != NULL );
231 
232  audio_inited = 1;
233 
234  OpenAL_ErrorCheck( alGenSources(1, &audio_sid), return );
235  OpenAL_ErrorPrint( alSourcef(audio_sid, AL_GAIN, 1.0f) );
236 
237  memset( &audio_buffers, 0, sizeof(ALuint) * OGG_AUDIO_BUFFERS );
238 
239  audiofd_fragsize = (((vinfo->channels * 16) / 8) * vinfo->rate);
240 
241  audiobuf = (short *) vm_malloc(audiofd_fragsize);
242 }
243 
244 static void OGG_audio_close()
245 {
246  if ( !audio_inited )
247  return;
248 
249  audio_inited = 0;
250 
251  ALint p = 0;
252 
253  OpenAL_ErrorPrint( alSourceStop(audio_sid) );
254  OpenAL_ErrorPrint( alGetSourcei(audio_sid, AL_BUFFERS_PROCESSED, &p) );
255  OpenAL_ErrorPrint( alSourceUnqueueBuffers(audio_sid, p, audio_buffers) );
256  OpenAL_ErrorPrint( alDeleteSources(1, &audio_sid) );
257 
258  for (int i = 0; i < OGG_AUDIO_BUFFERS; i++) {
259  // make sure that the buffer is real before trying to delete, it could crash for some otherwise
260  if ( (audio_buffers[i] != 0) && alIsBuffer(audio_buffers[i]) ) {
261  OpenAL_ErrorPrint( alDeleteBuffers(1, &audio_buffers[i]) );
262  }
263  }
264 
265  audio_sid = 0;
266  audio_buffer_tail = 0;
267 
268  audiobuf_fill = 0;
269  audiobuf_granulepos = 0;
270  audiofd_fragsize = 0;
271 
272  if (audiobuf != NULL) {
273  vm_free(audiobuf);
274  audiobuf = NULL;
275  }
276 }
277 
278 static void OGG_audio_write(vorbis_info *vorbis, bool *ready)
279 {
280  ALint status, queued, processed = 0;
281  ALuint bid = 0;
282 
283  if ( !audio_inited || !(*ready) )
284  return;
285 
286  OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_PROCESSED, &processed), return );
287 
288  while (processed-- > 2)
289  OpenAL_ErrorPrint( alSourceUnqueueBuffers(audio_sid, 1, &bid) );
290 
291  if ( !(*ready) )
292  return;
293 
294  OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_QUEUED, &queued), return );
295 
296  if ( audiobuf_fill && (queued < OGG_AUDIO_BUFFERS) ) {
297  if ( !audio_buffers[audio_buffer_tail] )
298  OpenAL_ErrorCheck( alGenBuffers(1, &audio_buffers[audio_buffer_tail]), return );
299 
300  ALenum format = (vorbis->channels == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
301 
302  OpenAL_ErrorCheck( alBufferData(audio_buffers[audio_buffer_tail], format, audiobuf, audiobuf_fill, vorbis->rate), return );
303 
304  OpenAL_ErrorCheck( alSourceQueueBuffers(audio_sid, 1, &audio_buffers[audio_buffer_tail]), return );
305 
306  if (++audio_buffer_tail == OGG_AUDIO_BUFFERS)
307  audio_buffer_tail = 0;
308 
309  audiobuf_fill = 0;
310  *ready = false;
311  } else {
312  // mprintf(("Theora WARN: Buffer overrun: Queue full\n"));
313  }
314 
315  OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_SOURCE_STATE, &status), return );
316 
317  OpenAL_ErrorCheck( alGetSourcei(audio_sid, AL_BUFFERS_QUEUED, &queued), return );
318 
319  if ( (status != AL_PLAYING) && (queued > 0) )
320  OpenAL_ErrorPrint( alSourcePlay(audio_sid) );
321 }
322 
323 //
324 // End Audio stuff
325 // -----------------------------------------------------------------------------
326 
327 // -----------------------------------------------------------------------------
328 // Video related stuff
329 //
330 
331 static void OGG_video_init(theora_info *tinfo)
332 {
333  float scale_by = 0.0f;
334 
335  if (video_inited)
336  return;
337 
338  Assert( tinfo != NULL );
339 
340  g_screenWidth = tinfo->frame_width;
341  g_screenHeight = tinfo->frame_height;
342 
343 
344  if (gr_screen.mode == GR_OPENGL) {
346  opengl_tcache_get_adjusted_texture_size(g_screenWidth, g_screenHeight, &wp2, &hp2);
347 
349  use_shaders = false;
350 
351  if(use_shaders) {
352  glGenTextures(1, &ytex);
353  glGenTextures(1, &utex);
354  glGenTextures(1, &vtex);
355 
356  Assert( ytex != 0 );
357  Assert( utex != 0 );
358  Assert( vtex != 0 );
359 
360 
361  if ( ytex + utex + vtex == 0 ) {
362  nprintf(("MOVIE", "ERROR: Can't create a GL texture"));
363  video_inited = 1;
364  return;
365  }
366 
367  int sdr_handle = gr_maybe_create_shader(SDR_TYPE_VIDEO_PROCESS, 0);
368 
369  if ( sdr_handle >= 0)
370  opengl_shader_set_current(sdr_handle);
371  else
372  use_shaders = false;
373  }
374 
375  if(!use_shaders) {
376  glGenTextures(1, &GLtex);
377 
378  Assert( GLtex != 0 );
379 
380  if ( GLtex == 0 ) {
381  nprintf(("MOVIE", "ERROR: Can't create a GL texture"));
382  video_inited = 1;
383  return;
384  }
385  }
386 
387  gr_set_lighting(false, false);
389 
390  if(!use_shaders) {
393  GL_state.Texture.Enable(GLtex);
399  }
403 
404  if(use_shaders) {
407  GL_state.Texture.Enable(ytex);
412 
413  // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
415 
418  GL_state.Texture.Enable(utex);
423 
424  // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
426 
429  GL_state.Texture.Enable(vtex);
434 
435  // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
437  }
438  float screen_ratio = (float)gr_screen.center_w / (float)gr_screen.center_h;
439  float movie_ratio = (float)g_screenWidth / (float)g_screenHeight;
440 
441  if (screen_ratio > movie_ratio)
442  scale_by = (float)gr_screen.center_h / (float)g_screenHeight;
443  else
444  scale_by = (float)gr_screen.center_w / (float)g_screenWidth;
445 
446  // don't bother setting anything if we aren't going to need it
447  if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
449  glPushMatrix();
450  glLoadIdentity();
451 
452  glScalef( scale_by, scale_by, -1.0f );
453  scale_video = 1;
454  }
455 
456  // set our color so that we can make sure that it's correct
457  GL_state.Color(255, 255, 255, 255);
458  }
459 
460  if(!use_shaders) {
461  pixelbuf = (ubyte *) vm_malloc_q(g_screenWidth * g_screenHeight * 3);
462 
463  if (pixelbuf == NULL) {
464  nprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
465  video_inited = 1;
466  return;
467  }
468  memset( pixelbuf, 0, g_screenWidth * g_screenHeight * 3 );
469  }
470 
471 
472  if (scale_video) {
473  g_screenX = ((fl2i(gr_screen.center_w / scale_by + 0.5f) - g_screenWidth) / 2) + fl2i(gr_screen.center_offset_x / scale_by + 0.5f);
474  g_screenY = ((fl2i(gr_screen.center_h / scale_by + 0.5f) - g_screenHeight) / 2) + fl2i(gr_screen.center_offset_y / scale_by + 0.5f);
475  } else {
476  // centers on 1024x768, fills on 640x480
477  g_screenX = ((gr_screen.center_w - g_screenWidth) / 2) + gr_screen.center_offset_x;
478  g_screenY = ((gr_screen.center_h - g_screenHeight) / 2) + gr_screen.center_offset_y;
479  }
480 
481  // set additional values for screen width/height and UV coords
482  if (gr_screen.mode == GR_OPENGL) {
483  gl_screenYH = g_screenY + g_screenHeight;
484  gl_screenXW = g_screenX + g_screenWidth;
485 
486  gl_screenU = i2fl(g_screenWidth) / i2fl(wp2);
487  gl_screenV = i2fl(g_screenHeight) / i2fl(hp2);
488 
489  if(use_shaders) {
490  gl_screenU = i2fl(tinfo->frame_width-1) / i2fl(2048) ;
491  gl_screenV = i2fl(tinfo->frame_height-1) / i2fl(2048);
493  GL_state.Uniform.setUniformi("ytex", 0);
494  GL_state.Uniform.setUniformi("utex", 1);
495  GL_state.Uniform.setUniformi("vtex", 2);
496  }
497 
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;
502 
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;
507 
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;
512 
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;
517 
519 
520  vertex_layout vert_def;
521 
522  vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(glVertices[0]), glVertices);
523  vert_def.add_vertex_component(vertex_format_data::TEX_COORD, sizeof(glVertices[0]), &(glVertices[0][2]));
524 
525  opengl_bind_vertex_layout(vert_def);
526  }
527  if(!use_shaders && tinfo->frame_height > 450) {
528  mprintf(("VIDEO: No shader support and hd video is beeing played this can get choppy."));
529  }
530  video_inited = 1;
531 }
532 
533 static void OGG_video_close()
534 {
535  if ( !video_inited ) {
536  return;
537  }
538 
539  if (gr_screen.mode == GR_OPENGL) {
540  if (scale_video) {
542  glPopMatrix();
543  }
544 
546  if(use_shaders) {
547  GL_state.Texture.Delete(ytex);
548  GL_state.Texture.Delete(utex);
549  GL_state.Texture.Delete(vtex);
550  glDeleteTextures(1, &ytex);
551  glDeleteTextures(1, &utex);
552  glDeleteTextures(1, &vtex);
553  } else {
554  GL_state.Texture.Delete(GLtex);
555  glDeleteTextures(1, &GLtex);
556  }
558 
559  ytex = utex = vtex = 0;
560  GLtex = 0;
563  }
564 
565  if (pixelbuf != NULL) {
566  vm_free(pixelbuf);
567  pixelbuf = NULL;
568  }
569 
570  video_inited = 0;
571  scale_video = 0;
572  pixelbuf = NULL;
573  g_screenWidth = 0;
574  g_screenHeight = 0;
575  g_screenX = 0;
576  g_screenY = 0;
577 
578  gl_screenYH = 0;
579  gl_screenXW = 0;
580  gl_screenU = 0;
581  gl_screenV = 0;
582 }
583 
584 static void convert_YUV_to_RGB(yuv_buffer *yuv)
585 {
586  int Y1, Y2, U, V;
587  int R = 0, G = 0, B = 0;
588  int C, D, E;
589  uint x, y;
590  uint width_2 = g_screenWidth/2;
591 
592  ubyte *pix = &pixelbuf[0];
593 
594  ubyte *y_ptr = (ubyte *)yuv->y;
595  ubyte *u_ptr = (ubyte *)yuv->u;
596  ubyte *v_ptr = (ubyte *)yuv->v;
597 
598  for (y = 0; y < g_screenHeight; y++) {
599  for (x = 0; x < width_2; x++) {
600  // we need two pixels of Y
601  Y1 = *y_ptr; y_ptr++;
602  Y2 = *y_ptr; y_ptr++;
603 
604  // only one pixel of U and V (half the size of Y)
605  U = u_ptr[x];
606  V = v_ptr[x];
607 
608  D = (U - 128);
609  E = (V - 128);
610 
611  // first pixel
612  C = (Y1 - 16) * 298;
613 
614  R = ((C + 409 * E + 128) >> 8);
615  G = ((C - 100 * D - 208 * E + 128) >> 8);
616  B = ((C + 516 * D + 128) >> 8);
617 
618  CLAMP(R, 0, 255);
619  CLAMP(G, 0, 255);
620  CLAMP(B, 0, 255);
621 
622  *pix++ = (ubyte)B;
623  *pix++ = (ubyte)G;
624  *pix++ = (ubyte)R;
625 
626  // second pixel (U and V values are resused)
627  C = (Y2 - 16) * 298;
628 
629  R = ((C + 409 * E + 128) >> 8);
630  G = ((C - 100 * D - 208 * E + 128) >> 8);
631  B = ((C + 516 * D + 128) >> 8);
632 
633  CLAMP(R, 0, 255);
634  CLAMP(G, 0, 255);
635  CLAMP(B, 0, 255);
636 
637  *pix++ = (ubyte)B;
638  *pix++ = (ubyte)G;
639  *pix++ = (ubyte)R;
640  }
641 
642  y_ptr += (yuv->y_stride - yuv->y_width);
643 
644  // u and v have to be done every other row (it's a 2x2 block)
645  if (y % 2) {
646  u_ptr += yuv->uv_stride;
647  v_ptr += yuv->uv_stride;
648  }
649  }
650 }
651 
652 extern int Mouse_hidden;
653 static void OGG_video_draw(theora_state *tstate)
654 {
655  yuv_buffer yuv;
656 
657  theora_decode_YUVout(tstate, &yuv);
658  if(!use_shaders)
659  convert_YUV_to_RGB(&yuv);
660  if (gr_screen.mode == GR_OPENGL) {
661  if(use_shaders) {
663  glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, yuv.y_stride, yuv.y_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv.y);
665  glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, yuv.uv_stride, yuv.uv_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv.u);
667  glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, yuv.uv_stride, yuv.uv_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv.v);
668  } else {
669  glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, g_screenWidth, g_screenHeight, GL_BGR, GL_UNSIGNED_BYTE, pixelbuf);
670  }
671 
673  }
674  Mouse_hidden = 1;
675  gr_flip();
676  os_poll();
677 
678  int k = key_inkey();
679  switch (k) {
680  case KEY_ESC:
681  case KEY_ENTER:
682  case KEY_SPACEBAR:
683  playing = 0;
684  }
685 }
686 
687 //
688 // End Video related stuff
689 // -----------------------------------------------------------------------------
690 
691 
692 // -----------------------------------------------------------------------------
693 // The global stuff...
694 //
695 
696 // close out a theora movie and free it's memory
698 {
699  Assert( movie != NULL );
700 
701  timer_started = 0;
702  base_time = -1;
703 
704  videobuf_granulepos = -1;
705  videobuf_time = 0;
706 
707  if (movie->vorbis_p) {
708  ogg_stream_clear(&movie->v_osstate);
709 
710  vorbis_block_clear(&movie->vblock);
711  vorbis_dsp_clear(&movie->vstate);
712  vorbis_comment_clear(&movie->vcomment);
713  vorbis_info_clear(&movie->vinfo);
714  }
715 
716  if (movie->theora_p) {
717  ogg_stream_clear(&movie->t_osstate);
718 
719  theora_clear(&movie->tstate);
720  theora_comment_clear(&movie->tcomment);
721  theora_info_clear(&movie->tinfo);
722  }
723 
724  ogg_sync_clear(&movie->osyncstate);
725 
726  // free the stream
727  if (movie->cfp)
728  cfclose(movie->cfp);
729 
730  // free the struct
731  vm_free(movie);
732 
733  playing = 1;
734 }
735 
736 // opens a theora file, verifies we can use it, initializes everything needed
737 // to decode and use the file
739 {
740  char lower_name[MAX_FILENAME_LEN];
741  THEORAFILE *movie;
742  int stateflag = 0;
743 
744 
745  // create the file
746  movie = (THEORAFILE *) vm_malloc_q( sizeof(THEORAFILE) );
747 
748  if (movie == NULL)
749  return NULL;
750 
751  memset( movie, 0, sizeof(THEORAFILE) );
752 
753  // lower case filename for checking
754  memset( lower_name, 0, sizeof(lower_name) );
755  strncpy( lower_name, filename, sizeof(lower_name)-1 );
756  strlwr(lower_name);
757 
758  char *p = strchr( lower_name, '.' );
759  if ( p ) *p = 0;
760 
761  strcat_s( lower_name, ".ogg" );
762 
763  // NOTE: Because the .ogg extension is used for both movies and sounds, we have to
764  // be a bit more specific about our search locations, so we look only in the
765  // two places that Theora movies might exist.
766  movie->cfp = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_ROOT);
767 
768  if (movie->cfp == NULL)
769  movie->cfp = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_MOVIES);
770 
771  if (movie->cfp == NULL) {
772  mprintf(("Theora ERROR: Unable to find and open movie file named '%s'\n", lower_name));
773  goto Error;
774  }
775 
776  // start up Ogg stream synchronization layer
777  ogg_sync_init(&movie->osyncstate);
778 
779  // init supporting Vorbis structures needed in header parsing
780  vorbis_info_init(&movie->vinfo);
781  vorbis_comment_init(&movie->vcomment);
782 
783  // init supporting Theora structures needed in header parsing
784  theora_comment_init(&movie->tcomment);
785  theora_info_init(&movie->tinfo);
786 
787  // ogg file open so parse the headers
788  // we are only interested in Vorbis/Theora streams
789  while ( !stateflag ) {
790  int ret = OGG_buffer_data(movie);
791 
792  if (ret == 0)
793  break;
794 
795  while (ogg_sync_pageout(&movie->osyncstate, &movie->opage) > 0) {
796  ogg_stream_state test;
797 
798  // is this a mandated initial header? If not, stop parsing
799  if ( !ogg_page_bos(&movie->opage) ) {
800  // don't leak the page; get it into the appropriate stream
801  OGG_queue_page(movie);
802  stateflag = 1;
803  break;
804  }
805 
806  ogg_stream_init(&test, ogg_page_serialno(&movie->opage));
807  ogg_stream_pagein(&test, &movie->opage);
808  ogg_stream_packetout(&test, &movie->opacket);
809 
810  // identify the codec: try theora
811  if ( !movie->theora_p && (theora_decode_header(&movie->tinfo, &movie->tcomment, &movie->opacket) >= 0) ) {
812  // it is theora
813  memcpy( &movie->t_osstate, &test, sizeof(test) );
814  movie->theora_p = 1;
815  } else if ( !movie->vorbis_p && (vorbis_synthesis_headerin(&movie->vinfo, &movie->vcomment, &movie->opacket) >= 0) ) {
816  // it is vorbis
817  memcpy( &movie->v_osstate, &test, sizeof(test) );
818  movie->vorbis_p = 1;
819  } else {
820  // whatever it is, we don't care about it
821  ogg_stream_clear(&test);
822  }
823  }
824  // fall through to non-bos page parsing
825  }
826 
827  // if we don't have usable video then bail out now
828  if ( !movie->theora_p ) {
829  mprintf(("Theora ERROR: Unable to find video data in '%s'\n", lower_name));
830  goto Error;
831  }
832 
833  // go ahead and do some audio stream error checking...
834  if ( (movie->vinfo.channels < 0) || (movie->vinfo.channels > 2) ) {
835  mprintf(("Theora ERROR: Unsupported number of audio channels!\n"));
836  movie->vorbis_p = 0;
837  }
838 
839  // if we don't have usable audio then close out it's partial setup and just use the video
840  if ( !Sound_enabled || !movie->vorbis_p ) {
841  vorbis_info_clear(&movie->vinfo);
842  vorbis_comment_clear(&movie->vcomment);
843  movie->vorbis_p = 0;
844  }
845 
846  int ret;
847 
848  // we're expecting more header packets.
849  while ( (movie->theora_p < 3) || (movie->vorbis_p && (movie->vorbis_p < 3)) ) {
850 
851  ret = ogg_stream_packetout(&movie->t_osstate, &movie->opacket);
852  // look for further theora headers
853  while ( (movie->theora_p < 3) && (ret) ){
854  if ( (ret < 0) || theora_decode_header(&movie->tinfo, &movie->tcomment, &movie->opacket) ) {
855  mprintf(("Theora ERROR: Error parsing Theora stream headers on '%s'! Corrupt stream?\n", lower_name));
856  goto Error;
857  }
858 
859  if (++movie->theora_p == 3)
860  break;
861 
862  ret = ogg_stream_packetout(&movie->t_osstate, &movie->opacket);
863  }
864 
865  ret = ogg_stream_packetout(&movie->v_osstate, &movie->opacket);
866 
867  // look for more vorbis header packets
868  while ( movie->vorbis_p && (movie->vorbis_p < 3) && (ret) ) {
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));
871  goto Error;
872  }
873 
874  if (++movie->vorbis_p == 3)
875  break;
876 
877  ret = ogg_stream_packetout(&movie->v_osstate, &movie->opacket);
878  }
879 
880  // The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec
881 
882  if ( ogg_sync_pageout(&movie->osyncstate, &movie->opage) > 0 ) {
883  OGG_queue_page(movie); // demux into the appropriate stream
884  } else {
885  ret = OGG_buffer_data(movie); // someone needs more data
886 
887  if ( ret == 0 ) {
888  mprintf(("Theora ERROR: End of file while searching for codec headers in '%s'!\n", lower_name));
889  goto Error;
890  }
891  }
892  }
893 
894  // initialize video decoder
895  theora_decode_init(&movie->tstate, &movie->tinfo);
896 
897  // and now for video stream error checking...
898  if (movie->tinfo.pixelformat != OC_PF_420) {
899  mprintf(("Theora ERROR: Only the yuv420p chroma is supported!\n"));
900  goto Error;
901  }
902 
903  if (movie->tinfo.offset_x || movie->tinfo.offset_y) {
904  mprintf(("Theora ERROR: Player does not support frame offsets!\n"));
905  goto Error;
906  }
907 
908  // initialize audio decoder, if there is audio
909  if (movie->vorbis_p) {
910  vorbis_synthesis_init(&movie->vstate, &movie->vinfo);
911  vorbis_block_init(&movie->vstate, &movie->vblock);
912  }
913 
914  return movie;
915 
916 
917 Error:
918  theora_close(movie);
919 
920  return NULL;
921 }
922 
923 // play that funky music... err, movie!
925 {
926  int i, j;
927  bool stateflag = false;
928  bool videobuf_ready = false;
929  bool audiobuf_ready = false;
930  double last_time = 0.0; // for frame skipper
931 
932  if ( (movie == NULL) || !movie->theora_p )
933  return;
934 
935  // only OGL is supported
936  if (gr_screen.mode != GR_OPENGL)
937  return;
938 
939  // open audio
940  if (movie->vorbis_p)
941  OGG_audio_init(&movie->vinfo);
942 
943  // open video
944  OGG_video_init(&movie->tinfo);
945 
946  // on to the main decode loop. We assume in this example that audio
947  // and video start roughly together, and don't begin playback until
948  // we have a start frame for both. This is not necessarily a valid
949  // assumption in Ogg A/V streams! It will always be true of the
950  // example_encoder (and most streams) though.
951 
952  while (playing) {
953  // we want a video and audio frame ready to go at all times. If
954  // we have to buffer incoming, buffer the compressed data (ie, let
955  // ogg do the buffering)
956  while ( movie->vorbis_p && !audiobuf_ready ) {
957  int ret;
958  float **pcm;
959 
960  // if there's pending, decoded audio, grab it
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;
964 
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;
968 
969  if (val > 32767.0f)
970  val = 32767.0f;
971 
972  if (val < -32768.0f)
973  val = -32768.0f;
974 
975  audiobuf[count++] = (short)val;
976  }
977  }
978 
979  vorbis_synthesis_read(&movie->vstate, i);
980 
981  audiobuf_fill += (i * movie->vinfo.channels * 2);
982 
983  if (audiobuf_fill == audiofd_fragsize)
984  audiobuf_ready = true;
985 
986  if (movie->vstate.granulepos >= 0)
987  audiobuf_granulepos = movie->vstate.granulepos - ret + i;
988  else
989  audiobuf_granulepos += i;
990  } else {
991  // no pending audio; is there a pending packet to decode?
992  if ( ogg_stream_packetout(&movie->v_osstate, &movie->opacket) > 0 ) {
993  if ( vorbis_synthesis(&movie->vblock, &movie->opacket) == 0 )
994  vorbis_synthesis_blockin(&movie->vstate, &movie->vblock);
995  }
996  // we need more data; break out to suck in another page
997  else {
998  break;
999  }
1000  }
1001  }
1002 
1003  while ( !videobuf_ready ) {
1004  // theora is one in, one out...
1005  if ( ogg_stream_packetout(&movie->t_osstate, &movie->opacket) <= 0 )
1006  break;
1007 
1008  theora_decode_packetin(&movie->tstate, &movie->opacket);
1009 
1010  videobuf_time = theora_granule_time(&movie->tstate, movie->tstate.granulepos);
1011 
1012  double now_time = OGG_get_time();
1013  double delay = videobuf_time - OGG_get_time();
1014 
1015  // if we are running slow then skip the frame, otherwise to go into ready mode
1016  if ( (delay >= 0.0) || (now_time - last_time >= 1.0) )
1017  videobuf_ready = true;
1018  }
1019 
1020  if ( !videobuf_ready && (movie->vorbis_p && !audiobuf_ready) && cfeof(movie->cfp) )
1021  break;
1022 
1023  if ( !videobuf_ready || (movie->vorbis_p && !audiobuf_ready) ) {
1024  // no data yet for somebody. Grab another page
1025  OGG_buffer_data(movie);
1026 
1027  while (ogg_sync_pageout(&movie->osyncstate, &movie->opage) > 0)
1028  OGG_queue_page(movie);
1029  }
1030 
1031  // If playback has begun, top audio buffer off immediately.
1032  if ( stateflag )
1033  OGG_audio_write(&movie->vinfo, &audiobuf_ready);
1034 
1035  // are we at or past time for this video frame?
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;
1040  }
1041 
1042  // if our buffers either don't exist or are ready to go, we can begin playback
1043  if ( videobuf_ready && (audiobuf_ready || !movie->vorbis_p) )
1044  stateflag = true;
1045 
1046  // bail if we run into eof
1047  // if ( cfeof(movie->cfp) )
1048  // break;
1049 
1050  if (videobuf_ready)
1051  OGG_timer_do_wait();
1052  }
1053 
1054  // tear it all down
1055  OGG_audio_close();
1056  OGG_video_close();
1057 }
unsigned int GLuint
Definition: Gl.h:52
ubyte theora_p
Definition: oggplayer.h:17
GLenum GLsizei GLenum format
Definition: Gl.h:1509
#define OGG_AUDIO_BUFFERS
Definition: oggplayer.cpp:70
ogg_stream_state v_osstate
Definition: oggplayer.h:23
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
#define vm_malloc_q(size)
Definition: pstypes.h:554
#define CFILE_NORMAL
Definition: cfile.h:89
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
#define OpenAL_ErrorCheck(x, y)
Definition: openal.h:24
int key_inkey()
Definition: key.cpp:424
WINGDIAPI void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count)
ogg_page opage
Definition: oggplayer.h:21
void gr_flip()
Definition: 2d.cpp:2113
void setUniformi(const SCP_string &name, const int value)
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
int mode
Definition: 2d.h:371
vorbis_info vinfo
Definition: oggplayer.h:30
opengl_array_state Array
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()
Definition: timer.cpp:160
void theora_close(THEORAFILE *movie)
Definition: oggplayer.cpp:697
WINGDIAPI void APIENTRY glLoadIdentity(void)
Assert(pm!=NULL)
int Cmdline_noscalevid
Definition: cmdline.cpp:328
#define mprintf(args)
Definition: pstypes.h:238
WINGDIAPI void APIENTRY glDeleteTextures(GLsizei n, const GLuint *textures)
#define gr_maybe_create_shader
Definition: 2d.h:943
int center_h
Definition: 2d.h:363
GLclampf f
Definition: Glext.h:7097
void SetZbufferType(gr_zbuffer_type zt)
int Mouse_hidden
Definition: mouse.cpp:52
int center_offset_y
Definition: 2d.h:364
#define OpenAL_ErrorPrint(x)
Definition: openal.h:34
ALuint *typedef ALuint *typedef ALenum
Definition: ds.cpp:133
int center_w
Definition: 2d.h:363
opengl_texture_state Texture
matrix * B
Definition: lua.cpp:445
GLenum GL_texture_target
#define GL_UNSIGNED_BYTE
Definition: Gl.h:192
#define GL_TEXTURE_WRAP_S
Definition: Gl.h:944
#define GR_OPENGL
Definition: 2d.h:648
theora_state tstate
Definition: oggplayer.h:28
WINGDIAPI void APIENTRY glPopMatrix(void)
#define CLAMP(x, min, max)
Definition: pstypes.h:488
CFILE * cfp
Definition: oggplayer.h:16
#define gr_set_lighting
Definition: 2d.h:924
#define CF_TYPE_ROOT
Definition: cfile.h:45
#define GL_FALSE
Definition: Gl.h:139
#define GL_LUMINANCE8
Definition: Gl.h:983
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define GL_RGB8
Definition: Gl.h:1000
#define GL_LINEAR
Definition: Gl.h:931
unsigned int uint
Definition: pstypes.h:64
void add_vertex_component(vertex_format_data::vertex_format format_type, uint stride, void *src)
Definition: 2d.h:1054
#define cfopen(...)
Definition: cfile.h:134
ALuint *typedef ALuint *typedef ALint
Definition: ds.cpp:133
#define nprintf(args)
Definition: pstypes.h:239
ogg_stream_state t_osstate
Definition: oggplayer.h:24
char * filename
ogg_sync_state osyncstate
Definition: oggplayer.h:20
vorbis_comment vcomment
Definition: oggplayer.h:33
WINGDIAPI void APIENTRY glGenTextures(GLsizei n, GLuint *textures)
vorbis_block vblock
Definition: oggplayer.h:32
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)
#define KEY_ENTER
Definition: key.h:125
GLuint buffer
Definition: Glext.h:5492
ushort * pixelbuf
Definition: mveplayer.cpp:67
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_LUMINANCE
Definition: Gl.h:786
#define GL_TRUE
Definition: Gl.h:138
#define GL_TEXTURE_MAG_FILTER
Definition: Gl.h:942
opengl_uniform_state Uniform
int Sound_enabled
Definition: sound.cpp:51
void SetTarget(GLenum tex_target)
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
for(int idx=0;idx< i;idx++)
Definition: multi_pxo.cpp:472
THEORAFILE * theora_open(char *filename)
Definition: oggplayer.cpp:738
unsigned char ubyte
Definition: pstypes.h:62
void Enable(GLuint tex_id=0)
void os_poll()
Definition: osapi.cpp:748
#define GL_MODELVIEW
Definition: Gl.h:751
#define GL_CLAMP_TO_EDGE
Definition: Glext.h:81
int cfeof(CFILE *cfile)
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
#define CF_TYPE_MOVIES
Definition: cfile.h:62
#define vm_malloc(size)
Definition: pstypes.h:547
void BindArrayBuffer(GLuint id)
GLuint GLfloat * val
Definition: Glext.h:6741
#define GL_TEXTURE_2D
Definition: Gl.h:570
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
#define GL_TEXTURE_WRAP_T
Definition: Gl.h:945
void theora_play(THEORAFILE *movie)
Definition: oggplayer.cpp:924
opengl_state GL_state
#define strcat_s(...)
Definition: safe_strings.h:68
#define KEY_ESC
Definition: key.h:124
WINGDIAPI void APIENTRY glPushMatrix(void)
#define fl2i(fl)
Definition: floating.h:33
#define GL_TEXTURE_MIN_FILTER
Definition: Gl.h:943
screen gr_screen
Definition: 2d.cpp:46
void opengl_set_texture_target(GLenum target)
__int64 longlong
Definition: pstypes.h:60
theora_comment tcomment
Definition: oggplayer.h:27
ogg_packet opacket
Definition: oggplayer.h:22
vorbis_dsp_state vstate
Definition: oggplayer.h:31
int center_offset_x
Definition: 2d.h:364
GLfloat GLfloat p
Definition: Glext.h:8373
void Sleep(int mili)
theora_info tinfo
Definition: oggplayer.h:26
void SetShaderMode(GLboolean mode)
#define KEY_SPACEBAR
Definition: key.h:128
#define i2fl(i)
Definition: floating.h:32
void Delete(GLuint tex_id)
GLint GLsizei count
Definition: Gl.h:1491
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()
Definition: gropengl.cpp:2064
void opengl_shader_set_current(opengl_shader_t *shader_obj)
#define GL_BGR
Definition: Glext.h:77
int cfclose(CFILE *cfile)
Definition: cfile.cpp:895
void strlwr(char *s)
int timer_get_milliseconds()
Definition: timer.cpp:150
void opengl_bind_vertex_layout(vertex_layout &layout)
ubyte vorbis_p
Definition: oggplayer.h:18
void SetActiveUnit(GLuint id=0)
GLuint GLuint end
Definition: Gl.h:1502
GLint y
Definition: Gl.h:1505
int GLint
Definition: Gl.h:48
float GLfloat
Definition: Gl.h:53
#define GL_TRIANGLE_STRIP
Definition: Gl.h:110