mirror of
https://github.com/korenkonder/ReDIVA.git
synced 2026-09-30 10:47:59 +03:00
Added TaskMovie
This commit is contained in:
@@ -380,7 +380,7 @@ void OggFileHandler::Ctrl() {
|
||||
streaming_channel->SetCallback(OggFileHandler::FillBufferStatic, this);
|
||||
break;
|
||||
case OGG_FILE_HANDLER_PLAYBACK_STATE_UNLOAD:
|
||||
streaming_channel->reset_state = 1;
|
||||
streaming_channel->SetResetOne();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1090,6 +1090,21 @@ namespace rndr {
|
||||
}
|
||||
else if (stage_index != stage_index_prev)
|
||||
reset_exposure = true;
|
||||
|
||||
if (movie_textures_data[0]) {
|
||||
extern texture* task_movie_get_texture(int32_t index = 0);
|
||||
texture* tex = task_movie_get_texture();
|
||||
if (tex) {
|
||||
movie_textures[0].Bind();
|
||||
gl_state_active_bind_texture_2d(0, tex->glid);
|
||||
gl_state_bind_sampler(0, rctx_ptr->render_samplers[0]);
|
||||
gl_state_set_viewport(0, 0, movie_textures_data[0]->width, movie_textures_data[0]->height);
|
||||
uniform_value[U_REDUCE] = 0;
|
||||
shaders_ft.set(SHADER_FT_REDUCE);
|
||||
draw_quad(tex->width, tex->height, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
gl_state_bind_framebuffer(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Render::render_texture_set(texture* render_texture, bool task_photo) {
|
||||
|
||||
+5
-1
@@ -794,7 +794,7 @@ namespace sound {
|
||||
}
|
||||
|
||||
void StreamingChannel::Reset() {
|
||||
reset_state = 1;
|
||||
SetResetOne();
|
||||
FillBuffer(0, 0, 0.0f);
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
@@ -831,6 +831,10 @@ namespace sound {
|
||||
std::unique_lock<std::mutex> u_lock(mtx);
|
||||
master_volume = value;
|
||||
}
|
||||
|
||||
void StreamingChannel::SetResetOne() {
|
||||
reset_state = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ namespace sound {
|
||||
bool SetCallback(CallbackFunc func, void* data);
|
||||
void SetChannelsVolume(int32_t mask, float_t value);
|
||||
void SetMasterVolume(float_t value);
|
||||
void SetResetOne();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -77,8 +77,8 @@
|
||||
#include "shared.hpp"
|
||||
#include "task_window.hpp"
|
||||
#include "x_pv_game.hpp"
|
||||
#if BAKE_VIDEO
|
||||
#include <glad/glad_wgl.h>
|
||||
#if BAKE_VIDEO
|
||||
#include <d3d11.h>
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#endif
|
||||
@@ -510,13 +510,13 @@ bool app_render_data::load() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BAKE_VIDEO
|
||||
if (!Vulkan::use && !gladLoadWGLLoader((GLADloadproc)glfwGetProcAddress, GetDC(window_handle)))
|
||||
return false;
|
||||
|
||||
|
||||
#if BAKE_VIDEO
|
||||
D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, 0, 0, 0,
|
||||
D3D11_SDK_VERSION, &d3d_device, 0, &d3d_device_context);
|
||||
|
||||
|
||||
if (!Vulkan::use && GLAD_WGL_NV_DX_interop2)
|
||||
d3d_gl_handle = wglDXOpenDeviceNV(d3d_device);
|
||||
#endif
|
||||
@@ -765,6 +765,10 @@ static render_context* render_context_load() {
|
||||
font_info_default_init();
|
||||
dw_console_c_buff_array_init();
|
||||
sound_init();
|
||||
|
||||
extern HRESULT mf_init();
|
||||
mf_init();
|
||||
|
||||
wave_audio_storage_init();
|
||||
ogg_file_handler_storage_init();
|
||||
ogg_playback_data_init();
|
||||
@@ -1754,6 +1758,10 @@ static void render_context_dispose(render_context* rctx) {
|
||||
ogg_playback_data_free();
|
||||
ogg_file_handler_storage_free();
|
||||
wave_audio_storage_free();
|
||||
|
||||
extern HRESULT mf_shutdown();
|
||||
mf_shutdown();
|
||||
|
||||
sound_free();
|
||||
dw_console_c_buff_array_free();
|
||||
font_info_default_free();
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "audio_decoder.hpp"
|
||||
#include "media_foundation.hpp"
|
||||
#include <mfapi.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
AudioDecoder::AudioDecoder(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source)
|
||||
: TransformBase(hr, media_stats_lock, media_clock, media_source),
|
||||
output_sub_type(MFAudioFormat_Float), sample_rate(), channels(), buffer_size() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
if (SUCCEEDED(hr)) {
|
||||
IMFMediaType* mf_media_type = 0;
|
||||
hr = media_source->GetAudioMFMediaType(&mf_media_type);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = GetMFTransform(mf_media_type, output_sub_type, mf_transform);
|
||||
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = InitFromMediaSource();
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
AudioDecoder::~AudioDecoder() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT AudioDecoder::ProcessOutput() {
|
||||
if (sample_list_active.GetSamplesCount() >= 10)
|
||||
return S_FALSE;
|
||||
|
||||
MFT_OUTPUT_DATA_BUFFER output_sample = {};
|
||||
DWORD output_status = 0;
|
||||
IMFMediaBuffer* mf_media_buffer = 0;
|
||||
IMFSample* mf_sample = 0;
|
||||
|
||||
if (SUCCEEDED(MFCreateMemoryBuffer(buffer_size, &mf_media_buffer))
|
||||
&& SUCCEEDED(MFCreateSample(&mf_sample)) && SUCCEEDED(mf_sample->AddBuffer(mf_media_buffer))) {
|
||||
output_sample.pSample = mf_sample;
|
||||
mf_sample->AddRef();
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
const int64_t process_output_begin = GetTimestamp();
|
||||
HRESULT hr = mf_transform->ProcessOutput(0, 1, &output_sample, &output_status);
|
||||
const int64_t process_output_end = GetTimestamp();
|
||||
|
||||
int64_t sample_time;
|
||||
if (FAILED(hr)) {
|
||||
switch (hr) {
|
||||
case MF_E_TRANSFORM_STREAM_CHANGE:
|
||||
hr = InitFromMediaSource();
|
||||
break;
|
||||
case MF_E_TRANSFORM_NEED_MORE_INPUT:
|
||||
hr = S_OK;
|
||||
if (stream_state == 2)
|
||||
stream_state = 3;
|
||||
else
|
||||
stream_state = 0;
|
||||
break;
|
||||
default:
|
||||
hr = S_FALSE;
|
||||
stream_state = 0;
|
||||
break;
|
||||
}
|
||||
goto End;
|
||||
}
|
||||
|
||||
sample_list_active.AddSample(mf_sample, false);
|
||||
|
||||
sample_time = 0;
|
||||
output_sample.pSample->GetSampleTime(&sample_time);
|
||||
|
||||
media_stats_lock->SetAudioProcessOutput(
|
||||
CalcTimeMsec(process_output_begin, process_output_end), (double_t)sample_time * 0.0000001);
|
||||
|
||||
End:
|
||||
if (output_sample.pSample) {
|
||||
output_sample.pSample->Release();
|
||||
output_sample.pSample = 0;
|
||||
}
|
||||
|
||||
if (output_sample.pEvents) {
|
||||
output_sample.pEvents->Release();
|
||||
output_sample.pEvents = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
void AudioDecoder::SetSampleTime(double_t value) {
|
||||
media_stats_lock->SetAudioSampleTime(value);
|
||||
}
|
||||
|
||||
HRESULT AudioDecoder::InitFromMediaSource() {
|
||||
IMFMediaType* video_mf_media_type = 0;
|
||||
IMFMediaType* mf_media_type = 0;
|
||||
|
||||
HRESULT hr;
|
||||
hr = media_source->GetAudioMFMediaType(&video_mf_media_type);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MoviePlayLib::GetMFMediaType(mf_transform, output_sub_type, mf_media_type);
|
||||
|
||||
uint32_t buffer_size = 0;
|
||||
MFT_OUTPUT_STREAM_INFO stream_info = {};
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = this->mf_transform->GetOutputStreamInfo(0, &stream_info);
|
||||
buffer_size = stream_info.cbSize;
|
||||
}
|
||||
this->buffer_size = buffer_size;
|
||||
|
||||
uint32_t bits_per_sample = 0;
|
||||
uint32_t block_alignment = 0;
|
||||
uint32_t avg_bytes_per_second = 0;
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &channels);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &sample_rate);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &avg_bytes_per_second);
|
||||
if (SUCCEEDED(hr))
|
||||
media_stats_lock->SetAudioParams(sample_rate, channels);
|
||||
|
||||
End:
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (video_mf_media_type) {
|
||||
video_mf_media_type->Release();
|
||||
video_mf_media_type = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT AudioDecoder::Create(MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source, IMediaTransform*& ptr) {
|
||||
HRESULT hr;
|
||||
AudioDecoder* p = new AudioDecoder(hr, media_stats_lock, media_clock, media_source);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
else
|
||||
p->Shutdown();
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void AudioDecoder::Destroy(AudioDecoder* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transform_base.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class AudioDecoder : public TransformBase {
|
||||
protected:
|
||||
GUID output_sub_type;
|
||||
uint32_t sample_rate;
|
||||
uint32_t channels;
|
||||
uint32_t buffer_size;
|
||||
|
||||
public:
|
||||
|
||||
AudioDecoder(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source);
|
||||
virtual ~AudioDecoder() override;
|
||||
|
||||
virtual HRESULT ProcessOutput() override;
|
||||
virtual void SetSampleTime(double_t value) override;
|
||||
|
||||
HRESULT InitFromMediaSource();
|
||||
|
||||
static HRESULT Create(MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source, IMediaTransform*& ptr);
|
||||
static void Destroy(AudioDecoder* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "command_args.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT CommandArgs::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(CommandArgs) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG CommandArgs::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG CommandArgs::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
CommandArgs::~CommandArgs() {
|
||||
if (str)
|
||||
SysFreeString(str);
|
||||
}
|
||||
|
||||
CommandArgs::Type CommandArgs::GetType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
int64_t CommandArgs::GetPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
OLECHAR* CommandArgs::GetPath() {
|
||||
return str;
|
||||
}
|
||||
|
||||
HRESULT CommandArgs::SetType(CommandArgs::Type value) {
|
||||
type = value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CommandArgs::SetPosition(int64_t value) {
|
||||
position = value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
CommandArgs::CommandArgs(CommandArgs::Type type)
|
||||
: ref_count(), lock(), type(type), position(), str() {
|
||||
|
||||
}
|
||||
|
||||
HRESULT CommandArgs::SetPath(const OLECHAR* value) {
|
||||
if (str) {
|
||||
SysFreeString(str);
|
||||
str = 0;
|
||||
}
|
||||
|
||||
if (!value)
|
||||
return S_OK;
|
||||
|
||||
str = SysAllocString(value);
|
||||
if (!str)
|
||||
return E_OUTOFMEMORY;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CommandArgs::Create(CommandArgs::Type type, CommandArgs*& ptr) {
|
||||
CommandArgs* p = new CommandArgs(type);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void CommandArgs::Destroy(CommandArgs* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class DECLSPEC_UUID("5D520F14-C168-43DA-B799-4FEC99669FB0")
|
||||
CommandArgs : public IUnknown {
|
||||
public:
|
||||
enum class Type {
|
||||
Open = 0,
|
||||
SetPosition,
|
||||
Play,
|
||||
Pause,
|
||||
Close,
|
||||
Max,
|
||||
};
|
||||
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
Type type;
|
||||
int64_t position;
|
||||
OLECHAR* str;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
CommandArgs(CommandArgs::Type type);
|
||||
virtual ~CommandArgs();
|
||||
|
||||
virtual Type GetType();
|
||||
virtual int64_t GetPosition();
|
||||
virtual OLECHAR* GetPath();
|
||||
virtual HRESULT SetType(Type value);
|
||||
virtual HRESULT SetPosition(int64_t value);
|
||||
virtual HRESULT SetPath(const OLECHAR* value);
|
||||
|
||||
static HRESULT Create(CommandArgs::Type type, CommandArgs*& ptr);
|
||||
static void Destroy(CommandArgs* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "dx_surface_buffer.hpp"
|
||||
#include <mfapi.h>
|
||||
#pragma comment(lib, "strmiids.lib")
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT DXSurfaceBuffer::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMFMediaBuffer)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IMFGetService)) {
|
||||
IMFGetService* mf_get_service = this;
|
||||
*ppvObject = mf_get_service;
|
||||
mf_get_service->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IDirect3DTexture9) && d3d_texture) {
|
||||
d3d_texture->AddRef();
|
||||
*ppvObject = d3d_texture;
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IDirect3DSurface9) && d3d_surface) {
|
||||
d3d_surface->AddRef();
|
||||
*ppvObject = d3d_surface;
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG DXSurfaceBuffer::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG DXSurfaceBuffer::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::Lock(BYTE** ppbBuffer, DWORD* pcbMaxLength, DWORD* pcbCurrentLength) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::Unlock() {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::GetCurrentLength(DWORD* pcbCurrentLength) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::SetCurrentLength(DWORD cbCurrentLength) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::GetMaxLength(DWORD* pcbMaxLength) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::GetService(const GUID& guidService, const IID& riid, LPVOID* ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (guidService == MR_BUFFER_SERVICE)
|
||||
return QueryInterface(riid, ppvObject);
|
||||
return MF_E_UNSUPPORTED_SERVICE;
|
||||
}
|
||||
|
||||
DXSurfaceBuffer::DXSurfaceBuffer(HRESULT& hr, IUnknown* object) : ref_count(), d3d_texture(), d3d_surface() {
|
||||
hr = object->QueryInterface(__uuidof(IDirect3DTexture9), (void**)&d3d_texture);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = d3d_texture->GetSurfaceLevel(0, &d3d_surface);
|
||||
else {
|
||||
hr = object->QueryInterface(__uuidof(IDirect3DSurface9), (void**)&d3d_surface);
|
||||
if (SUCCEEDED(hr))
|
||||
d3d_surface->GetContainer(__uuidof(IDirect3DTexture9), (void**)&d3d_texture);
|
||||
}
|
||||
}
|
||||
|
||||
DXSurfaceBuffer::~DXSurfaceBuffer() {
|
||||
if (d3d_texture) {
|
||||
d3d_texture->Release();
|
||||
d3d_texture = 0;
|
||||
}
|
||||
|
||||
if (d3d_surface) {
|
||||
d3d_surface->Release();
|
||||
d3d_surface = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DXSurfaceBuffer::Create(IUnknown* object, IMFMediaBuffer*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
DXSurfaceBuffer* p = new DXSurfaceBuffer(hr, object);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void DXSurfaceBuffer::Destroy(DXSurfaceBuffer* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <mfidl.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class DXSurfaceBuffer : public IMFMediaBuffer, public IMFGetService {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
IDirect3DTexture9* d3d_texture;
|
||||
IDirect3DSurface9* d3d_surface;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Lock(BYTE** ppbBuffer, DWORD* pcbMaxLength, DWORD* pcbCurrentLength) override;
|
||||
virtual HRESULT Unlock() override;
|
||||
virtual HRESULT GetCurrentLength(DWORD* pcbCurrentLength) override;
|
||||
virtual HRESULT SetCurrentLength(DWORD cbCurrentLength) override;
|
||||
virtual HRESULT GetMaxLength(DWORD* pcbMaxLength) override;
|
||||
|
||||
virtual HRESULT GetService(const GUID& guidService, const IID& riid, LPVOID* ppvObject) override;
|
||||
|
||||
DXSurfaceBuffer(HRESULT& hr, IUnknown* object);
|
||||
virtual ~DXSurfaceBuffer();
|
||||
|
||||
static HRESULT Create(IUnknown* object, IMFMediaBuffer*& ptr);
|
||||
static void Destroy(DXSurfaceBuffer* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "dxva2.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT GetDirectXVideoProcessor(IDirect3DDeviceManager9* d3d_device_manager,
|
||||
const DXVA2_VideoDesc* video_desc, IDirectXVideoProcessor*& ptr) {
|
||||
HANDLE output_type_info = 0;
|
||||
HRESULT hr;
|
||||
hr = d3d_device_manager->OpenDeviceHandle(&output_type_info);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
IDirectXVideoProcessorService* dx_video_processor_service = 0;
|
||||
IDirectXVideoProcessor* dx_video_processor = 0;
|
||||
UINT guid_count = 0;
|
||||
GUID* guids = 0;
|
||||
hr = d3d_device_manager->GetVideoService(output_type_info,
|
||||
__uuidof(IDirectXVideoProcessorService), (void**)&dx_video_processor_service);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = dx_video_processor_service->GetVideoProcessorDeviceGuids(video_desc, &guid_count, &guids);
|
||||
if (FAILED(hr) || !guids)
|
||||
goto End;
|
||||
|
||||
for (UINT i = 0; i < guid_count && !dx_video_processor; i++) {
|
||||
UINT format_count = 0;
|
||||
D3DFORMAT* formats = 0;
|
||||
hr = dx_video_processor_service->GetVideoProcessorRenderTargets(
|
||||
guids[i], video_desc, &format_count, &formats);
|
||||
if (SUCCEEDED(hr) && formats)
|
||||
for (UINT j = 0; j < format_count && !dx_video_processor; j++)
|
||||
if (formats[j] == D3DFMT_X8R8G8B8)
|
||||
hr = dx_video_processor_service->CreateVideoProcessor(guids[i],
|
||||
video_desc, D3DFMT_X8R8G8B8, 1, &dx_video_processor);
|
||||
|
||||
if (formats)
|
||||
CoTaskMemFree(formats);
|
||||
}
|
||||
|
||||
if (dx_video_processor) {
|
||||
ptr = dx_video_processor;
|
||||
ptr->AddRef();
|
||||
goto End;
|
||||
}
|
||||
else
|
||||
hr = MF_E_UNSUPPORTED_D3D_TYPE;
|
||||
|
||||
End:
|
||||
if (guids) {
|
||||
CoTaskMemFree(guids);
|
||||
guids = 0;
|
||||
}
|
||||
|
||||
if (dx_video_processor) {
|
||||
dx_video_processor->Release();
|
||||
dx_video_processor = 0;
|
||||
}
|
||||
|
||||
if (dx_video_processor_service) {
|
||||
dx_video_processor_service->Release();
|
||||
dx_video_processor_service = 0;
|
||||
}
|
||||
|
||||
HRESULT _hr = d3d_device_manager->CloseDeviceHandle(output_type_info);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = _hr;
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <dxva2api.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT GetDirectXVideoProcessor(IDirect3DDeviceManager9* d3d_device_manager,
|
||||
const DXVA2_VideoDesc* video_desc, IDirectXVideoProcessor*& ptr);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "external_clock.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT ExternalClock::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMediaClock) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG ExternalClock::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG ExternalClock::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT ExternalClock::TimeEnd() {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ExternalClock::TimeBegin() {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ExternalClock::SetTime(INT64 value) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
INT64 ExternalClock::GetTime() {
|
||||
if (callback)
|
||||
return callback() / 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExternalClock::ExternalClock(Callback callback) : ref_count(), callback(callback) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
ExternalClock::~ExternalClock() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT ExternalClock::Create(Callback callback, IMediaClock*& ptr) {
|
||||
ExternalClock* p = new ExternalClock(callback);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void ExternalClock::Destroy(ExternalClock* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class ExternalClock : public IMediaClock {
|
||||
public:
|
||||
typedef int64_t(*Callback)();
|
||||
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Callback callback;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT TimeEnd() override;
|
||||
virtual HRESULT TimeBegin() override;
|
||||
virtual HRESULT SetTime(INT64 value) override;
|
||||
virtual INT64 GetTime() override;
|
||||
|
||||
ExternalClock(Callback callback);
|
||||
virtual ~ExternalClock();
|
||||
|
||||
static HRESULT Create(Callback callback, IMediaClock*& ptr);
|
||||
static void Destroy(ExternalClock* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "../../CRE/texture.hpp"
|
||||
#include "gl_dx_interop_texture.hpp"
|
||||
#include <glad/glad_wgl.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
static uint16_t gl_dx_interop_texture_counter = 0;
|
||||
|
||||
HRESULT GLDXInteropTexture::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IGLDXInteropTexture) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG GLDXInteropTexture::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG GLDXInteropTexture::Release() {
|
||||
ULONG ref_count = --this->ref_count;
|
||||
if (ref_count == 1) {
|
||||
GetCurrentThreadId();
|
||||
wglGetCurrentContext();
|
||||
|
||||
if (index >= 0 && index < count) {
|
||||
wglDXUnlockObjectsNV(device, 1, &textures[index].object);
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
else if (!ref_count) {
|
||||
GetCurrentThreadId();
|
||||
wglGetCurrentContext();
|
||||
|
||||
if (index >= 0 && index < count) {
|
||||
wglDXUnlockObjectsNV(device, 1, &textures[index].object);
|
||||
index = -1;
|
||||
}
|
||||
|
||||
Destroy();
|
||||
}
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
texture* GLDXInteropTexture::GetTexture() {
|
||||
if (index >= 0 && index < count)
|
||||
return textures[index].tex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLDXInteropTexture::GLDXInteropTexture(HRESULT& hr) : ref_count(), thread_id(), wgl_ctx(),
|
||||
device(), d3d_device(), video_index(), index(-1), count(), textures() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
memset(textures, 0, sizeof(textures));
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (!GLAD_WGL_NV_DX_interop2)
|
||||
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||
else if (SUCCEEDED(hr)) {
|
||||
thread_id = GetCurrentThreadId();
|
||||
wgl_ctx = wglGetCurrentContext();
|
||||
if (!wgl_ctx)
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
GLDXInteropTexture::~GLDXInteropTexture() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT GLDXInteropTexture::SetMFSample(IMFSample* mf_sample) {
|
||||
GetCurrentThreadId();
|
||||
wglGetCurrentContext();
|
||||
|
||||
if (index >= 0 && index < count) {
|
||||
wglDXUnlockObjectsNV(device, 1, &textures[index].object);
|
||||
index = -1;
|
||||
}
|
||||
|
||||
IMFMediaBuffer* mf_media_buffer = 0;
|
||||
IDirect3DTexture9* d3d_texture = 0;
|
||||
IDirect3DDevice9* d3d_device = 0;
|
||||
|
||||
HRESULT hr;
|
||||
HANDLE share_handle;
|
||||
uint32_t video_index;
|
||||
uint32_t index;
|
||||
|
||||
hr = mf_sample->GetBufferByIndex(0, &mf_media_buffer);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
share_handle = 0;
|
||||
hr = mf_sample->GetUINT64(TextureSharedHandleGUID, (UINT64*)&share_handle);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
video_index = 0;
|
||||
hr = mf_sample->GetUINT32(VideoIndexGUID, &video_index);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_buffer->QueryInterface(__uuidof(IDirect3DTexture9), (void**)&d3d_texture);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = d3d_texture->GetDevice(&d3d_device);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
if (this->video_index != video_index || this->d3d_device != d3d_device) {
|
||||
CloseDevice();
|
||||
|
||||
this->video_index = video_index;
|
||||
this->d3d_device = d3d_device;
|
||||
|
||||
device = wglDXOpenDeviceNV(d3d_device);
|
||||
|
||||
if (!device)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
if (textures[index].share_handle == share_handle)
|
||||
break;
|
||||
|
||||
if (index == count) {
|
||||
if (count >= 10) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
goto End;
|
||||
}
|
||||
|
||||
if (LoadTexture(textures[index], d3d_texture, share_handle))
|
||||
count++;
|
||||
else {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReleaseTexture(textures[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
if (index < count && wglDXLockObjectsNV(device, 1, &textures[index].object))
|
||||
this->index = index;
|
||||
else
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
End:
|
||||
if (d3d_device) {
|
||||
d3d_device->Release();
|
||||
d3d_device = 0;
|
||||
}
|
||||
|
||||
if (d3d_texture) {
|
||||
d3d_texture->Release();
|
||||
d3d_texture = 0;
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
void GLDXInteropTexture::CloseDevice() {
|
||||
if (count) {
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
ReleaseTexture(textures[i]);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (device) {
|
||||
wglDXCloseDeviceNV(device);
|
||||
device = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GLDXInteropTexture::LoadTexture(Texture& gl_dx_tex, void* dxObject, HANDLE share_handle) {
|
||||
gl_dx_tex.tex = texture_load_tex_2d(texture_id(0x24, gl_dx_interop_texture_counter), GL_RGBA8, 1280, 720, 0, 0, 0);
|
||||
if (!gl_dx_tex.tex)
|
||||
return false;
|
||||
|
||||
gl_dx_interop_texture_counter++;
|
||||
|
||||
glDeleteTextures(1, &gl_dx_tex.tex->glid);
|
||||
glGenTextures(1, &gl_dx_tex.tex->glid);
|
||||
|
||||
wglDXSetResourceShareHandleNV(dxObject, share_handle);
|
||||
gl_dx_tex.share_handle = share_handle;
|
||||
gl_dx_tex.object = wglDXRegisterObjectNV(device, dxObject, gl_dx_tex.tex->glid, GL_TEXTURE_2D, GL_ZERO);
|
||||
return !!gl_dx_tex.object;
|
||||
}
|
||||
|
||||
void GLDXInteropTexture::ReleaseTexture(Texture& gl_dx_tex) {
|
||||
if (gl_dx_tex.object) {
|
||||
wglDXUnregisterObjectNV(device, gl_dx_tex.object);
|
||||
gl_dx_tex.object = 0;
|
||||
}
|
||||
|
||||
if (gl_dx_tex.tex) {
|
||||
texture_release(gl_dx_tex.tex);
|
||||
gl_dx_tex.tex = 0;
|
||||
}
|
||||
gl_dx_tex.share_handle = 0;
|
||||
}
|
||||
|
||||
HRESULT GLDXInteropTexture::Create(GLDXInteropTexture*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
GLDXInteropTexture* p = new GLDXInteropTexture(hr);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
p->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
inline void GLDXInteropTexture::Destroy(GLDXInteropTexture* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class GLDXInteropTexture : public IGLDXInteropTexture {
|
||||
public:
|
||||
struct Texture {
|
||||
HANDLE share_handle;
|
||||
HANDLE object;
|
||||
texture* tex;
|
||||
};
|
||||
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
int32_t thread_id;
|
||||
HGLRC wgl_ctx;
|
||||
HANDLE device;
|
||||
IDirect3DDevice9* d3d_device;
|
||||
uint32_t video_index;
|
||||
uint32_t index;
|
||||
uint32_t count;
|
||||
Texture textures[10];
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual texture* GetTexture() override;
|
||||
|
||||
GLDXInteropTexture(HRESULT& result);
|
||||
virtual ~GLDXInteropTexture();
|
||||
|
||||
virtual HRESULT SetMFSample(IMFSample* mf_sample);
|
||||
|
||||
void CloseDevice();
|
||||
bool LoadTexture(Texture& gl_dx_tex, void* dxObject, HANDLE shareHandle);
|
||||
void ReleaseTexture(Texture& gl_dx_tex);
|
||||
|
||||
static HRESULT Create(GLDXInteropTexture*& ptr);
|
||||
static void Destroy(GLDXInteropTexture* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "media_clock.hpp"
|
||||
#include <mfapi.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT MediaClock::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMediaClock) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG MediaClock::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG MediaClock::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT MediaClock::TimeEnd() {
|
||||
lock.Acquire();
|
||||
if (time_begin_init) {
|
||||
time = GetTimeInner();
|
||||
time_begin = GetTimestamp();
|
||||
time_begin_init = false;
|
||||
}
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaClock::TimeBegin() {
|
||||
lock.Acquire();
|
||||
if (!time_begin_init) {
|
||||
time_begin = GetTimestamp();
|
||||
time_begin_init = true;
|
||||
}
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaClock::SetTime(INT64 value) {
|
||||
lock.Acquire();
|
||||
time = value;
|
||||
time_begin = GetTimestamp();
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
INT64 MediaClock::GetTime() {
|
||||
lock.Acquire();
|
||||
int64_t res = GetTimeInner();
|
||||
lock.Release();
|
||||
return res;
|
||||
}
|
||||
|
||||
MediaClock::MediaClock() : ref_count(), lock(), time_begin_init(), time(), time_begin() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
MediaClock::~MediaClock() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
int64_t MediaClock::GetTimeInner() {
|
||||
if (!time_begin_init)
|
||||
return time;
|
||||
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
|
||||
return time + MFllMulDiv(GetTimestamp() - time_begin, 10000000, freq.QuadPart, 0);
|
||||
}
|
||||
|
||||
HRESULT MediaClock::Create(IMediaClock*& ptr) {
|
||||
MediaClock* p = new MediaClock;
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void MediaClock::Destroy(MediaClock* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class MediaClock : public IMediaClock {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
BOOL time_begin_init;
|
||||
int64_t time;
|
||||
int64_t time_begin;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT TimeEnd() override;
|
||||
virtual HRESULT TimeBegin() override;
|
||||
virtual HRESULT SetTime(INT64 value) override;
|
||||
virtual INT64 GetTime() override;
|
||||
|
||||
MediaClock();
|
||||
virtual ~MediaClock();
|
||||
|
||||
int64_t GetTimeInner();
|
||||
|
||||
static HRESULT Create(IMediaClock*& ptr);
|
||||
static void Destroy(MediaClock* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "media_foundation.hpp"
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#pragma comment(lib, "mfplat.lib")
|
||||
#pragma comment(lib, "mfuuid.lib")
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT GetMFMediaType(IMFTransform* mf_transform, const GUID& sub_type, IMFMediaType*& ptr) {
|
||||
DWORD type_index = 0;
|
||||
HRESULT hr;
|
||||
IMFMediaType* mf_media_type = 0;
|
||||
do {
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
|
||||
hr = mf_transform->GetOutputAvailableType(0, type_index, &mf_media_type);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
|
||||
GUID _sub_type = {};
|
||||
if (SUCCEEDED(mf_media_type->GetGUID(MF_MT_SUBTYPE, &_sub_type)) && _sub_type == sub_type) {
|
||||
hr = mf_transform->SetOutputType(0, mf_media_type, 0);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = mf_media_type;
|
||||
ptr->AddRef();
|
||||
break;
|
||||
}
|
||||
}
|
||||
type_index++;
|
||||
} while (true);
|
||||
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT GetMFTransform(IMFMediaType* mf_media_type, const GUID& sub_type, IMFTransform*& ptr) {
|
||||
ptr = 0;
|
||||
|
||||
GUID guid_major_type = {};
|
||||
GUID guid_format_subtype = {};
|
||||
IID* pclsidMFT = 0;
|
||||
UINT32 cMFTs = 0;
|
||||
IMFTransform* mf_transform = 0;
|
||||
|
||||
HRESULT hr;
|
||||
hr = mf_media_type->GetGUID(MF_MT_MAJOR_TYPE, &guid_major_type);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_type->GetGUID(MF_MT_SUBTYPE, &guid_format_subtype);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
MFT_REGISTER_TYPE_INFO formats;
|
||||
formats.guidMajorType = guid_major_type;
|
||||
formats.guidSubtype = guid_format_subtype;
|
||||
MFT_REGISTER_TYPE_INFO output_type_info;
|
||||
output_type_info.guidMajorType = guid_major_type;
|
||||
output_type_info.guidSubtype = sub_type;
|
||||
hr = MFTEnum(guid_major_type == MFMediaType_Audio ? MFT_CATEGORY_AUDIO_DECODER
|
||||
: MFT_CATEGORY_VIDEO_DECODER, 0, &formats, &output_type_info, 0, &pclsidMFT, &cMFTs);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
if (cMFTs)
|
||||
hr = CoCreateInstance(*pclsidMFT, 0, 1, __uuidof(IMFTransform), (void**)&mf_transform);
|
||||
|
||||
if (FAILED(hr) || !mf_transform)
|
||||
goto End;
|
||||
|
||||
hr = mf_transform->SetInputType(0, mf_media_type, 0);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = mf_transform;
|
||||
ptr->AddRef();
|
||||
}
|
||||
|
||||
End:
|
||||
if (mf_transform) {
|
||||
mf_transform->Release();
|
||||
mf_transform = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && !ptr)
|
||||
hr = MF_E_INVALIDTYPE;
|
||||
|
||||
if (pclsidMFT)
|
||||
CoTaskMemFree(pclsidMFT);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MFMediaEventQueryInterface(IMFMediaEvent* mf_media_event, const IID& iid, void** ppvObject) {
|
||||
PROPVARIANT pvar = {};
|
||||
HRESULT hr = mf_media_event->GetValue(&pvar);
|
||||
if (SUCCEEDED(hr) && pvar.vt == VT_UNKNOWN)
|
||||
hr = pvar.punkVal->QueryInterface(iid, ppvObject);
|
||||
PropVariantClear(&pvar);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MFSampleSetAllocator(IMFSample* mf_sample, IMFAsyncCallback* sample_allocator) {
|
||||
IMFTrackedSample* mf_tracked_sample = 0;
|
||||
HRESULT hr = mf_sample->QueryInterface(__uuidof(IMFTrackedSample), (void**)&mf_tracked_sample);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_tracked_sample->SetAllocator(sample_allocator, 0);
|
||||
|
||||
if (mf_tracked_sample) {
|
||||
mf_tracked_sample->Release();
|
||||
mf_tracked_sample = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL mf_init_status = FALSE;
|
||||
std::atomic_int32_t mf_media_session_count = 0;
|
||||
|
||||
void mf_media_session_count_increment() {
|
||||
mf_media_session_count++;
|
||||
}
|
||||
|
||||
void mf_media_session_count_decrement() {
|
||||
mf_media_session_count--;
|
||||
}
|
||||
|
||||
HRESULT mf_init() {
|
||||
if (mf_init_status)
|
||||
return S_OK;
|
||||
|
||||
HRESULT hr = MFStartup(0x20070u, 1u);
|
||||
if (SUCCEEDED(hr))
|
||||
mf_init_status = TRUE;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT mf_shutdown() {
|
||||
HRESULT hr = S_OK;
|
||||
const int64_t time_begin = MoviePlayLib::GetTimestamp();
|
||||
for (int32_t i = 0; i < 500; i++) {
|
||||
if (!mf_media_session_count)
|
||||
break;
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (mf_init_status) {
|
||||
hr = MFShutdown();
|
||||
mf_init_status = FALSE;
|
||||
}
|
||||
const int64_t time_end = MoviePlayLib::GetTimestamp();
|
||||
|
||||
const double_t time = MoviePlayLib::CalcTimeMsec(time_begin, time_end);
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <mftransform.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT GetMFMediaType(IMFTransform* mf_transform, const GUID& sub_type, IMFMediaType*& ptr);
|
||||
HRESULT GetMFTransform(IMFMediaType* mf_media_type, const GUID& sub_type, IMFTransform*& ptr);
|
||||
HRESULT MFMediaEventQueryInterface(IMFMediaEvent* mf_media_event, const IID& iid, void** ppvObject);
|
||||
HRESULT MFSampleSetAllocator(IMFSample* mf_sample, IMFAsyncCallback* sample_allocator);
|
||||
}
|
||||
@@ -0,0 +1,963 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "media_session.hpp"
|
||||
#include "../../KKdLib/prj/math.hpp"
|
||||
#include "audio_decoder.hpp"
|
||||
#include "command_args.hpp"
|
||||
#include "media_clock.hpp"
|
||||
#include "media_source.hpp"
|
||||
#include "video_decoder.hpp"
|
||||
#include <avrt.h>
|
||||
#pragma comment(lib, "avrt.lib")
|
||||
#include <dxva2api.h>
|
||||
#pragma comment(lib, "dxva2.lib")
|
||||
#include <mfapi.h>
|
||||
#pragma comment(lib, "mfplat.lib")
|
||||
|
||||
extern int32_t mf_media_session_count;
|
||||
extern void mf_media_session_count_increment();
|
||||
extern void mf_media_session_count_decrement();
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT MediaSession::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG MediaSession::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG MediaSession::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
MediaSession::MediaSession(HRESULT& hr, const wchar_t* path, IMediaClock* media_clock) : ref_count(), lock(),
|
||||
dwQueue(), state(), process(), media_stats_lock(), media_clock(), media_source(), audio_media_transform(),
|
||||
video_media_transform(), wasapi_renderer(), video_renderer(), interop_texture(), duration(),
|
||||
audio_sample_time(INT64_MAX), video_sample_time(INT64_MAX), hThread(), hTimer(), hEvent(), async_callback(this) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
mf_media_session_count_increment();
|
||||
|
||||
LARGE_INTEGER due_time;
|
||||
CommandArgs* command_args;
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
if (media_clock) {
|
||||
this->media_clock = media_clock;
|
||||
media_clock->AddRef();
|
||||
}
|
||||
else
|
||||
hr = MediaClock::Create(this->media_clock);
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hEvent = CreateEventA(0, TRUE, FALSE, 0);
|
||||
if (!hEvent)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hTimer = CreateWaitableTimerA(0, FALSE, 0);
|
||||
if (!hTimer)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
due_time = {};
|
||||
if (!SetWaitableTimer(hTimer, &due_time, 10, 0, 0, FALSE))
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hThread = (HANDLE)_beginthreadex(0, 0, (_beginthreadex_proc_type)MediaSession::ThreadMain, this, 0, 0);
|
||||
if (!hThread)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = MFAllocateWorkQueue(&dwQueue);
|
||||
command_args = 0;
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = CommandArgs::Create(CommandArgs::Type::Open, command_args);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = command_args->SetPath(path);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MFPutWorkItem(dwQueue, &async_callback, command_args);
|
||||
}
|
||||
}
|
||||
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
|
||||
End:
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
MediaSession::~MediaSession() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
Shutdown();
|
||||
|
||||
if (hThread) {
|
||||
SetEvent(hEvent);
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
hThread = 0;
|
||||
}
|
||||
|
||||
if (hTimer) {
|
||||
CloseHandle(hTimer);
|
||||
hTimer = 0;
|
||||
}
|
||||
|
||||
if (hEvent) {
|
||||
CloseHandle(hEvent);
|
||||
hEvent = 0;
|
||||
}
|
||||
|
||||
if (media_clock) {
|
||||
media_clock->Release();
|
||||
media_clock = 0;
|
||||
}
|
||||
|
||||
MFUnlockWorkQueue(dwQueue);
|
||||
dwQueue = 0;
|
||||
|
||||
mf_media_session_count_decrement();
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::AsyncCallback(IMFAsyncResult* mf_async_result) {
|
||||
IUnknown* object = 0;
|
||||
CommandArgs* command_args = 0;
|
||||
HRESULT hr = mf_async_result->GetState(&object);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = object->QueryInterface(__uuidof(CommandArgs), (void**)&command_args);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
switch (command_args->GetType()) {
|
||||
case CommandArgs::Type::Open:
|
||||
hr = open(command_args->GetPath());
|
||||
break;
|
||||
case CommandArgs::Type::SetPosition:
|
||||
hr = set_position(command_args->GetPosition());
|
||||
|
||||
state.SetInnerStateAdvance(state.state_inner);
|
||||
break;
|
||||
case CommandArgs::Type::Play:
|
||||
if (wasapi_renderer)
|
||||
wasapi_renderer->Open();
|
||||
if (video_renderer)
|
||||
video_renderer->Open();
|
||||
|
||||
SetProcessEnable();
|
||||
|
||||
Update();
|
||||
|
||||
media_clock->TimeBegin();
|
||||
|
||||
state.SetInnerStateAdvance(State::Play);
|
||||
break;
|
||||
case CommandArgs::Type::Pause:
|
||||
media_clock->TimeEnd();
|
||||
|
||||
SetProcessDisable();
|
||||
|
||||
if (wasapi_renderer)
|
||||
wasapi_renderer->Flush();
|
||||
if (video_renderer)
|
||||
video_renderer->Flush();
|
||||
|
||||
state.SetInnerStateAdvance(State::Pause);
|
||||
break;
|
||||
case CommandArgs::Type::Close:
|
||||
Shutdown();
|
||||
break;
|
||||
}
|
||||
|
||||
End:
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
|
||||
if (object) {
|
||||
object->Release();
|
||||
object = 0;
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
media_stats_lock.SetCurrError(hr, this);
|
||||
|
||||
return mf_async_result->SetStatus(hr);
|
||||
}
|
||||
|
||||
HRESULT MediaSession::Close() {
|
||||
lock.Acquire();
|
||||
if (interop_texture) {
|
||||
interop_texture->Release();
|
||||
interop_texture = 0;
|
||||
}
|
||||
|
||||
if (dwQueue) {
|
||||
CommandArgs* command_args = 0;
|
||||
HRESULT hr = CommandArgs::Create(CommandArgs::Type::Close, command_args);
|
||||
if (SUCCEEDED(hr))
|
||||
MFPutWorkItem(dwQueue, &async_callback, command_args);
|
||||
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
}
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::Play() {
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
switch (state.state) {
|
||||
case State::None:
|
||||
case State::Init:
|
||||
hr = MF_E_INVALIDREQUEST;
|
||||
break;
|
||||
case State::Open:
|
||||
case State::Wait:
|
||||
case State::Pause:
|
||||
case State::Stop: {
|
||||
CommandArgs* command_args = 0;
|
||||
hr = CommandArgs::Create(CommandArgs::Type::Play, command_args);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MFPutWorkItem(dwQueue, &async_callback, command_args);
|
||||
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
state.SetStateAdvance(State::Play);
|
||||
} break;
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::Pause() {
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
switch (state.state) {
|
||||
case State::None:
|
||||
case State::Init:
|
||||
hr = MF_E_INVALIDREQUEST;
|
||||
break;
|
||||
case State::Open:
|
||||
case State::Wait:
|
||||
case State::Play:
|
||||
case State::Stop: {
|
||||
CommandArgs* command_args = 0;
|
||||
hr = CommandArgs::Create(CommandArgs::Type::Pause, command_args);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MFPutWorkItem(dwQueue, &async_callback, command_args);
|
||||
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
state.lock.Acquire();
|
||||
state.state = State::Pause;
|
||||
state.command_index_queue++;
|
||||
state.lock.Release();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::SetTime(double_t value) {
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
switch (state.state) {
|
||||
case State::None:
|
||||
case State::Init:
|
||||
hr = MF_E_INVALIDREQUEST;
|
||||
break;
|
||||
case State::Open:
|
||||
case State::Wait:
|
||||
case State::Pause:
|
||||
case State::Play:
|
||||
case State::Stop: {
|
||||
CommandArgs* command_args = 0;
|
||||
hr = CommandArgs::Create(CommandArgs::Type::Pause, command_args);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = command_args->SetPosition((int64_t)prj::floor(value * 10000000.0));
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MFPutWorkItem(dwQueue, &async_callback, command_args);
|
||||
}
|
||||
|
||||
if (command_args) {
|
||||
command_args->Release();
|
||||
command_args = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
state.SetStateAdvance(state.state);
|
||||
} break;
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
State MediaSession::GetState() {
|
||||
return state.state;
|
||||
}
|
||||
|
||||
double_t MediaSession::GetDuration() {
|
||||
return (double_t)duration * 0.0000001;
|
||||
}
|
||||
|
||||
double_t MediaSession::GetTime() {
|
||||
return (double_t)media_clock->GetTime() * 0.0000001;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::GetAudioParams(AudioParams* value) {
|
||||
if (!value)
|
||||
return E_POINTER;
|
||||
else if (wasapi_renderer)
|
||||
return wasapi_renderer->GetParams(value);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::SetAudioParams(const AudioParams* value) {
|
||||
if (!value)
|
||||
return E_POINTER;
|
||||
else if (wasapi_renderer)
|
||||
return wasapi_renderer->SetParams(value);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::GetVideoParams(VideoParams* value) {
|
||||
if (!value)
|
||||
return E_POINTER;
|
||||
|
||||
MoviePlayLib::MediaStats stats;
|
||||
media_stats_lock.GetStats(stats);
|
||||
|
||||
value->width = stats.width;
|
||||
value->height = stats.height;
|
||||
value->frame_size_width = stats.frame_size_width;
|
||||
value->frame_size_height = stats.frame_size_height;
|
||||
value->frame_rate = stats.frame_rate;
|
||||
value->pixel_aspect_ratio = stats.pixel_aspect_ratio;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::GetD3D9Texture(IDirect3DDevice9* d3d_device, IDirect3DTexture9** ptr) {
|
||||
if (!d3d_device || !ptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ptr = 0;
|
||||
|
||||
HRESULT hr = S_FALSE;
|
||||
if (!video_renderer)
|
||||
return hr;
|
||||
|
||||
Update();
|
||||
|
||||
IMFSample* mf_sample = 0;
|
||||
HRESULT mf_sample_hr = video_renderer->GetMFSample(&mf_sample);
|
||||
if (mf_sample) {
|
||||
const int64_t time_begin = GetTimestamp();
|
||||
|
||||
HANDLE shared_handle = 0;
|
||||
IMFMediaBuffer* mf_media_buffer = 0;
|
||||
IDirect3DTexture9* dx_texture = 0;
|
||||
D3DSURFACE_DESC desc = {};
|
||||
|
||||
hr = mf_sample->GetUINT64(TextureSharedHandleGUID, (UINT64*)&shared_handle);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_sample->GetBufferByIndex(0, &mf_media_buffer);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_buffer->QueryInterface(__uuidof(IDirect3DTexture9), (void**)&dx_texture);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = dx_texture->GetLevelDesc(0, &desc);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = d3d_device->CreateTexture(desc.Width, desc.Height, 1,
|
||||
desc.Usage, desc.Format, desc.Pool, ptr, &shared_handle);
|
||||
|
||||
End:
|
||||
if (dx_texture) {
|
||||
dx_texture->Release();
|
||||
dx_texture = 0;
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
const int64_t time_end = GetTimestamp();
|
||||
|
||||
media_stats_lock.SetVideoOutputTime(CalcTimeMsec(time_begin, time_end));
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_sample_hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::GetD3D11Texture(ID3D11Device* d3d_device, ID3D11Texture2D** ptr) {
|
||||
if (!d3d_device || !ptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ptr = 0;
|
||||
|
||||
HRESULT hr = S_FALSE;
|
||||
if (!video_renderer)
|
||||
return hr;
|
||||
|
||||
Update();
|
||||
|
||||
IMFSample* mf_sample = 0;
|
||||
HRESULT mf_sample_hr = video_renderer->GetMFSample(&mf_sample);
|
||||
if (mf_sample) {
|
||||
const int64_t time_begin = GetTimestamp();
|
||||
|
||||
HANDLE shared_handle = 0;
|
||||
hr = mf_sample->GetUINT64(TextureSharedHandleGUID, (UINT64*)&shared_handle);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = d3d_device->OpenSharedResource(shared_handle, __uuidof(ID3D11Texture2D), (void**)ptr);
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
const int64_t time_end = GetTimestamp();
|
||||
|
||||
media_stats_lock.SetVideoOutputTime(CalcTimeMsec(time_begin, time_end));
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_sample_hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::GetGLDXIntreropTexture(IGLDXInteropTexture** ptr) {
|
||||
if (!ptr)
|
||||
return E_POINTER;
|
||||
|
||||
*ptr = 0;
|
||||
|
||||
HRESULT hr = S_FALSE;
|
||||
if (!video_renderer)
|
||||
return hr;
|
||||
|
||||
Update();
|
||||
|
||||
if (!interop_texture) {
|
||||
hr = GLDXInteropTexture::Create(interop_texture);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
}
|
||||
|
||||
IMFSample* mf_sample = 0;
|
||||
HRESULT mf_sample_hr = video_renderer->GetMFSample(&mf_sample);
|
||||
if (mf_sample) {
|
||||
const int64_t time_begin = GetTimestamp();
|
||||
|
||||
hr = interop_texture->SetMFSample(mf_sample);
|
||||
if (SUCCEEDED(hr)) {
|
||||
*ptr = interop_texture;
|
||||
interop_texture->AddRef();
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
const int64_t time_end = GetTimestamp();
|
||||
|
||||
media_stats_lock.SetVideoOutputTime(CalcTimeMsec(time_begin, time_end));
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_sample_hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
void MediaSession::GetSamplesCount() {
|
||||
uint32_t audio_mf_samples_wait_count = 0;
|
||||
uint32_t video_mf_samples_wait_count = 0;
|
||||
uint32_t audio_mf_samples_count = 0;
|
||||
uint32_t video_mf_samples_count = 0;
|
||||
|
||||
BOOL stop = FALSE;
|
||||
int64_t time = media_clock->GetTime();
|
||||
|
||||
if (audio_media_transform) {
|
||||
audio_mf_samples_wait_count = audio_media_transform->GetMFSamplesWaitCount();
|
||||
audio_mf_samples_count = audio_media_transform->GetMFSamplesCount();
|
||||
if (audio_media_transform->CanShutdown()
|
||||
&& (audio_sample_time == INT64_MAX || audio_sample_time <= time))
|
||||
stop = TRUE;
|
||||
}
|
||||
|
||||
if (video_media_transform) {
|
||||
video_mf_samples_wait_count = video_media_transform->GetMFSamplesWaitCount();
|
||||
video_mf_samples_count = video_media_transform->GetMFSamplesCount();
|
||||
if (video_media_transform->CanShutdown()
|
||||
&& (video_sample_time == INT64_MAX || video_sample_time <= time))
|
||||
stop = TRUE;
|
||||
}
|
||||
|
||||
if (stop)
|
||||
state.SetInnerState(State::Stop);
|
||||
|
||||
media_stats_lock.SetMFSamplesCount(audio_mf_samples_wait_count,
|
||||
video_mf_samples_wait_count, audio_mf_samples_count, video_mf_samples_count);
|
||||
}
|
||||
|
||||
void MediaSession::Process(const char* name, IMediaTransform* media_transform,
|
||||
IMediaRenderer* media_renderer, int64_t process_time) {
|
||||
if (!media_transform)
|
||||
return;
|
||||
|
||||
int64_t time = media_clock->GetTime();
|
||||
for (int64_t sample_time = media_transform->GetSampleTime();
|
||||
sample_time - process_time <= time;
|
||||
sample_time = media_transform->GetSampleTime()) {
|
||||
IMFSample* mf_sample = 0;
|
||||
media_transform->GetMFSample(mf_sample);
|
||||
if (!mf_sample)
|
||||
break;
|
||||
|
||||
int64_t sample_duration = 0;
|
||||
mf_sample->GetSampleDuration(&sample_duration);
|
||||
if (sample_time + sample_duration > time) {
|
||||
if (media_renderer)
|
||||
media_renderer->SetMFSample(mf_sample);
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
media_transform->SignalEvent();
|
||||
if (media_transform == audio_media_transform) {
|
||||
audio_sample_time = sample_time + sample_duration;
|
||||
|
||||
media_stats_lock.SetAudioTimeAdvance((double_t)(time - sample_time) * 0.0001);
|
||||
}
|
||||
else {
|
||||
video_sample_time = sample_time + sample_duration;
|
||||
|
||||
media_stats_lock.SetVideoTimeAdvance((double_t)(time - sample_time) * 0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaSession::SetProcessDisable() {
|
||||
lock.Acquire();
|
||||
process = FALSE;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaSession::SetProcessEnable() {
|
||||
lock.Acquire();
|
||||
process = TRUE;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaSession::Shutdown() {
|
||||
SetProcessDisable();
|
||||
|
||||
if (hThread) {
|
||||
SetEvent(hEvent);
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
}
|
||||
|
||||
if (video_renderer)
|
||||
video_renderer->Shutdown();
|
||||
if (wasapi_renderer)
|
||||
wasapi_renderer->Shutdown();
|
||||
if (video_media_transform)
|
||||
video_media_transform->Shutdown();
|
||||
if (audio_media_transform)
|
||||
audio_media_transform->Shutdown();
|
||||
if (media_source)
|
||||
media_source->Shutdown();
|
||||
|
||||
if (wasapi_renderer) {
|
||||
wasapi_renderer->Release();
|
||||
wasapi_renderer = 0;
|
||||
}
|
||||
|
||||
if (video_renderer) {
|
||||
video_renderer->Release();
|
||||
video_renderer = 0;
|
||||
}
|
||||
|
||||
if (video_media_transform) {
|
||||
video_media_transform->Release();
|
||||
video_media_transform = 0;
|
||||
}
|
||||
|
||||
if (audio_media_transform) {
|
||||
audio_media_transform->Release();
|
||||
audio_media_transform = 0;
|
||||
}
|
||||
|
||||
if (media_source) {
|
||||
media_source->Release();
|
||||
media_source = 0;
|
||||
}
|
||||
|
||||
state.SetInnerState(State::None);
|
||||
}
|
||||
|
||||
void MediaSession::Update() {
|
||||
lock.Acquire();
|
||||
if (process) {
|
||||
if (state.state_inner == State::Play) {
|
||||
Process("Audio", audio_media_transform, wasapi_renderer, 500000);
|
||||
Process("Video", video_media_transform, video_renderer, 100000);
|
||||
GetSamplesCount();
|
||||
}
|
||||
}
|
||||
|
||||
if (media_stats_lock.HasError())
|
||||
state.SetInnerState(State::None);
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
HRESULT MediaSession::WaitSamplesLoad(uint32_t min_audio_samples_count, uint32_t min_video_samples_count) {
|
||||
ULONGLONG tick_start = GetTickCount64();
|
||||
while (true) {
|
||||
BOOL ready = TRUE;
|
||||
if (audio_media_transform && !audio_media_transform->CanShutdown()
|
||||
&& audio_media_transform->GetMFSamplesCount() < min_audio_samples_count)
|
||||
ready = FALSE;
|
||||
|
||||
if (video_media_transform && !video_media_transform->CanShutdown()
|
||||
&& video_media_transform->GetMFSamplesCount() < min_audio_samples_count)
|
||||
ready = FALSE;
|
||||
|
||||
if (ready)
|
||||
break;
|
||||
|
||||
if (GetTickCount64() - tick_start >= 1000)
|
||||
return S_FALSE;
|
||||
|
||||
Sleep(0);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::open(const wchar_t* url) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
IMFMediaType* audio_mf_media_type = 0;
|
||||
IMFMediaType* video_mf_media_type = 0;
|
||||
IDirect3DDeviceManager9* d3d_device_manager = 0;
|
||||
IDirect3DDevice9Ex* d3d_device = 0;
|
||||
HRESULT hr;
|
||||
|
||||
const int64_t time0_begin = GetTimestamp();
|
||||
hr = MediaSource::Create(&media_stats_lock, media_clock, url, media_source);
|
||||
if (SUCCEEDED(hr)) {
|
||||
media_source->GetAudioMFMediaType(&audio_mf_media_type);
|
||||
media_source->GetVideoMFMediaType(&video_mf_media_type);
|
||||
}
|
||||
|
||||
if (video_mf_media_type && SUCCEEDED(hr)) {
|
||||
IDirect3D9Ex* d3d_device_temp = 0;
|
||||
hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d_device_temp);
|
||||
|
||||
D3DPRESENT_PARAMETERS present_param = {};
|
||||
present_param.BackBufferWidth = 1;
|
||||
present_param.BackBufferHeight = 1;
|
||||
present_param.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
present_param.Windowed = TRUE;
|
||||
present_param.SwapEffect = D3DSWAPEFFECT_COPY;
|
||||
present_param.Flags = D3DPRESENTFLAG_VIDEO;
|
||||
|
||||
UINT reset_token = 0;
|
||||
D3DCAPS9 caps = {};
|
||||
if (SUCCEEDED(hr))
|
||||
hr = d3d_device_temp->GetDeviceCaps(0, D3DDEVTYPE_HAL, &caps);
|
||||
|
||||
DWORD d3d_device_flags;
|
||||
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
|
||||
d3d_device_flags = D3DCREATE_NOWINDOWCHANGES | D3DCREATE_HARDWARE_VERTEXPROCESSING
|
||||
| D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;
|
||||
else
|
||||
d3d_device_flags = D3DCREATE_NOWINDOWCHANGES | D3DCREATE_SOFTWARE_VERTEXPROCESSING
|
||||
| D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = d3d_device_temp->CreateDeviceEx(0, D3DDEVTYPE_HAL, present_param.hDeviceWindow,
|
||||
d3d_device_flags, &present_param, 0i64, &d3d_device);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = DXVA2CreateDirect3DDeviceManager9(&reset_token, &d3d_device_manager);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = d3d_device_manager->ResetDevice(d3d_device, reset_token);
|
||||
}
|
||||
}
|
||||
|
||||
if (d3d_device_temp) {
|
||||
d3d_device_temp->Release();
|
||||
d3d_device_temp = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = VideoDecoder::Create(&media_stats_lock, media_clock, media_source,
|
||||
d3d_device_manager, d3d_device, video_media_transform);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = media_source->SetVideoMediaTransform(video_media_transform);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = VideoRenderer::Create(media_clock, video_renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_mf_media_type && SUCCEEDED(hr)) {
|
||||
hr = AudioDecoder::Create(&media_stats_lock, media_clock, media_source, audio_media_transform);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = media_source->SetAudioMediaTransform(audio_media_transform);
|
||||
if (SUCCEEDED(hr)) {
|
||||
uint32_t channels = 0;
|
||||
uint32_t sample_rate = 0;
|
||||
hr = audio_mf_media_type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &channels);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = audio_mf_media_type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &sample_rate);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = WASAPIRenderer::Create(media_clock, sample_rate, channels, wasapi_renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((video_mf_media_type || audio_mf_media_type) && SUCCEEDED(hr)) {
|
||||
duration = media_source->GetDuration();
|
||||
media_stats_lock.SetSessionInfo((double_t)duration * 0.0000001,
|
||||
!!audio_media_transform, !!video_media_transform);
|
||||
}
|
||||
|
||||
if (d3d_device) {
|
||||
d3d_device->Release();
|
||||
d3d_device = 0;
|
||||
}
|
||||
|
||||
if (d3d_device_manager) {
|
||||
d3d_device_manager->Release();
|
||||
d3d_device_manager = 0;
|
||||
}
|
||||
|
||||
if (video_mf_media_type) {
|
||||
video_mf_media_type->Release();
|
||||
video_mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (audio_mf_media_type) {
|
||||
audio_mf_media_type->Release();
|
||||
audio_mf_media_type = 0;
|
||||
}
|
||||
const int64_t time0_end = GetTimestamp();
|
||||
|
||||
const double_t time0 = CalcTimeMsec(time0_begin, time0_end);
|
||||
|
||||
const int64_t time1_begin = GetTimestamp();
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = media_clock->TimeEnd();
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = media_clock->SetTime(0);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = media_source->Start(0i64);
|
||||
}
|
||||
|
||||
if (audio_media_transform && SUCCEEDED(hr))
|
||||
hr = audio_media_transform->Open();
|
||||
|
||||
if (video_media_transform && SUCCEEDED(hr))
|
||||
hr = video_media_transform->Open();
|
||||
|
||||
if (wasapi_renderer && SUCCEEDED(hr))
|
||||
hr = wasapi_renderer->Open();
|
||||
|
||||
if (video_renderer && SUCCEEDED(hr))
|
||||
hr = video_renderer->Open();
|
||||
}
|
||||
const int64_t time1_end = GetTimestamp();
|
||||
|
||||
const double_t time1 = CalcTimeMsec(time1_begin, time1_end);
|
||||
|
||||
const int64_t time2_begin = GetTimestamp();
|
||||
if (SUCCEEDED(hr))
|
||||
hr = WaitSamplesLoad(5, 5);
|
||||
const int64_t time2_end = GetTimestamp();
|
||||
|
||||
const double_t time2 = CalcTimeMsec(time2_begin, time2_end);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
state.SetInnerState(State::Open);
|
||||
else
|
||||
state.SetInnerState(State::None);
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSession::set_position(int64_t time) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
time = clamp_def(time, 0, duration);
|
||||
|
||||
const int64_t time_begin = GetTimestamp();
|
||||
|
||||
HRESULT hr = media_clock->TimeEnd();
|
||||
if (SUCCEEDED(hr))
|
||||
hr = media_source->Stop();
|
||||
|
||||
SetProcessDisable();
|
||||
|
||||
if (audio_media_transform && SUCCEEDED(hr))
|
||||
hr = audio_media_transform->Flush();
|
||||
if (video_media_transform && SUCCEEDED(hr))
|
||||
hr = video_media_transform->Flush();
|
||||
if (wasapi_renderer && SUCCEEDED(hr))
|
||||
hr = wasapi_renderer->Flush();
|
||||
if (video_renderer && SUCCEEDED(hr))
|
||||
hr = video_renderer->Flush();
|
||||
|
||||
if (audio_media_transform && SUCCEEDED(hr))
|
||||
hr = audio_media_transform->Close();
|
||||
if (video_media_transform && SUCCEEDED(hr))
|
||||
hr = video_media_transform->Close();
|
||||
if (wasapi_renderer && SUCCEEDED(hr))
|
||||
hr = wasapi_renderer->Close();
|
||||
if (video_renderer && SUCCEEDED(hr))
|
||||
hr = video_renderer->Close();
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = media_clock->SetTime(time);
|
||||
|
||||
if (audio_media_transform && SUCCEEDED(hr))
|
||||
hr = audio_media_transform->Open();
|
||||
if (video_media_transform && SUCCEEDED(hr))
|
||||
hr = video_media_transform->Open();
|
||||
if (wasapi_renderer && SUCCEEDED(hr))
|
||||
hr = wasapi_renderer->Open();
|
||||
if (video_renderer && SUCCEEDED(hr))
|
||||
hr = video_renderer->Open();
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = media_source->Start(time);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = WaitSamplesLoad(2, 2);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IMediaTransform* media_transform = 0;
|
||||
if (video_media_transform && video_media_transform->GetMFSamplesCount())
|
||||
media_transform = video_media_transform;
|
||||
else if (audio_media_transform && audio_media_transform->GetMFSamplesCount())
|
||||
media_transform = audio_media_transform;
|
||||
|
||||
if (media_transform)
|
||||
time = media_transform->GetSampleTime();
|
||||
|
||||
hr = media_clock->SetTime(time);
|
||||
}
|
||||
|
||||
End:
|
||||
const int64_t time_end = GetTimestamp();
|
||||
|
||||
const double_t _time = CalcTimeMsec(time_begin, time_end);
|
||||
|
||||
if (SUCCEEDED(hr) && state.state_inner == State::Play) {
|
||||
SetProcessEnable();
|
||||
hr = media_clock->TimeBegin();
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return hr;
|
||||
}
|
||||
|
||||
inline void MediaSession::Destroy(MediaSession* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
uint32_t __stdcall MediaSession::ThreadMain(MediaSession* media_session) {
|
||||
DWORD task_index = 0;
|
||||
HANDLE avrt_handle = AvSetMmThreadCharacteristicsW(L"Audio", &task_index);
|
||||
if (!avrt_handle)
|
||||
#pragma warning(suppress: 6031)
|
||||
GetLastError();
|
||||
|
||||
HANDLE handles[2];
|
||||
handles[0] = media_session->hTimer;
|
||||
handles[1] = media_session->hEvent;
|
||||
while (!WaitForMultipleObjects(2, handles, 0, INFINITE))
|
||||
media_session->Update();
|
||||
|
||||
if (avrt_handle)
|
||||
AvRevertMmThreadCharacteristics(avrt_handle);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "gl_dx_interop_texture.hpp"
|
||||
#include "video_renderer.hpp"
|
||||
#include "wasapi_renderer.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
struct MediaSessionState {
|
||||
Lock lock;
|
||||
std::atomic<State> state;
|
||||
std::atomic<State> state_inner;
|
||||
int32_t command_index_queue;
|
||||
int32_t command_index;
|
||||
|
||||
inline MediaSessionState() : lock(), state(State::Init),
|
||||
state_inner(State::Init), command_index_queue(), command_index() {
|
||||
|
||||
}
|
||||
|
||||
inline void SetStateAdvance(State state) {
|
||||
lock.Acquire();
|
||||
this->state = state;
|
||||
command_index_queue++;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
inline void SetInnerState(State state) {
|
||||
lock.Acquire();
|
||||
state_inner = state;
|
||||
if (command_index == command_index_queue)
|
||||
this->state = state;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
inline void SetInnerStateAdvance(State state) {
|
||||
lock.Acquire();
|
||||
state_inner = state;
|
||||
if (++command_index == command_index_queue)
|
||||
this->state = state;
|
||||
lock.Release();
|
||||
}
|
||||
};
|
||||
|
||||
class MediaSession : public IUnknown {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
DWORD dwQueue;
|
||||
MediaSessionState state;
|
||||
BOOL process;
|
||||
MediaStatsLock media_stats_lock;
|
||||
IMediaClock* media_clock;
|
||||
IMediaSource* media_source;
|
||||
IMediaTransform* audio_media_transform;
|
||||
IMediaTransform* video_media_transform;
|
||||
WASAPIRenderer* wasapi_renderer;
|
||||
VideoRenderer* video_renderer;
|
||||
GLDXInteropTexture* interop_texture;
|
||||
int64_t duration;
|
||||
int64_t audio_sample_time;
|
||||
int64_t video_sample_time;
|
||||
HANDLE hThread;
|
||||
HANDLE hTimer;
|
||||
HANDLE hEvent;
|
||||
AsyncCallback<MediaSession> async_callback;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
MediaSession(HRESULT& hr, const wchar_t* path, IMediaClock* media_clock = 0);
|
||||
virtual ~MediaSession();
|
||||
|
||||
virtual HRESULT AsyncCallback(IMFAsyncResult* mf_async_result);
|
||||
virtual HRESULT Close();
|
||||
virtual HRESULT Play();
|
||||
virtual HRESULT Pause();
|
||||
virtual HRESULT SetTime(double_t value);
|
||||
virtual State GetState();
|
||||
virtual double_t GetDuration();
|
||||
virtual double_t GetTime();
|
||||
virtual HRESULT GetAudioParams(AudioParams* value);
|
||||
virtual HRESULT SetAudioParams(const AudioParams* value);
|
||||
virtual HRESULT GetVideoParams(VideoParams* value);
|
||||
virtual HRESULT GetD3D9Texture(IDirect3DDevice9* d3d_device, IDirect3DTexture9** ptr);
|
||||
virtual HRESULT GetD3D11Texture(ID3D11Device* d3d_device, ID3D11Texture2D** ptr);
|
||||
virtual HRESULT GetGLDXIntreropTexture(IGLDXInteropTexture** ptr);
|
||||
|
||||
void GetSamplesCount();
|
||||
void Process(const char* name, IMediaTransform* media_transform,
|
||||
IMediaRenderer* media_renderer, int64_t process_time);
|
||||
void SetProcessDisable();
|
||||
void SetProcessEnable();
|
||||
void Shutdown();
|
||||
void Update();
|
||||
HRESULT WaitSamplesLoad(uint32_t min_audio_samples_count, uint32_t min_video_samples_count);
|
||||
|
||||
HRESULT open(const wchar_t* url);
|
||||
HRESULT set_position(int64_t time);
|
||||
|
||||
static void Destroy(MediaSession* ptr);
|
||||
static uint32_t __stdcall ThreadMain(MediaSession* media_session);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "media_source.hpp"
|
||||
#include "media_foundation.hpp"
|
||||
#include <mfapi.h>
|
||||
#include <process.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT MediaSource::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMediaSource) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG MediaSource::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG MediaSource::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::Shutdown() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
lock.Acquire();
|
||||
async_lock.Acquire();
|
||||
|
||||
shutdown = TRUE;
|
||||
start = FALSE;
|
||||
|
||||
if (video_media_stream)
|
||||
video_media_stream->Shutdown();
|
||||
if (audio_media_stream)
|
||||
audio_media_stream->Shutdown();
|
||||
if (mf_media_source)
|
||||
mf_media_source->Shutdown();
|
||||
|
||||
async_lock.Release();
|
||||
lock.Release();
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::Stop() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr;
|
||||
if (shutdown)
|
||||
hr = MF_E_SHUTDOWN;
|
||||
else {
|
||||
hr = S_OK;
|
||||
|
||||
if (start) {
|
||||
start = FALSE;
|
||||
|
||||
hr = mf_media_source->Stop();
|
||||
if (SUCCEEDED(hr))
|
||||
WaitForSingleObject(hMediaSourceEvent, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::Start(INT64 time) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr;
|
||||
if (shutdown)
|
||||
hr = MF_E_SHUTDOWN;
|
||||
else {
|
||||
hr = S_OK;
|
||||
|
||||
if (!start) {
|
||||
PROPVARIANT propvar = {};
|
||||
start = TRUE;
|
||||
propvar.hVal.QuadPart = time;
|
||||
propvar.vt = VT_I8;
|
||||
hr = mf_media_source->Start(mf_presentation_descriptor, 0, &propvar);
|
||||
if (SUCCEEDED(hr))
|
||||
WaitForSingleObject(hMediaSourceEvent, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return hr;
|
||||
}
|
||||
|
||||
UINT64 MediaSource::GetDuration() {
|
||||
lock.Acquire();
|
||||
uint64_t value = this->duration;
|
||||
lock.Release();
|
||||
return value;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::GetAudioMFMediaType(IMFMediaType** ptr) {
|
||||
lock.Acquire();
|
||||
async_lock.Acquire();
|
||||
|
||||
HRESULT hr;
|
||||
if (shutdown)
|
||||
hr = MF_E_SHUTDOWN;
|
||||
else {
|
||||
hr = S_OK;
|
||||
|
||||
if (audio_mf_stream_identifier == -1)
|
||||
hr = MF_E_INVALIDSTREAMNUMBER;
|
||||
else if (audio_media_stream)
|
||||
hr = audio_media_stream->GetMFMediaType(ptr);
|
||||
else {
|
||||
*ptr = audio_mf_media_type;
|
||||
audio_mf_media_type->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
async_lock.Release();
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::GetVideoMFMediaType(IMFMediaType** ptr) {
|
||||
lock.Acquire();
|
||||
async_lock.Acquire();
|
||||
|
||||
HRESULT hr;
|
||||
if (shutdown)
|
||||
hr = MF_E_SHUTDOWN;
|
||||
else {
|
||||
hr = S_OK;
|
||||
|
||||
if (video_mf_stream_identifier == -1)
|
||||
hr = MF_E_INVALIDSTREAMNUMBER;
|
||||
else if (video_media_stream)
|
||||
hr = video_media_stream->GetMFMediaType(ptr);
|
||||
else {
|
||||
*ptr = video_mf_media_type;
|
||||
video_mf_media_type->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
async_lock.Release();
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::SetAudioMediaTransform(IMediaTransform* media_transform) {
|
||||
lock.Acquire();
|
||||
async_lock.Acquire();
|
||||
|
||||
if (!shutdown && audio_mf_stream_identifier != -1) {
|
||||
if (audio_media_transform) {
|
||||
audio_media_transform->Release();
|
||||
audio_media_transform = 0;
|
||||
}
|
||||
|
||||
audio_media_transform = media_transform;
|
||||
media_transform->AddRef();
|
||||
}
|
||||
|
||||
async_lock.Release();
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::SetVideoMediaTransform(IMediaTransform* media_transform) {
|
||||
lock.Acquire();
|
||||
async_lock.Acquire();
|
||||
|
||||
if (!shutdown && video_mf_stream_identifier != -1) {
|
||||
if (video_media_transform) {
|
||||
video_media_transform->Release();
|
||||
video_media_transform = 0;
|
||||
}
|
||||
|
||||
video_media_transform = media_transform;
|
||||
media_transform->AddRef();
|
||||
}
|
||||
|
||||
async_lock.Release();
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
MediaSource::MediaSource(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, const wchar_t* url) : ref_count(), lock(), async_lock(), media_stats_lock(),
|
||||
media_clock(), mf_media_source(), audio_mf_media_type(), video_mf_media_type(), audio_media_stream(),
|
||||
video_media_stream(), audio_media_transform(), video_media_transform(), mf_presentation_descriptor(),
|
||||
audio_mf_stream_identifier(-1), video_mf_stream_identifier(-1), shutdown(), start(), duration(),
|
||||
hThread(), hTimer(), hManualEvent(), hMediaStreamEvent(), hMediaSourceEvent(), async_callback(this) {
|
||||
LARGE_INTEGER due_time;
|
||||
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
this->media_clock = media_clock;
|
||||
media_clock->AddRef();
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hManualEvent = CreateEventA(0, TRUE, FALSE, 0);
|
||||
if (!hManualEvent)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hMediaSourceEvent = CreateEventA(0, FALSE, FALSE, 0);
|
||||
if (!hMediaSourceEvent)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hMediaStreamEvent = CreateEventA(0, FALSE, FALSE, 0);
|
||||
if (!hMediaStreamEvent)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hTimer = CreateWaitableTimerA(0, FALSE, 0);
|
||||
if (!hTimer)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
due_time = {};
|
||||
if (!SetWaitableTimer(hTimer, &due_time, 50, 0, 0, FALSE))
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hThread = (HANDLE)_beginthreadex(0, 0, (_beginthreadex_proc_type)MediaSource::ThreadMain, this, 0, 0);
|
||||
if (!hThread)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = LoadURL(url);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_media_source->BeginGetEvent(&async_callback, 0);
|
||||
End:
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
MediaSource::~MediaSource() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
shutdown = TRUE;
|
||||
start = FALSE;
|
||||
|
||||
if (video_media_stream)
|
||||
video_media_stream->Shutdown();
|
||||
if (audio_media_stream)
|
||||
audio_media_stream->Shutdown();
|
||||
if (mf_media_source)
|
||||
mf_media_source->Shutdown();
|
||||
|
||||
if (video_media_transform) {
|
||||
video_media_transform->Release();
|
||||
video_media_transform = 0;
|
||||
}
|
||||
|
||||
if (audio_media_transform) {
|
||||
audio_media_transform->Release();
|
||||
audio_media_transform = 0;
|
||||
}
|
||||
|
||||
if (video_media_stream) {
|
||||
video_media_stream->Release();
|
||||
video_media_stream = 0;
|
||||
}
|
||||
|
||||
if (audio_media_stream) {
|
||||
audio_media_stream->Release();
|
||||
audio_media_stream = 0;
|
||||
}
|
||||
|
||||
if (video_mf_media_type) {
|
||||
video_mf_media_type->Release();
|
||||
video_mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (audio_mf_media_type) {
|
||||
audio_mf_media_type->Release();
|
||||
audio_mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (mf_presentation_descriptor) {
|
||||
mf_presentation_descriptor->Release();
|
||||
mf_presentation_descriptor = 0;
|
||||
}
|
||||
|
||||
if (mf_media_source) {
|
||||
mf_media_source->Release();
|
||||
mf_media_source = 0;
|
||||
}
|
||||
|
||||
if (media_clock) {
|
||||
media_clock->Release();
|
||||
media_clock = 0;
|
||||
}
|
||||
|
||||
if (hThread) {
|
||||
SetEvent(hManualEvent);
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
hThread = 0;
|
||||
}
|
||||
|
||||
if (hTimer) {
|
||||
CloseHandle(hTimer);
|
||||
hTimer = 0;
|
||||
}
|
||||
|
||||
if (hMediaStreamEvent) {
|
||||
CloseHandle(hMediaStreamEvent);
|
||||
hMediaStreamEvent = 0;
|
||||
}
|
||||
|
||||
if (hMediaSourceEvent) {
|
||||
CloseHandle(hMediaSourceEvent);
|
||||
hMediaSourceEvent = 0;
|
||||
}
|
||||
|
||||
if (hManualEvent) {
|
||||
CloseHandle(hManualEvent);
|
||||
hManualEvent = 0;
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::AsyncCallback(IMFAsyncResult* mf_async_result) {
|
||||
async_lock.Acquire();
|
||||
HRESULT media_event_status = 0;
|
||||
MediaEventType media_event_type = 0;
|
||||
IMFMediaEvent* mf_media_event = 0;
|
||||
|
||||
HRESULT hr;
|
||||
hr = mf_media_source->EndGetEvent(mf_async_result, &mf_media_event);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_event->GetStatus(&media_event_status);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_event->GetType(&media_event_type);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
switch (media_event_type) {
|
||||
case MEError:
|
||||
hr = E_FAIL;
|
||||
if (media_event_status < 0)
|
||||
hr = media_event_status;
|
||||
break;
|
||||
case MESourceStarted:
|
||||
case MESourceStopped:
|
||||
SetEvent(hMediaSourceEvent);
|
||||
break;
|
||||
case MENewStream: {
|
||||
if (shutdown)
|
||||
break;
|
||||
|
||||
IMFMediaStream* mf_media_stream = 0;
|
||||
IMFStreamDescriptor* mf_stream_decriptor = 0;
|
||||
DWORD mf_stream_identifier = 0;
|
||||
MoviePlayLib::MediaStream** media_stream = 0;
|
||||
IMediaTransform* media_transform = 0;
|
||||
|
||||
hr = MFMediaEventQueryInterface(mf_media_event, __uuidof(IMFMediaStream), (void**)&mf_media_stream);
|
||||
if (FAILED(hr))
|
||||
goto MENewStreamEnd;
|
||||
|
||||
hr = mf_media_stream->GetStreamDescriptor(&mf_stream_decriptor);
|
||||
if (FAILED(hr))
|
||||
goto MENewStreamEnd;
|
||||
|
||||
hr = mf_stream_decriptor->GetStreamIdentifier(&mf_stream_identifier);
|
||||
if (FAILED(hr))
|
||||
goto MENewStreamEnd;
|
||||
|
||||
if (mf_stream_identifier == audio_mf_stream_identifier) {
|
||||
if (audio_media_stream) {
|
||||
audio_media_stream->Release();
|
||||
audio_media_stream = 0;
|
||||
}
|
||||
|
||||
media_stream = &audio_media_stream;
|
||||
media_transform = audio_media_transform;
|
||||
}
|
||||
else if (mf_stream_identifier == video_mf_stream_identifier) {
|
||||
if (video_media_stream) {
|
||||
video_media_stream->Release();
|
||||
video_media_stream = 0;
|
||||
}
|
||||
|
||||
media_stream = &video_media_stream;
|
||||
media_transform = video_media_transform;
|
||||
}
|
||||
else
|
||||
goto MENewStreamEnd;
|
||||
|
||||
if (media_transform)
|
||||
hr = MediaStream::Create(media_stats_lock, media_clock,
|
||||
mf_media_stream, hMediaStreamEvent, media_transform, *media_stream);
|
||||
|
||||
MENewStreamEnd:
|
||||
if (mf_stream_decriptor) {
|
||||
mf_stream_decriptor->Release();
|
||||
mf_stream_decriptor = 0;
|
||||
}
|
||||
|
||||
if (mf_media_stream) {
|
||||
mf_media_stream->Release();
|
||||
mf_media_stream = 0;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
End:
|
||||
if (mf_media_event) {
|
||||
mf_media_event->Release();
|
||||
mf_media_event = 0;
|
||||
}
|
||||
|
||||
if (!shutdown && SUCCEEDED(hr))
|
||||
hr = mf_media_source->BeginGetEvent(&async_callback, 0);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
media_stats_lock->SetCurrError(hr, this);
|
||||
|
||||
if (video_media_stream)
|
||||
video_media_stream->Shutdown();
|
||||
if (audio_media_stream)
|
||||
audio_media_stream->Shutdown();
|
||||
}
|
||||
|
||||
hr = mf_async_result->SetStatus(hr);
|
||||
async_lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::LoadURL(const wchar_t* url) {
|
||||
HRESULT hr = S_OK;
|
||||
if (!mf_media_source) {
|
||||
IMFSourceResolver* mf_source_resolver = 0;
|
||||
IUnknown* object = 0;
|
||||
MF_OBJECT_TYPE object_type = MF_OBJECT_INVALID;
|
||||
|
||||
hr = MFCreateSourceResolver(&mf_source_resolver);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_source_resolver->CreateObjectFromURL(url, MF_RESOLUTION_MEDIASOURCE, 0, &object_type, &object);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = object->QueryInterface(__uuidof(IMFMediaSource), (void**)&mf_media_source);
|
||||
}
|
||||
|
||||
if (object) {
|
||||
object->Release();
|
||||
object = 0;
|
||||
}
|
||||
|
||||
if (mf_source_resolver) {
|
||||
mf_source_resolver->Release();
|
||||
mf_source_resolver = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD v28 = 0;
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_media_source->CreatePresentationDescriptor(&mf_presentation_descriptor);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_presentation_descriptor->GetStreamDescriptorCount(&v28);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_presentation_descriptor->GetUINT64(MF_PD_DURATION, &duration);
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
for (DWORD index = 0; index < v28 && SUCCEEDED(hr); index++) {
|
||||
BOOL selected = FALSE;
|
||||
IMFStreamDescriptor* mf_stream_decriptor = 0;
|
||||
IMFMediaTypeHandler* mf_media_type_handler = 0;
|
||||
DWORD mf_stream_identifier = 0;
|
||||
|
||||
IMFMediaType* mf_media_type = 0;
|
||||
GUID guid_major_type = {};
|
||||
GUID guid_format_subtype = {};
|
||||
hr = mf_presentation_descriptor->GetStreamDescriptorByIndex(index, &selected, &mf_stream_decriptor);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_stream_decriptor->GetMediaTypeHandler(&mf_media_type_handler);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_stream_decriptor->GetStreamIdentifier(&mf_stream_identifier);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_media_type_handler->GetMajorType(&guid_major_type);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_media_type_handler->GetMediaTypeByIndex(0, &mf_media_type);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_media_type->GetGUID(MF_MT_SUBTYPE, &guid_format_subtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL select_stream = FALSE;
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (audio_mf_stream_identifier == -1
|
||||
&& guid_major_type == MFMediaType_Audio
|
||||
&& (guid_format_subtype == MFAudioFormat_WMAudioV8
|
||||
|| guid_format_subtype == MFAudioFormat_WMAudioV9
|
||||
|| guid_format_subtype == MFAudioFormat_WMAudio_Lossless
|
||||
|| guid_format_subtype == MFAudioFormat_AAC
|
||||
|| guid_format_subtype == MFAudioFormat_Vorbis
|
||||
|| guid_format_subtype == MFAudioFormat_MPEG
|
||||
|| guid_format_subtype == MFAudioFormat_MP3)) {
|
||||
audio_mf_media_type = mf_media_type;
|
||||
audio_mf_stream_identifier = mf_stream_identifier;
|
||||
mf_media_type->AddRef();
|
||||
select_stream = TRUE;
|
||||
}
|
||||
if (video_mf_stream_identifier == -1
|
||||
&& guid_major_type == MFMediaType_Video
|
||||
&& (guid_format_subtype == MFVideoFormat_WMV1
|
||||
|| guid_format_subtype == MFVideoFormat_WMV2
|
||||
|| guid_format_subtype == MFVideoFormat_WMV3
|
||||
|| guid_format_subtype == MFVideoFormat_WVC1
|
||||
|| guid_format_subtype == MFVideoFormat_H264
|
||||
|| guid_format_subtype == MFVideoFormat_VP80
|
||||
|| guid_format_subtype == MFVideoFormat_MPG1
|
||||
|| guid_format_subtype == MFVideoFormat_MPEG2)) {
|
||||
video_mf_media_type = mf_media_type;
|
||||
video_mf_stream_identifier = mf_stream_identifier;
|
||||
mf_media_type->AddRef();
|
||||
select_stream = TRUE;
|
||||
}
|
||||
|
||||
if (select_stream)
|
||||
hr = mf_presentation_descriptor->SelectStream(index);
|
||||
else
|
||||
hr = mf_presentation_descriptor->DeselectStream(index);
|
||||
}
|
||||
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (mf_media_type_handler) {
|
||||
mf_media_type_handler->Release();
|
||||
mf_media_type_handler = 0;
|
||||
}
|
||||
|
||||
if (mf_stream_decriptor) {
|
||||
mf_stream_decriptor->Release();
|
||||
mf_stream_decriptor = 0;
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaSource::Create(MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, const wchar_t* url, IMediaSource*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
MediaSource* p = new MediaSource(hr, media_stats_lock, media_clock, url);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
else
|
||||
p->Shutdown();
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void MediaSource::Destroy(MediaSource* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
uint32_t __stdcall MediaSource::ThreadMain(MediaSource* media_source) {
|
||||
HANDLE handles[3];
|
||||
handles[0] = media_source->hManualEvent;
|
||||
handles[1] = media_source->hMediaStreamEvent;
|
||||
handles[2] = media_source->hTimer;
|
||||
while (WaitForMultipleObjects(3, handles, 0, INFINITE) - 1 <= 1) {
|
||||
media_source->lock.Acquire();
|
||||
media_source->async_lock.Acquire();
|
||||
|
||||
if (!media_source->shutdown && media_source->start) {
|
||||
if (media_source->audio_media_stream)
|
||||
media_source->audio_media_stream->RequestSample();
|
||||
if (media_source->video_media_stream)
|
||||
media_source->video_media_stream->RequestSample();
|
||||
}
|
||||
|
||||
media_source->async_lock.Release();
|
||||
media_source->lock.Release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "media_stream.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class MediaSource : public IMediaSource {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
Lock async_lock;
|
||||
MediaStatsLock* media_stats_lock;
|
||||
IMediaClock* media_clock;
|
||||
IMFMediaSource* mf_media_source;
|
||||
IMFPresentationDescriptor* mf_presentation_descriptor;
|
||||
IMFMediaType* audio_mf_media_type;
|
||||
IMFMediaType* video_mf_media_type;
|
||||
MediaStream* audio_media_stream;
|
||||
MediaStream* video_media_stream;
|
||||
IMediaTransform* audio_media_transform;
|
||||
IMediaTransform* video_media_transform;
|
||||
DWORD audio_mf_stream_identifier;
|
||||
DWORD video_mf_stream_identifier;
|
||||
BOOL shutdown;
|
||||
BOOL start;
|
||||
uint64_t duration;
|
||||
HANDLE hThread;
|
||||
HANDLE hTimer;
|
||||
HANDLE hManualEvent;
|
||||
HANDLE hMediaStreamEvent;
|
||||
HANDLE hMediaSourceEvent;
|
||||
AsyncCallback<MediaSource> async_callback;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Shutdown() override;
|
||||
virtual HRESULT Stop() override;
|
||||
virtual HRESULT Start(INT64 time) override;
|
||||
virtual UINT64 GetDuration() override;
|
||||
virtual HRESULT GetAudioMFMediaType(IMFMediaType** ptr) override;
|
||||
virtual HRESULT GetVideoMFMediaType(IMFMediaType** ptr) override;
|
||||
virtual HRESULT SetAudioMediaTransform(IMediaTransform* media_transform) override;
|
||||
virtual HRESULT SetVideoMediaTransform(IMediaTransform* media_transform) override;
|
||||
|
||||
MediaSource(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, const wchar_t* url);
|
||||
virtual ~MediaSource();
|
||||
|
||||
virtual HRESULT AsyncCallback(IMFAsyncResult* mf_async_result);
|
||||
|
||||
HRESULT LoadURL(const wchar_t* url);
|
||||
|
||||
static HRESULT Create(MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, const wchar_t* url, IMediaSource*& ptr);
|
||||
static void Destroy(MediaSource* ptr);
|
||||
static uint32_t __stdcall ThreadMain(MoviePlayLib::MediaSource* media_source);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "media_stream.hpp"
|
||||
#include "media_foundation.hpp"
|
||||
#include "tracked_sample.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT MediaStream::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG MediaStream::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG MediaStream::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
MediaStream::MediaStream(HRESULT& hr, MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMFMediaStream* mf_media_stream, HANDLE hEvent, IMediaTransform* media_transform)
|
||||
: ref_count(), lock(), media_stats_lock(media_stats_lock), media_clock(), mf_media_stream(),
|
||||
media_transform(), shutdown(), sample_recieve(), hEvent(hEvent), sample_time(), async_callback(this) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
if (SUCCEEDED(hr)) {
|
||||
this->media_clock = media_clock;
|
||||
media_clock->AddRef();
|
||||
|
||||
this->mf_media_stream = mf_media_stream;
|
||||
mf_media_stream->AddRef();
|
||||
|
||||
this->media_transform = media_transform;
|
||||
media_transform->AddRef();
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = this->mf_media_stream->BeginGetEvent(&async_callback, 0);
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
MediaStream::~MediaStream() {
|
||||
|
||||
}
|
||||
|
||||
HRESULT MediaStream::AsyncCallback(IMFAsyncResult* mf_async_result) {
|
||||
lock.Acquire();
|
||||
HRESULT media_event_status = 0;
|
||||
MediaEventType media_event_type = 0;
|
||||
IMFMediaEvent* mf_media_event = 0;
|
||||
|
||||
HRESULT hr;
|
||||
hr = mf_media_stream->EndGetEvent(mf_async_result, &mf_media_event);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_event->GetStatus(&media_event_status);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_event->GetType(&media_event_type);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
switch (media_event_type) {
|
||||
case MEError:
|
||||
hr = E_FAIL;
|
||||
if (FAILED(media_event_status))
|
||||
hr = media_event_status;
|
||||
break;
|
||||
case MEStreamStarted:
|
||||
if (!shutdown) {
|
||||
sample_recieve = FALSE;
|
||||
sample_time = 0;
|
||||
RequestSampleInner();
|
||||
}
|
||||
break;
|
||||
case MEEndOfStream:
|
||||
if (!shutdown)
|
||||
media_transform->SendMFSample(0);
|
||||
break;
|
||||
case MEMediaSample:
|
||||
if (!shutdown) {
|
||||
TrackedSample* tracked_sample = 0;
|
||||
hr = MFMediaEventQueryInterface(mf_media_event, __uuidof(TrackedSample), (void**)&tracked_sample);
|
||||
if (SUCCEEDED(hr)) {
|
||||
int64_t sample_time = 0;
|
||||
if (SUCCEEDED(tracked_sample->GetSampleTime(&sample_time))) {
|
||||
media_transform->SendMFSample(tracked_sample);
|
||||
this->sample_time = sample_time;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracked_sample) {
|
||||
tracked_sample->Release();
|
||||
tracked_sample = 0;
|
||||
}
|
||||
|
||||
sample_recieve = FALSE;
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
End:
|
||||
if (mf_media_event) {
|
||||
mf_media_event->Release();
|
||||
mf_media_event = 0;
|
||||
}
|
||||
|
||||
if (!shutdown && SUCCEEDED(hr))
|
||||
hr = mf_media_stream->BeginGetEvent(&async_callback, 0);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
media_stats_lock->SetCurrError(hr, this);
|
||||
media_transform->SendMFSample(0);
|
||||
}
|
||||
|
||||
hr = mf_async_result->SetStatus(hr);
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaStream::Shutdown() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
lock.Acquire();
|
||||
media_transform->SendMFSample(0);
|
||||
shutdown = TRUE;
|
||||
lock.Release();
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MediaStream::GetMFMediaType(IMFMediaType** mf_media_type) {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown) {
|
||||
IMFStreamDescriptor* mf_stream_descriptor = 0;
|
||||
IMFMediaTypeHandler* mf_media_type_handler = 0;
|
||||
hr = mf_media_stream->GetStreamDescriptor(&mf_stream_descriptor);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_stream_descriptor->GetMediaTypeHandler(&mf_media_type_handler);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_media_type_handler->GetCurrentMediaType(mf_media_type);
|
||||
}
|
||||
|
||||
if (mf_media_type_handler) {
|
||||
mf_media_type_handler->Release();
|
||||
mf_media_type_handler = 0;
|
||||
}
|
||||
|
||||
if (mf_stream_descriptor) {
|
||||
mf_stream_descriptor->Release();
|
||||
mf_stream_descriptor = 0;
|
||||
}
|
||||
}
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MediaStream::RequestSample() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
RequestSampleInner();
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
void MediaStream::RequestSampleInner() {
|
||||
if (!shutdown && !sample_recieve
|
||||
&& sample_time < media_clock->GetTime() + 50000000
|
||||
&& SUCCEEDED(mf_media_stream->RequestSample(0)))
|
||||
sample_recieve = TRUE;
|
||||
}
|
||||
|
||||
HRESULT MediaStream::Create(MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMFMediaStream* mf_media_stream, HANDLE hEvent, IMediaTransform* media_transform, MediaStream*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
MediaStream* p = new MediaStream(hr, media_stats_lock, media_clock, mf_media_stream, hEvent, media_transform);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
else
|
||||
p->Shutdown();
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void MediaStream::Destroy(MediaStream* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <mfidl.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class MediaStream : public IUnknown {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
MediaStatsLock* media_stats_lock;
|
||||
IMediaClock* media_clock;
|
||||
IMFMediaStream* mf_media_stream;
|
||||
IMediaTransform* media_transform;
|
||||
BOOL shutdown;
|
||||
BOOL sample_recieve;
|
||||
HANDLE hEvent;
|
||||
int64_t sample_time;
|
||||
AsyncCallback<MediaStream> async_callback;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
MediaStream(HRESULT& hr, MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMFMediaStream* mf_media_stream, HANDLE hEvent, IMediaTransform* media_transform);
|
||||
virtual ~MediaStream();
|
||||
|
||||
virtual HRESULT AsyncCallback(IMFAsyncResult* mf_async_result);
|
||||
virtual HRESULT Shutdown();
|
||||
virtual HRESULT GetMFMediaType(IMFMediaType** mf_media_type);
|
||||
virtual HRESULT RequestSample();
|
||||
|
||||
void RequestSampleInner();
|
||||
|
||||
static HRESULT Create(MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMFMediaStream* mf_media_stream, HANDLE hEvent, IMediaTransform* media_transform, MediaStream*& ptr);
|
||||
static void Destroy(MediaStream* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "mf_sample_list.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
MFSampleList::MFSampleList() : lock(), index(1), shutdown(), list() {
|
||||
|
||||
}
|
||||
|
||||
MFSampleList::~MFSampleList() {
|
||||
ClearList();
|
||||
}
|
||||
|
||||
void MFSampleList::AddSample(IMFSample* mf_sample, bool check) {
|
||||
lock.Acquire();
|
||||
if (CheckVideoIndex(mf_sample, check)) {
|
||||
mf_sample->AddRef();
|
||||
list.push_back(mf_sample);
|
||||
}
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
bool MFSampleList::CheckVideoIndex(IMFSample* mf_sample, bool check) {
|
||||
if (!mf_sample)
|
||||
return false;
|
||||
|
||||
uint32_t video_index = 0;
|
||||
if (!check || SUCCEEDED(mf_sample->GetUINT32(VideoIndexGUID, &video_index)) && video_index == index)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t MFSampleList::ClearList() {
|
||||
lock.Acquire();
|
||||
while (list.size()) {
|
||||
list.front()->Release();
|
||||
list.pop_front();
|
||||
}
|
||||
uint32_t index = ++this->index;
|
||||
lock.Release();
|
||||
return index;
|
||||
}
|
||||
|
||||
IMFSample* MFSampleList::PopSample() {
|
||||
lock.Acquire();
|
||||
IMFSample* mf_sample = 0;
|
||||
if (list.size()) {
|
||||
mf_sample = list.front();
|
||||
list.pop_front();
|
||||
}
|
||||
lock.Release();
|
||||
return mf_sample;
|
||||
}
|
||||
|
||||
int64_t MFSampleList::GetSampleTime() {
|
||||
lock.Acquire();
|
||||
int64_t sample_time = INT64_MAX;
|
||||
if (list.size())
|
||||
list.front()->GetSampleTime(&sample_time);
|
||||
lock.Release();
|
||||
return sample_time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <list>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
struct MFSampleList {
|
||||
Lock lock;
|
||||
uint32_t index;
|
||||
bool shutdown;
|
||||
std::list<IMFSample*> list;
|
||||
|
||||
MFSampleList();
|
||||
~MFSampleList();
|
||||
|
||||
void AddSample(IMFSample* mf_sample, bool check);
|
||||
bool CheckVideoIndex(IMFSample* mf_sample, bool check);
|
||||
uint32_t ClearList();
|
||||
int64_t GetSampleTime();
|
||||
IMFSample* PopSample();
|
||||
|
||||
inline bool CanShutdown() {
|
||||
lock.Acquire();
|
||||
bool can_shutdown = shutdown && !list.size();
|
||||
lock.Release();
|
||||
return can_shutdown;
|
||||
}
|
||||
|
||||
inline size_t GetSamplesCount() {
|
||||
lock.Acquire();
|
||||
size_t size = list.size();
|
||||
lock.Release();
|
||||
return size;
|
||||
}
|
||||
|
||||
inline void ResetShutdown() {
|
||||
lock.Acquire();
|
||||
shutdown = FALSE;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
inline void SetShutdown() {
|
||||
lock.Acquire();
|
||||
shutdown = TRUE;
|
||||
lock.Release();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
const GUID TextureSharedHandleGUID = {
|
||||
0xB4127AF5, 0xA697, 0x4037, 0xAB, 0x09, 0xC8, 0xF4, 0xCF, 0x0F, 0x80, 0xAC,
|
||||
};
|
||||
|
||||
const GUID VideoIndexGUID = {
|
||||
0x1F42D8B6, 0x6A11, 0x4E2C, 0x91, 0x70, 0xF2, 0x01, 0x5D, 0xA7, 0x31, 0xF2
|
||||
};
|
||||
|
||||
void MediaStatsLock::GetStats(MediaStats& value) {
|
||||
lock.Acquire();
|
||||
value = stats;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetAudioParams(uint32_t sample_rate, uint32_t channels) {
|
||||
lock.Acquire();
|
||||
stats.sample_rate = sample_rate;
|
||||
stats.channels = channels;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetAudioProcessOutput(double_t output_time, double_t sample_time) {
|
||||
lock.Acquire();
|
||||
stats.audio_process_output_time = output_time;
|
||||
stats.audio_process_sample_time = sample_time;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetAudioSampleTime(double_t value) {
|
||||
lock.Acquire();
|
||||
stats.audio_sample_time = value;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetAudioTimeAdvance(double_t value) {
|
||||
lock.Acquire();
|
||||
stats.audio_time_advance = value;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
bool MediaStatsLock::HasError() {
|
||||
lock.Acquire();
|
||||
bool has_error = FAILED(stats.curr_error);
|
||||
lock.Release();
|
||||
return has_error;
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetCurrError(HRESULT hr, IUnknown* object) {
|
||||
lock.Acquire();
|
||||
stats.curr_error = hr;
|
||||
stats.curr_object = object;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetMFSamplesCount(
|
||||
uint32_t audio_mf_samples_wait_count, uint32_t video_mf_samples_wait_count,
|
||||
uint32_t audio_mf_samples_count, uint32_t video_mf_samples_count) {
|
||||
lock.Acquire();
|
||||
stats.audio_mf_samples_wait_count = audio_mf_samples_wait_count;
|
||||
stats.video_mf_samples_wait_count = video_mf_samples_wait_count;
|
||||
stats.audio_mf_samples_count = audio_mf_samples_count;
|
||||
stats.video_mf_samples_count = video_mf_samples_count;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetSessionInfo(double_t duration, bool audio_present, bool video_present) {
|
||||
lock.Acquire();
|
||||
stats.duration = duration;
|
||||
stats.audio_present = audio_present;
|
||||
stats.video_present = video_present;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetVideoOutputTime(double_t value) {
|
||||
lock.Acquire();
|
||||
stats.video_output_time = value;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetVideoParams(uint32_t width, uint32_t height, uint32_t frame_size_width,
|
||||
uint32_t frame_size_height, float_t frame_rate, float_t pixel_aspect_ratio) {
|
||||
lock.Acquire();
|
||||
stats.width = width;
|
||||
stats.height = height;
|
||||
stats.frame_size_width = frame_size_width;
|
||||
stats.frame_size_height = frame_size_height;
|
||||
stats.frame_rate = frame_rate;
|
||||
stats.pixel_aspect_ratio = pixel_aspect_ratio;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetVideoProcessOutput(double_t output_time, double_t blt_time, double_t sample_time) {
|
||||
lock.Acquire();
|
||||
stats.video_process_output_time = output_time;
|
||||
stats.video_process_blt_time = blt_time;
|
||||
stats.video_process_sample_time = sample_time;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetVideoSampleTime(double_t value) {
|
||||
lock.Acquire();
|
||||
stats.video_sample_time = value;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void MediaStatsLock::SetVideoTimeAdvance(double_t value) {
|
||||
lock.Acquire();
|
||||
stats.video_time_advance = value;
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void PrintDebug(const wchar_t* fmt, ...) {
|
||||
#ifdef DEBUG
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vwprintf(fmt, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../KKdLib/default.hpp"
|
||||
#include <unknwn.h>
|
||||
#include <mferror.h>
|
||||
#include <mfobjects.h>
|
||||
#include <d3d9.h>
|
||||
#pragma comment(lib, "d3d9.lib")
|
||||
#include <d3d11.h>
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#include <atomic>
|
||||
|
||||
struct texture;
|
||||
|
||||
#define MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN MoviePlayLib::PrintDebug(L">%S(%p)\n", __FUNCTION__, this)
|
||||
#define MOVIE_PLAY_LIB_PRINT_FUNC_END MoviePlayLib::PrintDebug(L"<%S(%p)\n", __FUNCTION__, this)
|
||||
|
||||
namespace MoviePlayLib {
|
||||
enum class State {
|
||||
None = 0x0,
|
||||
Init,
|
||||
Open,
|
||||
Wait,
|
||||
Pause,
|
||||
Play,
|
||||
Stop,
|
||||
Max,
|
||||
};
|
||||
|
||||
struct AudioParams {
|
||||
float_t spk_l_volume;
|
||||
float_t spk_r_volume;
|
||||
float_t field_8;
|
||||
float_t field_C;
|
||||
float_t hph_l_volume;
|
||||
float_t hph_r_volume;
|
||||
};
|
||||
|
||||
struct Lock : SRWLOCK {
|
||||
inline Lock() {
|
||||
InitializeSRWLock(this);
|
||||
}
|
||||
|
||||
inline void Acquire() {
|
||||
#pragma warning(suppress: 26110)
|
||||
AcquireSRWLockExclusive(this);
|
||||
}
|
||||
|
||||
inline void Release() {
|
||||
#pragma warning(suppress: 26110)
|
||||
ReleaseSRWLockExclusive(this);
|
||||
}
|
||||
};
|
||||
|
||||
struct MediaStats {
|
||||
double_t duration;
|
||||
bool audio_present;
|
||||
bool video_present;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t frame_size_width;
|
||||
uint32_t frame_size_height;
|
||||
float_t frame_rate;
|
||||
float_t pixel_aspect_ratio;
|
||||
uint32_t sample_rate;
|
||||
uint32_t channels;
|
||||
HRESULT curr_error;
|
||||
IUnknown* curr_object;
|
||||
uint32_t audio_mf_samples_wait_count;
|
||||
uint32_t video_mf_samples_wait_count;
|
||||
uint32_t audio_mf_samples_count;
|
||||
uint32_t video_mf_samples_count;
|
||||
double_t audio_sample_time;
|
||||
double_t video_sample_time;
|
||||
double_t audio_process_sample_time;
|
||||
double_t video_process_sample_time;
|
||||
double_t audio_process_output_time;
|
||||
double_t video_process_output_time;
|
||||
double_t video_process_blt_time;
|
||||
double_t audio_time_advance;
|
||||
double_t video_time_advance;
|
||||
double_t audio_output_time;
|
||||
double_t video_output_time;
|
||||
};
|
||||
|
||||
struct MediaStatsLock {
|
||||
protected:
|
||||
Lock lock;
|
||||
__int64 field_8;
|
||||
MediaStats stats;
|
||||
|
||||
public:
|
||||
inline MediaStatsLock() : field_8(), stats() {
|
||||
|
||||
}
|
||||
|
||||
void GetStats(MediaStats& value);
|
||||
bool HasError();
|
||||
void SetAudioParams(uint32_t sample_rate, uint32_t channels);
|
||||
void SetAudioProcessOutput(double_t output_time, double_t sample_time);
|
||||
void SetAudioSampleTime(double_t value);
|
||||
void SetAudioTimeAdvance(double_t value);
|
||||
void SetCurrError(HRESULT hr, IUnknown* object);
|
||||
void SetMFSamplesCount(
|
||||
uint32_t audio_mf_samples_wait_count, uint32_t video_mf_samples_wait_count,
|
||||
uint32_t audio_mf_samples_count, uint32_t video_mf_samples_count);
|
||||
void SetSessionInfo(double_t duration, bool audio_present, bool video_present);
|
||||
void SetVideoOutputTime(double_t value);
|
||||
void SetVideoParams(uint32_t width, uint32_t height, uint32_t frame_size_width,
|
||||
uint32_t frame_size_height, float_t frame_rate, float_t pixel_aspect_ratio);
|
||||
void SetVideoProcessOutput(double_t output_time, double_t blt_time, double_t sample_time);
|
||||
void SetVideoSampleTime(double_t value);
|
||||
void SetVideoTimeAdvance(double_t value);
|
||||
};
|
||||
|
||||
struct RefCount : std::atomic<ULONG> {
|
||||
RefCount() : std::atomic<ULONG>(1) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct VideoParams {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t frame_size_width;
|
||||
int32_t frame_size_height;
|
||||
float_t frame_rate;
|
||||
float_t pixel_aspect_ratio;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("BE82EF2E-4B9F-4470-9C6F-F78E002FCDEF")
|
||||
IMediaRenderer : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT Shutdown() = 0;
|
||||
virtual HRESULT Close() = 0;
|
||||
virtual HRESULT Flush() = 0;
|
||||
virtual HRESULT Open() = 0;
|
||||
virtual HRESULT SetMFSample(IMFSample* mf_sample) = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("4A5C73B9-FBFE-4A00-A298-FD3FAB0C5AF1")
|
||||
IAudioRenderer : public IMediaRenderer {
|
||||
public:
|
||||
virtual HRESULT GetParams(AudioParams* value) = 0;
|
||||
virtual HRESULT SetParams(const AudioParams* value) = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("D19AC022-EA49-4B6F-9D51-673C1C15ECE8")
|
||||
IGLDXInteropTexture : public IUnknown {
|
||||
public:
|
||||
virtual texture* GetTexture() = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("D4FA34F6-D03C-4F21-8591-6858161CA5D7")
|
||||
IMediaClock : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT TimeEnd() = 0;
|
||||
virtual HRESULT TimeBegin() = 0;
|
||||
virtual HRESULT SetTime(INT64 value) = 0;
|
||||
virtual INT64 GetTime() = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("0D41578B-4514-407D-B654-870BCCDC630A")
|
||||
IMediaTransform : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT Shutdown() = 0;
|
||||
virtual HRESULT Close() = 0;
|
||||
virtual HRESULT Flush() = 0;
|
||||
virtual HRESULT Open() = 0;
|
||||
virtual HRESULT GetMFMediaType(IMFMediaType** mf_media_type) = 0;
|
||||
virtual void SendMFSample(IMFSample* mf_sample) = 0;
|
||||
virtual BOOL SignalEvent() = 0;
|
||||
virtual BOOL CanShutdown() = 0;
|
||||
virtual UINT32 GetMFSamplesWaitCount() = 0;
|
||||
virtual UINT32 GetMFSamplesCount() = 0;
|
||||
virtual INT64 GetSampleTime() = 0;
|
||||
virtual void GetMFSample(IMFSample*& mf_sample) = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("0D41578B-4514-407D-B654-870BCCDC630A")
|
||||
IMediaSource : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT Shutdown() = 0;
|
||||
virtual HRESULT Stop() = 0;
|
||||
virtual HRESULT Start(INT64 time) = 0;
|
||||
virtual UINT64 GetDuration() = 0;
|
||||
virtual HRESULT GetAudioMFMediaType(IMFMediaType** ptr) = 0;
|
||||
virtual HRESULT GetVideoMFMediaType(IMFMediaType** ptr) = 0;
|
||||
virtual HRESULT SetAudioMediaTransform(IMediaTransform* media_transform) = 0;
|
||||
virtual HRESULT SetVideoMediaTransform(IMediaTransform* media_transform) = 0;
|
||||
};
|
||||
|
||||
class DECLSPEC_UUID("12AC5FCE-C816-47FE-84B4-4915D6B42FA7")
|
||||
IPlayer : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT Open(const wchar_t* path) = 0;
|
||||
virtual HRESULT Open(const char* path) = 0;
|
||||
virtual HRESULT Close() = 0;
|
||||
virtual HRESULT SetMediaClock(IMediaClock* media_clock) = 0;
|
||||
virtual State GetState() = 0;
|
||||
virtual HRESULT Play() = 0;
|
||||
virtual HRESULT Pause() = 0;
|
||||
virtual double_t GetDuration() = 0;
|
||||
virtual double_t GetTime() = 0;
|
||||
virtual HRESULT SetTime(double_t value) = 0;
|
||||
virtual HRESULT GetAudioParams(AudioParams* value) = 0;
|
||||
virtual HRESULT SetAudioParams(const AudioParams* value) = 0;
|
||||
virtual HRESULT GetVideoParams(VideoParams* value) = 0;
|
||||
virtual HRESULT GetD3D9Texture(IDirect3DDevice9* d3d_device, IDirect3DTexture9** ptr) = 0;
|
||||
virtual HRESULT GetD3D11Texture(ID3D11Device* d3d_device, ID3D11Texture2D** ptr) = 0;
|
||||
virtual HRESULT GetGLDXIntreropTexture(IGLDXInteropTexture** value) = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class AsyncCallback : public IMFAsyncCallback {
|
||||
public:
|
||||
HRESULT(*func)(T* ptr, IMFAsyncResult* mf_async_result);
|
||||
T* data;
|
||||
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IUnknown) || riid == __uuidof(IMFAsyncCallback)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
virtual ULONG AddRef() override {
|
||||
return data->AddRef();
|
||||
}
|
||||
|
||||
virtual ULONG Release() override {
|
||||
return data->Release();
|
||||
}
|
||||
|
||||
virtual HRESULT GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) override {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
virtual HRESULT Invoke(IMFAsyncResult* pAsyncResult) override {
|
||||
return func(data, pAsyncResult);
|
||||
}
|
||||
|
||||
AsyncCallback(T* data) : func(Callback), data(data) {
|
||||
|
||||
}
|
||||
|
||||
static HRESULT Callback(T* data, IMFAsyncResult* mf_async_result) {
|
||||
return data->AsyncCallback(mf_async_result);
|
||||
}
|
||||
};
|
||||
|
||||
inline double_t CalcTimeMsec(int64_t begin, int64_t end) {
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return (double_t)(end - begin) / (double_t)freq.QuadPart * 1000.0;
|
||||
}
|
||||
|
||||
inline int64_t GetTimestamp() {
|
||||
LARGE_INTEGER timestamp = {};
|
||||
QueryPerformanceCounter(×tamp);
|
||||
return timestamp.QuadPart;
|
||||
}
|
||||
|
||||
extern const GUID TextureSharedHandleGUID;
|
||||
extern const GUID VideoIndexGUID;
|
||||
|
||||
extern void PrintDebug(const wchar_t* fmt, ...);
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "player.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT Player::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IPlayer) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG Player::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG Player::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT Player::Open(const wchar_t* path) {
|
||||
lock.Acquire();
|
||||
|
||||
if (media_session) {
|
||||
media_session->Close();
|
||||
|
||||
if (media_session) {
|
||||
media_session->Release();
|
||||
media_session = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hr = 0;
|
||||
MediaSession* media_session = new MediaSession(hr, path, media_clock);
|
||||
if (media_session) {
|
||||
if (SUCCEEDED(hr)) {
|
||||
this->media_session = media_session;
|
||||
media_session->AddRef();
|
||||
}
|
||||
else
|
||||
media_session->Close();
|
||||
media_session->Release();
|
||||
}
|
||||
else
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::Open(const char* path) {
|
||||
if (!path)
|
||||
return E_INVALIDARG;
|
||||
|
||||
int32_t path_len = (int32_t)utf8_length(path);
|
||||
if (path_len > 0 && path_len < MAX_PATH) {
|
||||
wchar_t buf[MAX_PATH];
|
||||
int32_t buf_len = MultiByteToWideChar(0, 0, path, path_len, buf, sizeof(buf) / sizeof(wchar_t));
|
||||
if (buf_len > 0 && buf_len < MAX_PATH) {
|
||||
if (buf_len >= MAX_PATH)
|
||||
__debugbreak();
|
||||
buf[buf_len] = 0;
|
||||
return Open(buf);
|
||||
}
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT Player::Close() {
|
||||
lock.Acquire();
|
||||
if (media_session) {
|
||||
media_session->Close();
|
||||
|
||||
if (media_session) {
|
||||
media_session->Release();
|
||||
media_session = 0;
|
||||
}
|
||||
}
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Player::SetMediaClock(IMediaClock* media_clock) {
|
||||
lock.Acquire();
|
||||
HRESULT hr = S_OK;
|
||||
if (!media_session) {
|
||||
if (this->media_clock) {
|
||||
this->media_clock->Release();
|
||||
this->media_clock = 0;
|
||||
}
|
||||
|
||||
this->media_clock = media_clock;
|
||||
|
||||
if (media_clock)
|
||||
media_clock->AddRef();
|
||||
}
|
||||
else
|
||||
hr = MF_E_INVALIDREQUEST;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
State Player::GetState() {
|
||||
lock.Acquire();
|
||||
State state;
|
||||
if (media_session)
|
||||
state = media_session->GetState();
|
||||
else
|
||||
state = State::None;
|
||||
lock.Release();
|
||||
return state;
|
||||
}
|
||||
|
||||
HRESULT Player::Play() {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->Play();
|
||||
else
|
||||
hr = MF_E_NOT_INITIALIZED;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::Pause() {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->Pause();
|
||||
else
|
||||
hr = MF_E_NOT_INITIALIZED;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
double_t Player::GetDuration() {
|
||||
lock.Acquire();
|
||||
double_t duration;
|
||||
if (media_session)
|
||||
duration = media_session->GetDuration();
|
||||
else
|
||||
duration = 0.0;
|
||||
lock.Release();
|
||||
return duration;
|
||||
}
|
||||
|
||||
double_t Player::GetTime() {
|
||||
lock.Acquire();
|
||||
double_t time;
|
||||
if (media_session)
|
||||
time = media_session->GetTime();
|
||||
else
|
||||
time = 0.0;
|
||||
lock.Release();
|
||||
return time;
|
||||
}
|
||||
|
||||
HRESULT Player::SetTime(double_t value) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->SetTime(value);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::GetAudioParams(AudioParams* value) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->GetAudioParams(value);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::SetAudioParams(const AudioParams* value) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->SetAudioParams(value);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::GetVideoParams(VideoParams* value) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->GetVideoParams(value);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::GetD3D9Texture(IDirect3DDevice9* d3d_device, IDirect3DTexture9** ptr) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->GetD3D9Texture(d3d_device, ptr);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::GetD3D11Texture(ID3D11Device* d3d_device, ID3D11Texture2D** ptr) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->GetD3D11Texture(d3d_device, ptr);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Player::GetGLDXIntreropTexture(IGLDXInteropTexture** value) {
|
||||
lock.Acquire();
|
||||
HRESULT hr;
|
||||
if (media_session)
|
||||
hr = media_session->GetGLDXIntreropTexture(value);
|
||||
else
|
||||
hr = S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
Player::Player() : ref_count(), lock(), media_clock(), media_session() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
|
||||
if (media_session) {
|
||||
media_session->Close();
|
||||
|
||||
if (media_session) {
|
||||
media_session->Release();
|
||||
media_session = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (media_clock) {
|
||||
media_clock->Release();
|
||||
media_clock = 0;
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT Player::Create(IPlayer*& ptr) {
|
||||
Player* p = new Player;
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void Player::Destroy(Player* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "media_session.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class Player : public IPlayer {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
IMediaClock* media_clock;
|
||||
MediaSession* media_session;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Open(const wchar_t* path) override;
|
||||
virtual HRESULT Open(const char* path) override;
|
||||
virtual HRESULT Close() override;
|
||||
virtual HRESULT SetMediaClock(IMediaClock* media_clock) override;
|
||||
virtual State GetState() override;
|
||||
virtual HRESULT Play() override;
|
||||
virtual HRESULT Pause() override;
|
||||
virtual double_t GetDuration() override;
|
||||
virtual double_t GetTime() override;
|
||||
virtual HRESULT SetTime(double_t value) override;
|
||||
virtual HRESULT GetAudioParams(AudioParams* value) override;
|
||||
virtual HRESULT SetAudioParams(const AudioParams* value) override;
|
||||
virtual HRESULT GetVideoParams(VideoParams* value) override;
|
||||
virtual HRESULT GetD3D9Texture(IDirect3DDevice9* d3d_device, IDirect3DTexture9** ptr) override;
|
||||
virtual HRESULT GetD3D11Texture(ID3D11Device* d3d_device, ID3D11Texture2D** ptr) override;
|
||||
virtual HRESULT GetGLDXIntreropTexture(IGLDXInteropTexture** value) override;
|
||||
|
||||
Player();
|
||||
virtual ~Player();
|
||||
|
||||
static HRESULT Create(IPlayer*& ptr);
|
||||
static void Destroy(Player* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "tracked_sample.hpp"
|
||||
#include <mfapi.h>
|
||||
#pragma comment(lib, "mfplat.lib")
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT TrackedSample::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(TrackedSample)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IMFTrackedSample)) {
|
||||
IMFTrackedSample* mf_tracked_sample = this;
|
||||
*ppvObject = mf_tracked_sample;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IMFAsyncResult)) {
|
||||
IMFAsyncResult* mf_async_result = this;
|
||||
*ppvObject = mf_async_result;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
else if (riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG TrackedSample::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG TrackedSample::Release() {
|
||||
if (!--ref_count) {
|
||||
if (mf_async_callback) {
|
||||
AddRef();
|
||||
|
||||
IMFAsyncCallback* mf_async_callback = this->mf_async_callback;
|
||||
this->mf_async_callback = 0;
|
||||
mf_async_callback->Invoke(this);
|
||||
mf_async_callback->Release();
|
||||
return Release();
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetItem(const IID& guidKey, PROPVARIANT* pValue) {
|
||||
return mf_attributes->GetItem(guidKey, pValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetItemType(const GUID& guidKey, MF_ATTRIBUTE_TYPE* pType) {
|
||||
return mf_attributes->GetItemType(guidKey, pType);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::CompareItem(const GUID& guidKey, const PROPVARIANT& Value, BOOL* pbResult) {
|
||||
return mf_attributes->CompareItem(guidKey, Value, pbResult);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL* pbResult) {
|
||||
return mf_attributes->Compare(pTheirs, MatchType, pbResult);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetUINT32(const GUID& guidKey, UINT32* punValue) {
|
||||
return mf_attributes->GetUINT32(guidKey, punValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetUINT64(const GUID& guidKey, UINT64* punValue) {
|
||||
return mf_attributes->GetUINT64(guidKey, punValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetDouble(const GUID& guidKey, double* pfValue) {
|
||||
return mf_attributes->GetDouble(guidKey, pfValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetGUID(const GUID& guidKey, GUID* pguidValue) {
|
||||
return mf_attributes->GetGUID(guidKey, pguidValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetStringLength(const GUID& guidKey, UINT32* pcchLength) {
|
||||
return mf_attributes->GetStringLength(guidKey, pcchLength);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetString(const GUID& guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength) {
|
||||
return mf_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetAllocatedString(const GUID& guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) {
|
||||
return mf_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetBlobSize(const GUID& guidKey, UINT32* pcbBlobSize) {
|
||||
return mf_attributes->GetBlobSize(guidKey, pcbBlobSize);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetBlob(const GUID& guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize) {
|
||||
return mf_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetAllocatedBlob(const GUID& guidKey, UINT8** ppBuf, UINT32* pcbSize) {
|
||||
return mf_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetUnknown(const GUID& guidKey, const IID& riid, LPVOID* ppv) {
|
||||
return mf_attributes->GetUnknown(guidKey, riid, ppv);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetItem(const GUID& guidKey, const PROPVARIANT& Value) {
|
||||
return mf_attributes->SetItem(guidKey, Value);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::DeleteItem(const GUID& guidKey) {
|
||||
return mf_attributes->DeleteItem(guidKey);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::DeleteAllItems() {
|
||||
return mf_attributes->DeleteAllItems();
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetUINT32(const GUID& guidKey, UINT32 unValue) {
|
||||
return mf_attributes->SetUINT32(guidKey, unValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetUINT64(const GUID& guidKey, UINT64 unValue) {
|
||||
return mf_attributes->SetUINT64(guidKey, unValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetDouble(const GUID& guidKey, double fValue) {
|
||||
return mf_attributes->SetDouble(guidKey, fValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetGUID(const GUID& guidKey, const GUID& guidValue) {
|
||||
return mf_attributes->SetGUID(guidKey, guidValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetString(const GUID& guidKey, LPCWSTR wszValue) {
|
||||
return mf_attributes->SetString(guidKey, wszValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetBlob(const GUID& guidKey, const UINT8* pBuf, UINT32 cbBufSize) {
|
||||
return mf_attributes->SetBlob(guidKey, pBuf, cbBufSize);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetUnknown(const GUID& guidKey, IUnknown* pUnknown) {
|
||||
return mf_attributes->SetUnknown(guidKey, pUnknown);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::LockStore() {
|
||||
return mf_attributes->LockStore();
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::UnlockStore() {
|
||||
return mf_attributes->UnlockStore();
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetCount(UINT32* pcItems) {
|
||||
return mf_attributes->GetCount(pcItems);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) {
|
||||
return mf_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::CopyAllItems(IMFAttributes* pDest) {
|
||||
return mf_attributes->CopyAllItems(pDest);
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetSampleFlags(DWORD* pdwSampleFlags) {
|
||||
*pdwSampleFlags = sample_flags;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetSampleFlags(DWORD dwSampleFlags) {
|
||||
sample_flags = dwSampleFlags;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetSampleTime(LONGLONG* phnsSampleTime) {
|
||||
*phnsSampleTime = sample_time;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetSampleTime(LONGLONG hnsSampleTime) {
|
||||
sample_time = hnsSampleTime;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetSampleDuration(LONGLONG* phnsSampleDuration) {
|
||||
*phnsSampleDuration = sample_duration;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetSampleDuration(LONGLONG hnsSampleDuration) {
|
||||
sample_duration = hnsSampleDuration;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetBufferCount(DWORD* pdwBufferCount) {
|
||||
*pdwBufferCount = 1;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetBufferByIndex(DWORD dwIndex, IMFMediaBuffer** ppBuffer) {
|
||||
if (dwIndex != 0)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*ppBuffer = mf_media_buffer;
|
||||
mf_media_buffer->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::ConvertToContiguousBuffer(IMFMediaBuffer** ppBuffer) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::AddBuffer(IMFMediaBuffer* pBuffer) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::RemoveBufferByIndex(DWORD dwIndex) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::RemoveAllBuffers() {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetTotalLength(DWORD* pcbTotalLength) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::CopyToBuffer(IMFMediaBuffer* pBuffer) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetAllocator(IMFAsyncCallback* pSampleAllocator, IUnknown* pUnkState) {
|
||||
if (mf_async_callback) {
|
||||
mf_async_callback->Release();
|
||||
mf_async_callback = 0;
|
||||
}
|
||||
|
||||
if (pSampleAllocator) {
|
||||
mf_async_callback = pSampleAllocator;
|
||||
pSampleAllocator->AddRef();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetState(IUnknown** ppunkState) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetStatus() {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::SetStatus(HRESULT hrStatus) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::GetObject(IUnknown** ppObject) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IUnknown* TrackedSample::GetStateNoAddRef() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TrackedSample::TrackedSample(HRESULT& hr, IMFMediaBuffer* mf_media_buffer) : ref_count(), mf_attributes(),
|
||||
mf_media_buffer(), mf_async_callback(), sample_time(), sample_duration(), sample_flags() {
|
||||
hr = MFCreateAttributes(&mf_attributes, 1);
|
||||
if (SUCCEEDED(hr)) {
|
||||
this->mf_media_buffer = mf_media_buffer;
|
||||
mf_media_buffer->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
TrackedSample::~TrackedSample() {
|
||||
if (mf_attributes) {
|
||||
mf_attributes->Release();
|
||||
mf_attributes = 0;
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
|
||||
if (mf_async_callback) {
|
||||
mf_async_callback->Release();
|
||||
mf_async_callback = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT TrackedSample::Create(IMFMediaBuffer* mf_media_buffer, uint32_t video_index, IMFSample*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
TrackedSample* p = new TrackedSample(hr, mf_media_buffer);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = p->SetUINT32(VideoIndexGUID, video_index);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void TrackedSample::Destroy(TrackedSample* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef UNICODE
|
||||
#include "movie_play_lib.hpp"
|
||||
#include <mfidl.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class DECLSPEC_UUID("C40A00F2-B93A-4D80-AE8C-5A1C634F58E4")
|
||||
TrackedSample : public IMFSample, public IMFTrackedSample, public IMFAsyncResult {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
IMFAttributes* mf_attributes;
|
||||
IMFMediaBuffer* mf_media_buffer;
|
||||
IMFAsyncCallback* mf_async_callback;
|
||||
int64_t sample_time;
|
||||
int64_t sample_duration;
|
||||
uint32_t sample_flags;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT GetItem(const GUID& guidKey, PROPVARIANT* pValue) override;
|
||||
virtual HRESULT GetItemType(const GUID& guidKey, MF_ATTRIBUTE_TYPE* pType) override;
|
||||
virtual HRESULT CompareItem(const GUID& guidKey, const PROPVARIANT& Value, BOOL* pbResult) override;
|
||||
virtual HRESULT Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL* pbResult) override;
|
||||
virtual HRESULT GetUINT32(const GUID& guidKey, UINT32* punValue) override;
|
||||
virtual HRESULT GetUINT64(const GUID& guidKey, UINT64* punValue) override;
|
||||
virtual HRESULT GetDouble(const GUID& guidKey, double* pfValue) override;
|
||||
virtual HRESULT GetGUID(const GUID& guidKey, GUID* pguidValue) override;
|
||||
virtual HRESULT GetStringLength(const GUID& guidKey, UINT32* pcchLength) override;
|
||||
virtual HRESULT GetString(const GUID& guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength) override;
|
||||
virtual HRESULT GetAllocatedString(const GUID& guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) override;
|
||||
virtual HRESULT GetBlobSize(const GUID& guidKey, UINT32* pcbBlobSize) override;
|
||||
virtual HRESULT GetBlob(const GUID& guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize) override;
|
||||
virtual HRESULT GetAllocatedBlob(const GUID& guidKey, UINT8** ppBuf, UINT32* pcbSize) override;
|
||||
virtual HRESULT GetUnknown(const GUID& guidKey, const IID& riid, LPVOID* ppv) override;
|
||||
virtual HRESULT SetItem(const GUID& guidKey, const PROPVARIANT& Value) override;
|
||||
virtual HRESULT DeleteItem(const GUID& guidKey) override;
|
||||
virtual HRESULT DeleteAllItems() override;
|
||||
virtual HRESULT SetUINT32(const GUID& guidKey, UINT32 unValue) override;
|
||||
virtual HRESULT SetUINT64(const GUID& guidKey, UINT64 unValue) override;
|
||||
virtual HRESULT SetDouble(const GUID& guidKey, double fValue) override;
|
||||
virtual HRESULT SetGUID(const GUID& guidKey, const GUID& guidValue) override;
|
||||
virtual HRESULT SetString(const GUID& guidKey, LPCWSTR wszValue) override;
|
||||
virtual HRESULT SetBlob(const GUID& guidKey, const UINT8* pBuf, UINT32 cbBufSize) override;
|
||||
virtual HRESULT SetUnknown(const GUID& guidKey, IUnknown* pUnknown) override;
|
||||
virtual HRESULT LockStore() override;
|
||||
virtual HRESULT UnlockStore() override;
|
||||
virtual HRESULT GetCount(UINT32* pcItems) override;
|
||||
virtual HRESULT GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) override;
|
||||
virtual HRESULT CopyAllItems(IMFAttributes* pDest) override;
|
||||
|
||||
virtual HRESULT GetSampleFlags(DWORD* pdwSampleFlags) override;
|
||||
virtual HRESULT SetSampleFlags(DWORD dwSampleFlags) override;
|
||||
virtual HRESULT GetSampleTime(LONGLONG* phnsSampleTime) override;
|
||||
virtual HRESULT SetSampleTime(LONGLONG hnsSampleTime) override;
|
||||
virtual HRESULT GetSampleDuration(LONGLONG* phnsSampleDuration) override;
|
||||
virtual HRESULT SetSampleDuration(LONGLONG hnsSampleDuration) override;
|
||||
virtual HRESULT GetBufferCount(DWORD* pdwBufferCount) override;
|
||||
virtual HRESULT GetBufferByIndex(DWORD dwIndex, IMFMediaBuffer** ppBuffer) override;
|
||||
virtual HRESULT ConvertToContiguousBuffer(IMFMediaBuffer** ppBuffer) override;
|
||||
virtual HRESULT AddBuffer(IMFMediaBuffer* pBuffer) override;
|
||||
virtual HRESULT RemoveBufferByIndex(DWORD dwIndex) override;
|
||||
virtual HRESULT RemoveAllBuffers() override;
|
||||
virtual HRESULT GetTotalLength(DWORD* pcbTotalLength) override;
|
||||
virtual HRESULT CopyToBuffer(IMFMediaBuffer* pBuffer) override;
|
||||
|
||||
virtual HRESULT SetAllocator(IMFAsyncCallback* pSampleAllocator, IUnknown* pUnkState) override;
|
||||
|
||||
virtual HRESULT GetState(IUnknown** ppunkState) override;
|
||||
virtual HRESULT GetStatus() override;
|
||||
virtual HRESULT SetStatus(HRESULT hrStatus) override;
|
||||
virtual HRESULT GetObject(IUnknown** ppObject) override;
|
||||
virtual IUnknown* GetStateNoAddRef() override;
|
||||
|
||||
TrackedSample(HRESULT& hr, IMFMediaBuffer* mf_media_buffer);
|
||||
virtual ~TrackedSample();
|
||||
|
||||
static HRESULT Create(IMFMediaBuffer* mf_media_buffer, uint32_t video_index, IMFSample*& ptr);
|
||||
static void Destroy(TrackedSample* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "transform_base.hpp"
|
||||
#include <avrt.h>
|
||||
#pragma comment(lib, "avrt.lib")
|
||||
#include <process.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT TransformBase::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMediaTransform) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG TransformBase::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG TransformBase::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::Shutdown() {
|
||||
lock.Acquire();
|
||||
Free();
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::Close() {
|
||||
lock.Acquire();
|
||||
if (!shutdown)
|
||||
CommandFlush();
|
||||
HRESULT hr = CommandFlush();
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::Flush() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::Open() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown && !end_streaming) {
|
||||
end_streaming = TRUE;
|
||||
hr = Start();
|
||||
}
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::GetMFMediaType(IMFMediaType** mf_media_type) {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown)
|
||||
hr = mf_transform->GetOutputCurrentType(0, mf_media_type);
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
void TransformBase::SendMFSample(IMFSample* mf_sample) {
|
||||
if (mf_sample)
|
||||
sample_list_wait.AddSample(mf_sample, false);
|
||||
else
|
||||
sample_list_wait.SetShutdown();
|
||||
|
||||
SetEvent(hEvent);
|
||||
|
||||
if (mf_sample) {
|
||||
int64_t sample_time = 0;
|
||||
mf_sample->GetSampleTime(&sample_time);
|
||||
SetSampleTime((double_t)sample_time * 0.0000001);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL TransformBase::SignalEvent() {
|
||||
return SetEvent(hEvent);
|
||||
}
|
||||
|
||||
BOOL TransformBase::CanShutdown() {
|
||||
return sample_list_active.CanShutdown();
|
||||
}
|
||||
|
||||
UINT32 TransformBase::GetMFSamplesWaitCount() {
|
||||
return (uint32_t)sample_list_wait.GetSamplesCount();
|
||||
}
|
||||
|
||||
UINT32 TransformBase::GetMFSamplesCount() {
|
||||
return (uint32_t)sample_list_active.GetSamplesCount();
|
||||
}
|
||||
|
||||
INT64 TransformBase::GetSampleTime() {
|
||||
return sample_list_active.GetSampleTime();
|
||||
}
|
||||
|
||||
void TransformBase::GetMFSample(IMFSample*& mf_sample) {
|
||||
mf_sample = sample_list_active.PopSample();
|
||||
}
|
||||
|
||||
TransformBase::TransformBase(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source) : ref_count(), lock(),
|
||||
media_stats_lock(media_stats_lock), media_clock(), media_source(), mf_transform(), stream_state(),
|
||||
shutdown(), end_streaming(), hThread(), hEvent(), sample_list_wait(), sample_list_active() {
|
||||
if (SUCCEEDED(hr)) {
|
||||
hEvent = CreateEventA(0, FALSE, FALSE, 0);
|
||||
if (!hEvent)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hThread = (HANDLE)_beginthreadex(0, 0, (_beginthreadex_proc_type)TransformBase::ThreadMain, this, 0, 0);
|
||||
if (!hThread)
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
this->media_clock = media_clock;
|
||||
media_clock->AddRef();
|
||||
this->media_source = media_source;
|
||||
media_source->AddRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransformBase::~TransformBase() {
|
||||
if (hThread) {
|
||||
SetEvent(hEvent);
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
hThread = 0;
|
||||
}
|
||||
|
||||
if (hEvent) {
|
||||
CloseHandle(hEvent);
|
||||
hEvent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformBase::Free() {
|
||||
CommandFlush();
|
||||
|
||||
if (mf_transform) {
|
||||
mf_transform->Release();
|
||||
mf_transform = 0;
|
||||
}
|
||||
|
||||
if (media_source) {
|
||||
media_source->Release();
|
||||
media_source = 0;
|
||||
}
|
||||
|
||||
if (media_clock) {
|
||||
media_clock->Release();
|
||||
media_clock = 0;
|
||||
}
|
||||
|
||||
shutdown = TRUE;
|
||||
end_streaming = FALSE;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::CommandFlush() {
|
||||
sample_list_wait.ClearList();
|
||||
sample_list_active.ClearList();
|
||||
|
||||
HRESULT hr;
|
||||
if (mf_transform)
|
||||
hr = mf_transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||
else
|
||||
hr = S_OK;
|
||||
stream_state = 0;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::NotifyEndOfStream() {
|
||||
HRESULT hr = mf_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::NotifyEndStreaming() {
|
||||
return mf_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
|
||||
}
|
||||
|
||||
HRESULT TransformBase::Start() {
|
||||
HRESULT hr = mf_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
|
||||
stream_state = 0;
|
||||
|
||||
sample_list_wait.ResetShutdown();
|
||||
sample_list_active.ResetShutdown();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT TransformBase::ProcessInput() {
|
||||
while (!stream_state) {
|
||||
IMFSample* mf_sample = sample_list_wait.PopSample();
|
||||
if (mf_sample) {
|
||||
if (SUCCEEDED(mf_transform->ProcessInput(0, mf_sample, 0)))
|
||||
stream_state = 1;
|
||||
}
|
||||
else {
|
||||
if (!sample_list_wait.CanShutdown())
|
||||
return S_FALSE;
|
||||
|
||||
NotifyEndOfStream();
|
||||
stream_state = 2;
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
BOOL TransformBase::Playback() {
|
||||
BOOL shutdown = FALSE;
|
||||
HRESULT hr = S_OK;
|
||||
do
|
||||
{
|
||||
lock.Acquire();
|
||||
if (this->shutdown) {
|
||||
hr = MF_E_SHUTDOWN;
|
||||
shutdown = TRUE;
|
||||
}
|
||||
else if (!end_streaming)
|
||||
hr = S_FALSE;
|
||||
else if (stream_state || (hr = ProcessInput()) == S_OK) {
|
||||
if ((!stream_state || stream_state == 3
|
||||
|| (hr = ProcessOutput()) == S_OK) && stream_state == 3) {
|
||||
sample_list_active.SetShutdown();
|
||||
hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
lock.Release();
|
||||
Sleep(0);
|
||||
} while (hr == S_OK);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
media_stats_lock->SetCurrError(hr, this);
|
||||
sample_list_active.SetShutdown();
|
||||
}
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
inline void TransformBase::Destroy(TransformBase* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
uint32_t __stdcall TransformBase::ThreadMain(TransformBase* transform_base) {
|
||||
DWORD task_index = 0;
|
||||
HANDLE avrt_handle = AvSetMmThreadCharacteristicsW(L"Playback", &task_index);
|
||||
if (!avrt_handle)
|
||||
#pragma warning(suppress: 6031)
|
||||
GetLastError();
|
||||
|
||||
while (!WaitForSingleObject(transform_base->hEvent, INFINITE))
|
||||
if (transform_base->Playback())
|
||||
break;
|
||||
|
||||
if (avrt_handle)
|
||||
AvRevertMmThreadCharacteristics(avrt_handle);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "mf_sample_list.hpp"
|
||||
#include <mftransform.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class TransformBase : public IMediaTransform {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
MediaStatsLock* media_stats_lock;
|
||||
IMediaClock* media_clock;
|
||||
IMediaSource* media_source;
|
||||
IMFTransform* mf_transform;
|
||||
int32_t stream_state;
|
||||
BOOL shutdown;
|
||||
BOOL end_streaming;
|
||||
HANDLE hThread;
|
||||
HANDLE hEvent;
|
||||
MFSampleList sample_list_wait;
|
||||
MFSampleList sample_list_active;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Shutdown() override;
|
||||
virtual HRESULT Close() override;
|
||||
virtual HRESULT Flush() override;
|
||||
virtual HRESULT Open() override;
|
||||
virtual HRESULT GetMFMediaType(IMFMediaType** mf_media_type) override;
|
||||
virtual void SendMFSample(IMFSample* mf_sample) override;
|
||||
virtual BOOL SignalEvent() override;
|
||||
virtual BOOL CanShutdown() override;
|
||||
virtual UINT32 GetMFSamplesWaitCount() override;
|
||||
virtual UINT32 GetMFSamplesCount() override;
|
||||
virtual INT64 GetSampleTime() override;
|
||||
virtual void GetMFSample(IMFSample*& mf_sample) override;
|
||||
|
||||
TransformBase(HRESULT& hr, MediaStatsLock* media_stats_lock,
|
||||
IMediaClock* media_clock, IMediaSource* media_source);
|
||||
virtual ~TransformBase();
|
||||
|
||||
virtual void Free();
|
||||
virtual HRESULT CommandFlush();
|
||||
virtual HRESULT NotifyEndOfStream();
|
||||
virtual HRESULT NotifyEndStreaming();
|
||||
virtual HRESULT Start();
|
||||
virtual HRESULT ProcessInput();
|
||||
virtual HRESULT ProcessOutput() = 0;
|
||||
virtual void SetSampleTime(double_t value) = 0;
|
||||
virtual BOOL Playback();
|
||||
|
||||
static void Destroy(TransformBase* ptr);
|
||||
static uint32_t __stdcall ThreadMain(TransformBase* transform_base);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "video_decoder.hpp"
|
||||
#include "dx_surface_buffer.hpp"
|
||||
#include "media_foundation.hpp"
|
||||
#include "tracked_sample.hpp"
|
||||
#include <mfapi.h>
|
||||
|
||||
namespace MoviePlayLib {
|
||||
VideoDecoder::VideoDecoder(HRESULT& hr, MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMediaSource* media_source, IDirect3DDeviceManager9* d3d_device_manager, IDirect3DDevice9* d3d_device)
|
||||
: TransformBase(hr, media_stats_lock, media_clock, media_source), sub_type(MFVideoFormat_NV12),
|
||||
d3d_device_manager(), d3d_device(), dx_video_processor(), buffer_size(), width(), height(),
|
||||
frame_size_width(), frame_size_height(), frame_rate(), frame_rate_numerator(), frame_rate_denominator(),
|
||||
pixel_aspect_ratio_numerator(), pixel_aspect_ratio_denominator(), video_area(), dxva2_video_desc(),
|
||||
dxva2_video_process_blt_params(), dxva2_video_sample(), sample_list(), async_callback(this) {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
if (SUCCEEDED(hr)) {
|
||||
IMFMediaType* media_type = 0;
|
||||
hr = media_source->GetVideoMFMediaType(&media_type);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = GetMFTransform(media_type, sub_type, mf_transform);
|
||||
|
||||
if (media_type) {
|
||||
media_type->Release();
|
||||
media_type = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
this->d3d_device_manager = d3d_device_manager;
|
||||
d3d_device_manager->AddRef();
|
||||
this->d3d_device = d3d_device;
|
||||
d3d_device->AddRef();
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)d3d_device_manager);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = InitFromMediaSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
VideoDecoder::~VideoDecoder() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT VideoDecoder::ProcessOutput() {
|
||||
if (sample_list.GetSamplesCount() < 3)
|
||||
return TRUE;
|
||||
|
||||
IMFSample* mf_sample = sample_list.PopSample();
|
||||
if (!mf_sample)
|
||||
return TRUE;
|
||||
|
||||
HRESULT hr = MFSampleSetAllocator(mf_sample, &async_callback);
|
||||
|
||||
MFT_OUTPUT_DATA_BUFFER output_sample = {};
|
||||
DWORD output_status = 0;
|
||||
|
||||
int64_t process_output_begin;
|
||||
HRESULT process_output_hr;
|
||||
int64_t process_output_end;
|
||||
|
||||
int64_t sample_time;
|
||||
int64_t sample_duration;
|
||||
DWORD sample_flags;
|
||||
IDirect3DSurface9* src_surface;
|
||||
IDirect3DSurface9* dst_surface;
|
||||
IMFMediaBuffer* mf_media_buffer;
|
||||
IMFGetService* mf_get_service;
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
process_output_begin = GetTimestamp();
|
||||
process_output_hr = mf_transform->ProcessOutput(0, 1, &output_sample, &output_status);
|
||||
process_output_end = GetTimestamp();
|
||||
|
||||
if (FAILED(process_output_hr)) {
|
||||
switch (process_output_hr) {
|
||||
case MF_E_TRANSFORM_STREAM_CHANGE:
|
||||
hr = InitFromMediaSource();
|
||||
break;
|
||||
case MF_E_TRANSFORM_NEED_MORE_INPUT:
|
||||
hr = S_OK;
|
||||
if (stream_state == 2)
|
||||
stream_state = 3;
|
||||
else
|
||||
stream_state = 0;
|
||||
break;
|
||||
default:
|
||||
hr = S_FALSE;
|
||||
stream_state = 0;
|
||||
break;
|
||||
}
|
||||
goto End;
|
||||
}
|
||||
|
||||
sample_time = 0;
|
||||
sample_duration = 0;
|
||||
sample_flags = 0;
|
||||
hr = output_sample.pSample->GetSampleTime(&sample_time);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = output_sample.pSample->GetSampleDuration(&sample_duration);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = output_sample.pSample->GetSampleFlags(&sample_flags);
|
||||
}
|
||||
|
||||
if (sample_duration < 1)
|
||||
sample_duration = frame_rate;
|
||||
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_sample->SetSampleTime(sample_time);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_sample->SetSampleDuration(sample_duration);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_sample->SetSampleFlags(sample_flags);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
mf_media_buffer = 0;
|
||||
dst_surface = 0;
|
||||
hr = mf_sample->GetBufferByIndex(0, &mf_media_buffer);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_media_buffer->QueryInterface(__uuidof(IDirect3DSurface9), (void**)&dst_surface);
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
|
||||
mf_media_buffer = 0;
|
||||
mf_get_service = 0;
|
||||
src_surface = 0;
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = output_sample.pSample->GetBufferByIndex(0, &mf_media_buffer);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_media_buffer->QueryInterface(__uuidof(IMFGetService), (void**)&mf_get_service);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mf_get_service->GetService(MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&src_surface);
|
||||
}
|
||||
}
|
||||
|
||||
if (mf_get_service) {
|
||||
mf_get_service->Release();
|
||||
mf_get_service = 0;
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
dxva2_video_sample.SrcSurface = src_surface;
|
||||
const int64_t video_process_blt_begin = GetTimestamp();
|
||||
hr = dx_video_processor->VideoProcessBlt(dst_surface, &dxva2_video_process_blt_params, &dxva2_video_sample, 1, 0);
|
||||
const int64_t video_process_blt_end = GetTimestamp();
|
||||
if (SUCCEEDED(hr)) {
|
||||
sample_list_active.AddSample(mf_sample, false);
|
||||
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
media_stats_lock->SetVideoProcessOutput(
|
||||
CalcTimeMsec(process_output_begin, process_output_end),
|
||||
CalcTimeMsec(video_process_blt_begin, video_process_blt_end),
|
||||
(double_t)sample_time * 0.0000001);
|
||||
}
|
||||
}
|
||||
|
||||
if (src_surface) {
|
||||
src_surface->Release();
|
||||
src_surface = 0;
|
||||
}
|
||||
|
||||
if (dst_surface) {
|
||||
dst_surface->Release();
|
||||
dst_surface = 0;
|
||||
}
|
||||
|
||||
End:
|
||||
if (output_sample.pSample) {
|
||||
output_sample.pSample->Release();
|
||||
output_sample.pSample = 0;
|
||||
}
|
||||
|
||||
if (output_sample.pEvents) {
|
||||
output_sample.pEvents->Release();
|
||||
output_sample.pEvents = 0;
|
||||
}
|
||||
|
||||
mf_sample->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
void VideoDecoder::SetSampleTime(double_t value) {
|
||||
media_stats_lock->SetVideoSampleTime(value);
|
||||
}
|
||||
|
||||
HRESULT VideoDecoder::AsyncCallback(IMFAsyncResult* mf_async_result) {
|
||||
TrackedSample* tracked_sample = 0;
|
||||
if (SUCCEEDED(mf_async_result->QueryInterface(__uuidof(TrackedSample),
|
||||
(void**)&tracked_sample)) && tracked_sample)
|
||||
sample_list.AddSample(tracked_sample, true);
|
||||
|
||||
if (tracked_sample) {
|
||||
tracked_sample->Release();
|
||||
tracked_sample = 0;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VideoDecoder::InitFromMediaSource() {
|
||||
IMFMediaType* video_mf_media_type = 0;
|
||||
IMFMediaType* mf_media_type = 0;
|
||||
|
||||
HRESULT hr;
|
||||
hr = media_source->GetVideoMFMediaType(&video_mf_media_type);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = MoviePlayLib::GetMFMediaType(mf_transform, sub_type, mf_media_type);
|
||||
|
||||
uint32_t buffer_size = 0;
|
||||
MFT_OUTPUT_STREAM_INFO stream_info = {};
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_transform->GetOutputStreamInfo(0, &stream_info);
|
||||
buffer_size = stream_info.cbSize;
|
||||
}
|
||||
this->buffer_size = buffer_size;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
uint64_t frame_size = 0;
|
||||
hr = mf_media_type->GetUINT64(MF_MT_FRAME_SIZE, &frame_size);
|
||||
if (SUCCEEDED(hr)) {
|
||||
frame_size_height = (uint32_t)frame_size;
|
||||
frame_size_width = (uint32_t)(frame_size >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
width = frame_size_width;
|
||||
height = frame_size_height;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
uint64_t frame_rate = 0;
|
||||
if (SUCCEEDED(mf_media_type->GetUINT64(MF_MT_FRAME_RATE, &frame_rate))) {
|
||||
frame_rate_denominator = (uint32_t)frame_rate;
|
||||
frame_rate_numerator = (uint32_t)(frame_rate >> 32);
|
||||
}
|
||||
else {
|
||||
frame_rate_numerator = 60000;
|
||||
frame_rate_denominator = 1001;
|
||||
}
|
||||
|
||||
this->frame_rate = MFllMulDiv(frame_rate_denominator, 10000000, frame_rate_numerator, 0);
|
||||
|
||||
if (SUCCEEDED(mf_media_type->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
|
||||
(UINT8*)&video_area, sizeof(video_area), 0))) {
|
||||
if (video_area.Area.cx > 0 && video_area.Area.cy > 0) {
|
||||
frame_size_width = video_area.Area.cx;
|
||||
frame_size_height = video_area.Area.cy;
|
||||
}
|
||||
}
|
||||
else {
|
||||
video_area.OffsetX = {};
|
||||
video_area.OffsetY = {};
|
||||
video_area.Area.cx = frame_size_width;
|
||||
video_area.Area.cy = frame_size_height;
|
||||
}
|
||||
|
||||
uint64_t pixel_aspect_ratio = 0;
|
||||
if (SUCCEEDED(mf_media_type->GetUINT64(MF_MT_PIXEL_ASPECT_RATIO, &pixel_aspect_ratio))) {
|
||||
pixel_aspect_ratio_denominator = (uint32_t)pixel_aspect_ratio;
|
||||
pixel_aspect_ratio_numerator = (uint32_t)(pixel_aspect_ratio >> 32);
|
||||
}
|
||||
else {
|
||||
pixel_aspect_ratio_numerator = 1;
|
||||
pixel_aspect_ratio_denominator = 1;
|
||||
}
|
||||
|
||||
dxva2_video_desc = {};
|
||||
dxva2_video_desc.SampleWidth = frame_size_width;
|
||||
dxva2_video_desc.SampleHeight = frame_size_height;
|
||||
|
||||
dxva2_video_desc.SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
|
||||
dxva2_video_desc.SampleFormat.VideoChromaSubsampling
|
||||
= DXVA2_VideoChromaSubsampling_ProgressiveChroma | DXVA2_VideoChromaSubsampling_Vertically_Cosited;
|
||||
dxva2_video_desc.SampleFormat.VideoLighting = DXVA2_VideoLighting_dim;
|
||||
|
||||
uint32_t nominal_range;
|
||||
if (FAILED(mf_media_type->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominal_range)))
|
||||
nominal_range = DXVA2_NominalRange_Normal;
|
||||
dxva2_video_desc.SampleFormat.NominalRange = nominal_range;
|
||||
|
||||
uint32_t video_primaries;
|
||||
if (FAILED(mf_media_type->GetUINT32(MF_MT_VIDEO_PRIMARIES, &video_primaries)))
|
||||
video_primaries = DXVA2_VideoPrimaries_BT709;
|
||||
dxva2_video_desc.SampleFormat.VideoPrimaries = video_primaries;
|
||||
|
||||
uint32_t video_transfer_matrix;
|
||||
if (FAILED(mf_media_type->GetUINT32(MF_MT_YUV_MATRIX, &video_transfer_matrix)))
|
||||
video_transfer_matrix = DXVA2_VideoTransferMatrix_BT709;
|
||||
dxva2_video_desc.SampleFormat.VideoTransferMatrix = video_transfer_matrix;
|
||||
|
||||
uint32_t video_transfer_function;
|
||||
if (FAILED(mf_media_type->GetUINT32(MF_MT_TRANSFER_FUNCTION, &video_transfer_function)))
|
||||
video_transfer_function = DXVA2_VideoTransFunc_709;
|
||||
dxva2_video_desc.SampleFormat.VideoTransferFunction = video_transfer_function;
|
||||
|
||||
dxva2_video_desc.Format = (D3DFORMAT)sub_type.Data1;
|
||||
dxva2_video_desc.InputSampleFreq.Numerator = frame_rate_numerator;
|
||||
dxva2_video_desc.InputSampleFreq.Denominator = frame_rate_denominator;
|
||||
dxva2_video_desc.OutputFrameFreq.Numerator = frame_rate_numerator;
|
||||
dxva2_video_desc.OutputFrameFreq.Denominator = frame_rate_denominator;
|
||||
|
||||
if (dx_video_processor) {
|
||||
dx_video_processor->Release();
|
||||
dx_video_processor = 0;
|
||||
}
|
||||
|
||||
hr = GetDirectXVideoProcessor(d3d_device_manager, &dxva2_video_desc, dx_video_processor);
|
||||
if (SUCCEEDED(hr)) {
|
||||
DXVA2_ValueRange proc_amp_brightness = {};
|
||||
DXVA2_ValueRange proc_amp_contrast = {};
|
||||
DXVA2_ValueRange proc_amp_hue = {};
|
||||
DXVA2_ValueRange proc_amp_saturation = {};
|
||||
dx_video_processor->GetProcAmpRange(DXVA2_ProcAmp_Brightness, &proc_amp_brightness);
|
||||
dx_video_processor->GetProcAmpRange(DXVA2_ProcAmp_Contrast, &proc_amp_contrast);
|
||||
dx_video_processor->GetProcAmpRange(DXVA2_ProcAmp_Hue, &proc_amp_hue);
|
||||
dx_video_processor->GetProcAmpRange(DXVA2_ProcAmp_Saturation, &proc_amp_saturation);
|
||||
|
||||
dxva2_video_process_blt_params = {};
|
||||
dxva2_video_process_blt_params.TargetFrame = 0;
|
||||
dxva2_video_process_blt_params.TargetRect.left = 0;
|
||||
dxva2_video_process_blt_params.TargetRect.top = 0;
|
||||
dxva2_video_process_blt_params.TargetRect.right = frame_size_width;
|
||||
dxva2_video_process_blt_params.TargetRect.bottom = frame_size_height;
|
||||
dxva2_video_process_blt_params.BackgroundColor = { 0x8000, 0x8000, 0x1000, 0xFFFF };
|
||||
dxva2_video_process_blt_params.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
|
||||
dxva2_video_process_blt_params.DestFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_Unknown;
|
||||
dxva2_video_process_blt_params.DestFormat.NominalRange = DXVA2_NominalRange_Normal;
|
||||
dxva2_video_process_blt_params.DestFormat.VideoTransferMatrix = DXVA2_VideoTransferMatrix_Unknown;
|
||||
dxva2_video_process_blt_params.DestFormat.VideoLighting = DXVA2_VideoLighting_dim;
|
||||
dxva2_video_process_blt_params.DestFormat.VideoPrimaries = DXVA2_VideoPrimaries_Unknown;
|
||||
dxva2_video_process_blt_params.DestFormat.VideoTransferFunction = DXVA2_VideoTransFunc_Unknown;
|
||||
dxva2_video_process_blt_params.ProcAmpValues.Brightness = proc_amp_brightness.DefaultValue;
|
||||
dxva2_video_process_blt_params.ProcAmpValues.Contrast = proc_amp_contrast.DefaultValue;
|
||||
dxva2_video_process_blt_params.ProcAmpValues.Hue = proc_amp_hue.DefaultValue;
|
||||
dxva2_video_process_blt_params.ProcAmpValues.Saturation = proc_amp_saturation.DefaultValue;
|
||||
dxva2_video_process_blt_params.Alpha.ll = 0x10000;
|
||||
|
||||
dxva2_video_sample = {};
|
||||
dxva2_video_sample.Start = 0;
|
||||
dxva2_video_sample.End = 0;
|
||||
dxva2_video_sample.SampleFormat = dxva2_video_desc.SampleFormat;
|
||||
dxva2_video_sample.SrcSurface = 0;
|
||||
dxva2_video_sample.SrcRect.left = video_area.OffsetX.value;
|
||||
dxva2_video_sample.SrcRect.top = video_area.OffsetY.value;
|
||||
dxva2_video_sample.SrcRect.right = video_area.OffsetX.value + frame_size_width;
|
||||
dxva2_video_sample.SrcRect.bottom = video_area.OffsetY.value + frame_size_height;
|
||||
dxva2_video_sample.DstRect.left = 0;
|
||||
dxva2_video_sample.DstRect.top = 0;
|
||||
dxva2_video_sample.DstRect.right = frame_size_width;
|
||||
dxva2_video_sample.DstRect.bottom = frame_size_height;
|
||||
dxva2_video_sample.PlanarAlpha.ll = 0x10000;
|
||||
|
||||
uint32_t video_index = sample_list.ClearList();
|
||||
for (int32_t i = 0; i < 10; i++) {
|
||||
IMFSample* mf_sample = 0;
|
||||
IMFMediaBuffer* mf_media_buffer = 0;
|
||||
IDirect3DTexture9* d3d_texture = 0;
|
||||
HANDLE v59 = 0;
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = d3d_device->CreateTexture(frame_size_width, frame_size_height,
|
||||
1, 1, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &d3d_texture, &v59);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = DXSurfaceBuffer::Create(d3d_texture, mf_media_buffer);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = TrackedSample::Create(mf_media_buffer, video_index, mf_sample);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = mf_sample->SetUINT64(TextureSharedHandleGUID, (UINT64)v59);
|
||||
if (SUCCEEDED(hr))
|
||||
sample_list.AddSample(mf_sample, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d3d_texture) {
|
||||
d3d_texture->Release();
|
||||
d3d_texture = 0;
|
||||
}
|
||||
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
media_stats_lock->SetVideoParams(frame_size_width, frame_size_height, frame_size_width, frame_size_height,
|
||||
(float_t)(int32_t)frame_rate_numerator / (float_t)(int32_t)frame_rate_denominator,
|
||||
(float_t)(int32_t)pixel_aspect_ratio_numerator / (float_t)(int32_t)pixel_aspect_ratio_denominator);
|
||||
}
|
||||
}
|
||||
|
||||
if (mf_media_type) {
|
||||
mf_media_type->Release();
|
||||
mf_media_type = 0;
|
||||
}
|
||||
|
||||
if (video_mf_media_type) {
|
||||
video_mf_media_type->Release();
|
||||
video_mf_media_type = 0;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT VideoDecoder::Create(MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMediaSource* media_source, IDirect3DDeviceManager9* d3d_device_manager,
|
||||
IDirect3DDevice9* d3d_device, IMediaTransform*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
VideoDecoder* p = new VideoDecoder(hr, media_stats_lock,
|
||||
media_clock, media_source, d3d_device_manager, d3d_device);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
else
|
||||
p->Shutdown();
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void VideoDecoder::Destroy(VideoDecoder* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "dxva2.hpp"
|
||||
#include "transform_base.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class VideoDecoder : public TransformBase {
|
||||
protected:
|
||||
GUID sub_type;
|
||||
IDirect3DDeviceManager9* d3d_device_manager;
|
||||
IDirect3DDevice9* d3d_device;
|
||||
IDirectXVideoProcessor* dx_video_processor;
|
||||
uint32_t buffer_size;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t frame_size_width;
|
||||
uint32_t frame_size_height;
|
||||
int64_t frame_rate;
|
||||
uint32_t frame_rate_numerator;
|
||||
uint32_t frame_rate_denominator;
|
||||
uint32_t pixel_aspect_ratio_numerator;
|
||||
uint32_t pixel_aspect_ratio_denominator;
|
||||
MFVideoArea video_area;
|
||||
DXVA2_VideoDesc dxva2_video_desc;
|
||||
DXVA2_VideoProcessBltParams dxva2_video_process_blt_params;
|
||||
DXVA2_VideoSample dxva2_video_sample;
|
||||
MFSampleList sample_list;
|
||||
AsyncCallback<VideoDecoder> async_callback;
|
||||
|
||||
public:
|
||||
VideoDecoder(HRESULT& hr, MediaStatsLock* media_stats_lock, IMediaClock* media_clock,
|
||||
IMediaSource* media_source, IDirect3DDeviceManager9* d3d_device_manager, IDirect3DDevice9* d3d_device);
|
||||
virtual ~VideoDecoder() override;
|
||||
|
||||
virtual HRESULT ProcessOutput() override;
|
||||
virtual void SetSampleTime(double_t value) override;
|
||||
|
||||
virtual HRESULT AsyncCallback(IMFAsyncResult* mf_async_result);
|
||||
|
||||
HRESULT InitFromMediaSource();
|
||||
|
||||
static HRESULT Create(MediaStatsLock* media_stats_lock, IMediaClock* media_clock, IMediaSource* media_source,
|
||||
IDirect3DDeviceManager9* d3d_device_manager, IDirect3DDevice9* d3d_device, IMediaTransform*& ptr);
|
||||
static void Destroy(VideoDecoder* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "video_renderer.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT VideoRenderer::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IMediaRenderer) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG VideoRenderer::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG VideoRenderer::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::Shutdown() {
|
||||
lock.Acquire();
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
shutdown = TRUE;
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::Close() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown)
|
||||
if (mf_sample) {
|
||||
mf_sample->Release();
|
||||
mf_sample = 0;
|
||||
}
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::Flush() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::Open() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::SetMFSample(IMFSample* mf_sample) {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : 0;
|
||||
if (!shutdown) {
|
||||
if (mf_sample)
|
||||
mf_sample->Release();
|
||||
|
||||
this->mf_sample = mf_sample;
|
||||
mf_sample->AddRef();
|
||||
|
||||
sample_recieve = TRUE;
|
||||
}
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
VideoRenderer::VideoRenderer(IMediaClock* media_clock) : ref_count(),
|
||||
lock(), shutdown(), sample_recieve(), mf_sample() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
VideoRenderer::~VideoRenderer() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::GetMFSample(IMFSample** ptr) {
|
||||
lock.Acquire();
|
||||
bool ret = !sample_recieve;
|
||||
sample_recieve = FALSE;
|
||||
|
||||
*ptr = mf_sample;
|
||||
|
||||
if (mf_sample)
|
||||
mf_sample->AddRef();
|
||||
lock.Release();
|
||||
return ret ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
HRESULT VideoRenderer::Create(IMediaClock* media_clock, VideoRenderer*& ptr) {
|
||||
VideoRenderer* p = new VideoRenderer(media_clock);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void VideoRenderer::Destroy(VideoRenderer* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class VideoRenderer : public IMediaRenderer {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
BOOL shutdown;
|
||||
BOOL sample_recieve;
|
||||
IMFSample* mf_sample;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Shutdown() override;
|
||||
virtual HRESULT Close() override;
|
||||
virtual HRESULT Flush() override;
|
||||
virtual HRESULT Open() override;
|
||||
virtual HRESULT SetMFSample(IMFSample* mf_sample) override;
|
||||
|
||||
VideoRenderer(IMediaClock* media_clock);
|
||||
virtual ~VideoRenderer();
|
||||
|
||||
virtual HRESULT GetMFSample(IMFSample** ptr);
|
||||
|
||||
static HRESULT Create(IMediaClock* media_clock, VideoRenderer*& ptr);
|
||||
static void Destroy(VideoRenderer* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "wasapi_renderer.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
HRESULT WASAPIRenderer::QueryInterface(const IID& riid, void** ppvObject) {
|
||||
if (!ppvObject)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (riid == __uuidof(IAudioRenderer) || riid == __uuidof(IUnknown)) {
|
||||
*ppvObject = this;
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = 0;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG WASAPIRenderer::AddRef() {
|
||||
return ++ref_count;
|
||||
}
|
||||
|
||||
ULONG WASAPIRenderer::Release() {
|
||||
if (!--ref_count)
|
||||
Destroy();
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::Shutdown() {
|
||||
lock.Acquire();
|
||||
Stop();
|
||||
|
||||
if (streaming_channel) {
|
||||
streaming_channel->SetResetOne();
|
||||
streaming_channel = 0;
|
||||
}
|
||||
|
||||
shutdown = TRUE;
|
||||
sample_recieve = FALSE;
|
||||
lock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::Close() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown)
|
||||
Stop();
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::Flush() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown && sample_recieve)
|
||||
sample_recieve = FALSE;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::Open() {
|
||||
lock.Acquire();
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown && !sample_recieve)
|
||||
sample_recieve = TRUE;
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::SetMFSample(IMFSample* mf_sample) {
|
||||
lock.Acquire();
|
||||
|
||||
HRESULT hr = shutdown ? MF_E_SHUTDOWN : S_OK;
|
||||
if (!shutdown) {
|
||||
IMFMediaBuffer* mf_media_buffer = 0;
|
||||
DWORD current_length = 0;
|
||||
hr = mf_sample->ConvertToContiguousBuffer(&mf_media_buffer);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
hr = mf_media_buffer->GetCurrentLength(¤t_length);
|
||||
if (FAILED(hr))
|
||||
goto End;
|
||||
|
||||
if (current_length / (sizeof(float_t) * channels) && media_buffer_count < 10) {
|
||||
media_buffers[media_buffer_count] = mf_media_buffer;
|
||||
media_buffers[media_buffer_count]->AddRef();
|
||||
media_buffer_count++;
|
||||
}
|
||||
|
||||
End:
|
||||
if (mf_media_buffer) {
|
||||
mf_media_buffer->Release();
|
||||
mf_media_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::GetParams(AudioParams* value) {
|
||||
lock.Acquire();
|
||||
*value = params;
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::SetParams(const AudioParams* value) {
|
||||
lock.Acquire();
|
||||
params = *value;
|
||||
lock.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
WASAPIRenderer::WASAPIRenderer(HRESULT& hr, int32_t sample_rate, int32_t channels) : ref_count(),
|
||||
lock(), shutdown(), sample_recieve(), params(), channels(channels), sample_rate(sample_rate),
|
||||
streaming_channel(), media_buffers(), media_buffer_count(), curr_media_buffer(), buffer(), samples_count() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
if (SUCCEEDED(hr)) {
|
||||
params.spk_l_volume = 1.0f;
|
||||
params.spk_r_volume = 1.0f;
|
||||
params.field_8 = 1.0f;
|
||||
params.field_C = 1.0f;
|
||||
params.hph_l_volume = 1.0f;
|
||||
params.hph_r_volume = 1.0f;
|
||||
memset(media_buffers, 0, sizeof(media_buffers));
|
||||
|
||||
sound::wasapi::System* system = sound_wasapi_system_data_get();
|
||||
if (system) {
|
||||
if (sample_rate != system->config.sample_rate)
|
||||
hr = MF_E_INVALIDTYPE;
|
||||
|
||||
sound::wasapi::Mixer* mixer = system->mixer;
|
||||
if (mixer) {
|
||||
size_t streaming_channels_count = mixer->streaming_channels_count;
|
||||
if (streaming_channels_count) {
|
||||
sound::wasapi::StreamingChannel* streaming_channel
|
||||
= mixer->GetStreamingChannel(streaming_channels_count - 1);
|
||||
this->streaming_channel = streaming_channel;
|
||||
if (streaming_channel) {
|
||||
streaming_channel->SetMasterVolume(1.0f);
|
||||
streaming_channel->SetCallback(WASAPIRenderer::FillBufferStatic, this);
|
||||
}
|
||||
else
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
WASAPIRenderer::~WASAPIRenderer() {
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_BEGIN;
|
||||
MOVIE_PLAY_LIB_PRINT_FUNC_END;
|
||||
}
|
||||
|
||||
void WASAPIRenderer::FillBuffer(sound_buffer_data* buffer, size_t samples_count) {
|
||||
lock.Acquire();
|
||||
|
||||
const int8_t* channel_map = 0;
|
||||
switch (channels) {
|
||||
case 1: {
|
||||
static const int8_t channel_map_mono[] = { 0, 0, 0, 0 };
|
||||
channel_map = channel_map_mono;
|
||||
} break;
|
||||
case 2: {
|
||||
static const int8_t channel_map_stereo[] = { 0, 1, 0, 1 };
|
||||
channel_map = channel_map_stereo;
|
||||
} break;
|
||||
case 4: {
|
||||
static const int8_t channel_map_quad[] = { 0, 1, 2, 3 };
|
||||
channel_map = channel_map_quad;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (channel_map && !shutdown && sample_recieve)
|
||||
while (samples_count) {
|
||||
while (!curr_media_buffer) {
|
||||
if (!media_buffer_count)
|
||||
break;
|
||||
|
||||
curr_media_buffer = media_buffers[0];
|
||||
for (size_t j = 1; j < media_buffer_count; j++)
|
||||
media_buffers[j - 1] = media_buffers[j];
|
||||
media_buffer_count--;
|
||||
|
||||
BYTE* buffer = 0;
|
||||
DWORD current_length = 0;
|
||||
if (SUCCEEDED(curr_media_buffer->Lock(&buffer, 0, ¤t_length))) {
|
||||
this->buffer = (float_t*)buffer;
|
||||
this->samples_count = current_length / (4ULL * channels);
|
||||
}
|
||||
else {
|
||||
curr_media_buffer->Release();
|
||||
curr_media_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!curr_media_buffer)
|
||||
break;
|
||||
|
||||
size_t _samples_count = min_def(samples_count, this->samples_count);
|
||||
|
||||
const float_t* src_buffer = this->buffer;
|
||||
for (size_t v20 = _samples_count; v20; v20--, src_buffer += channels, buffer++) {
|
||||
buffer->spk_l = src_buffer[channel_map[0]] * this->params.spk_l_volume;
|
||||
buffer->spk_r = src_buffer[channel_map[1]] * this->params.spk_r_volume;
|
||||
buffer->hph_l = src_buffer[channel_map[2]] * this->params.hph_l_volume;
|
||||
buffer->hph_r = src_buffer[channel_map[3]] * this->params.hph_r_volume;
|
||||
}
|
||||
this->buffer = src_buffer;
|
||||
|
||||
this->samples_count -= _samples_count;
|
||||
samples_count -= _samples_count;
|
||||
|
||||
if (!this->samples_count) {
|
||||
curr_media_buffer->Unlock();
|
||||
curr_media_buffer->Release();
|
||||
curr_media_buffer = 0;
|
||||
this->buffer = 0;
|
||||
this->samples_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (samples_count--)
|
||||
*buffer++ = {};
|
||||
|
||||
lock.Release();
|
||||
}
|
||||
|
||||
void WASAPIRenderer::Stop() {
|
||||
if (curr_media_buffer) {
|
||||
curr_media_buffer->Unlock();
|
||||
curr_media_buffer->Release();
|
||||
curr_media_buffer = 0;
|
||||
|
||||
buffer = 0;
|
||||
samples_count = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < media_buffer_count; i++) {
|
||||
media_buffers[i]->Release();
|
||||
media_buffers[i] = 0;
|
||||
}
|
||||
media_buffer_count = 0;
|
||||
}
|
||||
|
||||
HRESULT WASAPIRenderer::Create(IMediaClock* media_clock,
|
||||
int32_t sample_rate, int32_t channels, WASAPIRenderer*& ptr) {
|
||||
HRESULT hr = S_OK;
|
||||
WASAPIRenderer* p = new WASAPIRenderer(hr, sample_rate, channels);
|
||||
if (!p)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
ptr = p;
|
||||
p->AddRef();
|
||||
}
|
||||
else
|
||||
p->Shutdown();
|
||||
|
||||
p->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline void WASAPIRenderer::Destroy(WASAPIRenderer* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void WASAPIRenderer::FillBufferStatic(sound_buffer_data* buffer, size_t samples_count, void* data) {
|
||||
if (data)
|
||||
((WASAPIRenderer*)data)->FillBuffer(buffer, samples_count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "movie_play_lib.hpp"
|
||||
#include "../../CRE/sound.hpp"
|
||||
|
||||
namespace MoviePlayLib {
|
||||
class WASAPIRenderer : public IAudioRenderer {
|
||||
protected:
|
||||
RefCount ref_count;
|
||||
Lock lock;
|
||||
BOOL shutdown;
|
||||
BOOL sample_recieve;
|
||||
AudioParams params;
|
||||
uint32_t channels;
|
||||
int32_t sample_rate;
|
||||
sound::wasapi::StreamingChannel* streaming_channel;
|
||||
IMFMediaBuffer* media_buffers[10];
|
||||
size_t media_buffer_count;
|
||||
IMFMediaBuffer* curr_media_buffer;
|
||||
const float_t* buffer;
|
||||
size_t samples_count;
|
||||
|
||||
public:
|
||||
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override;
|
||||
virtual ULONG AddRef() override;
|
||||
virtual ULONG Release() override;
|
||||
|
||||
virtual HRESULT Shutdown() override;
|
||||
virtual HRESULT Close() override;
|
||||
virtual HRESULT Flush() override;
|
||||
virtual HRESULT Open() override;
|
||||
virtual HRESULT SetMFSample(IMFSample* mf_sample) override;
|
||||
|
||||
virtual HRESULT GetParams(AudioParams* value) override;
|
||||
virtual HRESULT SetParams(const AudioParams* value) override;
|
||||
|
||||
WASAPIRenderer(HRESULT& hr, int32_t sample_rate, int32_t channels);
|
||||
virtual ~WASAPIRenderer();
|
||||
|
||||
void FillBuffer(sound_buffer_data* buffer, size_t samples_count);
|
||||
void Stop();
|
||||
|
||||
static HRESULT Create(IMediaClock* media_clock,
|
||||
int32_t sample_rate, int32_t channels, WASAPIRenderer*& ptr);
|
||||
static void Destroy(WASAPIRenderer* ptr);
|
||||
static void FillBufferStatic(sound_buffer_data* buffer, size_t samples_count, void* data);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "../game_state.hpp"
|
||||
#include "../imgui_helper.hpp"
|
||||
#include "../input.hpp"
|
||||
#include "../task_movie.hpp"
|
||||
#include "player_data.hpp"
|
||||
#include "pv_game_camera.hpp"
|
||||
#include "pv_game_music.hpp"
|
||||
@@ -301,7 +302,7 @@ disp_lyrics_now(), field_2D093(), field_2D094(), field_2D095(), field_2D096(), s
|
||||
field_2D09C(), use_osage_play_data(), pv_end_fadeout(), rival_percentage(), field_2D0A8(), field_2D0AC(),
|
||||
field_2D0B0(), field_2D0BC(), next_stage(), has_frame_texture(), field_2D0BF(), movie_index(), field_2D0C4(),
|
||||
field_2D7E8(), field_2D7EC(), field_2D808(), field_2D80C(), edit_effect(), camera_auth_3d_uid(),
|
||||
se_index(), task_effect_init(), field_2D87C(), current_field(), field_2D8A0(), field_2D8A4(),
|
||||
se_index(), task_effect_init(), field_2D87C(), current_field(), enable_movie(), field_2D8A4(),
|
||||
campv_index(), field_2D954(), field_2DAC8(), field_2DACC(), height_adjust(), field_2DB2C(), field_2DB34() {
|
||||
disp_lyrics = true;
|
||||
}
|
||||
@@ -1541,10 +1542,10 @@ int32_t pv_game::ctrl(float_t delta_time, int64_t curr_time) {
|
||||
if (data.play_data.field_64C) {
|
||||
if (!data.field_2D093)
|
||||
data.pv_data.ctrl(delta_time, curr_time, false);
|
||||
/*else
|
||||
for (int32_t i = 0; i < 2; i++)
|
||||
else
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(i)))
|
||||
task_movie_get(i)->sub_14041F4E0();*/
|
||||
task_movie_get(i)->Unload();
|
||||
|
||||
if (!sub_14013C8C0()->sub_1400E7920()) {
|
||||
hit_state hit_state = HIT_MAX;
|
||||
@@ -2553,27 +2554,28 @@ bool pv_game::load() {
|
||||
case 2: {
|
||||
const pv_db_pv_difficulty* diff = get_pv_db_pv()->get_difficulty(
|
||||
sub_14013C8C0()->difficulty, sub_14013C8C0()->edition);
|
||||
/*if (diff && diff->movie_list.size()) {
|
||||
if (diff && diff->movie_list.size()) {
|
||||
resolution_struct* res_wind = res_window_get();
|
||||
resolution_struct* res_wind_int = res_window_internal_get();
|
||||
|
||||
struc_15 v750;
|
||||
v750.field_0.field_0.x = 0.0f;
|
||||
v750.field_0.field_0.y = (float_t)(res_wind->height - res_wind_int->height) * 0.5f;
|
||||
v750.field_0.field_8.x = (float_t)res_wind_int->width;
|
||||
v750.field_0.field_8.y = (float_t)res_wind_int->height;
|
||||
v750.field_0.field_10 = res_wind->resolution_mode;
|
||||
v750.field_0.field_14 = -1.0f;
|
||||
v750.field_0.field_1C = diff->movie_surface == PV_MOVIE_SURFACE_FRONT ? 0 : 2;
|
||||
v750.field_20 = true;
|
||||
TaskMovie::SprParams spr_params;
|
||||
spr_params.disp.rect.pos.x = 0.0f;
|
||||
spr_params.disp.rect.pos.y = (float_t)(res_wind->height - res_wind_int->height) * 0.5f;
|
||||
spr_params.disp.rect.size.x = (float_t)res_wind_int->width;
|
||||
spr_params.disp.rect.size.y = (float_t)res_wind_int->height;
|
||||
spr_params.disp.resolution_mode = res_wind->resolution_mode;
|
||||
spr_params.disp.scale = -1.0f;
|
||||
spr_params.disp.index = diff->movie_surface == PV_MOVIE_SURFACE_FRONT ? 0 : 2;
|
||||
spr_params.prio = spr::SPR_PRIO_01;
|
||||
|
||||
for (pv_db_pv_movie& i : diff->movie_list)
|
||||
for (const pv_db_pv_movie& i : diff->movie_list)
|
||||
if (i.name.size() && aft_data->check_file_exists(i.name.c_str())) {
|
||||
task_movie_get(v43->index)->sub_14041F260(v750);
|
||||
task_movie_get(v43->index)->sub_14041F0A0(i.name);
|
||||
task_movie_get(v43->index)->sub_14041F240(0.0f);
|
||||
task_movie_get(i.index)->Reset(spr_params);
|
||||
movie_external_clock_set_true();
|
||||
task_movie_get(i.index)->LoadWait(i.name);
|
||||
task_movie_get(i.index)->SetVolume(0.0f);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (sub_14013C8C0()->sub_1400E7910() >= 4) {
|
||||
state = 12;
|
||||
@@ -2673,10 +2675,10 @@ bool pv_game::load() {
|
||||
case 3: {
|
||||
const pv_db_pv_difficulty* diff = get_pv_db_pv()->get_difficulty(
|
||||
sub_14013C8C0()->difficulty, sub_14013C8C0()->edition);
|
||||
/*for (pv_db_pv_movie& i : diff->movie_list)
|
||||
for (const pv_db_pv_movie& i : diff->movie_list)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(i.index))
|
||||
&& !task_movie_get(i.index)->sub_14041F0E0())
|
||||
return false;*/
|
||||
&& !task_movie_get(i.index)->CheckDisp())
|
||||
return false;
|
||||
|
||||
for (uint32_t& i : data.motion_set_ids)
|
||||
if (motion_storage_check_mot_file_not_ready(i)
|
||||
@@ -3983,7 +3985,7 @@ void pv_game::reset() {
|
||||
data.current_field = 0;
|
||||
data.field_data.clear();
|
||||
|
||||
data.field_2D8A0 = true;
|
||||
data.enable_movie = true;
|
||||
|
||||
data.camera_auth_3d_uid = -1;
|
||||
data.campv_string.clear();
|
||||
@@ -4444,9 +4446,9 @@ bool pv_game::unload() {
|
||||
case 0: {
|
||||
pv_game_music_get()->stop();
|
||||
|
||||
/*for (int32_t i = 0; i < 2; i++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get()))
|
||||
sub_14041F4E0(task_movie_get());*/
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(i)))
|
||||
task_movie_get(i)->Unload();
|
||||
|
||||
std::string effect_se_file_name = get_effect_se_file_name();
|
||||
if (effect_se_file_name.size())
|
||||
@@ -4544,7 +4546,7 @@ bool pv_game::unload() {
|
||||
data.current_field = 0;
|
||||
data.field_data.clear();
|
||||
|
||||
data.field_2D8A0 = true;
|
||||
data.enable_movie = true;
|
||||
|
||||
for (auto& i : data.auth_3d)
|
||||
i.second.unload(rctx_ptr);
|
||||
@@ -4625,13 +4627,13 @@ bool pv_game::unload() {
|
||||
data.effect_rs_list.clear();
|
||||
data.effect_rs_list_hashes.clear();
|
||||
|
||||
/*for (int32_t i = 0; i < 2; i++) {
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++) {
|
||||
if (!app::TaskWork::check_task_ready(task_movie_get(i)))
|
||||
continue;
|
||||
|
||||
task_movie_get(i)->sub_14041F4E0();
|
||||
task_movie->DelTask();
|
||||
}*/
|
||||
task_movie_get(i)->Unload();
|
||||
task_movie_get(i)->del();
|
||||
}
|
||||
|
||||
data.title_image_state = 0;
|
||||
|
||||
@@ -4845,17 +4847,18 @@ void pv_game::sub_140106640() {
|
||||
pv_game_music_get()->load(4, get_play_data_song_file_name(), true, sub_1400FCEB0(), false);
|
||||
}
|
||||
|
||||
/*for (int32_t i = 0; i < 2; i++) {
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++) {
|
||||
if (!app::TaskWork::check_task_ready(task_movie_get(i)))
|
||||
continue;
|
||||
|
||||
task_movie_get(i)->sub_14041F4E0();
|
||||
std::string name = get_pv_movie_file_name(i);
|
||||
task_movie_get(i)->Unload();
|
||||
|
||||
std::string name(get_pv_movie_file_name(i));
|
||||
if (name.size()) {
|
||||
task_movie_get(i)->sub_14041F0A0(name);
|
||||
while (!task_movie_get(i)->sub_14041F0E0(v14));
|
||||
task_movie_get(i)->LoadWait(name);
|
||||
while (!task_movie_get(i)->CheckDisp());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
sub_140104FB0();
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ struct pv_game_data {
|
||||
int32_t field_2D87C;
|
||||
size_t current_field;
|
||||
std::vector<pv_game_field> field_data;
|
||||
bool field_2D8A0;
|
||||
bool enable_movie;
|
||||
int32_t field_2D8A4;
|
||||
std::map<int32_t, int64_t> auth_3d_time;
|
||||
std::map<int32_t, int64_t> light_time;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "../../CRE/pv_expression.hpp"
|
||||
#include "../../CRE/pv_param.hpp"
|
||||
#include "../information/dw_console.hpp"
|
||||
#include "../task_movie.hpp"
|
||||
#include "pv_game.hpp"
|
||||
#include "pv_game_camera.hpp"
|
||||
#include "pv_game_music.hpp"
|
||||
@@ -360,12 +361,12 @@ int64_t pv_game_pv_data::ctrl(float_t delta_time, int64_t curr_time, bool a4) {
|
||||
this->curr_time = curr_time + field_2BF38 - field_2BF30;
|
||||
curr_time_float = (float_t)((double_t)this->curr_time * 0.000000001);
|
||||
|
||||
/*for (int32_t i = 0; i < 2; i++)
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(i))) {
|
||||
if (pv_game_get()->loaded)
|
||||
sub_14041F1B0(this->curr_time);
|
||||
movie_current_time_set(this->curr_time);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
|
||||
dsc_data* prev_dsc_data_ptr = dsc_data_ptr;
|
||||
targets_remaining = 0;
|
||||
@@ -1707,39 +1708,43 @@ bool pv_game_pv_data::dsc_ctrl(float_t delta_time, int64_t curr_time,
|
||||
}
|
||||
} break;
|
||||
case DSC_FT_MOVIE_PLAY: {
|
||||
int32_t v408 = data[0];
|
||||
int32_t play = data[0];
|
||||
|
||||
/*if (pv_game_get()->loaded && v408 && app::TaskWork::check_task_ready(task_movie_get(0))) {
|
||||
v410 = task_movie_get(0);
|
||||
task_movie_get(0)->sub_14041EF10();
|
||||
pv_game_get()->data.movie_index = 0;
|
||||
sub_14041F1D0(curr_time);
|
||||
}*/
|
||||
} break;
|
||||
case DSC_FT_MOVIE_DISP: {
|
||||
int32_t v412 = data[0];
|
||||
if (!pv_game_get()->loaded)
|
||||
break;
|
||||
|
||||
/*for (int32_t i = 0; i < 2; i++) {
|
||||
if (play && app::TaskWork::check_task_ready(task_movie_get(0))) {
|
||||
task_movie_get(0)->Play();
|
||||
pv_game_get()->data.movie_index = 0;
|
||||
movie_play_time_set_begin(curr_time);
|
||||
}
|
||||
} break;
|
||||
case DSC_FT_MOVIE_DISP: {
|
||||
int32_t state = data[0];
|
||||
if (!pv_game_get()->loaded)
|
||||
break;
|
||||
|
||||
for (int32_t i = 0; i < TASK_MOVIE_COUNT; i++) {
|
||||
if (!app::TaskWork::check_task_ready(task_movie_get(i)))
|
||||
continue;
|
||||
|
||||
int32_t v415 = 0;
|
||||
TaskMovie::DispState disp_state = TaskMovie::DispState::None;
|
||||
if (pv_game_get()->data.movie_index == i) {
|
||||
if (pv_game->data.field_2D8A0)
|
||||
switch (v412) {
|
||||
case 1:
|
||||
v415 = 1;
|
||||
break;
|
||||
case 2:
|
||||
v415 = 2;
|
||||
break;
|
||||
}
|
||||
if (!pv_game->data.enable_movie)
|
||||
state = 0;
|
||||
|
||||
switch (state) {
|
||||
case 1:
|
||||
disp_state = TaskMovie::DispState::Disp;
|
||||
break;
|
||||
case 2:
|
||||
disp_state = TaskMovie::DispState::Hide;
|
||||
break;
|
||||
}
|
||||
}
|
||||
task_movie_get(l)->field_88 = v415;
|
||||
task_movie_get(i)->disp_state = disp_state;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
} break;
|
||||
case DSC_FT_WIND: {
|
||||
chara_id = data[0];
|
||||
@@ -1971,32 +1976,35 @@ bool pv_game_pv_data::dsc_ctrl(float_t delta_time, int64_t curr_time,
|
||||
pv_game->set_pv_param_post_process_chara_item_alpha_data(chara_id, alpha, type, duration);
|
||||
} break;
|
||||
case DSC_FT_MOVIE_CUT_CHG: {
|
||||
/*int32_t v486 = data[0];
|
||||
int32_t v487 = data[1];
|
||||
int32_t index = data[0];
|
||||
int32_t state = data[1];
|
||||
|
||||
if (pv_game_get()->loaded) {
|
||||
for (int32_t m = 0; m < 2; m++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(m)))
|
||||
task_movie_get(m)->field_88 = 0;
|
||||
if (!pv_game_get()->loaded)
|
||||
break;
|
||||
|
||||
if (v486 != -1 && app::TaskWork::check_task_ready(task_movie_get(v486))) {
|
||||
task_movie_get(v486)->sub_14041EF10((__int64)v492);
|
||||
pv_game_get()->data.movie_index = v486;
|
||||
//sub_14041F210(curr_time);
|
||||
if (!pv_game->data.field_2D8A0)
|
||||
v487 = 0;
|
||||
if (v487)
|
||||
switch (v487) {
|
||||
case 1:
|
||||
v488 = 1;
|
||||
break;
|
||||
case 2:
|
||||
v488 = 2;
|
||||
break;
|
||||
}
|
||||
task_movie_get(v486)->field_88 = v488;
|
||||
for (int32_t m = 0; m < TASK_MOVIE_COUNT; m++)
|
||||
if (app::TaskWork::check_task_ready(task_movie_get(m)))
|
||||
task_movie_get(m)->disp_state = TaskMovie::DispState::None;
|
||||
|
||||
if (index != -1 && app::TaskWork::check_task_ready(task_movie_get(index))) {
|
||||
task_movie_get(index)->Play();
|
||||
pv_game_get()->data.movie_index = index;
|
||||
movie_play_time_set(curr_time);
|
||||
|
||||
TaskMovie::DispState disp_state = TaskMovie::DispState::None;
|
||||
if (!pv_game->data.enable_movie)
|
||||
state = 0;
|
||||
|
||||
switch (state) {
|
||||
case 1:
|
||||
disp_state = TaskMovie::DispState::Disp;
|
||||
break;
|
||||
case 2:
|
||||
disp_state = TaskMovie::DispState::Hide;
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
task_movie_get(index)->disp_state = disp_state;
|
||||
}
|
||||
} break;
|
||||
case DSC_FT_CHARA_LIGHT: {
|
||||
int32_t id = data[0];
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#include "task_movie.hpp"
|
||||
#include "../CRE/data.hpp"
|
||||
#include "movie_play_lib/movie_play_lib.hpp"
|
||||
#include "movie_play_lib/external_clock.hpp"
|
||||
#include "movie_play_lib/player.hpp"
|
||||
#include <atomic>
|
||||
|
||||
const int32_t TASK_MOVIE_COUNT = 2;
|
||||
|
||||
std::atomic_int32_t movie_external_clock;
|
||||
std::atomic_int64_t movie_current_time;
|
||||
std::atomic_int64_t movie_play_time;
|
||||
|
||||
TaskMovie task_movie[TASK_MOVIE_COUNT];
|
||||
|
||||
static int32_t dword_1411A1840 = 0;
|
||||
|
||||
static bool movie_external_clock_get();
|
||||
static void movie_reset();
|
||||
|
||||
static texture* sub_14041E560();
|
||||
|
||||
inline TaskMovie::DispParams::DispParams() : rect(), resolution_mode(), field_18() {
|
||||
scale = -1.0f;
|
||||
index = -1;
|
||||
}
|
||||
|
||||
TaskMovie::SprParams::SprParams() {
|
||||
prio = spr::SPR_PRIO_07;
|
||||
|
||||
resolution_struct* res_wind = res_window_get();
|
||||
disp.rect.size.x = (float_t)res_wind->width;
|
||||
disp.rect.size.y = (float_t)res_wind->height;
|
||||
disp.resolution_mode = res_wind->resolution_mode;
|
||||
}
|
||||
|
||||
TaskMovie::Player::Player() : pause(), volume(), state(), player(),
|
||||
external_clock(), video_params(), interop_texture(), duration(), time() {
|
||||
set_audio_params = true;
|
||||
player_state = MoviePlayLib::State::Wait;
|
||||
}
|
||||
|
||||
void TaskMovie::Player::Ctrl() {
|
||||
player_state = MoviePlayLib::State::Wait;
|
||||
time = 0.0f;
|
||||
|
||||
if (player && !dword_1411A1840) {
|
||||
player_state = player->GetState();
|
||||
time = player->GetTime();
|
||||
}
|
||||
|
||||
switch (player_state) {
|
||||
case MoviePlayLib::State::None:
|
||||
case MoviePlayLib::State::Init:
|
||||
state = TaskMovie::State::Init;
|
||||
break;
|
||||
case MoviePlayLib::State::Open:
|
||||
player->GetVideoParams(&video_params);
|
||||
|
||||
duration = player->GetDuration();
|
||||
player->Play();
|
||||
|
||||
state = TaskMovie::State::Init;
|
||||
break;
|
||||
case MoviePlayLib::State::Wait:
|
||||
state = TaskMovie::State::Wait;
|
||||
break;
|
||||
case MoviePlayLib::State::Pause:
|
||||
if (!pause)
|
||||
player->Play();
|
||||
|
||||
state = TaskMovie::State::Disp;
|
||||
break;
|
||||
case MoviePlayLib::State::Play:
|
||||
if (interop_texture && interop_texture->GetTexture() && pause)
|
||||
player->Pause();
|
||||
|
||||
state = TaskMovie::State::Disp;
|
||||
break;
|
||||
case MoviePlayLib::State::Stop:
|
||||
if (time <= 0.0f || time >= duration)
|
||||
state = TaskMovie::State::Stop;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == TaskMovie::State::Disp && player && set_audio_params) {
|
||||
MoviePlayLib::AudioParams audio_params;
|
||||
audio_params.spk_l_volume = volume;
|
||||
audio_params.spk_r_volume = volume;
|
||||
audio_params.field_8 = 0.0f;
|
||||
audio_params.field_C = 0.0f;
|
||||
audio_params.hph_l_volume = volume;
|
||||
audio_params.hph_r_volume = volume;
|
||||
if (player->SetAudioParams(&audio_params) >= 0)
|
||||
set_audio_params = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskMovie::Player::Destroy(Player* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
if (ptr->interop_texture) {
|
||||
ptr->interop_texture->Release();
|
||||
ptr->interop_texture = 0;
|
||||
}
|
||||
|
||||
if (ptr->player) {
|
||||
ptr->player->Release();
|
||||
ptr->player = 0;
|
||||
}
|
||||
|
||||
if (ptr->external_clock) {
|
||||
ptr->external_clock->Release();
|
||||
ptr->external_clock = 0;
|
||||
}
|
||||
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void TaskMovie::Player::GetInteropTexture(texture*& ptr) {
|
||||
if (!interop_texture)
|
||||
return;
|
||||
|
||||
texture* tex = interop_texture->GetTexture();
|
||||
if (!tex)
|
||||
return;
|
||||
|
||||
ptr = tex;
|
||||
ptr->width = video_params.frame_size_width;
|
||||
ptr->height = video_params.frame_size_height;
|
||||
}
|
||||
|
||||
void TaskMovie::Player::UpdateInteropTexture(TaskMovie::PlayerVideoParams* player_video_params) {
|
||||
if (interop_texture) {
|
||||
interop_texture->Release();
|
||||
interop_texture = 0;
|
||||
}
|
||||
|
||||
if (player)
|
||||
player->GetGLDXIntreropTexture(&interop_texture);
|
||||
|
||||
if (!player_video_params)
|
||||
return;
|
||||
|
||||
if (state == TaskMovie::State::Disp) {
|
||||
player_video_params->width = video_params.width;
|
||||
player_video_params->height = video_params.height;
|
||||
player_video_params->frame_size_width = video_params.frame_size_width;
|
||||
player_video_params->frame_size_height = video_params.frame_size_height;
|
||||
}
|
||||
else {
|
||||
player_video_params->width = 0;
|
||||
player_video_params->height = 0;
|
||||
player_video_params->frame_size_width = 0;
|
||||
player_video_params->frame_size_height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TaskMovie::TaskMovie() : player(), player_video_params(), tex(), pause(), wait_play() {
|
||||
disp_state = TaskMovie::DispState::Disp;
|
||||
state = TaskMovie::State::Wait;
|
||||
}
|
||||
|
||||
TaskMovie::~TaskMovie() {
|
||||
|
||||
}
|
||||
|
||||
bool TaskMovie::init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskMovie::ctrl() {
|
||||
state = TaskMovie::State::Wait;
|
||||
if (player) {
|
||||
player->Ctrl();
|
||||
state = player->state;
|
||||
}
|
||||
|
||||
if (CheckState()) {
|
||||
if (player->interop_texture) {
|
||||
player->interop_texture->Release();
|
||||
player->interop_texture = 0;
|
||||
}
|
||||
|
||||
player->UpdateInteropTexture(player_video_params);
|
||||
|
||||
if (player_video_params->frame_size_width && player_video_params->frame_size_height) {
|
||||
if (!tex)
|
||||
tex = sub_14041E560();
|
||||
|
||||
if (disp_state != TaskMovie::DispState::None)
|
||||
player->GetInteropTexture(tex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaskMovie::dest() {
|
||||
Unload();
|
||||
|
||||
if (player_video_params) {
|
||||
delete player_video_params;
|
||||
player_video_params = 0;
|
||||
}
|
||||
|
||||
if (tex)
|
||||
tex = 0;
|
||||
|
||||
if (player) {
|
||||
player->Destroy();
|
||||
player = 0;
|
||||
}
|
||||
|
||||
movie_reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TaskMovie::disp() {
|
||||
if (state != TaskMovie::State::Disp || disp_state != TaskMovie::DispState::Disp || !tex)
|
||||
return;
|
||||
|
||||
float_t width = (float_t)player_video_params->width;
|
||||
if (player_video_params->width < 0)
|
||||
width += (float_t)UINT64_MAX;
|
||||
if (fabsf(width) <= 0.000001f)
|
||||
return;
|
||||
|
||||
float_t height = (float_t)player_video_params->height;
|
||||
if (player_video_params->height < 0)
|
||||
height += (float_t)UINT64_MAX;
|
||||
if (fabsf(height) <= 0.000001f)
|
||||
return;
|
||||
|
||||
if (!tex->width || !tex->height)
|
||||
return;
|
||||
|
||||
float_t scale = spr_params.disp.scale;
|
||||
|
||||
vec2 sprite_size;
|
||||
sprite_size.x = width;
|
||||
sprite_size.y = height;
|
||||
|
||||
float_t tex_x = 0.0f;
|
||||
float_t tex_y = (float_t)tex->height - height;
|
||||
float_t tex_width;
|
||||
float_t tex_height;
|
||||
|
||||
if (scale <= 0.0f) {
|
||||
scale = max_def(spr_params.disp.rect.size.x / width, spr_params.disp.rect.size.y / height);
|
||||
|
||||
tex_width = (1.0f / scale) * spr_params.disp.rect.size.x;
|
||||
tex_height = (1.0f / scale) * spr_params.disp.rect.size.y;
|
||||
|
||||
sprite_size.x = tex_width;
|
||||
sprite_size.y = tex_height;
|
||||
|
||||
tex_x = width * 0.5f - tex_width * 0.5f;
|
||||
tex_y = (float_t)tex->height - height * 0.5f - tex_height * 0.5f;
|
||||
}
|
||||
else {
|
||||
tex_width = width;
|
||||
tex_height = height;
|
||||
}
|
||||
|
||||
data_struct* aft_data = &data_list[DATA_AFT];
|
||||
sprite_database* aft_spr_db = &aft_data->data_ft.spr_db;
|
||||
|
||||
spr::SprArgs args;
|
||||
args.Reset();
|
||||
args.texture = tex;
|
||||
args.SetSpriteSize(sprite_size);
|
||||
args.SetTexturePosSize(tex_x, tex_y, tex_width, tex_height);
|
||||
args.attr = (spr::SprAttr)(spr::SPR_ATTR_CTR_CC | spr::SPR_ATTR_FLIP_V);
|
||||
args.index = spr_params.disp.index;
|
||||
args.prio = spr_params.prio;
|
||||
args.resolution_mode_screen = spr_params.disp.resolution_mode;
|
||||
args.resolution_mode_sprite = spr_params.disp.resolution_mode;
|
||||
args.trans.x = spr_params.disp.rect.pos.x + spr_params.disp.rect.size.x * 0.5f;
|
||||
args.trans.y = spr_params.disp.rect.pos.y + spr_params.disp.rect.size.y * 0.5f;
|
||||
args.trans.z = 0.0f;
|
||||
args.scale.x = scale;
|
||||
args.scale.y = scale;
|
||||
args.scale.z = 1.0f;
|
||||
spr::put_sprite(args, aft_spr_db);
|
||||
}
|
||||
|
||||
bool TaskMovie::CheckDisp() {
|
||||
if (!player || !wait_play)
|
||||
return false;
|
||||
|
||||
player->Ctrl();
|
||||
|
||||
return player->state == TaskMovie::State::Disp
|
||||
|| player->state == TaskMovie::State::Max;
|
||||
}
|
||||
|
||||
bool TaskMovie::CheckState() {
|
||||
return state == TaskMovie::State::Init || state == TaskMovie::State::Disp;
|
||||
}
|
||||
|
||||
void TaskMovie::CtrlPlayer() {
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
if (pause || wait_play) {
|
||||
player->pause = true;
|
||||
player->Ctrl();
|
||||
}
|
||||
else {
|
||||
player->pause = false;
|
||||
player->Ctrl();
|
||||
}
|
||||
}
|
||||
|
||||
texture* TaskMovie::GetTexture() {
|
||||
if (app::TaskWork::check_task_ready(this) && CheckState())
|
||||
return tex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TaskMovie::Load(const std::string& path) {
|
||||
Shutdown();
|
||||
|
||||
wait_play = false;
|
||||
|
||||
CtrlPlayer();
|
||||
Start(path);
|
||||
}
|
||||
|
||||
void TaskMovie::LoadWait(const std::string& path) {
|
||||
Shutdown();
|
||||
|
||||
wait_play = true;
|
||||
|
||||
CtrlPlayer();
|
||||
Start(path);
|
||||
}
|
||||
|
||||
bool TaskMovie::Play() {
|
||||
if (!player || !wait_play)
|
||||
return false;
|
||||
|
||||
wait_play = false;
|
||||
|
||||
CtrlPlayer();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskMovie::Reset(const TaskMovie::SprParams& spr_params) {
|
||||
this->spr_params = spr_params;
|
||||
|
||||
bool ret;
|
||||
if (app::TaskWork::check_task_ready(this)) {
|
||||
if (player) {
|
||||
player->Destroy();
|
||||
player = 0;
|
||||
}
|
||||
|
||||
player = new TaskMovie::Player;
|
||||
|
||||
ret = true;
|
||||
}
|
||||
else {
|
||||
player = new TaskMovie::Player;
|
||||
player_video_params = new TaskMovie::PlayerVideoParams;
|
||||
|
||||
ret = app::TaskWork::add_task(this, "TaskMovie");
|
||||
}
|
||||
|
||||
SetVolume(1.0f);
|
||||
|
||||
movie_reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TaskMovie::SetPause(bool value) {
|
||||
pause = value;
|
||||
|
||||
CtrlPlayer();
|
||||
}
|
||||
|
||||
bool TaskMovie::SetVolume(float_t value) {
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
player->volume = value;
|
||||
player->set_audio_params = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskMovie::Shutdown() {
|
||||
TaskMovie::Player* player = this->player;
|
||||
|
||||
bool res = false;
|
||||
if (player) {
|
||||
if (player->interop_texture) {
|
||||
player->interop_texture->Release();
|
||||
player->interop_texture = 0;
|
||||
}
|
||||
|
||||
tex = 0;
|
||||
|
||||
if (player->interop_texture) {
|
||||
player->interop_texture->Release();
|
||||
player->interop_texture = 0;
|
||||
}
|
||||
|
||||
if (player->player) {
|
||||
player->player->Release();
|
||||
player->player = 0;
|
||||
}
|
||||
|
||||
if (player->external_clock) {
|
||||
player->external_clock->Release();
|
||||
player->external_clock = 0;
|
||||
}
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
||||
path.clear();
|
||||
return res;
|
||||
}
|
||||
|
||||
void TaskMovie::Start(const std::string& path) {
|
||||
SetPause(false);
|
||||
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
data_struct* aft_data = &data_list[DATA_AFT];
|
||||
|
||||
std::string _path(path);
|
||||
aft_data->get_file_path(_path);
|
||||
this->path.assign(_path);
|
||||
|
||||
player->external_clock = 0;
|
||||
if (movie_external_clock_get())
|
||||
MoviePlayLib::ExternalClock::Create(TaskMovie::GetElapsedTime, player->external_clock);
|
||||
|
||||
MoviePlayLib::Player::Create(player->player);
|
||||
|
||||
if (player->player) {
|
||||
player->player->SetMediaClock(player->external_clock);
|
||||
player->player->Open(_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskMovie::Unload() {
|
||||
bool res = TaskMovie::Shutdown();
|
||||
disp_state = TaskMovie::DispState::Disp;
|
||||
return res;
|
||||
}
|
||||
|
||||
int64_t TaskMovie::GetElapsedTime() {
|
||||
if (!movie_external_clock_get())
|
||||
return 0;
|
||||
|
||||
return max_def(movie_current_time.load() - movie_play_time.load(), 0);
|
||||
}
|
||||
|
||||
TaskMovie* task_movie_get(int32_t index) {
|
||||
if (index >= 0 && index < TASK_MOVIE_COUNT)
|
||||
return &task_movie[index];
|
||||
return &task_movie[0];
|
||||
}
|
||||
|
||||
texture* task_movie_get_texture(int32_t index) {
|
||||
TaskMovie* task_movie = task_movie_get(index);
|
||||
return task_movie ? task_movie->tex : 0;
|
||||
}
|
||||
|
||||
void movie_current_time_set(int64_t value) {
|
||||
if (movie_external_clock_get())
|
||||
movie_current_time = value;
|
||||
}
|
||||
|
||||
void movie_external_clock_set_true() {
|
||||
movie_external_clock = 1;
|
||||
}
|
||||
|
||||
void movie_play_time_set(int64_t value) {
|
||||
if (movie_external_clock_get())
|
||||
movie_play_time = value;
|
||||
}
|
||||
|
||||
void movie_play_time_set_begin(int64_t value) {
|
||||
if (movie_external_clock_get() && movie_play_time < 0)
|
||||
movie_play_time = value;
|
||||
}
|
||||
|
||||
static bool movie_external_clock_get() {
|
||||
return !!movie_external_clock;
|
||||
}
|
||||
|
||||
static void movie_reset() {
|
||||
movie_external_clock = 0;
|
||||
movie_current_time = 0;
|
||||
movie_play_time = -1;
|
||||
}
|
||||
|
||||
static texture* sub_14041E560() {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
by korenkonder
|
||||
GitHub/GitLab: korenkonder
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../KKdLib/default.hpp"
|
||||
#include "../CRE/sprite.hpp"
|
||||
#include "../CRE/task.hpp"
|
||||
#include "../CRE/texture.hpp"
|
||||
#include "movie_play_lib/movie_play_lib.hpp"
|
||||
|
||||
class TaskMovie : public app::Task {
|
||||
public:
|
||||
enum class State {
|
||||
Wait = 0,
|
||||
Init,
|
||||
Disp,
|
||||
Stop,
|
||||
Max,
|
||||
};
|
||||
|
||||
enum class DispState {
|
||||
None = 0,
|
||||
Disp,
|
||||
Hide,
|
||||
Max,
|
||||
};
|
||||
|
||||
struct PlayerVideoParams {
|
||||
int64_t width;
|
||||
int64_t height;
|
||||
int64_t frame_size_width;
|
||||
int64_t frame_size_height;
|
||||
|
||||
inline PlayerVideoParams() : width(), height(), frame_size_width(), frame_size_height() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct DispParams {
|
||||
rectangle rect;
|
||||
resolution_mode resolution_mode;
|
||||
float_t scale;
|
||||
uint32_t field_18;
|
||||
int32_t index;
|
||||
|
||||
DispParams();
|
||||
};
|
||||
|
||||
struct SprParams {
|
||||
DispParams disp;
|
||||
spr::SprPrio prio;
|
||||
|
||||
SprParams();
|
||||
};
|
||||
|
||||
struct Player {
|
||||
bool pause;
|
||||
float_t volume;
|
||||
bool set_audio_params;
|
||||
State state;
|
||||
MoviePlayLib::IPlayer* player;
|
||||
MoviePlayLib::IMediaClock* external_clock;
|
||||
MoviePlayLib::State player_state;
|
||||
MoviePlayLib::VideoParams video_params;
|
||||
MoviePlayLib::IGLDXInteropTexture* interop_texture;
|
||||
double_t duration;
|
||||
double_t time;
|
||||
|
||||
Player();
|
||||
|
||||
void Ctrl();
|
||||
void GetInteropTexture(texture*& ptr);
|
||||
void UpdateInteropTexture(TaskMovie::PlayerVideoParams* player_video_params);
|
||||
|
||||
static void Destroy(Player* ptr);
|
||||
|
||||
inline void Destroy() {
|
||||
Destroy(this);
|
||||
}
|
||||
};
|
||||
|
||||
std::string path;
|
||||
DispState disp_state;
|
||||
SprParams spr_params;
|
||||
State state;
|
||||
Player* player;
|
||||
PlayerVideoParams* player_video_params;
|
||||
texture* tex;
|
||||
bool pause;
|
||||
bool wait_play;
|
||||
|
||||
TaskMovie();
|
||||
virtual ~TaskMovie() override;
|
||||
|
||||
virtual bool init() override;
|
||||
virtual bool ctrl() override;
|
||||
virtual bool dest() override;
|
||||
virtual void disp() override;
|
||||
|
||||
bool CheckDisp();
|
||||
bool CheckState();
|
||||
void CtrlPlayer();
|
||||
texture* GetTexture();
|
||||
void Load(const std::string& path);
|
||||
void LoadWait(const std::string& path);
|
||||
bool Play();
|
||||
bool Reset(const TaskMovie::SprParams& spr_params);
|
||||
void SetPause(bool value);
|
||||
bool SetVolume(float_t value);
|
||||
bool Shutdown();
|
||||
void Start(const std::string& path);
|
||||
bool Unload();
|
||||
|
||||
static int64_t GetElapsedTime();
|
||||
};
|
||||
|
||||
extern const int32_t TASK_MOVIE_COUNT;
|
||||
|
||||
extern TaskMovie* task_movie_get(int32_t index = 0);
|
||||
|
||||
extern void movie_current_time_set(int64_t value);
|
||||
extern void movie_external_clock_set_true();
|
||||
extern void movie_play_time_set(int64_t value);
|
||||
extern void movie_play_time_set_begin(int64_t value);
|
||||
Reference in New Issue
Block a user