Пример #1
0
        public void Stop()
        {
            checkDisposed();

            // Check the player state before attempting anything.
            if (State == MediaState.Stopped)
            {
                return;
            }

            // Update the player state.
            State = MediaState.Stopped;

            // Wait for the player to end if it's still going.
            System.Console.Write("Signaled Theora player to stop, waiting...");
            timer.Stop();
            timer.Reset();
            if (audioStream != null)
            {
                audioStream.Stop();
                audioStream.Dispose();
                audioStream = null;
            }
            if (previousFrame != IntPtr.Zero)
            {
                TheoraPlay.THEORAPLAY_freeVideo(previousFrame);
            }
            Video.AttachedToPlayer = false;
            Video.Dispose();
            System.Console.WriteLine(" Done!");
        }
Пример #2
0
        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);
        }
Пример #3
0
        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.
            GL.UseProgram(shaderProgram);

            // Set up the vertex pointers/arrays.
            OpenGLDevice.Instance.Attributes[0].CurrentBuffer = int.MaxValue;
            OpenGLDevice.Instance.Attributes[1].CurrentBuffer = int.MaxValue;
            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.
            OpenGLDevice.Framebuffer.BindFramebuffer(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)
            {
                // I hope we've still got something in videoTexture!
                System.Console.WriteLine(
                    "WARNING: THEORA FRAME COPY FAILED: " +
                    e.Message
                    );
            }
#endif

            return(videoTexture);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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();
        }