|
|
|
@ -42,7 +42,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
|
|
|
|
|
|
#include "plMoviePlayer.h" |
|
|
|
|
|
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
# define VPX_CODEC_DISABLE_COMPAT 1 |
|
|
|
|
# include <vpx/vpx_decoder.h> |
|
|
|
|
# include <vpx/vp8dx.h> |
|
|
|
@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
|
#include "plPipeline/hsGDeviceRef.h" |
|
|
|
|
#include "plPipeline/plPlates.h" |
|
|
|
|
#include "plResMgr/plLocalization.h" |
|
|
|
|
#include "plStatusLog/plStatusLog.h" |
|
|
|
|
|
|
|
|
|
#include "plPlanarImage.h" |
|
|
|
|
#include "webm/mkvreader.hpp" |
|
|
|
@ -83,11 +84,12 @@ class VPX
|
|
|
|
|
{ |
|
|
|
|
VPX() { } |
|
|
|
|
|
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
public: |
|
|
|
|
vpx_codec_ctx_t codec; |
|
|
|
|
|
|
|
|
|
~VPX() { |
|
|
|
|
~VPX() |
|
|
|
|
{ |
|
|
|
|
if (vpx_codec_destroy(&codec)) |
|
|
|
|
hsAssert(false, vpx_codec_error_detail(&codec)); |
|
|
|
|
} |
|
|
|
@ -190,7 +192,7 @@ plMoviePlayer::~plMoviePlayer()
|
|
|
|
|
if (fPlate) |
|
|
|
|
// The plPlate owns the Mipmap Texture, so it destroys it for us
|
|
|
|
|
plPlateManager::Instance().DestroyPlate(fPlate); |
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
if (fReader) |
|
|
|
|
{ |
|
|
|
|
fReader->Close(); |
|
|
|
@ -201,7 +203,7 @@ plMoviePlayer::~plMoviePlayer()
|
|
|
|
|
|
|
|
|
|
bool plMoviePlayer::IOpenMovie() |
|
|
|
|
{ |
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
if (!plFileInfo(fMoviePath).Exists()) |
|
|
|
|
{ |
|
|
|
|
hsAssert(false, "Tried to play a movie that doesn't exist"); |
|
|
|
@ -254,7 +256,7 @@ bool plMoviePlayer::IOpenMovie()
|
|
|
|
|
|
|
|
|
|
bool plMoviePlayer::ILoadAudio() |
|
|
|
|
{ |
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
// Fetch audio track information
|
|
|
|
|
const mkvparser::AudioTrack* audio = static_cast<const mkvparser::AudioTrack*>(fAudioTrack->GetTrack()); |
|
|
|
|
plWAVHeader header; |
|
|
|
@ -269,7 +271,7 @@ bool plMoviePlayer::ILoadAudio()
|
|
|
|
|
// Initialize Opus
|
|
|
|
|
if (strcmp(audio->GetCodecId(), WEBM_CODECID_OPUS) != 0) |
|
|
|
|
{ |
|
|
|
|
hsAssert(false, "Not an Opus audio track!"); |
|
|
|
|
plStatusLog::AddLineS("movie.log", "%s: Not an Opus audio track!", fMoviePath.AsString().c_str()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
int error; |
|
|
|
@ -284,19 +286,19 @@ bool plMoviePlayer::ILoadAudio()
|
|
|
|
|
std::vector<int16_t> decoded; |
|
|
|
|
decoded.reserve(frames.size() * audio->GetChannels() * maxFrameSize); |
|
|
|
|
|
|
|
|
|
for (auto it = frames.begin(); it != frames.end(); ++it) |
|
|
|
|
int16_t* frameData = new int16_t[maxFrameSize * audio->GetChannels()]; |
|
|
|
|
for (const auto& frame : frames) |
|
|
|
|
{ |
|
|
|
|
const std::unique_ptr<uint8_t>& buf = std::get<0>(*it); |
|
|
|
|
int32_t size = std::get<1>(*it); |
|
|
|
|
const std::unique_ptr<uint8_t>& buf = std::get<0>(frame); |
|
|
|
|
int32_t size = std::get<1>(frame); |
|
|
|
|
|
|
|
|
|
int16_t* pcm = new int16_t[maxFrameSize * audio->GetChannels()]; |
|
|
|
|
int samples = opus_decode(opus, buf.get(), size, pcm, maxFrameSize, 0); |
|
|
|
|
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(pcm[i]); |
|
|
|
|
delete[] pcm; |
|
|
|
|
decoded.push_back(frameData[i]); |
|
|
|
|
} |
|
|
|
|
delete[] frameData; |
|
|
|
|
|
|
|
|
|
fAudioSound->FillSoundBuffer(reinterpret_cast<uint8_t*>(decoded.data()), decoded.size() * sizeof(int16_t)); |
|
|
|
|
opus_decoder_destroy(opus); |
|
|
|
@ -316,15 +318,15 @@ bool plMoviePlayer::ICheckLanguage(const mkvparser::Track* track)
|
|
|
|
|
|
|
|
|
|
void plMoviePlayer::IProcessVideoFrame(const std::vector<blkbuf_t>& frames) |
|
|
|
|
{ |
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#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 (auto it = frames.begin(); it != frames.end(); ++it) |
|
|
|
|
for (const auto& frame : frames) |
|
|
|
|
{ |
|
|
|
|
const std::unique_ptr<uint8_t>& buf = std::get<0>(*it); |
|
|
|
|
int32_t size = std::get<1>(*it); |
|
|
|
|
img = fVpx->Decode(buf.get(), static_cast<uint32_t>(size)); |
|
|
|
|
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) |
|
|
|
@ -355,7 +357,7 @@ bool plMoviePlayer::Start()
|
|
|
|
|
if (fPlaying) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
if (!IOpenMovie()) |
|
|
|
|
return false; |
|
|
|
|
hsAssert(fVideoTrack, "nil video track -- expect bad things to happen!"); |
|
|
|
@ -364,7 +366,7 @@ bool plMoviePlayer::Start()
|
|
|
|
|
const mkvparser::VideoTrack* video = static_cast<const mkvparser::VideoTrack*>(fVideoTrack->GetTrack()); |
|
|
|
|
if (strcmp(video->GetCodecId(), WEBM_CODECID_VP9) != 0) |
|
|
|
|
{ |
|
|
|
|
hsAssert(false, "Not a VP9 video track!"); |
|
|
|
|
plStatusLog::AddLineS("movie.log", "%s: Not a VP9 video track!", fMoviePath.AsString().c_str()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
if (VPX* vpx = VPX::Create()) |
|
|
|
@ -397,7 +399,7 @@ bool plMoviePlayer::Start()
|
|
|
|
|
return true; |
|
|
|
|
#else |
|
|
|
|
return false; |
|
|
|
|
#endif // VIDEO_AVAILABLE
|
|
|
|
|
#endif // MOVIE_AVAILABLE
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool plMoviePlayer::NextFrame() |
|
|
|
@ -412,7 +414,7 @@ bool plMoviePlayer::NextFrame()
|
|
|
|
|
if (fPaused) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
#ifdef VIDEO_AVAILABLE |
|
|
|
|
#ifdef MOVIE_AVAILABLE |
|
|
|
|
// Get our current timecode
|
|
|
|
|
fMovieTime += frameTimeDelta; |
|
|
|
|
|
|
|
|
@ -430,7 +432,7 @@ bool plMoviePlayer::NextFrame()
|
|
|
|
|
return true; |
|
|
|
|
#else |
|
|
|
|
return false; |
|
|
|
|
#endif // VIDEO_AVAILABLE
|
|
|
|
|
#endif // MOVIE_AVAILABLE
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool plMoviePlayer::Pause(bool on) |
|
|
|
@ -451,8 +453,8 @@ bool plMoviePlayer::Stop()
|
|
|
|
|
if (fPlate) |
|
|
|
|
fPlate->SetVisible(false); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < fCallbacks.size(); i++) |
|
|
|
|
fCallbacks[i]->Send(); |
|
|
|
|
for (auto cb : fCallbacks) |
|
|
|
|
cb->Send(); |
|
|
|
|
fCallbacks.clear(); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|