private void InitAudioStream() { // The number of buffers to queue into the source. const int NUM_BUFFERS = 4; // Generate the source. IntPtr audioPtr = IntPtr.Zero; do { audioPtr = TheoraPlay.THEORAPLAY_getAudio(Video.theoraDecoder); } while (audioPtr == IntPtr.Zero); TheoraPlay.THEORAPLAY_AudioPacket packet = TheoraPlay.getAudioPacket(audioPtr); audioStream = new DynamicSoundEffectInstance( packet.freq, (AudioChannels)packet.channels ); audioStream.BufferNeeded += OnBufferRequest; UpdateVolume(); // Fill and queue the buffers. for (int i = 0; i < NUM_BUFFERS; i += 1) { if (!StreamAudio()) { break; } } }
private bool StreamAudio(int buffer) { // The size of our abstracted buffer. const int BUFFER_SIZE = 4096 * 2; // Store our abstracted buffer into here. List <float> data = new List <float>(); // Be sure we have an audio stream first! if (Video.audioStream == IntPtr.Zero) { return(false); // NOPE } // Add to the buffer from the decoder until it's large enough. while (data.Count < BUFFER_SIZE && State != MediaState.Stopped) { data.AddRange( TheoraPlay.getSamples( currentAudio.samples, currentAudio.frames * currentAudio.channels ) ); // We've copied the audio, so free this. TheoraPlay.THEORAPLAY_freeAudio(Video.audioStream); do { Video.audioStream = TheoraPlay.THEORAPLAY_getAudio(Video.theoraDecoder); if (State == MediaState.Stopped) { // Screw it, just bail out ASAP. return(false); } } while (Video.audioStream == IntPtr.Zero); currentAudio = TheoraPlay.getAudioPacket(Video.audioStream); if ((BUFFER_SIZE - data.Count) < 4096) { break; } } // If we actually got data, buffer it into OpenAL. if (data.Count > 0) { AL.BufferData( buffer, (currentAudio.channels == 2) ? ALFormat.StereoFloat32Ext : ALFormat.MonoFloat32Ext, data.ToArray(), data.Count * 2 * currentAudio.channels, // Dear OpenAL: WTF?! Love, flibit currentAudio.freq ); return(true); } return(false); }
private void InitializeTheoraStream() { // 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) { // Generate the source. IntPtr audioPtr = IntPtr.Zero; do { // The decoder miiight not be ready yet. audioPtr = TheoraPlay.THEORAPLAY_getAudio(Video.theoraDecoder); } while (audioPtr == IntPtr.Zero); TheoraPlay.THEORAPLAY_AudioPacket packet = TheoraPlay.getAudioPacket(audioPtr); audioStream = new DynamicSoundEffectInstance( packet.freq, (AudioChannels)packet.channels ); audioStream.BufferNeeded += OnBufferRequest; UpdateVolume(); // Fill and queue the buffers. for (int i = 0; i < 4; i += 1) { if (!StreamAudio()) { break; } } } // Grab the first bit of video. 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); } }
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 Play(Video 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 (playerThread != null && playerThread.IsAlive) { Stop(); } // Create new Thread instances in case we use this player multiple times. playerThread = new Thread(new ThreadStart(this.RunVideo)); 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) { currentAudio = TheoraPlay.getAudioPacket(Video.audioStream); 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); Texture2D overlap = videoTexture; videoTexture = new Texture2D( 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! System.Console.Write("Starting Theora player..."); playerThread.Start(); System.Console.Write(" Waiting for initialization..."); while (!playerThread.IsAlive) { ; } System.Console.WriteLine(" Done!"); }