/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "sludge/movie.h" #include "sludge/newfatal.h" #include "sludge/sound.h" #include "sludge/timing.h" namespace Sludge { // sound_openal.cpp void playMovieStream(int a); #if 0 int initMovieSound(int f, ALenum format, int audioChannels, ALuint samplerate, ALuint(*callback)(void *userdata, ALubyte *data, ALuint bytes)); #endif MovieStates movieIsPlaying = nothing; int movieIsEnding = 0; float movieAspect = 1.6F; #if 0 typedef struct audioBuffers { char *buffer; uint size; audioBuffers *next; Uint32 time_ms; }audioBuffers; typedef struct audioQueue { audioBuffers *first, *last; int size; SDL_mutex *mutex; SDL_cond *cond; }audioQueue; audioQueue audioQ; Uint32 movieStartTick, movieCurrentTime; long long audioNsPerByte; long long audioNsPlayed; long long audioNsBuffered; long long audioBufferLen; bool movieSoundPlaying = false; int movieAudioIndex; GLuint yTextureName = 0; GLuint uTextureName = 0; GLuint vTextureName = 0; typedef struct videoBuffers { GLubyte *ytex; GLubyte *utex; GLubyte *vtex; videoBuffers *next; GLsizei w, h; Uint32 time_ms; }videoBuffers; typedef struct videoQueue { videoBuffers *first, *last; int size; SDL_mutex *mutex; SDL_cond *cond; }videoQueue; videoQueue videoQ; void audio_queue_init(audioQueue *q) { memset(q, 0, sizeof(audioQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int audio_queue_put(audioQueue *q, char *buffer, uint size, long long time_ms) { audioBuffers *audioBuf = new audioBuffers; if (!audioBuf) return -1; audioBuf->buffer = buffer; audioBuf->next = NULL; audioBuf->size = size; audioBuf->time_ms = time_ms; SDL_LockMutex(q->mutex); if (!q->last) q->first = audioBuf; else q->last->next = audioBuf; q->last = audioBuf; q->size ++; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } inline static int audio_queue_get(audioQueue *q, char **buffer) { int ret = 0; audioBuffers *audioBuf; SDL_LockMutex(q->mutex); audioBuf = q->first; if (audioBuf) { // Synch video timer to audio Uint32 tick = SDL_GetTicks() + 100; if (ABS((long int)((tick - movieStartTick) - (audioBuf->time_ms))) > 300) { movieStartTick = tick - audioBuf->time_ms; } q->first = audioBuf->next; if (!q->first) q->last = NULL; q->size--; *buffer = audioBuf->buffer; ret = audioBuf->size; delete audioBuf; } SDL_UnlockMutex(q->mutex); return ret; } void video_queue_init(videoQueue *q) { memset(q, 0, sizeof(videoQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int video_queue_put(videoQueue *q, GLubyte *ytex, GLubyte *utex, GLubyte *vtex, GLsizei w, GLsizei h, long long time_ms) { videoBuffers *videoBuf = new videoBuffers; if (!videoBuf) return -1; videoBuf->ytex = ytex; videoBuf->utex = utex; videoBuf->vtex = vtex; videoBuf->next = NULL; videoBuf->w = w; videoBuf->h = h; videoBuf->time_ms = time_ms; SDL_LockMutex(q->mutex); if (!q->last) q->first = videoBuf; else q->last->next = videoBuf; q->last = videoBuf; q->size ++; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } inline static int video_queue_get(videoQueue *q, GLubyte **ytex, GLubyte **utex, GLubyte **vtex, GLsizei *w, GLsizei *h) { videoBuffers *videoBuf; int ret = 0; SDL_LockMutex(q->mutex); videoBuf = q->first; if (videoBuf) { q->first = videoBuf->next; if (!q->first) q->last = NULL; q->size--; *ytex = videoBuf->ytex; *utex = videoBuf->utex; *vtex = videoBuf->vtex; *w = videoBuf->w; *h = videoBuf->h; ret = 1; delete videoBuf; } SDL_UnlockMutex(q->mutex); return ret; } #if 0 static void die_codec(vpx_codec_ctx_t *ctx, const char *s) { //const char *detail = vpx_codec_error_detail(ctx); fatal(s, vpx_codec_error(ctx)); } #endif void setMovieViewport() { float realAspect = (float) realWinWidth / realWinHeight; int vpHeight, vpWidth, vpOffsetX, vpOffsetY; if (realAspect > movieAspect) { vpHeight = realWinHeight; vpWidth = (int)(realWinHeight * movieAspect); vpOffsetY = 0; vpOffsetX = (realWinWidth - vpWidth) / 2; } else { vpWidth = realWinWidth; vpHeight = (int)((float) realWinWidth / movieAspect); vpOffsetY = (realWinHeight - vpHeight) / 2; vpOffsetX = 0; } #if 0 glViewport(vpOffsetX, vpOffsetY, vpWidth, vpHeight); #endif const GLfloat bPMVMatrix[] = { 2.0f / 640.f, .0, .0, .0, .0, -2.0f / 400.f, .0, .0, .0, .0, 1.0f, .0, -1.0, 1.0f, .0, 1.0f }; for (int i = 0; i < 16; i++) { aPMVMatrix[i] = bPMVMatrix[i]; } } static uint64_t xiph_lace_value(byte **np) { uint64_t lace; uint64_t value; byte *p = *np; lace = *p++; value = lace; while (lace == 255) { lace = *p++; value += lace; } *np = p; return value; } vorbis_dsp_state vorbisDspState; long long audioChannels; bool fakeAudio = false; // send audio to audio device... ALuint feedAudio(void *userdata, ALubyte *data, ALuint length) { static char *buffer = NULL; static uint bufOffset = 0; static uint bufSize = 0; ALuint got = 0; int bufLen; if (! buffer) { bufSize = audio_queue_get(&audioQ, &buffer); bufOffset = 0; if (bufSize <= 0) { bufSize = 0; buffer = NULL; if (! got) { got = audioChannels * 2; memset(data, 0, got); fprintf(stderr, "Faking audio...\n"); fakeAudio = true; } // SDL_CondSignal(audioQ.cond); return got; } } fakeAudio = false; if (length > bufSize - bufOffset) bufLen = bufSize - bufOffset; else bufLen = length; memcpy(data, buffer + bufOffset, bufLen); bufOffset += bufLen; length -= bufLen; got += bufLen; if (bufSize <= bufOffset) { buffer = NULL; delete [] buffer; } // fprintf (stderr, "Sending %d bytes of audio.\n", got); return got; } #endif int playMovie(int fileNumber) { #if 0 if (specialSettings & SPECIAL_SILENT) return 0; if (movieIsPlaying) return 0; movieSoundPlaying = false; vpx_codec_ctx_t codec; float pausefade = 1.0; using namespace mkvparser; MkvReader reader; if (reader.Open(fileNumber)) { warning(ERROR_MOVIE_ODDNESS); return 0; } long long pos = 0; EBMLHeader ebmlHeader; ebmlHeader.Parse(&reader, pos); mkvparser::Segment *pSegment; long long ret = mkvparser::Segment::CreateInstance(&reader, pos, pSegment); if (ret) { fatal("Movie error: Segment::CreateInstance() failed.\n"); } ret = pSegment->Load(); if (ret < 0) { fatal("Movie error: Segment::Load() failed.\n"); } //const SegmentInfo* const pSegmentInfo = pSegment->GetInfo(); //const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale(); //const long long duration_ns = pSegmentInfo->GetDuration(); //const char* const pTitle = pSegmentInfo->GetTitleAsUTF8(); //const char* const pMuxingApp = pSegmentInfo->GetMuxingAppAsUTF8(); //const char* const pWritingApp = pSegmentInfo->GetWritingAppAsUTF8(); const mkvparser::Tracks *pTracks = pSegment->GetTracks(); unsigned long i = 0; const unsigned long j = pTracks->GetTracksCount(); enum {VIDEO_TRACK = 1, AUDIO_TRACK = 2}; int videoTrack = -1; int audioTrack = -1; long long audioBitDepth; double audioSampleRate; ogg_packet oggPacket; vorbis_info vorbisInfo; vorbis_comment vorbisComment; vorbis_block vorbisBlock; while (i != j) { const Track *const pTrack = pTracks->GetTrackByIndex(i++); if (pTrack == NULL) continue; const long long trackType = pTrack->GetType(); //const unsigned long long trackUid = pTrack->GetUid(); //const char* pTrackName = pTrack->GetNameAsUTF8(); if (trackType == VIDEO_TRACK && videoTrack < 0) { videoTrack = pTrack->GetNumber(); const VideoTrack *const pVideoTrack = static_cast(pTrack); const long long width = pVideoTrack->GetWidth(); const long long height = pVideoTrack->GetHeight(); const double rate = pVideoTrack->GetFrameRate(); if (rate > 0) Init_Special_Timer(rate); movieAspect = (float)width / height; } if (trackType == AUDIO_TRACK && audioTrack < 0) { audioTrack = pTrack->GetNumber(); const AudioTrack *const pAudioTrack = static_cast(pTrack); audioChannels = pAudioTrack->GetChannels(); audioBitDepth = pAudioTrack->GetBitDepth(); audioSampleRate = pAudioTrack->GetSamplingRate(); uint audioHeaderSize; const byte *audioHeader = pAudioTrack->GetCodecPrivate(audioHeaderSize); if (audioHeaderSize < 1) { warning("Strange audio track in movie."); audioTrack = -1; continue; } byte *p = (byte *)audioHeader; uint count = *p++ + 1; if (count != 3) { warning("Strange audio track in movie."); audioTrack = -1; continue; } uint64_t sizes[3], total; int i = 0; total = 0; while (--count) { sizes[i] = xiph_lace_value(&p); total += sizes[i]; i += 1; } sizes[i] = audioHeaderSize - total - (p - audioHeader); // initialize vorbis vorbis_info_init(&vorbisInfo); vorbis_comment_init(&vorbisComment); memset(&vorbisDspState, 0, sizeof(vorbisDspState)); memset(&vorbisBlock, 0, sizeof(vorbisBlock)); oggPacket.e_o_s = false; oggPacket.granulepos = 0; oggPacket.packetno = 0; int r; for (int i = 0; i < 3; i++) { oggPacket.packet = p; oggPacket.bytes = sizes[i]; oggPacket.b_o_s = oggPacket.packetno == 0; r = vorbis_synthesis_headerin(&vorbisInfo, &vorbisComment, &oggPacket); if (r) fprintf(stderr, "vorbis_synthesis_headerin failed, error: %d", r); oggPacket.packetno++; p += sizes[i]; } r = vorbis_synthesis_init(&vorbisDspState, &vorbisInfo); if (r) fprintf(stderr, "vorbis_synthesis_init failed, error: %d", r); r = vorbis_block_init(&vorbisDspState, &vorbisBlock); if (r) fprintf(stderr, "vorbis_block_init failed, error: %d", r); ALenum audioFormat = alureGetSampleFormat(audioChannels, 16, 0); movieAudioIndex = initMovieSound(fileNumber, audioFormat, audioChannels, (ALuint) audioSampleRate, feedAudio); fprintf(stderr, "Movie sound inited.\n"); audio_queue_init(&audioQ); audioNsPerByte = (1000000000 / audioSampleRate) / (audioChannels * 2); audioNsBuffered = 0; audioBufferLen = audioChannels * audioSampleRate; } } if (videoTrack < 0) fatal("Movie error: No video in movie file."); if (audioTrack < 0) fatal("Movie error: No sound found."); video_queue_init(&videoQ); const unsigned long clusterCount = pSegment->GetCount(); if (clusterCount == 0) { fatal("Movie error: Segment has no clusters.\n"); } /* Initialize video codec */ if (vpx_codec_dec_init(&codec, interface, NULL, 0)) die_codec(&codec, "Failed to initialize decoder for movie."); byte *frame = new byte[256 * 1024]; if (! checkNew(frame)) return false; const mkvparser::Cluster *pCluster = pSegment->GetFirst(); setMovieViewport(); movieIsPlaying = playing; movieIsEnding = 0; glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); //const long long timeCode = pCluster->GetTimeCode(); long long time_ns = pCluster->GetTime(); const BlockEntry *pBlockEntry = pCluster->GetFirst(); if ((pBlockEntry == NULL) || pBlockEntry->EOS()) { pCluster = pSegment->GetNext(pCluster); if ((pCluster == NULL) || pCluster->EOS()) { fatal("Error: No movie found in the movie file."); } pBlockEntry = pCluster->GetFirst(); } const Block *pBlock = pBlockEntry->GetBlock(); long long trackNum = pBlock->GetTrackNumber(); unsigned long tn = static_cast(trackNum); const Track *pTrack = pTracks->GetTrackByNumber(tn); long long trackType = pTrack->GetType(); int frameCount = pBlock->GetFrameCount(); time_ns = pBlock->GetTime(pCluster); const GLfloat texCoords[] = { 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 }; const GLfloat vertices[] = { 0.0, 0.0, 0.1, 640.0, 0.0, 0.1, 0.0, 400.0, 0.1, 640.0, 400.0, 0.1 }; const GLfloat vertices1[] = { 7.0, 7.0, 0.1, 17.0, 7.0, 0.1, 7.0, 29.0, 0.1, 17.0, 29.0, 0.1 }; const GLfloat vertices2[] = { 27.0, 7.0, 0.1, 37.0, 7.0, 0.1, 27.0, 29.0, 0.1, 37.0, 29.0, 0.1 }; const GLfloat vertices3[] = { 5.0, 5.0, 0.1, 15.0, 5.0, 0.1, 5.0, 27.0, 0.1, 15.0, 27.0, 0.1 }; const GLfloat vertices4[] = { 25.0, 5.0, 0.1, 35.0, 5.0, 0.1, 25.0, 27.0, 0.1, 35.0, 27.0, 0.1 }; int frameCounter = 0; movieStartTick = SDL_GetTicks(); while (movieIsPlaying) { checkInput(); if (weAreDoneSoQuit) break; handleInput(); if (movieIsPlaying && (! movieIsEnding) && (videoQ.size < 100 || audioQ.size < 100)) { // Decode a frame! if ((pCluster != NULL) && !pCluster->EOS()) { if (frameCounter >= frameCount) { pBlockEntry = pCluster->GetNext(pBlockEntry); if ((pBlockEntry == NULL) || pBlockEntry->EOS()) { pCluster = pSegment->GetNext(pCluster); if ((pCluster == NULL) || pCluster->EOS()) { goto movieHasEnded; } pBlockEntry = pCluster->GetFirst(); } pBlock = pBlockEntry->GetBlock(); trackNum = pBlock->GetTrackNumber(); tn = static_cast(trackNum); pTrack = pTracks->GetTrackByNumber(tn); trackType = pTrack->GetType(); frameCount = pBlock->GetFrameCount(); time_ns = pBlock->GetTime(pCluster); frameCounter = 0; } const Block::Frame &theFrame = pBlock->GetFrame(frameCounter); const long size = theFrame.len; // const long long offset = theFrame.pos; if (size > sizeof(frame)) { if (frame) delete [] frame; frame = new byte[size]; if (! checkNew(frame)) return 0; } /* fprintf (stderr, "Block :%s,%s,%15lld\n", (trackType == VIDEO_TRACK) ? "V" : "A", pBlock->IsKey() ? "I" : "P", time_ns); */ if (trackNum == videoTrack) { theFrame.Read(&reader, frame); /* Decode the frame */ if (vpx_codec_decode(&codec, frame, size, NULL, 0)) die_codec(&codec, "Failed to decode frame"); // Let's decode an image frame! vpx_codec_iter_t iter = NULL; vpx_image_t *img; /* Get frame data */ while ((img = vpx_codec_get_frame(&codec, &iter))) { if (img->fmt != VPX_IMG_FMT_I420) fatal("Movie error. The movie is not in I420 colour format, which is the only one I can hanlde at the moment."); uint y; GLubyte *ytex = NULL; GLubyte *utex = NULL; GLubyte *vtex = NULL; if (! ytex) { ytex = new GLubyte [img->d_w * img->d_h]; utex = new GLubyte [(img->d_w >> 1) * (img->d_h >> 1)]; vtex = new GLubyte [(img->d_w >> 1) * (img->d_h >> 1)]; if (!ytex || !utex || !vtex) fatal(ERROR_OUT_OF_MEMORY); } byte *buf = img->planes[0]; for (y = 0; y < img->d_h; y++) { memcpy(ytex + y * img->d_w, buf, img->d_w); buf += img->stride[0]; } buf = img->planes[1]; for (y = 0; y < img->d_h >> 1; y++) { memcpy(utex + y * (img->d_w >> 1), buf, img->d_w >> 1); buf += img->stride[1]; } buf = img->planes[2]; for (y = 0; y < img->d_h >> 1; y++) { memcpy(vtex + y * (img->d_w >> 1), buf, img->d_w >> 1); buf += img->stride[2]; } video_queue_put(&videoQ, ytex, utex, vtex, img->d_w, img->d_h, time_ns / 1000000); } } else if (trackNum == audioTrack) { // Use this Audio Track if (size > 0) { theFrame.Read(&reader, frame); oggPacket.packet = frame; oggPacket.bytes = size; oggPacket.b_o_s = false; oggPacket.packetno++; oggPacket.granulepos = -1; if (! vorbis_synthesis(&vorbisBlock, &oggPacket)) { if (vorbis_synthesis_blockin(&vorbisDspState, &vorbisBlock)) fprintf(stderr, "Vorbis Synthesis block in error.\n"); } else { fprintf(stderr, "Vorbis Synthesis error.\n"); } float **pcm; int numSamples = vorbis_synthesis_pcmout(&vorbisDspState, &pcm); if (numSamples > 0) { int word = 2; int sgned = 1; int i, j; long bytespersample = audioChannels * word; vorbis_fpu_control fpu; char *buffer = new char[bytespersample * numSamples]; if (! checkNew(buffer)) return false; /* a tight loop to pack each size */ { int val; if (word == 1) { int off = (sgned ? 0 : 128); vorbis_fpu_setround(&fpu); for (j = 0; j < numSamples; j++) for (i = 0; i < audioChannels; i++) { val = vorbis_ftoi(pcm[i][j] * 128.f); if (val > 127)val = 127; else if (val < -128)val = -128; *buffer++ = val + off; } vorbis_fpu_restore(fpu); } else { int off = (sgned ? 0 : 32768); if (sgned) { vorbis_fpu_setround(&fpu); for (i = 0; i < audioChannels; i++) { /* It's faster in this order */ float *src = pcm[i]; int16 *dest = ((int16 *)buffer) + i; for (j = 0; j < numSamples; j++) { val = vorbis_ftoi(src[j] * 32768.f); if (val > 32767)val = 32767; else if (val < -32768)val = -32768; *dest = val; dest += audioChannels; } } vorbis_fpu_restore(fpu); } else { vorbis_fpu_setround(&fpu); for (i = 0; i < audioChannels; i++) { float *src = pcm[i]; int16 *dest = ((int16 *)buffer) + i; for (j = 0; j < numSamples; j++) { val = vorbis_ftoi(src[j] * 32768.f); if (val > 32767)val = 32767; else if (val < -32768)val = -32768; *dest = val + off; dest += audioChannels; } } vorbis_fpu_restore(fpu); } } } vorbis_synthesis_read(&vorbisDspState, numSamples); audioBufferLen = bytespersample * numSamples; audio_queue_put(&audioQ, buffer, audioBufferLen, time_ns / 1000000); //fprintf (stderr, "Audio buffered: %lld byte %lld ns\n",audioBufferLen, audioNsPerByte*audioBufferLen); if (! movieSoundPlaying && size > 1) { fprintf(stderr, "** starting sound ** \n"); playMovieStream(movieAudioIndex); movieSoundPlaying = true; } } } } frameCounter++; } else { movieHasEnded: movieIsEnding = 1; } } bool videoUpdated = false; // Get a video frame. if (movieIsPlaying == playing) { videoBuffers *vB; // Do we have decoded video waiting? if (vB = videoQ.first) { Uint32 tick = SDL_GetTicks() - movieStartTick; // Is it time to display the frame yet? if ((tick + 1) < vB->time_ms) { SDL_Delay(1); } else { GLubyte *ytex = NULL; GLubyte *utex = NULL; GLubyte *vtex = NULL; GLsizei w, h; if (video_queue_get(&videoQ, &ytex, &utex, &vtex, &w, &h)) { if (! yTextureName) glGenTextures(1, &yTextureName); if (! uTextureName) glGenTextures(1, &uTextureName); if (! vTextureName) glGenTextures(1, &vTextureName); glBindTexture(GL_TEXTURE_2D, yTextureName); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, ytex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, uTextureName); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w >> 1, h >> 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, utex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, vTextureName); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w >> 1, h >> 1, 0, GL_ALPHA, GL_UNSIGNED_BYTE, vtex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, yTextureName); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, ytex); glBindTexture(GL_TEXTURE_2D, uTextureName); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w >> 1, h >> 1, GL_ALPHA, GL_UNSIGNED_BYTE, utex); glBindTexture(GL_TEXTURE_2D, vTextureName); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w >> 1, h >> 1, GL_ALPHA, GL_UNSIGNED_BYTE, vtex); delete [] ytex; delete [] utex; delete [] vtex; ytex = utex = vtex = NULL; videoUpdated = true; } } } else if (movieIsEnding) { // We have reached the end of the movie. movieIsPlaying = nothing; } } // Update the screen if there's new video, or if we're paused if (videoUpdated || movieIsPlaying == paused) { // Clear The Screen glClear(GL_COLOR_BUFFER_BIT); // Display the current frame here if (shader.yuv) { glUseProgram(shader.yuv); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, uTextureName); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, vTextureName); glActiveTexture(GL_TEXTURE0); } glBindTexture(GL_TEXTURE_2D, yTextureName); glEnable(GL_BLEND); //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); setPMVMatrix(shader.yuv); drawQuad(shader.yuv, vertices, 1, texCoords); glUseProgram(0); if (movieIsPlaying == paused) { pausefade -= 1.0 / 24; if (pausefade < -1.0) pausefade = 1.0; // Paused. glEnable(GL_BLEND); glUseProgram(shader.color); setPMVMatrix(shader.color); setPrimaryColor(0.0f, 0.0f, 0.0f, fabs(pausefade)); drawQuad(shader.color, vertices1, 0); drawQuad(shader.color, vertices2, 0); setPrimaryColor(1.0f, 1.0f, 1.0f, fabs(pausefade)); drawQuad(shader.color, vertices3, 0); drawQuad(shader.color, vertices4, 0); glUseProgram(0); glDisable(GL_BLEND); Wait_Frame(); } glFlush(); EGL_SwapBuffers(); } videoUpdated = false; } // Cleanup glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); movieIsPlaying = nothing; for (int i = 0; i < 10; i++) Wait_Frame(); huntKillFreeSound(fileNumber); if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); vorbis_dsp_clear(&vorbisDspState); vorbis_block_clear(&vorbisBlock); vorbis_comment_clear(&vorbisComment); vorbis_info_clear(&vorbisInfo); delete pSegment; deleteTextures(1, &yTextureName); deleteTextures(1, &uTextureName); deleteTextures(1, &vTextureName); yTextureName = uTextureName = vTextureName = 0; // Delete any remaining buffers videoBuffers *vB = videoQ.first; while (vB = videoQ.first) { videoQ.first = vB->next; delete [] vB->ytex; delete [] vB->utex; delete [] vB->vtex; delete vB; } videoQ.size = 0; audioBuffers *aB = audioQ.first; while (aB = audioQ.first) { audioQ.first = aB->next; delete [] aB->buffer; delete aB; } audioQ.size = 0; Init_Timer(); glViewport(viewportOffsetX, viewportOffsetY, viewportWidth, viewportHeight); setPixelCoords(false); #endif return 1; } int stopMovie() { int r = movieIsPlaying; movieIsPlaying = nothing; return r; } int pauseMovie() { #if 0 if (movieIsPlaying == playing) { ALuint source = getSoundSource(movieAudioIndex); if (source) { alurePauseSource(source); } movieIsPlaying = paused; fprintf(stderr, "** Pausing **\n"); } else if (movieIsPlaying == paused) { ALuint source = getSoundSource(movieAudioIndex); if (source) { alureResumeSource(source); } fprintf(stderr, "** Restarted movie ** sound: %d source: %d\n", movieSoundPlaying, source); movieIsPlaying = playing; } #endif return movieIsPlaying; } } // End of namespace Sludge