diff --git a/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp b/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp index 95ee77e2..1f55eb69 100644 --- a/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp +++ b/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp @@ -135,7 +135,7 @@ public: const mkvparser::Track* GetTrack() { return fTrack; } - bool GetFrames(mkvparser::MkvReader* reader, int64_t movieTime, std::vector& frames) + bool GetFrames(mkvparser::MkvReader* reader, int64_t movieTimeNs, std::vector& frames) { // If we have no block yet, grab the first one if (!fCurrentBlock) @@ -146,7 +146,7 @@ public: { const mkvparser::Block* block = fCurrentBlock->GetBlock(); int64_t time = block->GetTime(fCurrentBlock->GetCluster()) - fTrack->GetCodecDelay(); - if (time <= movieTime * 1000000) // Block time is nano seconds + if (time <= movieTimeNs) { // We want to play this block, add it to the frames buffer frames.reserve(frames.size() + block->GetFrameCount()); @@ -180,8 +180,7 @@ plMoviePlayer::plMoviePlayer() : fLastFrameTime(0), fPosition(hsPoint2()), fPlaying(false), - fPaused(false), - fOpusDecoder(nullptr) + fPaused(false) { fScale.Set(1.0f, 1.0f); } @@ -192,8 +191,6 @@ plMoviePlayer::~plMoviePlayer() // The plPlate owns the Mipmap Texture, so it destroys it for us plPlateManager::Instance().DestroyPlate(fPlate); #ifdef VIDEO_AVAILABLE - if (fOpusDecoder) - opus_decoder_destroy(fOpusDecoder); if (fReader) { fReader->Close(); @@ -255,6 +252,60 @@ bool plMoviePlayer::IOpenMovie() #endif } +bool plMoviePlayer::ILoadAudio() +{ +#ifdef VIDEO_AVAILABLE + // Fetch audio track information + const mkvparser::AudioTrack* audio = static_cast(fAudioTrack->GetTrack()); + plWAVHeader header; + header.fFormatTag = plWAVHeader::kPCMFormatTag; + header.fNumChannels = audio->GetChannels(); + header.fBitsPerSample = audio->GetBitDepth() == 8 ? 8 : 16; + header.fNumSamplesPerSec = 48000; // OPUS specs say we shall always decode at 48kHz + header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 8; + header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign; + fAudioSound.reset(new plWin32VideoSound(header)); + + // Initialize Opus + if (strcmp(audio->GetCodecId(), WEBM_CODECID_OPUS) != 0) + { + hsAssert(false, "Not an Opus audio track!"); + return false; + } + int error; + OpusDecoder* opus = opus_decoder_create(48000, audio->GetChannels(), &error); + if (error != OPUS_OK) + hsAssert(false, "Error occured initalizing opus"); + + // Decode audio track + std::vector frames; + fAudioTrack->GetFrames(fReader, fSegment->GetDuration(), frames); + static const int maxFrameSize = 5760; // for max packet duration at 48kHz + std::vector decoded; + decoded.reserve(frames.size() * audio->GetChannels() * maxFrameSize); + + for (auto it = frames.begin(); it != frames.end(); ++it) + { + const std::unique_ptr& buf = std::get<0>(*it); + int32_t size = std::get<1>(*it); + + int16_t* pcm = new int16_t[maxFrameSize * audio->GetChannels()]; + int samples = opus_decode(opus, buf.get(), size, pcm, maxFrameSize, 0); + if (samples < 0) + hsAssert(false, "opus error"); + for (size_t i = 0; i < samples * audio->GetChannels(); i++) + decoded.push_back(pcm[i]); + delete[] pcm; + } + + fAudioSound->FillSoundBuffer(reinterpret_cast(decoded.data()), decoded.size() * sizeof(int16_t)); + opus_decoder_destroy(opus); + return true; +#else + return false; +#endif +} + bool plMoviePlayer::ICheckLanguage(const mkvparser::Track* track) { auto codes = plLocalization::GetLanguageCodes(plLocalization::GetLanguage()); @@ -299,28 +350,6 @@ void plMoviePlayer::IProcessVideoFrame(const std::vector& frames) #endif } -void plMoviePlayer::IProcessAudioFrame(const std::vector& frames) -{ -#ifdef VIDEO_AVAILABLE - const uint8_t* data = nullptr; - int32_t size = 0; - for (auto it = frames.begin(); it != frames.end(); ++it) - { - const std::unique_ptr& buf = std::get<0>(*it); - data = buf.get(); - size = std::get<1>(*it); - - static const int frameSize = 5760; //max packet duration at 48kHz - const mkvparser::AudioTrack* audio = static_cast(fAudioTrack->GetTrack()); - int16_t* pcm = new int16_t[frameSize * audio->GetChannels() * sizeof(int16_t)]; - int samples = opus_decode(fOpusDecoder, data, size, pcm, frameSize, 0); - if (samples < 0) - hsAssert(false, "opus error"); - fAudioSound->UpdateSoundBuffer(reinterpret_cast(pcm), samples * audio->GetChannels() * sizeof(int16_t)); - } -#endif -} - bool plMoviePlayer::Start() { if (fPlaying) @@ -357,29 +386,12 @@ bool plMoviePlayer::Start() plateMgr.SetPlatePixelSize(fPlate, plateWidth, plateHeight); fTexture = fPlate->CreateMaterial(static_cast(video->GetWidth()), static_cast(video->GetHeight()), false); - // Fetch audio track information - const mkvparser::AudioTrack* audio = static_cast(fAudioTrack->GetTrack()); - plWAVHeader header; - header.fFormatTag = plWAVHeader::kPCMFormatTag; - header.fNumChannels = audio->GetChannels(); - header.fBitsPerSample = audio->GetBitDepth() == 8 ? 8 : 16; - header.fNumSamplesPerSec = 48000; // OPUS specs say we shall always decode at 48kHz - header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 8; - header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign; - fAudioSound.reset(new plWin32VideoSound(header)); - - // Initialize Opus - if (strcmp(audio->GetCodecId(), WEBM_CODECID_OPUS) != 0) - { - hsAssert(false, "Not an Opus audio track!"); + // Decode the audio track and load it into a sound buffer + if (!ILoadAudio()) return false; - } - int error; - fOpusDecoder = opus_decoder_create(48000, audio->GetChannels(), &error); - if (error != OPUS_OK) - hsAssert(false, "Error occured initalizing opus"); fLastFrameTime = static_cast(hsTimer::GetMilliSeconds()); + fAudioSound->Play(); fPlaying = true; return true; @@ -404,20 +416,8 @@ bool plMoviePlayer::NextFrame() // Get our current timecode fMovieTime += frameTimeDelta; - std::vector audio; std::vector video; - uint8_t tracksWithData = 0; - if (fAudioTrack) - { - if (fAudioTrack->GetFrames(fReader, fMovieTime, audio)) - tracksWithData++; - } - if (fVideoTrack) - { - if (fVideoTrack->GetFrames(fReader, fMovieTime, video)) - tracksWithData++; - } - if (!tracksWithData) + if (!fVideoTrack || !fVideoTrack->GetFrames(fReader, fMovieTime * 1000000, video)) { Stop(); return false; @@ -425,7 +425,7 @@ bool plMoviePlayer::NextFrame() // Show our mess IProcessVideoFrame(video); - IProcessAudioFrame(audio); + fAudioSound->RefreshVolume(); return true; #else @@ -438,6 +438,7 @@ bool plMoviePlayer::Pause(bool on) if (!fPlaying) return false; + fAudioSound->Pause(on); fPaused = on; return true; } @@ -449,6 +450,7 @@ bool plMoviePlayer::Stop() fAudioSound->Stop(); if (fPlate) fPlate->SetVisible(false); + for (int i = 0; i < fCallbacks.size(); i++) fCallbacks[i]->Send(); fCallbacks.clear(); diff --git a/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h b/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h index 62a48494..023dd3f1 100644 --- a/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h +++ b/Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h @@ -75,11 +75,9 @@ protected: std::unique_ptr fSegment; std::unique_ptr fAudioTrack, fVideoTrack; // TODO: vector of tracks? std::unique_ptr fAudioSound; - std::unique_ptr fVpx; - class OpusDecoder* fOpusDecoder; - int64_t fMovieTime, fLastFrameTime; + int64_t fMovieTime, fLastFrameTime; // in ms hsPoint2 fPosition, fScale; plFileName fMoviePath; @@ -87,9 +85,9 @@ protected: bool fPaused; bool IOpenMovie(); + bool ILoadAudio(); bool ICheckLanguage(const mkvparser::Track* track); void IProcessVideoFrame(const std::vector& frames); - void IProcessAudioFrame(const std::vector& frames); public: plMoviePlayer(); diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp index 108ee9ae..ec78a270 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -619,6 +619,16 @@ void plDSoundBuffer::Play( void ) } +//// Pause /////////////////////////////////////////////////////////////////// + +void plDSoundBuffer::Pause() +{ + if (!source) + return; + alSourcePause(source); + alGetError(); +} + //// Stop //////////////////////////////////////////////////////////////////// void plDSoundBuffer::Stop( void ) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h index ac161a5a..b9fab606 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h @@ -73,8 +73,9 @@ public: void Play( void ); void Stop( void ); - void Rewind() ; - + void Rewind(); + void Pause(); + uint32_t GetLengthInBytes( void ) const; void SetScalarVolume( float volume ); // Sets the volume, but on a range from 0 to 1 diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp index 819136ed..299dd095 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp @@ -47,16 +47,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plDSoundBuffer.h" static int uniqueID = 0; -plWin32VideoSound::plWin32VideoSound(plWAVHeader& header) : plWin32Sound() +plWin32VideoSound::plWin32VideoSound(const plWAVHeader& header) : plWin32Sound() { fCurrVolume = 1.0f; fDesiredVol = 1.0f; fSoftVolume = 1.0f; fType = kGUISound; - fDSoundBuffer = new plDSoundBuffer(0, header, false, false, false, true); - fDSoundBuffer->SetupVoiceSource(); - fDSoundBuffer->SetScalarVolume(1.0f); + fWAVHeader = header; + fDSoundBuffer = new plDSoundBuffer(0, fWAVHeader, false, false); uniqueID++; hsgResMgr::ResMgr()->NewKey(plFormat("videosound#{}", uniqueID), this, plLocation::kGlobalFixedLoc); @@ -68,22 +67,24 @@ plWin32VideoSound::~plWin32VideoSound() delete fDSoundBuffer; } -void plWin32VideoSound::UpdateSoundBuffer(void* buffer, size_t size) +void plWin32VideoSound::Play() { - uint32_t bufferId; - uint32_t chunk; + IActuallyPlay(); +} - fDSoundBuffer->UnQueueVoiceBuffers(); - while (size > 0) - { - chunk = size < STREAM_BUFFER_SIZE ? size : STREAM_BUFFER_SIZE; - if (!fDSoundBuffer->GetAvailableBufferId(&bufferId)) - break; +void plWin32VideoSound::Pause(bool on) +{ + if (on) + fDSoundBuffer->Pause(); + else if (!fReallyPlaying) + fDSoundBuffer->Play(); + fReallyPlaying = !on; +} - fDSoundBuffer->VoiceFillBuffer(buffer, chunk, bufferId); - size -= chunk; - } - IActuallyPlay(); +void plWin32VideoSound::FillSoundBuffer(void* buffer, size_t size) +{ + fDSoundBuffer->FillBuffer(buffer, size, &fWAVHeader); + fDSoundBuffer->SetScalarVolume(1.0f); } void plWin32VideoSound::IDerivedActuallyPlay() diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h b/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h index bdddf7d8..9291ee19 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h @@ -48,10 +48,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plWin32VideoSound : public plWin32Sound { public: - plWin32VideoSound(plWAVHeader& header); + plWin32VideoSound(const plWAVHeader& header); virtual ~plWin32VideoSound(); - void UpdateSoundBuffer(void* buffer, size_t size); + virtual void Play(); + virtual void Pause(bool on); + void FillSoundBuffer(void* buffer, size_t size); protected: void IDerivedActuallyPlay(void); @@ -60,5 +62,6 @@ protected: float GetActualTimeSec(); void ISetActualTime(double t); + plWAVHeader fWAVHeader; }; #endif