Browse Source

Decode the whole audio track on startup

Florian Meißner 10 years ago
parent
commit
5563b803c4
  1. 126
      Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp
  2. 6
      Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h
  3. 10
      Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp
  4. 5
      Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h
  5. 35
      Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
  6. 7
      Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h

126
Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp

@ -135,7 +135,7 @@ public:
const mkvparser::Track* GetTrack() { return fTrack; } const mkvparser::Track* GetTrack() { return fTrack; }
bool GetFrames(mkvparser::MkvReader* reader, int64_t movieTime, std::vector<blkbuf_t>& frames) bool GetFrames(mkvparser::MkvReader* reader, int64_t movieTimeNs, std::vector<blkbuf_t>& frames)
{ {
// If we have no block yet, grab the first one // If we have no block yet, grab the first one
if (!fCurrentBlock) if (!fCurrentBlock)
@ -146,7 +146,7 @@ public:
{ {
const mkvparser::Block* block = fCurrentBlock->GetBlock(); const mkvparser::Block* block = fCurrentBlock->GetBlock();
int64_t time = block->GetTime(fCurrentBlock->GetCluster()) - fTrack->GetCodecDelay(); 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 // We want to play this block, add it to the frames buffer
frames.reserve(frames.size() + block->GetFrameCount()); frames.reserve(frames.size() + block->GetFrameCount());
@ -180,8 +180,7 @@ plMoviePlayer::plMoviePlayer() :
fLastFrameTime(0), fLastFrameTime(0),
fPosition(hsPoint2()), fPosition(hsPoint2()),
fPlaying(false), fPlaying(false),
fPaused(false), fPaused(false)
fOpusDecoder(nullptr)
{ {
fScale.Set(1.0f, 1.0f); fScale.Set(1.0f, 1.0f);
} }
@ -192,8 +191,6 @@ plMoviePlayer::~plMoviePlayer()
// The plPlate owns the Mipmap Texture, so it destroys it for us // The plPlate owns the Mipmap Texture, so it destroys it for us
plPlateManager::Instance().DestroyPlate(fPlate); plPlateManager::Instance().DestroyPlate(fPlate);
#ifdef VIDEO_AVAILABLE #ifdef VIDEO_AVAILABLE
if (fOpusDecoder)
opus_decoder_destroy(fOpusDecoder);
if (fReader) if (fReader)
{ {
fReader->Close(); fReader->Close();
@ -255,6 +252,60 @@ bool plMoviePlayer::IOpenMovie()
#endif #endif
} }
bool plMoviePlayer::ILoadAudio()
{
#ifdef VIDEO_AVAILABLE
// Fetch audio track information
const mkvparser::AudioTrack* audio = static_cast<const mkvparser::AudioTrack*>(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<blkbuf_t> frames;
fAudioTrack->GetFrames(fReader, fSegment->GetDuration(), frames);
static const int maxFrameSize = 5760; // for max packet duration at 48kHz
std::vector<int16_t> decoded;
decoded.reserve(frames.size() * audio->GetChannels() * maxFrameSize);
for (auto it = frames.begin(); it != frames.end(); ++it)
{
const std::unique_ptr<uint8_t>& 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<uint8_t*>(decoded.data()), decoded.size() * sizeof(int16_t));
opus_decoder_destroy(opus);
return true;
#else
return false;
#endif
}
bool plMoviePlayer::ICheckLanguage(const mkvparser::Track* track) bool plMoviePlayer::ICheckLanguage(const mkvparser::Track* track)
{ {
auto codes = plLocalization::GetLanguageCodes(plLocalization::GetLanguage()); auto codes = plLocalization::GetLanguageCodes(plLocalization::GetLanguage());
@ -299,28 +350,6 @@ void plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames)
#endif #endif
} }
void plMoviePlayer::IProcessAudioFrame(const std::vector<blkbuf_t>& 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<uint8_t>& 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<const mkvparser::AudioTrack*>(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<uint8_t*>(pcm), samples * audio->GetChannels() * sizeof(int16_t));
}
#endif
}
bool plMoviePlayer::Start() bool plMoviePlayer::Start()
{ {
if (fPlaying) if (fPlaying)
@ -357,29 +386,12 @@ bool plMoviePlayer::Start()
plateMgr.SetPlatePixelSize(fPlate, plateWidth, plateHeight); plateMgr.SetPlatePixelSize(fPlate, plateWidth, plateHeight);
fTexture = fPlate->CreateMaterial(static_cast<uint32_t>(video->GetWidth()), static_cast<uint32_t>(video->GetHeight()), false); fTexture = fPlate->CreateMaterial(static_cast<uint32_t>(video->GetWidth()), static_cast<uint32_t>(video->GetHeight()), false);
// Fetch audio track information // Decode the audio track and load it into a sound buffer
const mkvparser::AudioTrack* audio = static_cast<const mkvparser::AudioTrack*>(fAudioTrack->GetTrack()); if (!ILoadAudio())
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; 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<int64_t>(hsTimer::GetMilliSeconds()); fLastFrameTime = static_cast<int64_t>(hsTimer::GetMilliSeconds());
fAudioSound->Play();
fPlaying = true; fPlaying = true;
return true; return true;
@ -404,20 +416,8 @@ bool plMoviePlayer::NextFrame()
// Get our current timecode // Get our current timecode
fMovieTime += frameTimeDelta; fMovieTime += frameTimeDelta;
std::vector<blkbuf_t> audio;
std::vector<blkbuf_t> video; std::vector<blkbuf_t> video;
uint8_t tracksWithData = 0; if (!fVideoTrack || !fVideoTrack->GetFrames(fReader, fMovieTime * 1000000, video))
if (fAudioTrack)
{
if (fAudioTrack->GetFrames(fReader, fMovieTime, audio))
tracksWithData++;
}
if (fVideoTrack)
{
if (fVideoTrack->GetFrames(fReader, fMovieTime, video))
tracksWithData++;
}
if (!tracksWithData)
{ {
Stop(); Stop();
return false; return false;
@ -425,7 +425,7 @@ bool plMoviePlayer::NextFrame()
// Show our mess // Show our mess
IProcessVideoFrame(video); IProcessVideoFrame(video);
IProcessAudioFrame(audio); fAudioSound->RefreshVolume();
return true; return true;
#else #else
@ -438,6 +438,7 @@ bool plMoviePlayer::Pause(bool on)
if (!fPlaying) if (!fPlaying)
return false; return false;
fAudioSound->Pause(on);
fPaused = on; fPaused = on;
return true; return true;
} }
@ -449,6 +450,7 @@ bool plMoviePlayer::Stop()
fAudioSound->Stop(); fAudioSound->Stop();
if (fPlate) if (fPlate)
fPlate->SetVisible(false); fPlate->SetVisible(false);
for (int i = 0; i < fCallbacks.size(); i++) for (int i = 0; i < fCallbacks.size(); i++)
fCallbacks[i]->Send(); fCallbacks[i]->Send();
fCallbacks.clear(); fCallbacks.clear();

6
Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h

@ -75,11 +75,9 @@ protected:
std::unique_ptr<mkvparser::Segment> fSegment; std::unique_ptr<mkvparser::Segment> fSegment;
std::unique_ptr<class TrackMgr> fAudioTrack, fVideoTrack; // TODO: vector of tracks? std::unique_ptr<class TrackMgr> fAudioTrack, fVideoTrack; // TODO: vector of tracks?
std::unique_ptr<class plWin32VideoSound> fAudioSound; std::unique_ptr<class plWin32VideoSound> fAudioSound;
std::unique_ptr<class VPX> fVpx; std::unique_ptr<class VPX> fVpx;
class OpusDecoder* fOpusDecoder;
int64_t fMovieTime, fLastFrameTime; int64_t fMovieTime, fLastFrameTime; // in ms
hsPoint2 fPosition, fScale; hsPoint2 fPosition, fScale;
plFileName fMoviePath; plFileName fMoviePath;
@ -87,9 +85,9 @@ protected:
bool fPaused; bool fPaused;
bool IOpenMovie(); bool IOpenMovie();
bool ILoadAudio();
bool ICheckLanguage(const mkvparser::Track* track); bool ICheckLanguage(const mkvparser::Track* track);
void IProcessVideoFrame(const std::vector<blkbuf_t>& frames); void IProcessVideoFrame(const std::vector<blkbuf_t>& frames);
void IProcessAudioFrame(const std::vector<blkbuf_t>& frames);
public: public:
plMoviePlayer(); plMoviePlayer();

10
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 //////////////////////////////////////////////////////////////////// //// Stop ////////////////////////////////////////////////////////////////////
void plDSoundBuffer::Stop( void ) void plDSoundBuffer::Stop( void )

5
Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h

@ -73,8 +73,9 @@ public:
void Play( void ); void Play( void );
void Stop( void ); void Stop( void );
void Rewind() ; void Rewind();
void Pause();
uint32_t GetLengthInBytes( void ) const; uint32_t GetLengthInBytes( void ) const;
void SetScalarVolume( float volume ); // Sets the volume, but on a range from 0 to 1 void SetScalarVolume( float volume ); // Sets the volume, but on a range from 0 to 1

35
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp

@ -47,16 +47,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "plDSoundBuffer.h" #include "plDSoundBuffer.h"
static int uniqueID = 0; static int uniqueID = 0;
plWin32VideoSound::plWin32VideoSound(plWAVHeader& header) : plWin32Sound() plWin32VideoSound::plWin32VideoSound(const plWAVHeader& header) : plWin32Sound()
{ {
fCurrVolume = 1.0f; fCurrVolume = 1.0f;
fDesiredVol = 1.0f; fDesiredVol = 1.0f;
fSoftVolume = 1.0f; fSoftVolume = 1.0f;
fType = kGUISound; fType = kGUISound;
fDSoundBuffer = new plDSoundBuffer(0, header, false, false, false, true); fWAVHeader = header;
fDSoundBuffer->SetupVoiceSource(); fDSoundBuffer = new plDSoundBuffer(0, fWAVHeader, false, false);
fDSoundBuffer->SetScalarVolume(1.0f);
uniqueID++; uniqueID++;
hsgResMgr::ResMgr()->NewKey(plFormat("videosound#{}", uniqueID), this, plLocation::kGlobalFixedLoc); hsgResMgr::ResMgr()->NewKey(plFormat("videosound#{}", uniqueID), this, plLocation::kGlobalFixedLoc);
@ -68,22 +67,24 @@ plWin32VideoSound::~plWin32VideoSound()
delete fDSoundBuffer; delete fDSoundBuffer;
} }
void plWin32VideoSound::UpdateSoundBuffer(void* buffer, size_t size) void plWin32VideoSound::Play()
{ {
uint32_t bufferId; IActuallyPlay();
uint32_t chunk; }
fDSoundBuffer->UnQueueVoiceBuffers(); void plWin32VideoSound::Pause(bool on)
while (size > 0) {
{ if (on)
chunk = size < STREAM_BUFFER_SIZE ? size : STREAM_BUFFER_SIZE; fDSoundBuffer->Pause();
if (!fDSoundBuffer->GetAvailableBufferId(&bufferId)) else if (!fReallyPlaying)
break; fDSoundBuffer->Play();
fReallyPlaying = !on;
}
fDSoundBuffer->VoiceFillBuffer(buffer, chunk, bufferId); void plWin32VideoSound::FillSoundBuffer(void* buffer, size_t size)
size -= chunk; {
} fDSoundBuffer->FillBuffer(buffer, size, &fWAVHeader);
IActuallyPlay(); fDSoundBuffer->SetScalarVolume(1.0f);
} }
void plWin32VideoSound::IDerivedActuallyPlay() void plWin32VideoSound::IDerivedActuallyPlay()

7
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 class plWin32VideoSound : public plWin32Sound
{ {
public: public:
plWin32VideoSound(plWAVHeader& header); plWin32VideoSound(const plWAVHeader& header);
virtual ~plWin32VideoSound(); 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: protected:
void IDerivedActuallyPlay(void); void IDerivedActuallyPlay(void);
@ -60,5 +62,6 @@ protected:
float GetActualTimeSec(); float GetActualTimeSec();
void ISetActualTime(double t); void ISetActualTime(double t);
plWAVHeader fWAVHeader;
}; };
#endif #endif

Loading…
Cancel
Save