1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-18 19:29:09 +00:00

Make audio work

Includes some reorganization and cleanup
This commit is contained in:
Florian Meißner
2014-10-26 22:54:37 +01:00
parent 9de5c4db54
commit 7cd8f51eb4
11 changed files with 214 additions and 235 deletions

View File

@ -3,10 +3,10 @@ include_directories(../../NucleusLib)
include_directories(../../NucleusLib/inc)
include_directories(../../PubUtilLib)
if (VPX_AVAILABLE)
if (VIDEO_AVAILABLE)
include_directories(${VPX_INCLUDE_DIR})
include_directories(${Opus_INCLUDE_DIR})
endif (VPX_AVAILABLE)
endif (VIDEO_AVAILABLE)
set(pfMoviePlayer_SOURCES
plMoviePlayer.cpp

View File

@ -41,18 +41,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
*==LICENSE==*/
#include "plMoviePlayer.h"
#include <tuple>
#include <memory>
#ifdef VPX_AVAILABLE
#ifdef VIDEO_AVAILABLE
# define VPX_CODEC_DISABLE_COMPAT 1
# include <vpx/vpx_decoder.h>
# include <vpx/vp8dx.h>
# define iface (vpx_codec_vp9_dx())
# include <opus.h>
#endif
#include <opus.h>
#include "plGImage/plMipmap.h"
#include "pnKeyedObject/plUoid.h"
#include "plPipeline/hsGDeviceRef.h"
@ -61,12 +58,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "hsResMgr.h"
#include "hsTimer.h"
#include "plAudio/plWin32VideoSound.h"
#include "../Apps/plClient/plClient.h"
#include "plScene/plSceneNode.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plAudioInterface.h"
#include "pnMessage/plSoundMsg.h"
#include "plgDispatch.h"
#include "webm/mkvreader.hpp"
#include "webm/mkvparser.hpp"
@ -88,6 +79,7 @@ class VPX
{
VPX() { }
#ifdef VIDEO_AVAILABLE
public:
vpx_codec_ctx_t codec;
@ -121,6 +113,7 @@ public:
// if this proves false, move decoder function into IProcessVideoFrame
return vpx_codec_get_frame(&codec, &iter);
}
#endif
};
// =====================================================
@ -133,57 +126,46 @@ class TrackMgr
bool PeekNextBlockEntry(const std::unique_ptr<mkvparser::Segment>& segment)
{
// Assume that if blk_entry == nullptr, we need to start from the beginning
// Load the current cluster
const mkvparser::Cluster* cluster;
if (blk_entry)
cluster = blk_entry->GetCluster();
else
cluster = segment->GetFirst();
while (true)
{
if (cluster->EOS() && cluster != NULL)
{
cluster = segment->GetNext(cluster);
blk_entry = nullptr;
if (!cluster)
return false;
}
if (blk_entry)
{
SAFE_OP(cluster->GetNext(blk_entry, blk_entry), "get next block");
}
else if (cluster->m_pSegment != NULL)
// As long as we have clusters, they contain blocks that we have to process
while (cluster && !cluster->EOS()) {
// If we have no block yet, get the first one, otherwise the next one
if (!blk_entry)
{
SAFE_OP(cluster->GetFirst(blk_entry), "get first block");
}
else
{
blk_entry = nullptr;
return false; //reached end of movie. I hope.
SAFE_OP(cluster->GetNext(blk_entry, blk_entry), "get next block");
}
if (blk_entry)
{
if (blk_entry->EOS())
continue;
// Are there any blocks left?
while (blk_entry && !blk_entry->EOS()) {
// Is this the next block we want for our track? Awesome, we're done!
if (blk_entry->GetBlock()->GetTrackNumber() == number)
return true;
SAFE_OP(cluster->GetNext(blk_entry, blk_entry), "get next block");
}
else
{
cluster = segment->GetNext(cluster);
blk_entry = nullptr;
if (!cluster)
return false;
}
// No blocks left, go to next cluster
blk_entry = nullptr;
cluster = segment->GetNext(cluster);
}
return false; // if this happens, boom.
// That's it, nothing left...
return false;
}
public:
uint32_t number;
int32_t number;
TrackMgr(uint32_t num) : blk_entry(nullptr), valid(true), number(num) { }
TrackMgr(int32_t num) : blk_entry(nullptr), valid(true), number(num) { }
bool GetFrames(plMoviePlayer* p, int64_t movieTime, std::vector<blkbuf_t>& frames)
{
@ -204,7 +186,8 @@ public:
data.Read(p->fReader, buf);
frames.push_back(std::make_tuple(std::unique_ptr<uint8_t>(buf), static_cast<int32_t>(data.len)));
}
} else
}
else
{
blk_entry = prev;
return true;
@ -213,42 +196,6 @@ public:
}
return true;
}
#if 0
bool Advance(plMoviePlayer* p, int64_t movieTime=0)
{
if (!valid)
return false;
// This keeps us from getting behind due to freezes
// Assumption: Audio will not skip ahead in time. FIXME?
while ((valid = PeekNextBlockEntry(p->fSegment)))
{
const mkvparser::Block* blk = blk_entry->GetBlock();
if (blk->GetTime(blk_entry->GetCluster()) < movieTime)
continue;
else
return true;
}
return false; // ran out of blocks
}
int32_t GetBlockData(plMoviePlayer* p, std::vector<blkbuf_t>& frames) const
{
const mkvparser::Block* block = blk_entry->GetBlock();
// Return the frames
frames.reserve(block->GetFrameCount());
for (int32_t i = 0; i < block->GetFrameCount(); ++i)
{
const mkvparser::Block::Frame frame = block->GetFrame(i);
std::shared_ptr<uint8_t> data(new uint8_t[frame.len]);
frame.Read(p->fReader, data.get());
frames.push_back(std::make_tuple(data, frame.len));
}
return block->GetFrameCount();
}
#endif 0
};
// =====================================================
@ -257,44 +204,27 @@ plMoviePlayer::plMoviePlayer() :
fPlate(nullptr),
fTexture(nullptr),
fReader(nullptr),
fTimeScale(0),
fStartTime(0),
fAudioPlayer(),
fPosition(hsPoint2()),
fAudioInterface(),
fPlaying(true),
fOpusDecoder(nil)
fPlaying(false),
fOpusDecoder(nullptr)
{
fScale.Set(1.0f, 1.0f);
fAudioSound = std::shared_ptr<plWin32VideoSound>(new plWin32VideoSound());
fAudioPlayer.SetSound(fAudioSound);
plSceneNode* sceneNode = plClient::GetInstance()->GetCurrentScene();
if (sceneNode != nullptr)
{
hsTArray<plSceneObject*>& sceneObjects = sceneNode->GetSceneObjects();
for (int i = 0; i < sceneObjects.GetCount(); ++i)
{
if (sceneObjects[i]->GetAudioInterface() == nullptr)
{
fAudioInterface.ISetAudible(&fAudioPlayer);
sceneObjects[i]->SetAudioInterface(&fAudioInterface);
break;
}
}
}
}
plMoviePlayer::~plMoviePlayer()
{
opus_decoder_destroy(fOpusDecoder);
if (fPlate)
// The plPlate owns the Mipmap Texture, so it destroys it for us
plPlateManager::Instance().DestroyPlate(fPlate);
#ifdef VPX_AVAILABLE
#ifdef VIDEO_AVAILABLE
if (fOpusDecoder)
opus_decoder_destroy(fOpusDecoder);
if (fReader)
{
fReader->Close();
delete fReader;
}
#endif
}
@ -305,7 +235,7 @@ int64_t plMoviePlayer::GetMovieTime() const
bool plMoviePlayer::IOpenMovie()
{
#ifdef VPX_AVAILABLE
#ifdef VIDEO_AVAILABLE
if (!plFileInfo(fMoviePath).Exists())
{
hsAssert(false, "Tried to play a movie that doesn't exist");
@ -326,9 +256,6 @@ bool plMoviePlayer::IOpenMovie()
SAFE_OP(seg->Load(), "load segment from webm");
fSegment.reset(seg);
// Just in case someone gives us a weird file, find out the timecode offset
fTimeScale = fSegment->GetInfo()->GetTimeCodeScale();
// TODO: Figure out video and audio based on current language
// For now... just take the first one.
const mkvparser::Tracks* tracks = fSegment->GetTracks();
@ -360,8 +287,9 @@ bool plMoviePlayer::IOpenMovie()
#endif
}
bool plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames)
void plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames)
{
#ifdef VIDEO_AVAILABLE
vpx_image_t* img = nullptr;
// We have to decode all the frames, but we only want to display the most recent one to the user.
@ -390,34 +318,45 @@ bool plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames)
// Flush new data to the device
if (fTexture->GetDeviceRef())
fTexture->GetDeviceRef()->SetDirty(true);
return true;
}
return false;
#endif
}
void plMoviePlayer::IProcessAudioFrame(const std::vector<blkbuf_t>& frames)
{
#ifdef VIDEO_AVAILABLE
const unsigned char* 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*>(fSegment->GetTracks()->GetTrackByNumber(fAudioTrack->number));
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()
{
plSceneNode* sceneNode = plClient::GetInstance()->GetCurrentScene();
if (sceneNode != nullptr)
sceneNode->GetKey();
#ifdef VPX_AVAILABLE
#ifdef VIDEO_AVAILABLE
if (!IOpenMovie())
return false;
hsAssert(fVideoTrack, "nil video track -- expect bad things to happen!");
// Initialize VP8
// Initialize VPX
if (VPX* vpx = VPX::Create())
fVpx.reset(vpx);
else
return false;
//initialize opus
int error;
fOpusDecoder = opus_decoder_create(48000, 1, &error);
if (error != OPUS_OK)
hsAssert(false, "Error occured initalizing opus");
// Need to figure out scaling based on pipe size.
plPlateManager& plateMgr = plPlateManager::Instance();
const mkvparser::VideoTrack* video = static_cast<const mkvparser::VideoTrack*>(fSegment->GetTracks()->GetTrackByNumber(fVideoTrack->number));
@ -428,19 +367,34 @@ bool plMoviePlayer::Start()
fPlate->SetVisible(true);
fTexture = fPlate->CreateMaterial(static_cast<uint32_t>(video->GetWidth()), static_cast<uint32_t>(video->GetHeight()), nullptr);
//initialize opus
const mkvparser::AudioTrack* audio = static_cast<const mkvparser::AudioTrack*>(fSegment->GetTracks()->GetTrackByNumber(fAudioTrack->number));
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 / 2;
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
fAudioSound.reset(new plWin32VideoSound(header));
int error;
fOpusDecoder = opus_decoder_create(48000, audio->GetChannels(), &error);
if (error != OPUS_OK)
hsAssert(false, "Error occured initalizing opus");
fPlaying = true;
return true;
#else
return false;
#endif // VPX_AVAILABLE
#endif // VIDEO_AVAILABLE
}
bool plMoviePlayer::NextFrame()
{
if (fPlaying)
{
#ifdef VPX_AVAILABLE
#ifdef VIDEO_AVAILABLE
// Get our current timecode
int64_t movieTime = 0;
if (fStartTime == 0)
@ -450,23 +404,21 @@ bool plMoviePlayer::NextFrame()
std::vector<blkbuf_t> audio;
std::vector<blkbuf_t> video;
uint8_t tracksWithData = 0;
if (fAudioTrack)
{
uint8_t tracksWithData = 0;
if (fAudioTrack)
{
if (fAudioTrack->GetFrames(this, movieTime, audio))
tracksWithData++;
}
if (fVideoTrack)
{
if (fVideoTrack->GetFrames(this, movieTime, video))
tracksWithData++;
}
if (tracksWithData == 0)
{
Stop();
return false;
}
if (fAudioTrack->GetFrames(this, movieTime, audio))
tracksWithData++;
}
if (fVideoTrack)
{
if (fVideoTrack->GetFrames(this, movieTime, video))
tracksWithData++;
}
if (!tracksWithData)
{
Stop();
return false;
}
// Show our mess
@ -476,7 +428,7 @@ bool plMoviePlayer::NextFrame()
return true;
#else
return false;
#endif // VPX_AVAILABLE
#endif // VIDEO_AVAILABLE
}
return false;
}
@ -484,36 +436,10 @@ bool plMoviePlayer::NextFrame()
bool plMoviePlayer::Stop()
{
fPlaying = false;
if (fAudioSound)
fAudioSound->Stop();
for (int i = 0; i < fCallbacks.size(); i++)
fCallbacks[i]->Send();
fCallbacks.clear();
return false;
}
bool plMoviePlayer::IProcessAudioFrame(const std::vector<blkbuf_t>& frames)
{
const unsigned char* data = NULL;
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);
int error;
const int frameSize = 5760; //max packet duration at 48kHz
opus_int16* pcm = new opus_int16[frameSize * 1 * sizeof(opus_int16)];
error = opus_decode(fOpusDecoder, data, size, pcm, frameSize, 0);
if (error < 0)
hsAssert(false, "opus error");
fAudioSound->UpdateSoundBuffer(reinterpret_cast<unsigned char*>(pcm), error);
if (plClient::GetInstance()->GetCurrentScene() != nullptr)
{
plSoundMsg* soundMsg = new plSoundMsg();
soundMsg->SetCmd(plSoundMsg::kPlay);
soundMsg->SetBCastFlag(plMessage::kBCastByType);
fAudioInterface.MsgReceive(soundMsg);
}
}
return true;
}

View File

@ -48,16 +48,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "hsPoint2.h"
#include "hsColorRGBA.h"
#include "plMessage/plMovieMsg.h"
#include "plAudio/plWin32VideoSound.h"
#include "plAudible/plVideoAudible.h"
#include "pnSceneObject/plAudioInterface.h"
#include <memory>
#include <vector>
#include <tuple>
#include <opus.h>
namespace mkvparser
{
class BlockEntry;
@ -79,23 +74,21 @@ protected:
mkvparser::MkvReader* fReader;
std::unique_ptr<mkvparser::Segment> fSegment;
std::unique_ptr<class TrackMgr> fAudioTrack, fVideoTrack; // TODO: vector of tracks?
std::unique_ptr<class VPX> fVpx;
int64_t fTimeScale, fStartTime;
std::unique_ptr<class plWin32VideoSound> fAudioSound;
std::unique_ptr<class VPX> fVpx;
class OpusDecoder* fOpusDecoder;
int64_t fStartTime;
hsPoint2 fPosition, fScale;
plFileName fMoviePath;
OpusDecoder* fOpusDecoder;
std::shared_ptr<plWin32VideoSound> fAudioSound;
plVideoAudible fAudioPlayer;
plAudioInterface fAudioInterface;
bool fPlaying;
int64_t GetMovieTime() const;
bool IOpenMovie();
bool IProcessVideoFrame(const std::vector<blkbuf_t>& frames);
bool IProcessAudioFrame(const std::vector<blkbuf_t>& frames);
void IProcessVideoFrame(const std::vector<blkbuf_t>& frames);
void IProcessAudioFrame(const std::vector<blkbuf_t>& frames);
public:
plMoviePlayer();