mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-18 11:19:10 +00:00
@ -41,6 +41,8 @@ find_package(PNG REQUIRED)
|
|||||||
find_package(Ogg REQUIRED) #TODO: Not required if we aren't building the client
|
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(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(Speex REQUIRED) #TODO: Not required if we aren't building the client
|
||||||
|
find_package(VPX)
|
||||||
|
find_package(Opus)
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
find_package(Freetype)
|
find_package(Freetype)
|
||||||
|
|
||||||
@ -49,6 +51,11 @@ if(WIN32)
|
|||||||
find_package(DirectX REQUIRED)
|
find_package(DirectX REQUIRED)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
|
if(VPX_FOUND AND Opus_FOUND)
|
||||||
|
set(MOVIE_AVAILABLE TRUE)
|
||||||
|
add_definitions(-DMOVIE_AVAILABLE)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(PrecompiledHeader) #Precompiled Header helper macros
|
include(PrecompiledHeader) #Precompiled Header helper macros
|
||||||
|
|
||||||
# libCurl isn't smart enough to detect this for us, so we have to configure it ourselves
|
# libCurl isn't smart enough to detect this for us, so we have to configure it ourselves
|
||||||
|
@ -12,6 +12,11 @@ include_directories(${OPENSSL_INCLUDE_DIR})
|
|||||||
include_directories(${PYTHON_INCLUDE_DIR})
|
include_directories(${PYTHON_INCLUDE_DIR})
|
||||||
include_directories(${CURL_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
|
# Test for Python Interpreter, which will be used for extra build scripts if available
|
||||||
find_package(PythonInterp)
|
find_package(PythonInterp)
|
||||||
if(PYTHONINTERP_FOUND)
|
if(PYTHONINTERP_FOUND)
|
||||||
@ -167,6 +172,11 @@ if(USE_VLD)
|
|||||||
target_link_libraries(plClient ${VLD_LIBRARY})
|
target_link_libraries(plClient ${VLD_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(MOVIE_AVAILABLE)
|
||||||
|
target_link_libraries(plClient ${VPX_LIBRARY})
|
||||||
|
target_link_libraries(plClient ${Opus_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(plClient rpcrt4)
|
target_link_libraries(plClient rpcrt4)
|
||||||
target_link_libraries(plClient version)
|
target_link_libraries(plClient version)
|
||||||
|
@ -825,18 +825,18 @@ bool plClient::IHandleMovieMsg(plMovieMsg* mov)
|
|||||||
if (mov->GetFileName().IsEmpty())
|
if (mov->GetFileName().IsEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
int i = fMovies.GetCount();
|
size_t i = fMovies.size();
|
||||||
if (!(mov->GetCmd() & plMovieMsg::kMake))
|
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)
|
if (mov->GetFileName().CompareI(fMovies[i]->GetFileName().AsString()) == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == fMovies.GetCount())
|
if (i == fMovies.size())
|
||||||
{
|
{
|
||||||
fMovies.Append(new plMoviePlayer);
|
fMovies.push_back(new plMoviePlayer());
|
||||||
fMovies[i]->SetFileName(mov->GetFileName());
|
fMovies[i]->SetFileName(mov->GetFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,7 +892,8 @@ bool plClient::IHandleMovieMsg(plMovieMsg* mov)
|
|||||||
if (!fMovies[i]->GetFileName().IsValid())
|
if (!fMovies[i]->GetFileName().IsValid())
|
||||||
{
|
{
|
||||||
delete fMovies[i];
|
delete fMovies[i];
|
||||||
fMovies.Remove(i);
|
fMovies[i] = fMovies.back();
|
||||||
|
fMovies.pop_back();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1436,10 +1437,6 @@ bool plClient::StartInit()
|
|||||||
|
|
||||||
plgAudioSys::Activate(true);
|
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
|
// Init Net before loading things
|
||||||
//
|
//
|
||||||
@ -1453,7 +1450,7 @@ bool plClient::StartInit()
|
|||||||
pModMsg2->SetCmd(plCmdIfaceModMsg::kAdd);
|
pModMsg2->SetCmd(plCmdIfaceModMsg::kAdd);
|
||||||
plgDispatch::MsgSend(pModMsg2);
|
plgDispatch::MsgSend(pModMsg2);
|
||||||
|
|
||||||
// create new the virtual camera
|
// create new virtual camera
|
||||||
fNewCamera = new plVirtualCam1;
|
fNewCamera = new plVirtualCam1;
|
||||||
fNewCamera->RegisterAs( kVirtualCamera1_KEY );
|
fNewCamera->RegisterAs( kVirtualCamera1_KEY );
|
||||||
fNewCamera->Init();
|
fNewCamera->Init();
|
||||||
@ -1464,6 +1461,10 @@ bool plClient::StartInit()
|
|||||||
plMouseDevice::Instance()->SetDisplayResolution((float)fPipeline->Width(), (float)fPipeline->Height());
|
plMouseDevice::Instance()->SetDisplayResolution((float)fPipeline->Width(), (float)fPipeline->Height());
|
||||||
plInputManager::SetRecenterMouse(false);
|
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:
|
// create the listener for the audio system:
|
||||||
plListener* pLMod = new plListener;
|
plListener* pLMod = new plListener;
|
||||||
pLMod->RegisterAs(kListenerMod_KEY );
|
pLMod->RegisterAs(kListenerMod_KEY );
|
||||||
@ -1827,13 +1828,13 @@ bool plClient::IDraw()
|
|||||||
|
|
||||||
void plClient::IServiceMovies()
|
void plClient::IServiceMovies()
|
||||||
{
|
{
|
||||||
int i;
|
for (size_t i = 0; i < fMovies.size(); i++)
|
||||||
for (i = 0; i < fMovies.GetCount(); i++)
|
|
||||||
{
|
{
|
||||||
if (!fMovies[i]->NextFrame())
|
if (!fMovies[i]->NextFrame())
|
||||||
{
|
{
|
||||||
delete fMovies[i];
|
delete fMovies[i];
|
||||||
fMovies.Remove(i);
|
fMovies[i] = fMovies.back();
|
||||||
|
fMovies.pop_back();
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1841,9 +1842,9 @@ void plClient::IServiceMovies()
|
|||||||
|
|
||||||
void plClient::IKillMovies()
|
void plClient::IKillMovies()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < fMovies.GetCount(); i++)
|
for (size_t i = 0; i < fMovies.size(); i++)
|
||||||
delete fMovies[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 */)
|
bool plClient::IPlayIntroMovie(const char* movieName, float endDelay, float posX, float posY, float scaleX, float scaleY, float volume /* = 1.0 */)
|
||||||
|
@ -147,7 +147,7 @@ protected:
|
|||||||
int fQuality;
|
int fQuality;
|
||||||
|
|
||||||
bool fQuitIntro;
|
bool fQuitIntro;
|
||||||
hsTArray<plMoviePlayer*> fMovies;
|
std::vector<plMoviePlayer*> fMovies;
|
||||||
|
|
||||||
plMessagePumpProc fMessagePumpProc;
|
plMessagePumpProc fMessagePumpProc;
|
||||||
|
|
||||||
|
@ -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 //
|
// tint=yes/no - Defines whether or not this decal is tinted //
|
||||||
// with the cover. Overridden by the tintfirst //
|
// with the cover. Overridden by the tintfirst //
|
||||||
// option on the <cover> tag. Defaults to no //
|
// 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 //
|
// src=<movie name> - Selects the movie to be used. (nead search //
|
||||||
// methods here eventually) //
|
// methods here eventually) //
|
||||||
// align=left/right/center //
|
// align=left/right/center //
|
||||||
|
@ -3,15 +3,30 @@ include_directories(../../NucleusLib)
|
|||||||
include_directories(../../NucleusLib/inc)
|
include_directories(../../NucleusLib/inc)
|
||||||
include_directories(../../PubUtilLib)
|
include_directories(../../PubUtilLib)
|
||||||
|
|
||||||
|
if(MOVIE_AVAILABLE)
|
||||||
|
include_directories(${VPX_INCLUDE_DIR})
|
||||||
|
include_directories(${Opus_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
set(pfMoviePlayer_SOURCES
|
set(pfMoviePlayer_SOURCES
|
||||||
plMoviePlayer.cpp
|
plMoviePlayer.cpp
|
||||||
|
plPlanarImage.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(pfMoviePlayer_HEADERS
|
set(pfMoviePlayer_HEADERS
|
||||||
plMoviePlayer.h
|
plMoviePlayer.h
|
||||||
|
plPlanarImage.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(pfMoviePlayer STATIC ${pfMoviePlayer_SOURCES} ${pfMoviePlayer_HEADERS})
|
set(pfMoviePlayer_WEBM
|
||||||
|
webm/mkvparser.cpp
|
||||||
|
webm/mkvparser.hpp
|
||||||
|
webm/mkvreader.cpp
|
||||||
|
webm/mkvreader.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(pfMoviePlayer STATIC ${pfMoviePlayer_SOURCES} ${pfMoviePlayer_HEADERS} ${pfMoviePlayer_WEBM})
|
||||||
|
|
||||||
source_group("Source Files" FILES ${pfMoviePlayer_SOURCES})
|
source_group("Source Files" FILES ${pfMoviePlayer_SOURCES})
|
||||||
source_group("Header Files" FILES ${pfMoviePlayer_HEADERS})
|
source_group("Header Files" FILES ${pfMoviePlayer_HEADERS})
|
||||||
|
source_group("WebM" FILES ${pfMoviePlayer_WEBM})
|
||||||
|
@ -42,25 +42,395 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|||||||
|
|
||||||
#include "plMoviePlayer.h"
|
#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 "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"
|
||||||
|
|
||||||
plMoviePlayer::plMoviePlayer() :
|
#define SAFE_OP(x, err) \
|
||||||
fTimeScale(0),
|
{ \
|
||||||
fStartTime(0)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
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::IOpenMovie()
|
||||||
|
{
|
||||||
|
#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()
|
bool plMoviePlayer::Stop()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < fCallbacks.GetCount(); i++)
|
fPlaying = false;
|
||||||
fCallbacks[i]->Send();
|
if (fAudioSound)
|
||||||
fCallbacks.Reset();
|
fAudioSound->Stop();
|
||||||
return false;
|
if (fPlate)
|
||||||
|
fPlate->SetVisible(false);
|
||||||
|
|
||||||
|
for (auto cb : fCallbacks)
|
||||||
|
cb->Send();
|
||||||
|
fCallbacks.clear();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -49,27 +49,54 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|||||||
#include "hsColorRGBA.h"
|
#include "hsColorRGBA.h"
|
||||||
#include "plMessage/plMovieMsg.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
|
class plMoviePlayer
|
||||||
{
|
{
|
||||||
protected:
|
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;
|
hsPoint2 fPosition, fScale;
|
||||||
plFileName fMoviePath;
|
plFileName fMoviePath;
|
||||||
|
|
||||||
int64_t GetMovieTime() const;
|
bool fPlaying;
|
||||||
bool IOpenMovie() { return false; };
|
bool fPaused;
|
||||||
|
|
||||||
|
bool IOpenMovie();
|
||||||
|
bool ILoadAudio();
|
||||||
|
bool ICheckLanguage(const mkvparser::Track* track);
|
||||||
|
void IProcessVideoFrame(const std::vector<blkbuf_t>& frames);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
plMoviePlayer();
|
plMoviePlayer();
|
||||||
~plMoviePlayer() {}
|
~plMoviePlayer();
|
||||||
|
|
||||||
bool Start() { return false; }
|
bool Start();
|
||||||
bool Pause(bool on) { return false; }
|
bool Pause(bool on);
|
||||||
bool Stop();
|
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; }
|
uint32_t GetNumCallbacks() const { return 0; }
|
||||||
plMessage* GetCallback(int i) const { return nullptr; }
|
plMessage* GetCallback(int i) const { return nullptr; }
|
||||||
|
|
||||||
@ -78,6 +105,8 @@ public:
|
|||||||
|
|
||||||
void SetColor(const hsColorRGBA& c) { }
|
void SetColor(const hsColorRGBA& c) { }
|
||||||
const hsColorRGBA GetColor() const { return hsColorRGBA(); }
|
const hsColorRGBA GetColor() const { return hsColorRGBA(); }
|
||||||
|
|
||||||
|
/** The volume is handled by the options menu slider, as of now. */
|
||||||
void SetVolume(float v) { }
|
void SetVolume(float v) { }
|
||||||
|
|
||||||
hsPoint2 GetPosition() const { return fPosition; }
|
hsPoint2 GetPosition() const { return fPosition; }
|
||||||
@ -95,7 +124,7 @@ public:
|
|||||||
void SetFadeToColor(hsColorRGBA c) { }
|
void SetFadeToColor(hsColorRGBA c) { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
hsTArray<plMessage*> fCallbacks;
|
std::vector<plMessage*> fCallbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _plMoviePlayer_inc
|
#endif // _plMoviePlayer_inc
|
||||||
|
97
Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.cpp
Normal file
97
Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.cpp
Normal file
@ -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
Normal file
53
Sources/Plasma/FeatureLib/pfMoviePlayer/plPlanarImage.h
Normal file
@ -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
Normal file
8354
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
945
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.hpp
Normal file
945
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvparser.hpp
Normal file
@ -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
Normal file
132
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.cpp
Normal file
@ -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
Normal file
45
Sources/Plasma/FeatureLib/pfMoviePlayer/webm/mkvreader.hpp
Normal file
@ -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
|
@ -23,6 +23,7 @@ set(plAudio_SOURCES
|
|||||||
plWin32StaticSound.cpp
|
plWin32StaticSound.cpp
|
||||||
plWin32StreamingSound.cpp
|
plWin32StreamingSound.cpp
|
||||||
plWinMicLevel.cpp
|
plWinMicLevel.cpp
|
||||||
|
plWin32VideoSound.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(plAudio_HEADERS
|
set(plAudio_HEADERS
|
||||||
@ -40,6 +41,7 @@ set(plAudio_HEADERS
|
|||||||
plWin32StaticSound.h
|
plWin32StaticSound.h
|
||||||
plWin32StreamingSound.h
|
plWin32StreamingSound.h
|
||||||
plWinMicLevel.h
|
plWinMicLevel.h
|
||||||
|
plWin32VideoSound.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(plAudio STATIC ${plAudio_SOURCES} ${plAudio_HEADERS})
|
add_library(plAudio STATIC ${plAudio_SOURCES} ${plAudio_HEADERS})
|
||||||
|
@ -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 )
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ bool plVoiceSound::LoadSound( bool is3D )
|
|||||||
header.fBitsPerSample = 16;
|
header.fBitsPerSample = 16;
|
||||||
header.fNumChannels = 1;
|
header.fNumChannels = 1;
|
||||||
header.fNumSamplesPerSec = FREQUENCY;
|
header.fNumSamplesPerSec = FREQUENCY;
|
||||||
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2;
|
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 8;
|
||||||
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
|
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
|
||||||
|
|
||||||
fDSoundBuffer = new plDSoundBuffer(0, header, true, false, false, true);
|
fDSoundBuffer = new plDSoundBuffer(0, header, true, false, false, true);
|
||||||
|
119
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
Normal file
119
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
Normal file
@ -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
Normal file
67
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h
Normal file
@ -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
|
@ -155,7 +155,7 @@ public:
|
|||||||
plMovieMsg& SetScaleX(float x) { fScale.fX = x; return *this; }
|
plMovieMsg& SetScaleX(float x) { fScale.fX = x; return *this; }
|
||||||
plMovieMsg& SetScaleY(float y) { fScale.fY = y; 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.
|
// String is copied, not pointer copy.
|
||||||
plString GetFileName() const { return fFileName; }
|
plString GetFileName() const { return fFileName; }
|
||||||
plMovieMsg& SetFileName(const plString& name) { fFileName = name; return *this; }
|
plMovieMsg& SetFileName(const plString& name) { fFileName = name; return *this; }
|
||||||
|
@ -56,6 +56,17 @@ const char* plLocalization::fLangTags[] =
|
|||||||
};
|
};
|
||||||
const int kLangTagLen = 4;
|
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[] =
|
const char* plLocalization::fLangNames[] =
|
||||||
{
|
{
|
||||||
"English", // kEnglish
|
"English", // kEnglish
|
||||||
|
@ -44,6 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <set>
|
||||||
#include "plFileSystem.h"
|
#include "plFileSystem.h"
|
||||||
|
|
||||||
class plLocalization
|
class plLocalization
|
||||||
@ -78,6 +79,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
static Language fLanguage;
|
static Language fLanguage;
|
||||||
static const char* fLangTags[kNumLanguages];
|
static const char* fLangTags[kNumLanguages];
|
||||||
|
static std::set<plString> fLangCodes[kNumLanguages];
|
||||||
static const char* fLangNames[kNumLanguages];
|
static const char* fLangNames[kNumLanguages];
|
||||||
static bool fUsesUnicode[kNumLanguages];
|
static bool fUsesUnicode[kNumLanguages];
|
||||||
static encodingTypes fUnicodeEncoding[kNumLanguages];
|
static encodingTypes fUnicodeEncoding[kNumLanguages];
|
||||||
@ -89,6 +91,7 @@ public:
|
|||||||
static Language GetLanguage() { return fLanguage; }
|
static Language GetLanguage() { return fLanguage; }
|
||||||
|
|
||||||
static const char* GetLanguageName(Language lang) { return fLangNames[lang]; }
|
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 bool UsingUnicode() { return fUsesUnicode[fLanguage]; }
|
||||||
static encodingTypes UnicodeEncoding() { return fUnicodeEncoding[fLanguage]; }
|
static encodingTypes UnicodeEncoding() { return fUnicodeEncoding[fLanguage]; }
|
||||||
|
37
cmake/FindOpus.cmake
Normal file
37
cmake/FindOpus.cmake
Normal file
@ -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
Normal file
16
cmake/FindVPX.cmake
Normal file
@ -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()
|
Reference in New Issue
Block a user