Browse Source

Merge pull request #458 from Mystler/webm

WebM Movie Player (VP9/Opus)
Adam Johnson 10 years ago
parent
commit
990f42c780
  1. 7
      CMakeLists.txt
  2. 10
      Sources/Plasma/Apps/plClient/CMakeLists.txt
  3. 31
      Sources/Plasma/Apps/plClient/plClient.cpp
  4. 2
      Sources/Plasma/Apps/plClient/plClient.h
  5. 2
      Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h
  6. 17
      Sources/Plasma/FeatureLib/pfMoviePlayer/CMakeLists.txt
  7. 388
      Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.cpp
  8. 47
      Sources/Plasma/FeatureLib/pfMoviePlayer/plMoviePlayer.h
  9. 97
      Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.cpp
  10. 53
      Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.h
  11. 8354
      Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.cpp
  12. 945
      Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.hpp
  13. 132
      Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.cpp
  14. 45
      Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.hpp
  15. 2
      Sources/Plasma/PubUtilLib/plAudio/CMakeLists.txt
  16. 10
      Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp
  17. 5
      Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h
  18. 2
      Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp
  19. 119
      Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
  20. 67
      Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h
  21. 2
      Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h
  22. 11
      Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp
  23. 3
      Sources/Plasma/PubUtilLib/plResMgr/plLocalization.h
  24. 37
      cmake/FindOpus.cmake
  25. 16
      cmake/FindVPX.cmake

7
CMakeLists.txt

@ -41,6 +41,8 @@ find_package(PNG REQUIRED)
find_package(Ogg REQUIRED) #TODO: Not required if we aren't building the client
find_package(Vorbis REQUIRED) #TODO: Not required if we aren't building the client
find_package(Speex REQUIRED) #TODO: Not required if we aren't building the client
find_package(VPX)
find_package(Opus)
find_package(CURL REQUIRED)
find_package(Freetype)
@ -49,6 +51,11 @@ if(WIN32)
find_package(DirectX REQUIRED)
endif(WIN32)
if(VPX_FOUND AND Opus_FOUND)
set(MOVIE_AVAILABLE TRUE)
add_definitions(-DMOVIE_AVAILABLE)
endif()
include(PrecompiledHeader) #Precompiled Header helper macros
# libCurl isn't smart enough to detect this for us, so we have to configure it ourselves

10
Sources/Plasma/Apps/plClient/CMakeLists.txt

@ -12,6 +12,11 @@ include_directories(${OPENSSL_INCLUDE_DIR})
include_directories(${PYTHON_INCLUDE_DIR})
include_directories(${CURL_INCLUDE_DIR})
if(MOVIE_AVAILABLE)
include_directories(${VPX_INCLUDE_DIR})
include_directories(${Opus_INCLUDE_DIR})
endif()
# Test for Python Interpreter, which will be used for extra build scripts if available
find_package(PythonInterp)
if(PYTHONINTERP_FOUND)
@ -167,6 +172,11 @@ if(USE_VLD)
target_link_libraries(plClient ${VLD_LIBRARY})
endif()
if(MOVIE_AVAILABLE)
target_link_libraries(plClient ${VPX_LIBRARY})
target_link_libraries(plClient ${Opus_LIBRARIES})
endif()
if (WIN32)
target_link_libraries(plClient rpcrt4)
target_link_libraries(plClient version)

31
Sources/Plasma/Apps/plClient/plClient.cpp

@ -825,18 +825,18 @@ bool plClient::IHandleMovieMsg(plMovieMsg* mov)
if (mov->GetFileName().IsEmpty())
return true;
int i = fMovies.GetCount();
size_t i = fMovies.size();
if (!(mov->GetCmd() & plMovieMsg::kMake))
{
for (i = 0; i < fMovies.GetCount(); i++)
for (i = 0; i < fMovies.size(); i++)
{
if (mov->GetFileName().CompareI(fMovies[i]->GetFileName().AsString()) == 0)
break;
}
}
if (i == fMovies.GetCount())
if (i == fMovies.size())
{
fMovies.Append(new plMoviePlayer);
fMovies.push_back(new plMoviePlayer());
fMovies[i]->SetFileName(mov->GetFileName());
}
@ -892,7 +892,8 @@ bool plClient::IHandleMovieMsg(plMovieMsg* mov)
if (!fMovies[i]->GetFileName().IsValid())
{
delete fMovies[i];
fMovies.Remove(i);
fMovies[i] = fMovies.back();
fMovies.pop_back();
}
return true;
}
@ -1436,10 +1437,6 @@ bool plClient::StartInit()
plgAudioSys::Activate(true);
IPlayIntroMovie("avi/CyanWorlds.avi", 0.f, 0.f, 0.f, 1.f, 1.f, 0.75);
if( GetDone() ) return false;
plgDispatch::Dispatch()->RegisterForExactType(plMovieMsg::Index(), GetKey());
//
// Init Net before loading things
//
@ -1453,7 +1450,7 @@ bool plClient::StartInit()
pModMsg2->SetCmd(plCmdIfaceModMsg::kAdd);
plgDispatch::MsgSend(pModMsg2);
// create new the virtual camera
// create new virtual camera
fNewCamera = new plVirtualCam1;
fNewCamera->RegisterAs( kVirtualCamera1_KEY );
fNewCamera->Init();
@ -1464,6 +1461,10 @@ bool plClient::StartInit()
plMouseDevice::Instance()->SetDisplayResolution((float)fPipeline->Width(), (float)fPipeline->Height());
plInputManager::SetRecenterMouse(false);
IPlayIntroMovie("avi/CyanWorlds.webm", 0.f, 0.f, 0.f, 1.f, 1.f, 0.75);
if(GetDone()) return false;
plgDispatch::Dispatch()->RegisterForExactType(plMovieMsg::Index(), GetKey());
// create the listener for the audio system:
plListener* pLMod = new plListener;
pLMod->RegisterAs(kListenerMod_KEY );
@ -1827,13 +1828,13 @@ bool plClient::IDraw()
void plClient::IServiceMovies()
{
int i;
for (i = 0; i < fMovies.GetCount(); i++)
for (size_t i = 0; i < fMovies.size(); i++)
{
if (!fMovies[i]->NextFrame())
{
delete fMovies[i];
fMovies.Remove(i);
fMovies[i] = fMovies.back();
fMovies.pop_back();
i--;
}
}
@ -1841,9 +1842,9 @@ void plClient::IServiceMovies()
void plClient::IKillMovies()
{
for (int i = 0; i < fMovies.GetCount(); i++)
for (size_t i = 0; i < fMovies.size(); i++)
delete fMovies[i];
fMovies.Reset();
fMovies.clear();
}
bool plClient::IPlayIntroMovie(const char* movieName, float endDelay, float posX, float posY, float scaleX, float scaleY, float volume /* = 1.0 */)

2
Sources/Plasma/Apps/plClient/plClient.h

@ -147,7 +147,7 @@ protected:
int fQuality;
bool fQuitIntro;
hsTArray<plMoviePlayer*> fMovies;
std::vector<plMoviePlayer*> fMovies;
plMessagePumpProc fMessagePumpProc;

2
Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h

@ -129,7 +129,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
// tint=yes/no - Defines whether or not this decal is tinted //
// with the cover. Overridden by the tintfirst //
// option on the <cover> tag. Defaults to no //
// <movie> - Places a movie (.avi file) inline with the text. Options: //
// <movie> - Places a movie (.webm file) inline with the text. Options://
// src=<movie name> - Selects the movie to be used. (nead search //
// methods here eventually) //
// align=left/right/center //

17
Sources/Plasma/FeatureLib/pfMoviePlayer/CMakeLists.txt

@ -3,15 +3,30 @@ include_directories(../../NucleusLib)
include_directories(../../NucleusLib/inc)
include_directories(../../PubUtilLib)
if(MOVIE_AVAILABLE)
include_directories(${VPX_INCLUDE_DIR})
include_directories(${Opus_INCLUDE_DIR})
endif()
set(pfMoviePlayer_SOURCES
plMoviePlayer.cpp
plPlanarImage.cpp
)
set(pfMoviePlayer_HEADERS
plMoviePlayer.h
plPlanarImage.h
)
set(pfMoviePlayer_WEBM
webm/mkvparser.cpp
webm/mkvparser.hpp
webm/mkvreader.cpp
webm/mkvreader.hpp
)
add_library(pfMoviePlayer STATIC ${pfMoviePlayer_SOURCES} ${pfMoviePlayer_HEADERS})
add_library(pfMoviePlayer STATIC ${pfMoviePlayer_SOURCES} ${pfMoviePlayer_HEADERS} ${pfMoviePlayer_WEBM})
source_group("Source Files" FILES ${pfMoviePlayer_SOURCES})
source_group("Header Files" FILES ${pfMoviePlayer_HEADERS})
source_group("WebM" FILES ${pfMoviePlayer_WEBM})

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

@ -42,25 +42,395 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "plMoviePlayer.h"
#ifdef MOVIE_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>
# define WEBM_CODECID_VP9 "V_VP9"
# define WEBM_CODECID_OPUS "A_OPUS"
#endif
#include "hsResMgr.h"
#include "hsTimer.h"
#include "plAudio/plWin32VideoSound.h"
#include "plGImage/plMipmap.h"
#include "pnKeyedObject/plUoid.h"
#include "plPipeline/hsGDeviceRef.h"
#include "plPipeline/plPlates.h"
#include "plResMgr/plLocalization.h"
#include "plStatusLog/plStatusLog.h"
#include "plPlanarImage.h"
#include "webm/mkvreader.hpp"
#include "webm/mkvparser.hpp"
#define SAFE_OP(x, err) \
{ \
int64_t ret = 0; \
ret = x; \
if (ret < 0) { \
hsAssert(false, "failed to " err); \
return false; \
} \
}
// =====================================================
class VPX
{
VPX() { }
#ifdef MOVIE_AVAILABLE
public:
vpx_codec_ctx_t codec;
~VPX()
{
if (vpx_codec_destroy(&codec))
hsAssert(false, vpx_codec_error_detail(&codec));
}
static VPX* Create()
{
VPX* instance = new VPX;
if (vpx_codec_dec_init(&instance->codec, iface, nullptr, 0)) {
hsAssert(false, vpx_codec_error_detail(&instance->codec));
delete instance;
return nullptr;
}
return instance;
}
vpx_image_t* Decode(uint8_t* buf, uint32_t size)
{
if (vpx_codec_decode(&codec, buf, size, nullptr, 0) != VPX_CODEC_OK) {
const char* detail = vpx_codec_error_detail(&codec);
hsAssert(false, detail ? detail : "unspecified decode error");
return nullptr;
}
vpx_codec_iter_t iter = nullptr;
// ASSUMPTION: only one image per frame
// if this proves false, move decoder function into IProcessVideoFrame
return vpx_codec_get_frame(&codec, &iter);
}
#endif
};
// =====================================================
class TrackMgr
{
protected:
const mkvparser::Track* fTrack;
const mkvparser::BlockEntry* fCurrentBlock;
int32_t fStatus;
public:
TrackMgr(const mkvparser::Track* track) : fTrack(track), fCurrentBlock(nullptr), fStatus(0) { }
const mkvparser::Track* GetTrack() { return fTrack; }
bool GetFrames(mkvparser::MkvReader* reader, int64_t movieTimeNs, std::vector<blkbuf_t>& frames)
{
// If we have no block yet, grab the first one
if (!fCurrentBlock)
fStatus = fTrack->GetFirst(fCurrentBlock);
// Continue through the blocks until our current movie time
while (fCurrentBlock && fStatus == 0) {
const mkvparser::Block* block = fCurrentBlock->GetBlock();
int64_t time = block->GetTime(fCurrentBlock->GetCluster()) - fTrack->GetCodecDelay();
if (time <= movieTimeNs) {
// We want to play this block, add it to the frames buffer
frames.reserve(frames.size() + block->GetFrameCount());
for (int32_t i = 0; i < block->GetFrameCount(); i++) {
const mkvparser::Block::Frame data = block->GetFrame(i);
uint8_t* buf = new uint8_t[data.len];
data.Read(reader, buf);
frames.push_back(std::make_tuple(std::unique_ptr<uint8_t>(buf), static_cast<int32_t>(data.len)));
}
fStatus = fTrack->GetNext(fCurrentBlock, fCurrentBlock);
} else {
// We've got all frames that have to play... come back for more later!
return true;
}
}
plMoviePlayer::plMoviePlayer() :
fTimeScale(0),
fStartTime(0)
return false; // No more blocks... We're done!
}
};
// =====================================================
plMoviePlayer::plMoviePlayer()
: fPlate(nullptr),
fTexture(nullptr),
fReader(nullptr),
fMovieTime(0),
fLastFrameTime(0),
fPosition(hsPoint2()),
fPlaying(false),
fPaused(false)
{
fScale.Set(1.0f, 1.0f);
}
int64_t plMoviePlayer::GetMovieTime() const
plMoviePlayer::~plMoviePlayer()
{
return ((int64_t) hsTimer::GetSeconds() * fTimeScale) - fStartTime;
if (fPlate)
// The plPlate owns the Mipmap Texture, so it destroys it for us
plPlateManager::Instance().DestroyPlate(fPlate);
#ifdef MOVIE_AVAILABLE
if (fReader) {
fReader->Close();
delete fReader;
}
#endif
}
bool plMoviePlayer::Stop()
bool plMoviePlayer::IOpenMovie()
{
for (int i = 0; i < fCallbacks.GetCount(); i++)
fCallbacks[i]->Send();
fCallbacks.Reset();
#ifdef MOVIE_AVAILABLE
if (!plFileInfo(fMoviePath).Exists()) {
hsAssert(false, "Tried to play a movie that doesn't exist");
return false;
}
// open the movie with libwebm
fReader = new mkvparser::MkvReader;
SAFE_OP(fReader->Open(fMoviePath.AsString().c_str()), "open movie");
// opens the segment
// it contains everything you ever want to know about the movie
long long pos = 0;
mkvparser::EBMLHeader ebmlHeader;
SAFE_OP(ebmlHeader.Parse(fReader, pos), "read mkv header");
mkvparser::Segment* seg;
SAFE_OP(mkvparser::Segment::CreateInstance(fReader, pos, seg), "get segment info");
SAFE_OP(seg->Load(), "load segment from webm");
fSegment.reset(seg);
// Use first tracks unless another one matches the current game language
const mkvparser::Tracks* tracks = fSegment->GetTracks();
for (uint32_t i = 0; i < tracks->GetTracksCount(); ++i) {
const mkvparser::Track* track = tracks->GetTrackByIndex(i);
if (!track)
continue;
switch (track->GetType()) {
case mkvparser::Track::kAudio:
if (!fAudioTrack || ICheckLanguage(track))
fAudioTrack.reset(new TrackMgr(track));
break;
case mkvparser::Track::kVideo:
if (!fVideoTrack || ICheckLanguage(track))
fVideoTrack.reset(new TrackMgr(track));
break;
}
}
return true;
#else
return false;
#endif
}
bool plMoviePlayer::ILoadAudio()
{
#ifdef MOVIE_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 (strncmp(audio->GetCodecId(), WEBM_CODECID_OPUS, arrsize(WEBM_CODECID_OPUS)) != 0) {
plStatusLog::AddLineS("movie.log", "%s: Not an Opus audio track!", fMoviePath.AsString().c_str());
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);
int16_t* frameData = new int16_t[maxFrameSize * audio->GetChannels()];
for (const auto& frame : frames) {
const std::unique_ptr<uint8_t>& buf = std::get<0>(frame);
int32_t size = std::get<1>(frame);
int samples = opus_decode(opus, buf.get(), size, frameData, maxFrameSize, 0);
if (samples < 0)
hsAssert(false, "opus error");
for (size_t i = 0; i < samples * audio->GetChannels(); i++)
decoded.push_back(frameData[i]);
}
delete[] frameData;
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)
{
auto codes = plLocalization::GetLanguageCodes(plLocalization::GetLanguage());
if (codes.find(track->GetLanguage()) != codes.end())
return true;
return false;
}
void plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames)
{
#ifdef MOVIE_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.
for (const auto& frame : frames) {
const std::unique_ptr<uint8_t>& buf = std::get<0>(frame);
uint32_t size = static_cast<uint32_t>(std::get<1>(frame));
img = fVpx->Decode(buf.get(), size);
}
if (img) {
// According to VideoLAN[1], I420 is the most common image format in videos. I am inclined to believe this as our
// attemps to convert the common Uru videos use I420 image data. So, as a shortcut, we will only implement that format.
// If for some reason we need other formats, please, be my guest!
// [1] = http://wiki.videolan.org/YUV#YUV_4:2:0_.28I420.2FJ420.2FYV12.29
switch (img->fmt) {
case VPX_IMG_FMT_I420:
plPlanarImage::Yuv420ToRgba(img->d_w, img->d_h, img->stride, img->planes, reinterpret_cast<uint8_t*>(fTexture->GetImage()));
break;
DEFAULT_FATAL("image format");
}
// Flush new data to the device
if (fTexture->GetDeviceRef())
fTexture->GetDeviceRef()->SetDirty(true);
fPlate->SetVisible(true);
}
#endif
}
bool plMoviePlayer::Start()
{
if (fPlaying)
return false;
#ifdef MOVIE_AVAILABLE
if (!IOpenMovie())
return false;
hsAssert(fVideoTrack, "nil video track -- expect bad things to happen!");
// Initialize VPX
const mkvparser::VideoTrack* video = static_cast<const mkvparser::VideoTrack*>(fVideoTrack->GetTrack());
if (strncmp(video->GetCodecId(), WEBM_CODECID_VP9, arrsize(WEBM_CODECID_VP9)) != 0) {
plStatusLog::AddLineS("movie.log", "%s: Not a VP9 video track!", fMoviePath.AsString().c_str());
return false;
}
if (VPX* vpx = VPX::Create())
fVpx.reset(vpx);
else
return false;
// Need to figure out scaling based on pipe size.
plPlateManager& plateMgr = plPlateManager::Instance();
float plateWidth = video->GetWidth() * fScale.fX;
float plateHeight = video->GetHeight() * fScale.fY;
if (plateWidth > plateMgr.GetPipeWidth() || plateHeight > plateMgr.GetPipeHeight()) {
float scale = std::min(plateMgr.GetPipeWidth() / plateWidth, plateMgr.GetPipeHeight() / plateHeight);
plateWidth *= scale;
plateHeight *= scale;
}
plateMgr.CreatePlate(&fPlate, fPosition.fX, fPosition.fY, 0, 0);
plateMgr.SetPlatePixelSize(fPlate, plateWidth, plateHeight);
fTexture = fPlate->CreateMaterial(static_cast<uint32_t>(video->GetWidth()), static_cast<uint32_t>(video->GetHeight()), false);
// Decode the audio track and load it into a sound buffer
if (!ILoadAudio())
return false;
fLastFrameTime = static_cast<int64_t>(hsTimer::GetMilliSeconds());
fAudioSound->Play();
fPlaying = true;
return true;
#else
return false;
#endif // MOVIE_AVAILABLE
}
bool plMoviePlayer::NextFrame()
{
if (!fPlaying)
return false;
int64_t frameTime = static_cast<int64_t>(hsTimer::GetMilliSeconds());
int64_t frameTimeDelta = frameTime - fLastFrameTime;
fLastFrameTime = frameTime;
if (fPaused)
return true;
#ifdef MOVIE_AVAILABLE
// Get our current timecode
fMovieTime += frameTimeDelta;
std::vector<blkbuf_t> video;
if (!fVideoTrack || !fVideoTrack->GetFrames(fReader, fMovieTime * 1000000, video)) {
Stop();
return false;
}
// Show our mess
IProcessVideoFrame(video);
fAudioSound->RefreshVolume();
return true;
#else
return false;
#endif // MOVIE_AVAILABLE
}
bool plMoviePlayer::Pause(bool on)
{
if (!fPlaying)
return false;
fAudioSound->Pause(on);
fPaused = on;
return true;
}
bool plMoviePlayer::Stop()
{
fPlaying = false;
if (fAudioSound)
fAudioSound->Stop();
if (fPlate)
fPlate->SetVisible(false);
for (auto cb : fCallbacks)
cb->Send();
fCallbacks.clear();
return true;
}

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

@ -49,27 +49,54 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "hsColorRGBA.h"
#include "plMessage/plMovieMsg.h"
#include <memory>
#include <vector>
#include <tuple>
namespace mkvparser
{
class BlockEntry;
class MkvReader;
class Segment;
class Track;
}
typedef std::tuple<std::unique_ptr<uint8_t>, int32_t> blkbuf_t;
class plMoviePlayer
{
protected:
int64_t fTimeScale, fStartTime;
class plPlate* fPlate;
class plMipmap* fTexture;
mkvparser::MkvReader* fReader;
std::unique_ptr<mkvparser::Segment> fSegment;
std::unique_ptr<class TrackMgr> fAudioTrack, fVideoTrack; // TODO: vector of tracks?
std::unique_ptr<class plWin32VideoSound> fAudioSound;
std::unique_ptr<class VPX> fVpx;
int64_t fMovieTime, fLastFrameTime; // in ms
hsPoint2 fPosition, fScale;
plFileName fMoviePath;
int64_t GetMovieTime() const;
bool IOpenMovie() { return false; };
bool fPlaying;
bool fPaused;
bool IOpenMovie();
bool ILoadAudio();
bool ICheckLanguage(const mkvparser::Track* track);
void IProcessVideoFrame(const std::vector<blkbuf_t>& frames);
public:
plMoviePlayer();
~plMoviePlayer() {}
~plMoviePlayer();
bool Start() { return false; }
bool Pause(bool on) { return false; }
bool Start();
bool Pause(bool on);
bool Stop();
bool NextFrame() { return Stop(); }
bool NextFrame();
void AddCallback(plMessage* msg) { hsRefCnt_SafeRef(msg); fCallbacks.Append(msg); }
void AddCallback(plMessage* msg) { hsRefCnt_SafeRef(msg); fCallbacks.push_back(msg); }
uint32_t GetNumCallbacks() const { return 0; }
plMessage* GetCallback(int i) const { return nullptr; }
@ -78,6 +105,8 @@ public:
void SetColor(const hsColorRGBA& c) { }
const hsColorRGBA GetColor() const { return hsColorRGBA(); }
/** The volume is handled by the options menu slider, as of now. */
void SetVolume(float v) { }
hsPoint2 GetPosition() const { return fPosition; }
@ -95,7 +124,7 @@ public:
void SetFadeToColor(hsColorRGBA c) { }
private:
hsTArray<plMessage*> fCallbacks;
std::vector<plMessage*> fCallbacks;
};
#endif // _plMoviePlayer_inc

97
Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.cpp

@ -0,0 +1,97 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plPlanarImage.h"
///////////////////////////////////////////////////////////////////////////////
static uint8_t Clip(int32_t val) {
if (val < 0) {
return 0;
} else if (val > 255) {
return 255;
}
return static_cast<uint8_t>(val);
}
#define YG 74 /* static_cast<int8>(1.164 * 64 + 0.5) */
#define UB 127 /* min(63,static_cast<int8>(2.018 * 64)) */
#define UG -25 /* static_cast<int8>(-0.391 * 64 - 0.5) */
#define UR 0
#define VB 0
#define VG -52 /* static_cast<int8>(-0.813 * 64 - 0.5) */
#define VR 102 /* static_cast<int8>(1.596 * 64 + 0.5) */
// Bias
#define BB UB * 128 + VB * 128
#define BG UG * 128 + VG * 128
#define BR UR * 128 + VR * 128
void plPlanarImage::Yuv420ToRgba(uint32_t w, uint32_t h, const int32_t* stride, uint8_t** planes, uint8_t* const dest)
{
const uint8_t* y_src = planes[0];
const uint8_t* u_src = planes[1];
const uint8_t* v_src = planes[2];
for (uint32_t i = 0; i < h; ++i)
{
for (uint32_t j = 0; j < w; ++j)
{
size_t y_idx = stride[0] * i + j;
size_t u_idx = stride[1] * (i/2) + (j/2);
size_t v_idx = stride[2] * (i/2) + (j/2);
size_t dest_idx = w * i + j;
int32_t y = static_cast<int32_t>(y_src[y_idx]);
int32_t u = static_cast<int32_t>(u_src[u_idx]);
int32_t v = static_cast<int32_t>(v_src[v_idx]);
int32_t y1 = (y - 16) * YG;
dest[dest_idx*4+0] = Clip(((u * UB + v * VB) - (BB) + y1) >> 6);
dest[dest_idx*4+1] = Clip(((u * UG + v * VG) - (BG) + y1) >> 6);
dest[dest_idx*4+2] = Clip(((u * UR + v * VR) - (BR) + y1) >> 6);
dest[dest_idx*4+3] = 0xff;
}
}
}

53
Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.h

@ -0,0 +1,53 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef _plPlanarImage_inc
#define _plPlanarImage_inc
#include "HeadSpin.h"
namespace plPlanarImage
{
void Yuv420ToRgba(uint32_t w, uint32_t h, const int32_t* stride, uint8_t** planes, uint8_t* const dest);
};
#endif // _plPlanarImage_inc

8354
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.cpp

File diff suppressed because it is too large Load Diff

945
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.hpp

@ -0,0 +1,945 @@
// Copyright (c) 2012 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVPARSER_HPP
#define MKVPARSER_HPP
#include <cstdlib>
#include <cstdio>
#include <cstddef>
namespace mkvparser {
const int E_FILE_FORMAT_INVALID = -2;
const int E_BUFFER_NOT_FULL = -3;
class IMkvReader {
public:
virtual int Read(long long pos, long len, unsigned char* buf) = 0;
virtual int Length(long long* total, long long* available) = 0;
protected:
virtual ~IMkvReader();
};
long long GetUIntLength(IMkvReader*, long long, long&);
long long ReadUInt(IMkvReader*, long long, long&);
long long UnserializeUInt(IMkvReader*, long long pos, long long size);
long UnserializeFloat(IMkvReader*, long long pos, long long size, double&);
long UnserializeInt(IMkvReader*, long long pos, long len, long long& result);
long UnserializeString(IMkvReader*, long long pos, long long size, char*& str);
long ParseElementHeader(IMkvReader* pReader,
long long& pos, // consume id and size fields
long long stop, // if you know size of element's parent
long long& id, long long& size);
bool Match(IMkvReader*, long long&, unsigned long, long long&);
bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
void GetVersion(int& major, int& minor, int& build, int& revision);
struct EBMLHeader {
EBMLHeader();
~EBMLHeader();
long long m_version;
long long m_readVersion;
long long m_maxIdLength;
long long m_maxSizeLength;
char* m_docType;
long long m_docTypeVersion;
long long m_docTypeReadVersion;
long long Parse(IMkvReader*, long long&);
void Init();
};
class Segment;
class Track;
class Cluster;
class Block {
Block(const Block&);
Block& operator=(const Block&);
public:
const long long m_start;
const long long m_size;
Block(long long start, long long size, long long discard_padding);
~Block();
long Parse(const Cluster*);
long long GetTrackNumber() const;
long long GetTimeCode(const Cluster*) const; // absolute, but not scaled
long long GetTime(const Cluster*) const; // absolute, and scaled (ns)
bool IsKey() const;
void SetKey(bool);
bool IsInvisible() const;
enum Lacing { kLacingNone, kLacingXiph, kLacingFixed, kLacingEbml };
Lacing GetLacing() const;
int GetFrameCount() const; // to index frames: [0, count)
struct Frame {
long long pos; // absolute offset
long len;
long Read(IMkvReader*, unsigned char*) const;
};
const Frame& GetFrame(int frame_index) const;
long long GetDiscardPadding() const;
private:
long long m_track; // Track::Number()
short m_timecode; // relative to cluster
unsigned char m_flags;
Frame* m_frames;
int m_frame_count;
protected:
const long long m_discard_padding;
};
class BlockEntry {
BlockEntry(const BlockEntry&);
BlockEntry& operator=(const BlockEntry&);
protected:
BlockEntry(Cluster*, long index);
public:
virtual ~BlockEntry();
bool EOS() const;
const Cluster* GetCluster() const;
long GetIndex() const;
virtual const Block* GetBlock() const = 0;
enum Kind { kBlockEOS, kBlockSimple, kBlockGroup };
virtual Kind GetKind() const = 0;
protected:
Cluster* const m_pCluster;
const long m_index;
};
class SimpleBlock : public BlockEntry {
SimpleBlock(const SimpleBlock&);
SimpleBlock& operator=(const SimpleBlock&);
public:
SimpleBlock(Cluster*, long index, long long start, long long size);
long Parse();
Kind GetKind() const;
const Block* GetBlock() const;
protected:
Block m_block;
};
class BlockGroup : public BlockEntry {
BlockGroup(const BlockGroup&);
BlockGroup& operator=(const BlockGroup&);
public:
BlockGroup(Cluster*, long index,
long long block_start, // absolute pos of block's payload
long long block_size, // size of block's payload
long long prev, long long next, long long duration,
long long discard_padding);
long Parse();
Kind GetKind() const;
const Block* GetBlock() const;
long long GetPrevTimeCode() const; // relative to block's time
long long GetNextTimeCode() const; // as above
long long GetDurationTimeCode() const;
private:
Block m_block;
const long long m_prev;
const long long m_next;
const long long m_duration;
};
///////////////////////////////////////////////////////////////
// ContentEncoding element
// Elements used to describe if the track data has been encrypted or
// compressed with zlib or header stripping.
class ContentEncoding {
public:
enum { kCTR = 1 };
ContentEncoding();
~ContentEncoding();
// ContentCompression element names
struct ContentCompression {
ContentCompression();
~ContentCompression();
unsigned long long algo;
unsigned char* settings;
long long settings_len;
};
// ContentEncAESSettings element names
struct ContentEncAESSettings {
ContentEncAESSettings() : cipher_mode(kCTR) {}
~ContentEncAESSettings() {}
unsigned long long cipher_mode;
};
// ContentEncryption element names
struct ContentEncryption {
ContentEncryption();
~ContentEncryption();
unsigned long long algo;
unsigned char* key_id;
long long key_id_len;
unsigned char* signature;
long long signature_len;
unsigned char* sig_key_id;
long long sig_key_id_len;
unsigned long long sig_algo;
unsigned long long sig_hash_algo;
ContentEncAESSettings aes_settings;
};
// Returns ContentCompression represented by |idx|. Returns NULL if |idx|
// is out of bounds.
const ContentCompression* GetCompressionByIndex(unsigned long idx) const;
// Returns number of ContentCompression elements in this ContentEncoding
// element.
unsigned long GetCompressionCount() const;
// Parses the ContentCompression element from |pReader|. |start| is the
// starting offset of the ContentCompression payload. |size| is the size in
// bytes of the ContentCompression payload. |compression| is where the parsed
// values will be stored.
long ParseCompressionEntry(long long start, long long size,
IMkvReader* pReader,
ContentCompression* compression);
// Returns ContentEncryption represented by |idx|. Returns NULL if |idx|
// is out of bounds.
const ContentEncryption* GetEncryptionByIndex(unsigned long idx) const;
// Returns number of ContentEncryption elements in this ContentEncoding
// element.
unsigned long GetEncryptionCount() const;
// Parses the ContentEncAESSettings element from |pReader|. |start| is the
// starting offset of the ContentEncAESSettings payload. |size| is the
// size in bytes of the ContentEncAESSettings payload. |encryption| is
// where the parsed values will be stored.
long ParseContentEncAESSettingsEntry(long long start, long long size,
IMkvReader* pReader,
ContentEncAESSettings* aes);
// Parses the ContentEncoding element from |pReader|. |start| is the
// starting offset of the ContentEncoding payload. |size| is the size in
// bytes of the ContentEncoding payload. Returns true on success.
long ParseContentEncodingEntry(long long start, long long size,
IMkvReader* pReader);
// Parses the ContentEncryption element from |pReader|. |start| is the
// starting offset of the ContentEncryption payload. |size| is the size in
// bytes of the ContentEncryption payload. |encryption| is where the parsed
// values will be stored.
long ParseEncryptionEntry(long long start, long long size,
IMkvReader* pReader, ContentEncryption* encryption);
unsigned long long encoding_order() const { return encoding_order_; }
unsigned long long encoding_scope() const { return encoding_scope_; }
unsigned long long encoding_type() const { return encoding_type_; }
private:
// Member variables for list of ContentCompression elements.
ContentCompression** compression_entries_;
ContentCompression** compression_entries_end_;
// Member variables for list of ContentEncryption elements.
ContentEncryption** encryption_entries_;
ContentEncryption** encryption_entries_end_;
// ContentEncoding element names
unsigned long long encoding_order_;
unsigned long long encoding_scope_;
unsigned long long encoding_type_;
// LIBWEBM_DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
ContentEncoding(const ContentEncoding&);
ContentEncoding& operator=(const ContentEncoding&);
};
class Track {
Track(const Track&);
Track& operator=(const Track&);
public:
class Info;
static long Create(Segment*, const Info&, long long element_start,
long long element_size, Track*&);
enum Type { kVideo = 1, kAudio = 2, kSubtitle = 0x11, kMetadata = 0x21 };
Segment* const m_pSegment;
const long long m_element_start;
const long long m_element_size;
virtual ~Track();
long GetType() const;
long GetNumber() const;
unsigned long long GetUid() const;
const char* GetNameAsUTF8() const;
const char* GetLanguage() const;
const char* GetCodecNameAsUTF8() const;
const char* GetCodecId() const;
const unsigned char* GetCodecPrivate(size_t&) const;
bool GetLacing() const;
unsigned long long GetDefaultDuration() const;
unsigned long long GetCodecDelay() const;
unsigned long long GetSeekPreRoll() const;
const BlockEntry* GetEOS() const;
struct Settings {
long long start;
long long size;
};
class Info {
public:
Info();
~Info();
int Copy(Info&) const;
void Clear();
long type;
long number;
unsigned long long uid;
unsigned long long defaultDuration;
unsigned long long codecDelay;
unsigned long long seekPreRoll;
char* nameAsUTF8;
char* language;
char* codecId;
char* codecNameAsUTF8;
unsigned char* codecPrivate;
size_t codecPrivateSize;
bool lacing;
Settings settings;
private:
Info(const Info&);
Info& operator=(const Info&);
int CopyStr(char* Info::*str, Info&) const;
};
long GetFirst(const BlockEntry*&) const;
long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
virtual bool VetEntry(const BlockEntry*) const;
virtual long Seek(long long time_ns, const BlockEntry*&) const;
const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
unsigned long GetContentEncodingCount() const;
long ParseContentEncodingsEntry(long long start, long long size);
protected:
Track(Segment*, long long element_start, long long element_size);
Info m_info;
class EOSBlock : public BlockEntry {
public:
EOSBlock();
Kind GetKind() const;
const Block* GetBlock() const;
};
EOSBlock m_eos;
private:
ContentEncoding** content_encoding_entries_;
ContentEncoding** content_encoding_entries_end_;
};
class VideoTrack : public Track {
VideoTrack(const VideoTrack&);
VideoTrack& operator=(const VideoTrack&);
VideoTrack(Segment*, long long element_start, long long element_size);
public:
static long Parse(Segment*, const Info&, long long element_start,
long long element_size, VideoTrack*&);
long long GetWidth() const;
long long GetHeight() const;
double GetFrameRate() const;
bool VetEntry(const BlockEntry*) const;
long Seek(long long time_ns, const BlockEntry*&) const;
private:
long long m_width;
long long m_height;
double m_rate;
};
class AudioTrack : public Track {
AudioTrack(const AudioTrack&);
AudioTrack& operator=(const AudioTrack&);
AudioTrack(Segment*, long long element_start, long long element_size);
public:
static long Parse(Segment*, const Info&, long long element_start,
long long element_size, AudioTrack*&);
double GetSamplingRate() const;
long long GetChannels() const;
long long GetBitDepth() const;
private:
double m_rate;
long long m_channels;
long long m_bitDepth;
};
class Tracks {
Tracks(const Tracks&);
Tracks& operator=(const Tracks&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
Tracks(Segment*, long long start, long long size, long long element_start,
long long element_size);
~Tracks();
long Parse();
unsigned long GetTracksCount() const;
const Track* GetTrackByNumber(long tn) const;
const Track* GetTrackByIndex(unsigned long idx) const;
private:
Track** m_trackEntries;
Track** m_trackEntriesEnd;
long ParseTrackEntry(long long payload_start, long long payload_size,
long long element_start, long long element_size,
Track*&) const;
};
class Chapters {
Chapters(const Chapters&);
Chapters& operator=(const Chapters&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
Chapters(Segment*, long long payload_start, long long payload_size,
long long element_start, long long element_size);
~Chapters();
long Parse();
class Atom;
class Edition;
class Display {
friend class Atom;
Display();
Display(const Display&);
~Display();
Display& operator=(const Display&);
public:
const char* GetString() const;
const char* GetLanguage() const;
const char* GetCountry() const;
private:
void Init();
void ShallowCopy(Display&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
char* m_string;
char* m_language;
char* m_country;
};
class Atom {
friend class Edition;
Atom();
Atom(const Atom&);
~Atom();
Atom& operator=(const Atom&);
public:
unsigned long long GetUID() const;
const char* GetStringUID() const;
long long GetStartTimecode() const;
long long GetStopTimecode() const;
long long GetStartTime(const Chapters*) const;
long long GetStopTime(const Chapters*) const;
int GetDisplayCount() const;
const Display* GetDisplay(int index) const;
private:
void Init();
void ShallowCopy(Atom&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
static long long GetTime(const Chapters*, long long timecode);
long ParseDisplay(IMkvReader*, long long pos, long long size);
bool ExpandDisplaysArray();
char* m_string_uid;
unsigned long long m_uid;
long long m_start_timecode;
long long m_stop_timecode;
Display* m_displays;
int m_displays_size;
int m_displays_count;
};
class Edition {
friend class Chapters;
Edition();
Edition(const Edition&);
~Edition();
Edition& operator=(const Edition&);
public:
int GetAtomCount() const;
const Atom* GetAtom(int index) const;
private:
void Init();
void ShallowCopy(Edition&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
long ParseAtom(IMkvReader*, long long pos, long long size);
bool ExpandAtomsArray();
Atom* m_atoms;
int m_atoms_size;
int m_atoms_count;
};
int GetEditionCount() const;
const Edition* GetEdition(int index) const;
private:
long ParseEdition(long long pos, long long size);
bool ExpandEditionsArray();
Edition* m_editions;
int m_editions_size;
int m_editions_count;
};
class SegmentInfo {
SegmentInfo(const SegmentInfo&);
SegmentInfo& operator=(const SegmentInfo&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
SegmentInfo(Segment*, long long start, long long size,
long long element_start, long long element_size);
~SegmentInfo();
long Parse();
long long GetTimeCodeScale() const;
long long GetDuration() const; // scaled
const char* GetMuxingAppAsUTF8() const;
const char* GetWritingAppAsUTF8() const;
const char* GetTitleAsUTF8() const;
private:
long long m_timecodeScale;
double m_duration;
char* m_pMuxingAppAsUTF8;
char* m_pWritingAppAsUTF8;
char* m_pTitleAsUTF8;
};
class SeekHead {
SeekHead(const SeekHead&);
SeekHead& operator=(const SeekHead&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
SeekHead(Segment*, long long start, long long size, long long element_start,
long long element_size);
~SeekHead();
long Parse();
struct Entry {
// the SeekHead entry payload
long long id;
long long pos;
// absolute pos of SeekEntry ID
long long element_start;
// SeekEntry ID size + size size + payload
long long element_size;
};
int GetCount() const;
const Entry* GetEntry(int idx) const;
struct VoidElement {
// absolute pos of Void ID
long long element_start;
// ID size + size size + payload size
long long element_size;
};
int GetVoidElementCount() const;
const VoidElement* GetVoidElement(int idx) const;
private:
Entry* m_entries;
int m_entry_count;
VoidElement* m_void_elements;
int m_void_element_count;
static bool ParseEntry(IMkvReader*,
long long pos, // payload
long long size, Entry*);
};
class Cues;
class CuePoint {
friend class Cues;
CuePoint(long, long long);
~CuePoint();
CuePoint(const CuePoint&);
CuePoint& operator=(const CuePoint&);
public:
long long m_element_start;
long long m_element_size;
void Load(IMkvReader*);
long long GetTimeCode() const; // absolute but unscaled
long long GetTime(const Segment*) const; // absolute and scaled (ns units)
struct TrackPosition {
long long m_track;
long long m_pos; // of cluster
long long m_block;
// codec_state //defaults to 0
// reference = clusters containing req'd referenced blocks
// reftime = timecode of the referenced block
void Parse(IMkvReader*, long long, long long);
};
const TrackPosition* Find(const Track*) const;
private:
const long m_index;
long long m_timecode;
TrackPosition* m_track_positions;
size_t m_track_positions_count;
};
class Cues {
friend class Segment;
Cues(Segment*, long long start, long long size, long long element_start,
long long element_size);
~Cues();
Cues(const Cues&);
Cues& operator=(const Cues&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
bool Find( // lower bound of time_ns
long long time_ns, const Track*, const CuePoint*&,
const CuePoint::TrackPosition*&) const;
#if 0
bool FindNext( //upper_bound of time_ns
long long time_ns,
const Track*,
const CuePoint*&,
const CuePoint::TrackPosition*&) const;
#endif
const CuePoint* GetFirst() const;
const CuePoint* GetLast() const;
const CuePoint* GetNext(const CuePoint*) const;
const BlockEntry* GetBlock(const CuePoint*,
const CuePoint::TrackPosition*) const;
bool LoadCuePoint() const;
long GetCount() const; // loaded only
// long GetTotal() const; //loaded + preloaded
bool DoneParsing() const;
private:
void Init() const;
void PreloadCuePoint(long&, long long) const;
mutable CuePoint** m_cue_points;
mutable long m_count;
mutable long m_preload_count;
mutable long long m_pos;
};
class Cluster {
friend class Segment;
Cluster(const Cluster&);
Cluster& operator=(const Cluster&);
public:
Segment* const m_pSegment;
public:
static Cluster* Create(Segment*,
long index, // index in segment
long long off); // offset relative to segment
// long long element_size);
Cluster(); // EndOfStream
~Cluster();
bool EOS() const;
long long GetTimeCode() const; // absolute, but not scaled
long long GetTime() const; // absolute, and scaled (nanosecond units)
long long GetFirstTime() const; // time (ns) of first (earliest) block
long long GetLastTime() const; // time (ns) of last (latest) block
long GetFirst(const BlockEntry*&) const;
long GetLast(const BlockEntry*&) const;
long GetNext(const BlockEntry* curr, const BlockEntry*& next) const;
const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
const BlockEntry* GetEntry(const CuePoint&,
const CuePoint::TrackPosition&) const;
// const BlockEntry* GetMaxKey(const VideoTrack*) const;
// static bool HasBlockEntries(const Segment*, long long);
static long HasBlockEntries(const Segment*, long long idoff, long long& pos,
long& size);
long GetEntryCount() const;
long Load(long long& pos, long& size) const;
long Parse(long long& pos, long& size) const;
long GetEntry(long index, const mkvparser::BlockEntry*&) const;
protected:
Cluster(Segment*, long index, long long element_start);
// long long element_size);
public:
const long long m_element_start;
long long GetPosition() const; // offset relative to segment
long GetIndex() const;
long long GetElementSize() const;
// long long GetPayloadSize() const;
// long long Unparsed() const;
private:
long m_index;
mutable long long m_pos;
// mutable long long m_size;
mutable long long m_element_size;
mutable long long m_timecode;
mutable BlockEntry** m_entries;
mutable long m_entries_size;
mutable long m_entries_count;
long ParseSimpleBlock(long long, long long&, long&);
long ParseBlockGroup(long long, long long&, long&);
long CreateBlock(long long id, long long pos, long long size,
long long discard_padding);
long CreateBlockGroup(long long start_offset, long long size,
long long discard_padding);
long CreateSimpleBlock(long long, long long);
};
class Segment {
friend class Cues;
friend class Track;
friend class VideoTrack;
Segment(const Segment&);
Segment& operator=(const Segment&);
private:
Segment(IMkvReader*, long long elem_start,
// long long elem_size,
long long pos, long long size);
public:
IMkvReader* const m_pReader;
const long long m_element_start;
// const long long m_element_size;
const long long m_start; // posn of segment payload
const long long m_size; // size of segment payload
Cluster m_eos; // TODO: make private?
static long long CreateInstance(IMkvReader*, long long, Segment*&);
~Segment();
long Load(); // loads headers and all clusters
// for incremental loading
// long long Unparsed() const;
bool DoneParsing() const;
long long ParseHeaders(); // stops when first cluster is found
// long FindNextCluster(long long& pos, long& size) const;
long LoadCluster(long long& pos, long& size); // load one cluster
long LoadCluster();
long ParseNext(const Cluster* pCurr, const Cluster*& pNext, long long& pos,
long& size);
#if 0
//This pair parses one cluster, but only changes the state of the
//segment object when the cluster is actually added to the index.
long ParseCluster(long long& cluster_pos, long long& new_pos) const;
bool AddCluster(long long cluster_pos, long long new_pos);
#endif
const SeekHead* GetSeekHead() const;
const Tracks* GetTracks() const;
const SegmentInfo* GetInfo() const;
const Cues* GetCues() const;
const Chapters* GetChapters() const;
long long GetDuration() const;
unsigned long GetCount() const;
const Cluster* GetFirst() const;
const Cluster* GetLast() const;
const Cluster* GetNext(const Cluster*);
const Cluster* FindCluster(long long time_nanoseconds) const;
// const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
const Cluster* FindOrPreloadCluster(long long pos);
long ParseCues(long long cues_off, // offset relative to start of segment
long long& parse_pos, long& parse_len);
private:
long long m_pos; // absolute file posn; what has been consumed so far
Cluster* m_pUnknownSize;
SeekHead* m_pSeekHead;
SegmentInfo* m_pInfo;
Tracks* m_pTracks;
Cues* m_pCues;
Chapters* m_pChapters;
Cluster** m_clusters;
long m_clusterCount; // number of entries for which m_index >= 0
long m_clusterPreloadCount; // number of entries for which m_index < 0
long m_clusterSize; // array size
long DoLoadCluster(long long&, long&);
long DoLoadClusterUnknownSize(long long&, long&);
long DoParseNext(const Cluster*&, long long&, long&);
void AppendCluster(Cluster*);
void PreloadCluster(Cluster*, ptrdiff_t);
// void ParseSeekHead(long long pos, long long size);
// void ParseSeekEntry(long long pos, long long size);
// void ParseCues(long long);
const BlockEntry* GetBlock(const CuePoint&, const CuePoint::TrackPosition&);
};
} // end namespace mkvparser
inline long mkvparser::Segment::LoadCluster() {
long long pos;
long size;
return LoadCluster(pos, size);
}
#endif // MKVPARSER_HPP

132
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.cpp

@ -0,0 +1,132 @@
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "mkvreader.hpp"
#include <cassert>
namespace mkvparser {
MkvReader::MkvReader() : m_file(NULL), reader_owns_file_(true) {}
MkvReader::MkvReader(FILE* fp) : m_file(fp), reader_owns_file_(false) {
GetFileSize();
}
MkvReader::~MkvReader() {
if (reader_owns_file_)
Close();
m_file = NULL;
}
int MkvReader::Open(const char* fileName) {
if (fileName == NULL)
return -1;
if (m_file)
return -1;
#ifdef _MSC_VER
const errno_t e = fopen_s(&m_file, fileName, "rb");
if (e)
return -1; // error
#else
m_file = fopen(fileName, "rb");
if (m_file == NULL)
return -1;
#endif
return !GetFileSize();
}
bool MkvReader::GetFileSize() {
if (m_file == NULL)
return false;
#ifdef _MSC_VER
int status = _fseeki64(m_file, 0L, SEEK_END);
if (status)
return false; // error
m_length = _ftelli64(m_file);
#else
fseek(m_file, 0L, SEEK_END);
m_length = ftell(m_file);
#endif
assert(m_length >= 0);
if (m_length < 0)
return false;
#ifdef _MSC_VER
status = _fseeki64(m_file, 0L, SEEK_SET);
if (status)
return false; // error
#else
fseek(m_file, 0L, SEEK_SET);
#endif
return true;
}
void MkvReader::Close() {
if (m_file != NULL) {
fclose(m_file);
m_file = NULL;
}
}
int MkvReader::Length(long long* total, long long* available) {
if (m_file == NULL)
return -1;
if (total)
*total = m_length;
if (available)
*available = m_length;
return 0;
}
int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
if (m_file == NULL)
return -1;
if (offset < 0)
return -1;
if (len < 0)
return -1;
if (len == 0)
return 0;
if (offset >= m_length)
return -1;
#ifdef _MSC_VER
const int status = _fseeki64(m_file, offset, SEEK_SET);
if (status)
return -1; // error
#else
fseek(m_file, offset, SEEK_SET);
#endif
const size_t size = fread(buffer, 1, len, m_file);
if (size < size_t(len))
return -1; // error
return 0; // success
}
} // end namespace mkvparser

45
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.hpp

@ -0,0 +1,45 @@
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef MKVREADER_HPP
#define MKVREADER_HPP
#include "mkvparser.hpp"
#include <cstdio>
namespace mkvparser {
class MkvReader : public IMkvReader {
public:
MkvReader();
explicit MkvReader(FILE* fp);
virtual ~MkvReader();
int Open(const char*);
void Close();
virtual int Read(long long position, long length, unsigned char* buffer);
virtual int Length(long long* total, long long* available);
private:
MkvReader(const MkvReader&);
MkvReader& operator=(const MkvReader&);
// Determines the size of the file. This is called either by the constructor
// or by the Open function depending on file ownership. Returns true on
// success.
bool GetFileSize();
long long m_length;
FILE* m_file;
bool reader_owns_file_;
};
} // end namespace mkvparser
#endif // MKVREADER_HPP

2
Sources/Plasma/PubUtilLib/plAudio/CMakeLists.txt

@ -23,6 +23,7 @@ set(plAudio_SOURCES
plWin32StaticSound.cpp
plWin32StreamingSound.cpp
plWinMicLevel.cpp
plWin32VideoSound.cpp
)
set(plAudio_HEADERS
@ -40,6 +41,7 @@ set(plAudio_HEADERS
plWin32StaticSound.h
plWin32StreamingSound.h
plWinMicLevel.h
plWin32VideoSound.h
)
add_library(plAudio STATIC ${plAudio_SOURCES} ${plAudio_HEADERS})

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

5
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

2
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp

@ -435,7 +435,7 @@ bool plVoiceSound::LoadSound( bool is3D )
header.fBitsPerSample = 16;
header.fNumChannels = 1;
header.fNumSamplesPerSec = FREQUENCY;
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2;
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 8;
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
fDSoundBuffer = new plDSoundBuffer(0, header, true, false, false, true);

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

@ -0,0 +1,119 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plWin32VideoSound.h"
#include "hsResMgr.h"
#include "plFormat.h"
#include "plDSoundBuffer.h"
static int uniqueID = 0;
plWin32VideoSound::plWin32VideoSound(const plWAVHeader& header) : plWin32Sound()
{
fCurrVolume = 1.0f;
fDesiredVol = 1.0f;
fSoftVolume = 1.0f;
fType = kGUISound;
fWAVHeader = header;
fDSoundBuffer = new plDSoundBuffer(0, fWAVHeader, false, false);
uniqueID++;
hsgResMgr::ResMgr()->NewKey(plFormat("videosound#{}", uniqueID), this, plLocation::kGlobalFixedLoc);
}
plWin32VideoSound::~plWin32VideoSound()
{
if (fDSoundBuffer)
delete fDSoundBuffer;
}
void plWin32VideoSound::Play()
{
IActuallyPlay();
}
void plWin32VideoSound::Pause(bool on)
{
if (on)
fDSoundBuffer->Pause();
else if (!fReallyPlaying)
fDSoundBuffer->Play();
fReallyPlaying = !on;
}
void plWin32VideoSound::FillSoundBuffer(void* buffer, size_t size)
{
fDSoundBuffer->FillBuffer(buffer, size, &fWAVHeader);
fDSoundBuffer->SetScalarVolume(1.0f);
}
void plWin32VideoSound::IDerivedActuallyPlay()
{
if (!fReallyPlaying) {
fDSoundBuffer->Play();
fReallyPlaying = true;
}
}
bool plWin32VideoSound::LoadSound(bool is3D)
{
hsAssert(false, "unimplemented cause unnecessary for this class");
return false;
}
void plWin32VideoSound::SetStartPos(unsigned bytes)
{
//do nothing
hsAssert(false, "unimplemented cause unnecessary for this class");
}
float plWin32VideoSound::GetActualTimeSec()
{
hsAssert(false, "unimplemented cause unnecessary for this class");
return 0;
}
void plWin32VideoSound::ISetActualTime(double t)
{
hsAssert(false, "unimplemented cause unnecessary for this class");
}

67
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h

@ -0,0 +1,67 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWin32VideoSound_h
#define plWin32VideoSound_h
#include "plWin32Sound.h"
class plWin32VideoSound : public plWin32Sound
{
public:
plWin32VideoSound(const plWAVHeader& header);
virtual ~plWin32VideoSound();
virtual void Play();
virtual void Pause(bool on);
void FillSoundBuffer(void* buffer, size_t size);
protected:
void IDerivedActuallyPlay(void);
bool LoadSound(bool is3D);
void SetStartPos(unsigned bytes);
float GetActualTimeSec();
void ISetActualTime(double t);
plWAVHeader fWAVHeader;
};
#endif

2
Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h

@ -155,7 +155,7 @@ public:
plMovieMsg& SetScaleX(float x) { fScale.fX = x; return *this; }
plMovieMsg& SetScaleY(float y) { fScale.fY = y; return *this; }
// Include the movie folder, e.g. "avi/movie.avi"
// Include the movie folder, e.g. "avi/movie.webm"
// String is copied, not pointer copy.
plString GetFileName() const { return fFileName; }
plMovieMsg& SetFileName(const plString& name) { fFileName = name; return *this; }

11
Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp

@ -56,6 +56,17 @@ const char* plLocalization::fLangTags[] =
};
const int kLangTagLen = 4;
// ISO 639, e.g. used in video tracks
std::set<plString> plLocalization::fLangCodes[] =
{
{"eng", "en"},
{"fre", "fra", "fr"},
{"ger", "deu", "de"},
{"spa", "es"},
{"ita", "it"},
{"jpn", "ja"}
};
const char* plLocalization::fLangNames[] =
{
"English", // kEnglish

3
Sources/Plasma/PubUtilLib/plResMgr/plLocalization.h

@ -44,6 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include <vector>
#include <string>
#include <set>
#include "plFileSystem.h"
class plLocalization
@ -78,6 +79,7 @@ public:
protected:
static Language fLanguage;
static const char* fLangTags[kNumLanguages];
static std::set<plString> fLangCodes[kNumLanguages];
static const char* fLangNames[kNumLanguages];
static bool fUsesUnicode[kNumLanguages];
static encodingTypes fUnicodeEncoding[kNumLanguages];
@ -89,6 +91,7 @@ public:
static Language GetLanguage() { return fLanguage; }
static const char* GetLanguageName(Language lang) { return fLangNames[lang]; }
static std::set<plString> GetLanguageCodes(Language lang) { return fLangCodes[lang]; }
static bool UsingUnicode() { return fUsesUnicode[fLanguage]; }
static encodingTypes UnicodeEncoding() { return fUnicodeEncoding[fLanguage]; }

37
cmake/FindOpus.cmake

@ -0,0 +1,37 @@
if(Opus_INCLUDE_DIR AND Opus_LIBRARY)
set(Opus_FIND_QUIETLY TRUE)
endif()
find_path(Opus_INCLUDE_DIR opus.h
/usr/local/include
/usr/include
)
find_library(Opus_LIBRARY NAMES opus
PATHS /usr/local/lib /usr/lib)
find_library(Celt_LIBRARY NAMES celt
PATHS /usr/local/lib /usr/lib)
find_library(Silk_LIBRARY NAMES silk_common
PATHS /usr/local/lib /usr/lib)
set(Opus_LIBRARIES
${Opus_LIBRARY}
${Celt_LIBRARY}
${Silk_LIBRARY}
)
if(Opus_INCLUDE_DIR AND Opus_LIBRARY)
set(Opus_FOUND TRUE)
endif()
if (Opus_FOUND)
if(NOT Opus_FIND_QUIETLY)
message(STATUS "Found libopus: ${Opus_INCLUDE_DIR}")
endif()
else()
if(Opus_FIND_REQUIRED)
message(FATAL_ERROR "Could not find libopus")
endif()
endif()

16
cmake/FindVPX.cmake

@ -0,0 +1,16 @@
if(VPX_INCLUDE_DIR AND VPX_LIBRARY)
set(VPX_FIND_QUIETLY TRUE)
endif()
find_path(VPX_INCLUDE_DIR vpx/vp8.h
/usr/local/include
/usr/include
)
find_library(VPX_LIBRARY NAMES vpxmt vpxmd vpx
PATHS /usr/local/lib /usr/lib
)
if(VPX_INCLUDE_DIR AND VPX_LIBRARY)
set(VPX_FOUND TRUE)
endif()
Loading…
Cancel
Save