public void Terminate() { // Stop and unassign the decoder. if (_theoraDecoder != IntPtr.Zero) { TheoraPlay.THEORAPLAY_stopDecode(_theoraDecoder); _theoraDecoder = IntPtr.Zero; } // Free and unassign the video stream. if (_videoStream != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(_videoStream); _videoStream = IntPtr.Zero; } _currentVideo = new TheoraPlay.THEORAPLAY_VideoFrame(); _nextVideo = new TheoraPlay.THEORAPLAY_VideoFrame(); TheoraPlay.THEORAPLAY_freeVideo(_previousFrame); _previousFrame = IntPtr.Zero; _videoStream = IntPtr.Zero; _disposed = true; }
internal void Initialize() { if (!IsDisposed) { Dispose(); // We need to start from the beginning, don't we? :P } // Initialize the decoder. theoraDecoder = TheoraPlay.THEORAPLAY_startDecodeFile( fileName, 150, // Max frames to buffer. Arbitrarily set 5 seconds, assuming 30fps. TheoraPlay.THEORAPLAY_VideoFormat.THEORAPLAY_VIDFMT_IYUV ); // Wait until the decoder is ready. while (TheoraPlay.THEORAPLAY_isInitialized(theoraDecoder) == 0) { Thread.Sleep(10); } // Initialize the video stream pointer and get our first frame. if (TheoraPlay.THEORAPLAY_hasVideoStream(theoraDecoder) != 0) { while (videoStream == IntPtr.Zero) { videoStream = TheoraPlay.THEORAPLAY_getVideo(theoraDecoder); Thread.Sleep(10); } TheoraPlay.THEORAPLAY_VideoFrame frame = TheoraPlay.getVideoFrame(videoStream); // We get the FramesPerSecond from the first frame. FramesPerSecond = (float)frame.fps; Width = (int)frame.width; Height = (int)frame.height; } IsDisposed = false; }
public void Play(Video video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; video.AttachedToPlayer = true; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); Texture2D overlap = videoTexture; videoTexture = new Texture2D( currentDevice, (int)currentVideo.width, (int)currentVideo.height, false, SurfaceFormat.Color ); overlap.Dispose(); #if VIDEOPLAYER_OPENGL GL_setupTargets( (int)currentVideo.width, (int)currentVideo.height ); #endif } // Initialize the thread! System.Console.Write("Starting Theora player..."); timer.Start(); if (audioStream != null) { audioStream.Play(); } System.Console.WriteLine(" Done!"); }
public Texture2D GetTexture() { checkDisposed(); // Be sure we can even get something from TheoraPlay... if (State == MediaState.Stopped || Video.theoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.theoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) == 0) { return(videoTexture); // Screw it, give them the old one. } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.videoStream; Video.videoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Kill the audio, no matter what. if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.AttachedToPlayer = false; Video.Dispose(); Video.AttachedToPlayer = true; Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } // Start! Again! timer.Start(); if (audioStream != null) { audioStream.Play(); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.AttachedToPlayer = false; Video.Dispose(); // We're done, so give them the last frame. return(videoTexture); } } } #if VIDEOPLAYER_OPENGL // Set up an environment to muck about in. GL_pushState(); // Bind our shader program. currentDevice.GLDevice.glUseProgram(shaderProgram); // We're using client-side arrays like CAVEMEN currentDevice.GLDevice.BindVertexBuffer(OpenGLDevice.OpenGLVertexBuffer.NullBuffer); // Set up the vertex pointers/arrays. currentDevice.GLDevice.AttributeEnabled[0] = true; currentDevice.GLDevice.AttributeEnabled[1] = true; for (int i = 2; i < currentDevice.GLDevice.AttributeEnabled.Length; i += 1) { currentDevice.GLDevice.AttributeEnabled[i] = false; } currentDevice.GLDevice.FlushGLVertexAttributes(); currentDevice.GLDevice.VertexAttribPointer( 0, 2, VertexElementFormat.Single, false, 2 * sizeof(float), vertPosPtr ); currentDevice.GLDevice.VertexAttribPointer( 1, 2, VertexElementFormat.Single, false, 2 * sizeof(float), vertTexPtr ); // Bind our target framebuffer. currentDevice.GLDevice.BindDrawFramebuffer(rgbaFramebuffer); // Prepare YUV GL textures with our current frame data currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[0] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int)currentVideo.width, (int)currentVideo.height, OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, currentVideo.pixels ); currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 + 1 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[1] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int)(currentVideo.width / 2), (int)(currentVideo.height / 2), OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 + 2 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[2] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int)(currentVideo.width / 2), (int)(currentVideo.height / 2), OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Flip the viewport, because loldirectx currentDevice.GLDevice.glViewport( 0, 0, (int)currentVideo.width, (int)currentVideo.height ); // Draw the YUV textures to the framebuffer with our shader. currentDevice.GLDevice.glDrawArrays( OpenGLDevice.GLenum.GL_TRIANGLE_STRIP, 0, 4 ); // Clean up after ourselves. GL_popState(); #else // Just copy it to an array, since it's RGBA anyway. try { byte[] theoraPixels = TheoraPlay.getPixels( currentVideo.pixels, (int)currentVideo.width * (int)currentVideo.height * 4 ); // TexImage2D. videoTexture.SetData <byte>(theoraPixels); } catch (Exception e) { // I hope we've still got something in videoTexture! System.Console.WriteLine( "WARNING: THEORA FRAME COPY FAILED: " + e.Message ); } #endif return(videoTexture); }
public Texture2D GetTexture() { checkDisposed(); if (Video == null) { throw new InvalidOperationException(); } // Be sure we can even get something from TheoraPlay... if (State == MediaState.Stopped || Video.theoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.theoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) == 0) { // Screw it, give them the old one. return(videoTexture[0].RenderTarget as Texture2D); } // Get the latest video frames. bool hasFrames = true; while (nextVideo.playms <= timer.ElapsedMilliseconds && hasFrames) { currentVideo = nextVideo; hasFrames = TheoraPlay.THEORAPLAY_availableVideo(Video.theoraDecoder) > 0; if (hasFrames) { IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.videoStream; Video.videoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } } // Check for the end... if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is part of the Duration hack! if (Video.needsDurationHack) { Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); } // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // Kill whatever audio/video we've got if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.AttachedToPlayer = false; Video.Dispose(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Starting over! Video.AttachedToPlayer = true; InitializeTheoraStream(); // Start! Again! timer.Start(); if (audioStream != null) { audioStream.Play(); } } else { // We out, give them the last frame. State = MediaState.Stopped; return(videoTexture[0].RenderTarget as Texture2D); } } // Set up an environment to muck about in. GL_pushState(); // Prepare YUV GL textures with our current frame data currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[0], currentVideo.pixels ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[1], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[2], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Draw the YUV textures to the framebuffer with our shader. currentDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 0, 2 ); // Clean up after ourselves. GL_popState(); // Finally. return(videoTexture[0].RenderTarget as Texture2D); }
public Texture2D GetTexture() { checkDisposed(); // Be sure we can even get something from TheoraPlay... if (State == MediaState.Stopped || Video.theoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.theoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) == 0) { // Screw it, give them the old one. return(videoTexture[0].RenderTarget as Texture2D); } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.videoStream; Video.videoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Kill the audio, no matter what. if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.AttachedToPlayer = false; Video.Dispose(); Video.AttachedToPlayer = true; Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } // Start! Again! timer.Start(); if (audioStream != null) { audioStream.Play(); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.AttachedToPlayer = false; Video.Dispose(); // We're done, so give them the last frame. return(videoTexture[0].RenderTarget as Texture2D); } } } // Set up an environment to muck about in. GL_pushState(); // Prepare YUV GL textures with our current frame data currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[0], currentVideo.pixels ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[1], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[2], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Draw the YUV textures to the framebuffer with our shader. currentDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 0, 2 ); // Clean up after ourselves. GL_popState(); // Finally. return(videoTexture[0].RenderTarget as Texture2D); }
public void Play(Video video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; video.AttachedToPlayer = true; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); // The VideoPlayer will use the GraphicsDevice that is set now. if (currentDevice != Video.GraphicsDevice) { GL_dispose(); currentDevice = Video.GraphicsDevice; GL_initialize(); } RenderTargetBinding overlap = videoTexture[0]; videoTexture[0] = new RenderTargetBinding( new RenderTarget2D( currentDevice, (int)currentVideo.width, (int)currentVideo.height, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents ) ); if (overlap.RenderTarget != null) { overlap.RenderTarget.Dispose(); } GL_setupTextures( (int)currentVideo.width, (int)currentVideo.height ); } // Initialize the thread! FNAPlatform.Log("Starting Theora player..."); timer.Start(); if (audioStream != null) { audioStream.Play(); } FNAPlatform.Log("Started!"); }
public void Play(OgvComponent video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // In rare cases, the thread might still be going. Wait until it's done. if (audioDecoderThread != null && audioDecoderThread.IsAlive) { Stop(); } // Create new Thread instances in case we use this player multiple times. audioDecoderThread = new Thread(new ThreadStart(this.DecodeAudio)); // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.TheoraDecoder) != 0) { audioDecoderThread.Start(); } else { audioStarted = true; // Welp. } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.VideoStream); previousFrame = Video.VideoStream; do { // The decoder miiight not be ready yet. Video.VideoStream = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); } while (Video.VideoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); Texture overlap = videoTexture; videoTexture = new Texture( // Game.Instance.GraphicsDevice, // (int)currentVideo.width, // (int)currentVideo.height, // false, // SurfaceFormat.Color ); overlap.Dispose(); #if VIDEOPLAYER_OPENGL GL_setupTargets( (int)currentVideo.width, (int)currentVideo.height ); #endif } // Initialize the thread! Log.Editor.Write("Starting Theora player..."); while (!audioStarted) ; timer.Start(); if (audioSourceIndex != -1) { AL.SourcePlay(audioSourceIndex); } Log.Editor.Write(" Done starting Theora player!"); }
public void Play(Video video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; video.AttachedToPlayer = true; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); // The VideoPlayer will use the GraphicsDevice that is set now. if (currentDevice != Video.GraphicsDevice) { if (currentDevice != null) { GL_dispose(); } currentDevice = Video.GraphicsDevice; GL_initialize(); } RenderTargetBinding overlap = videoTexture[0]; videoTexture[0] = new RenderTargetBinding( new RenderTarget2D( currentDevice, (int) currentVideo.width, (int) currentVideo.height, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.PreserveContents ) ); if (overlap.RenderTarget != null) { overlap.RenderTarget.Dispose(); } GL_setupTextures( (int) currentVideo.width, (int) currentVideo.height ); } // Initialize the thread! FNAPlatform.Log("Starting Theora player..."); timer.Start(); if (audioStream != null) { audioStream.Play(); } FNAPlatform.Log("Started!"); }
public Texture2D GetTexture() { checkDisposed(); if (Video == null) { throw new InvalidOperationException(); } // Be sure we can even get something from TheoraPlay... if ( State == MediaState.Stopped || Video.theoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.theoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) == 0 ) { // Screw it, give them the old one. return videoTexture[0].RenderTarget as Texture2D; } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.videoStream; Video.videoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int) currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Kill the audio, no matter what. if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.AttachedToPlayer = false; Video.Dispose(); Video.AttachedToPlayer = true; Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } // Start! Again! timer.Start(); if (audioStream != null) { audioStream.Play(); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.AttachedToPlayer = false; Video.Dispose(); // We're done, so give them the last frame. return videoTexture[0].RenderTarget as Texture2D; } } } // Set up an environment to muck about in. GL_pushState(); // Prepare YUV GL textures with our current frame data currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[0], currentVideo.pixels ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[1], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); currentDevice.GLDevice.SetTextureData2DPointer( yuvTextures[2], new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Draw the YUV textures to the framebuffer with our shader. currentDevice.DrawPrimitives( PrimitiveType.TriangleStrip, 0, 2 ); // Clean up after ourselves. GL_popState(); // Finally. return videoTexture[0].RenderTarget as Texture2D; }
private void RunVideo() { // FIXME: Maybe use an actual thread synchronization technique. while (!audioStarted && State != MediaState.Stopped) { ; } while (State != MediaState.Stopped) { // Someone needs to look at their memory management... if (Game.Instance == null) { System.Console.WriteLine("Game exited before video player! Halting..."); State = MediaState.Stopped; } // Sleep when paused, update the video state when playing. if (State == MediaState.Paused) { // Pause the OpenAL source. if (AL.GetSourceState(audioSourceIndex) == ALSourceState.Playing) { AL.SourcePause(audioSourceIndex); } // Stop the timer in here so we know when we really stopped. if (timer.IsRunning) { timer.Stop(); } // Arbitrarily 1 frame in a 30fps movie. Thread.Sleep(33); } else { // Start the timer, whether we're starting or unpausing. if (!timer.IsRunning) { timer.Start(); } // If we're getting here, we should be playing the audio... if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { if (AL.GetSourceState(audioSourceIndex) != ALSourceState.Playing) { AL.SourcePlay(audioSourceIndex); } } // Get the next video from from the decoder, if a stream exists. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { // Only step when it's time to do so. if (nextVideo.playms <= timer.ElapsedMilliseconds) { // Wait until GetTexture() is done. // FIXME: Maybe use an actual thread synchronization technique. while (frameLocked) { ; } // Assign the new currentVideo, free the old one. currentVideo = nextVideo; // Get the next frame ready, free the old one. IntPtr oldestFrame = previousFrame; previousFrame = Video.videoStream; Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); if (Video.videoStream != IntPtr.Zero) { // Assign next frame, if it exists. nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); // Then free the _really_ old frame. TheoraPlay.THEORAPLAY_freeVideo(oldestFrame); } } } // If we're done decoding, we hit the end. if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is a part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); // Stop and reset the timer. // If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Wait for the audio thread to end. State = MediaState.Stopped; audioDecoderThread.Join(); // Now we pretend we're playing again. State = MediaState.Playing; // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.Dispose(); Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { currentAudio = TheoraPlay.getAudioPacket(Video.audioStream); audioDecoderThread = new Thread(new ThreadStart(DecodeAudio)); audioDecoderThread.Start(); } else { audioStarted = true; // Welp. } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } // FIXME: Maybe use an actual thread synchronization technique. while (!audioStarted && State != MediaState.Stopped) { ; } } else { State = MediaState.Stopped; } } } } // Reset the video timer. timer.Stop(); timer.Reset(); // Stop the decoding, we don't need it anymore. audioDecoderThread.Join(); // We're desperately trying to keep this until the very end. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); // We're not playing any video anymore. Video.Dispose(); }
public void UpdateVideo(float elapsedFrameTime) { while (_currentVideo.playms <= elapsedFrameTime && TheoraPlay.THEORAPLAY_availableVideo(_theoraDecoder)!=0) { _currentVideo = _nextVideo; var nextFrame = TheoraPlay.THEORAPLAY_getVideo(_theoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(_previousFrame); _previousFrame = _videoStream; _videoStream = nextFrame; _nextVideo = GetVideoFrame(_videoStream); } } IsFinished = TheoraPlay.THEORAPLAY_isDecoding(_theoraDecoder) == 0; }
public void Terminate() { // Stop and unassign the decoder. if (_theoraDecoder != IntPtr.Zero) { TheoraPlay.THEORAPLAY_stopDecode(_theoraDecoder); _theoraDecoder = IntPtr.Zero; } // Free and unassign the video stream. if (_videoStream != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(_videoStream); _videoStream = IntPtr.Zero; } _currentVideo = new TheoraPlay.THEORAPLAY_VideoFrame(); _nextVideo = new TheoraPlay.THEORAPLAY_VideoFrame(); TheoraPlay.THEORAPLAY_freeVideo(_previousFrame); _previousFrame = IntPtr.Zero; _videoStream = IntPtr.Zero; _disposed = true; }
public void Play(Video video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; video.AttachedToPlayer = true; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); Texture2D overlap = videoTexture; videoTexture = new Texture2D( currentDevice, (int) currentVideo.width, (int) currentVideo.height, false, SurfaceFormat.Color ); overlap.Dispose(); #if VIDEOPLAYER_OPENGL GL_setupTargets( (int) currentVideo.width, (int) currentVideo.height ); #endif } // Initialize the thread! System.Console.Write("Starting Theora player..."); timer.Start(); if (audioStream != null) { audioStream.Play(); } System.Console.WriteLine(" Done!"); }
public Texture2D GetTexture() { checkDisposed(); // Be sure we can even get something from TheoraPlay... if ( State == MediaState.Stopped || Video.theoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.theoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) == 0 ) { return videoTexture; // Screw it, give them the old one. } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.videoStream; Video.videoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.theoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int) currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Kill the audio, no matter what. if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.AttachedToPlayer = false; Video.Dispose(); Video.AttachedToPlayer = true; Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.theoraDecoder) != 0) { InitAudioStream(); } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.theoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.videoStream); previousFrame = Video.videoStream; do { // The decoder miiight not be ready yet. Video.videoStream = TheoraPlay.THEORAPLAY_getVideo(Video.theoraDecoder); } while (Video.videoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.videoStream); } // Start! Again! timer.Start(); if (audioStream != null) { audioStream.Play(); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioStream != null) { audioStream.Stop(); audioStream.Dispose(); audioStream = null; } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.AttachedToPlayer = false; Video.Dispose(); // We're done, so give them the last frame. return videoTexture; } } } #if VIDEOPLAYER_OPENGL // Set up an environment to muck about in. GL_pushState(); // Bind our shader program. currentDevice.GLDevice.glUseProgram(shaderProgram); // We're using client-side arrays like CAVEMEN currentDevice.GLDevice.BindVertexBuffer(OpenGLDevice.OpenGLVertexBuffer.NullBuffer); // Set up the vertex pointers/arrays. currentDevice.GLDevice.AttributeEnabled[0] = true; currentDevice.GLDevice.AttributeEnabled[1] = true; for (int i = 2; i < currentDevice.GLDevice.AttributeEnabled.Length; i += 1) { currentDevice.GLDevice.AttributeEnabled[i] = false; } currentDevice.GLDevice.FlushGLVertexAttributes(); currentDevice.GLDevice.VertexAttribPointer( 0, 2, VertexElementFormat.Single, false, 2 * sizeof(float), vertPosPtr ); currentDevice.GLDevice.VertexAttribPointer( 1, 2, VertexElementFormat.Single, false, 2 * sizeof(float), vertTexPtr ); // Bind our target framebuffer. currentDevice.GLDevice.BindDrawFramebuffer(rgbaFramebuffer); // Prepare YUV GL textures with our current frame data currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[0] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int) currentVideo.width, (int) currentVideo.height, OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, currentVideo.pixels ); currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 + 1 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[1] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int) (currentVideo.width / 2), (int) (currentVideo.height / 2), OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); currentDevice.GLDevice.glActiveTexture( OpenGLDevice.GLenum.GL_TEXTURE0 + 2 ); currentDevice.GLDevice.glBindTexture( OpenGLDevice.GLenum.GL_TEXTURE_2D, yuvTextures[2] ); currentDevice.GLDevice.glTexSubImage2D( OpenGLDevice.GLenum.GL_TEXTURE_2D, 0, 0, 0, (int) (currentVideo.width / 2), (int) (currentVideo.height / 2), OpenGLDevice.GLenum.GL_LUMINANCE, OpenGLDevice.GLenum.GL_UNSIGNED_BYTE, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Flip the viewport, because loldirectx currentDevice.GLDevice.glViewport( 0, 0, (int) currentVideo.width, (int) currentVideo.height ); // Draw the YUV textures to the framebuffer with our shader. currentDevice.GLDevice.glDrawArrays( OpenGLDevice.GLenum.GL_TRIANGLE_STRIP, 0, 4 ); // Clean up after ourselves. GL_popState(); #else // Just copy it to an array, since it's RGBA anyway. try { byte[] theoraPixels = TheoraPlay.getPixels( currentVideo.pixels, (int) currentVideo.width * (int) currentVideo.height * 4 ); // TexImage2D. videoTexture.SetData<byte>(theoraPixels); } catch(Exception e) { // I hope we've still got something in videoTexture! System.Console.WriteLine( "WARNING: THEORA FRAME COPY FAILED: " + e.Message ); } #endif return videoTexture; }
public Texture GetTexture() { checkDisposed(); // Be sure we can even get something from TheoraPlay... if (State == MediaState.Stopped || Video.TheoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.TheoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) == 0) { return(videoTexture); // Screw it, give them the old one. } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.VideoStream; Video.VideoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.TheoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Wait for the audio thread to end. State = MediaState.Stopped; if (audioDecoderThread.ThreadState != System.Threading.ThreadState.Unstarted && audioDecoderThread.IsAlive == true) { audioDecoderThread.Join(); } // Now we pretend we're playing again. State = MediaState.Playing; // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.Dispose(); Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.TheoraDecoder) != 0) { audioDecoderThread = new Thread(new ThreadStart(DecodeAudio)); audioDecoderThread.Start(); } else { audioStarted = true; // Welp. } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.VideoStream); previousFrame = Video.VideoStream; do { // The decoder miiight not be ready yet. Video.VideoStream = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); } while (Video.VideoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); } // FIXME: Maybe use an actual thread synchronization technique. while (!audioStarted) { ; } // Start! Again! timer.Start(); if (audioSourceIndex != -1) { AL.SourcePlay(audioSourceIndex); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioDecoderThread.ThreadState != System.Threading.ThreadState.Unstarted && audioDecoderThread.IsAlive == true) { audioDecoderThread.Join(); } TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.Dispose(); // We're done, so give them the last frame. return(videoTexture); } } } #if VIDEOPLAYER_OPENGL // Set up an environment to muck about in. GL_pushState(); // Bind our shader program. GL.UseProgram(shaderProgram); // Set uniform values. GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp0"), 0 ); GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp1"), 1 ); GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp2"), 2 ); // Set up the vertex pointers/arrays. GL.VertexAttribPointer( 0, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), vert_pos ); GL.VertexAttribPointer( 1, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), vert_tex ); GL.EnableVertexAttribArray(0); GL.EnableVertexAttribArray(1); // Bind our target framebuffer. GL.BindFramebuffer(FramebufferTarget.Framebuffer, rgbaFramebuffer); // Prepare YUV GL textures with our current frame data GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[0]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width, (int)currentVideo.height, PixelFormat.Luminance, PixelType.UnsignedByte, currentVideo.pixels ); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[1]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width / 2, (int)currentVideo.height / 2, PixelFormat.Luminance, PixelType.UnsignedByte, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); GL.ActiveTexture(TextureUnit.Texture2); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[2]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width / 2, (int)currentVideo.height / 2, PixelFormat.Luminance, PixelType.UnsignedByte, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Flip the viewport, because loldirectx GL.Viewport( 0, 0, (int)currentVideo.width, (int)currentVideo.height ); // Draw the YUV textures to the framebuffer with our shader. GL.DrawArrays(BeginMode.TriangleStrip, 0, 4); // Clean up after ourselves. GL_popState(); #else // Just copy it to an array, since it's RGBA anyway. try { byte[] theoraPixels = TheoraPlay.getPixels( currentVideo.pixels, (int)currentVideo.width * (int)currentVideo.height * 4 ); // TexImage2D. videoTexture.SetData <byte>(theoraPixels); } catch (Exception e) { System.Console.WriteLine( "WARNING: THEORA FRAME COPY FAILED: " + e.Message ); frameLocked = false; return(videoTexture); // Hope this still has something in it... } #endif return(videoTexture); }
public void Play(OgvComponent video) { checkDisposed(); // We need to assign this regardless of what happens next. Video = video; // FIXME: This is a part of the Duration hack! Video.Duration = TimeSpan.MaxValue; // Check the player state before attempting anything. if (State != MediaState.Stopped) { return; } // In rare cases, the thread might still be going. Wait until it's done. if (audioDecoderThread != null && audioDecoderThread.IsAlive) { Stop(); } // Create new Thread instances in case we use this player multiple times. audioDecoderThread = new Thread(new ThreadStart(this.DecodeAudio)); // Update the player state now, for the thread we're about to make. State = MediaState.Playing; // Start the video if it hasn't been yet. if (Video.IsDisposed) { video.Initialize(); } // Grab the first bit of audio. We're trying to start the decoding ASAP. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.TheoraDecoder) != 0) { audioDecoderThread.Start(); } else { audioStarted = true; // Welp. } // Grab the first bit of video, set up the texture. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.VideoStream); previousFrame = Video.VideoStream; do { // The decoder miiight not be ready yet. Video.VideoStream = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); } while (Video.VideoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); Texture overlap = videoTexture; videoTexture = new Texture( // Game.Instance.GraphicsDevice, // (int)currentVideo.width, // (int)currentVideo.height, // false, // SurfaceFormat.Color ); overlap.Dispose(); #if VIDEOPLAYER_OPENGL GL_setupTargets( (int)currentVideo.width, (int)currentVideo.height ); #endif } // Initialize the thread! Log.Editor.Write("Starting Theora player..."); while (!audioStarted) { ; } timer.Start(); if (audioSourceIndex != -1) { AL.SourcePlay(audioSourceIndex); } Log.Editor.Write(" Done starting Theora player!"); }
public Texture GetTexture() { checkDisposed(); // Be sure we can even get something from TheoraPlay... if (State == MediaState.Stopped || Video.TheoraDecoder == IntPtr.Zero || TheoraPlay.THEORAPLAY_isInitialized(Video.TheoraDecoder) == 0 || TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) == 0) { return videoTexture; // Screw it, give them the old one. } // Get the latest video frames. bool missedFrame = false; while (nextVideo.playms <= timer.ElapsedMilliseconds && !missedFrame) { currentVideo = nextVideo; IntPtr nextFrame = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); if (nextFrame != IntPtr.Zero) { TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = Video.VideoStream; Video.VideoStream = nextFrame; nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); missedFrame = false; } else { // Don't mind me, just ignoring that complete failure above! missedFrame = true; } if (TheoraPlay.THEORAPLAY_isDecoding(Video.TheoraDecoder) == 0) { // FIXME: This is part of the Duration hack! Video.Duration = new TimeSpan(0, 0, 0, 0, (int)currentVideo.playms); // Stop and reset the timer. If we're looping, the loop will start it again. timer.Stop(); timer.Reset(); // If looping, go back to the start. Otherwise, we'll be exiting. if (IsLooped && State == MediaState.Playing) { // Wait for the audio thread to end. State = MediaState.Stopped; if (audioDecoderThread.ThreadState != System.Threading.ThreadState.Unstarted && audioDecoderThread.IsAlive == true) audioDecoderThread.Join(); // Now we pretend we're playing again. State = MediaState.Playing; // Free everything and start over. TheoraPlay.THEORAPLAY_freeVideo(previousFrame); previousFrame = IntPtr.Zero; Video.Dispose(); Video.Initialize(); // Grab the initial audio again. if (TheoraPlay.THEORAPLAY_hasAudioStream(Video.TheoraDecoder) != 0) { audioDecoderThread = new Thread(new ThreadStart(DecodeAudio)); audioDecoderThread.Start(); } else { audioStarted = true; // Welp. } // Grab the initial video again. if (TheoraPlay.THEORAPLAY_hasVideoStream(Video.TheoraDecoder) != 0) { currentVideo = TheoraPlay.getVideoFrame(Video.VideoStream); previousFrame = Video.VideoStream; do { // The decoder miiight not be ready yet. Video.VideoStream = TheoraPlay.THEORAPLAY_getVideo(Video.TheoraDecoder); } while (Video.VideoStream == IntPtr.Zero); nextVideo = TheoraPlay.getVideoFrame(Video.VideoStream); } // FIXME: Maybe use an actual thread synchronization technique. while (!audioStarted) ; // Start! Again! timer.Start(); if (audioSourceIndex != -1) { AL.SourcePlay(audioSourceIndex); } } else { // Stop everything, clean up. We out. State = MediaState.Stopped; if (audioDecoderThread.ThreadState != System.Threading.ThreadState.Unstarted && audioDecoderThread.IsAlive == true) audioDecoderThread.Join(); TheoraPlay.THEORAPLAY_freeVideo(previousFrame); Video.Dispose(); // We're done, so give them the last frame. return videoTexture; } } } #if VIDEOPLAYER_OPENGL // Set up an environment to muck about in. GL_pushState(); // Bind our shader program. GL.UseProgram(shaderProgram); // Set uniform values. GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp0"), 0 ); GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp1"), 1 ); GL.Uniform1( GL.GetUniformLocation(shaderProgram, "samp2"), 2 ); // Set up the vertex pointers/arrays. GL.VertexAttribPointer( 0, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), vert_pos ); GL.VertexAttribPointer( 1, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), vert_tex ); GL.EnableVertexAttribArray(0); GL.EnableVertexAttribArray(1); // Bind our target framebuffer. GL.BindFramebuffer(FramebufferTarget.Framebuffer, rgbaFramebuffer); // Prepare YUV GL textures with our current frame data GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[0]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width, (int)currentVideo.height, PixelFormat.Luminance, PixelType.UnsignedByte, currentVideo.pixels ); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[1]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width / 2, (int)currentVideo.height / 2, PixelFormat.Luminance, PixelType.UnsignedByte, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) ) ); GL.ActiveTexture(TextureUnit.Texture2); GL.BindTexture(TextureTarget.Texture2D, yuvTextures[2]); GL.TexSubImage2D( TextureTarget.Texture2D, 0, 0, 0, (int)currentVideo.width / 2, (int)currentVideo.height / 2, PixelFormat.Luminance, PixelType.UnsignedByte, new IntPtr( currentVideo.pixels.ToInt64() + (currentVideo.width * currentVideo.height) + (currentVideo.width / 2 * currentVideo.height / 2) ) ); // Flip the viewport, because loldirectx GL.Viewport( 0, 0, (int)currentVideo.width, (int)currentVideo.height ); // Draw the YUV textures to the framebuffer with our shader. GL.DrawArrays(BeginMode.TriangleStrip, 0, 4); // Clean up after ourselves. GL_popState(); #else // Just copy it to an array, since it's RGBA anyway. try { byte[] theoraPixels = TheoraPlay.getPixels( currentVideo.pixels, (int) currentVideo.width * (int) currentVideo.height * 4 ); // TexImage2D. videoTexture.SetData<byte>(theoraPixels); } catch(Exception e) { System.Console.WriteLine( "WARNING: THEORA FRAME COPY FAILED: " + e.Message ); frameLocked = false; return videoTexture; // Hope this still has something in it... } #endif return videoTexture; }