Added TaskMovie

This commit is contained in:
korenkonder
2025-02-14 00:41:49 +03:00
parent e54f255b4b
commit 0986fd3ab8
48 changed files with 6863 additions and 93 deletions
+1 -1
View File
@@ -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;
}
}
+15
View File
@@ -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
View File
@@ -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;
}
}
}
+1
View File
@@ -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
View File
@@ -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();
+193
View File
@@ -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);
}
};
}
+74
View File
@@ -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;
}
}
+14
View File
@@ -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);
}
};
}
+107
View File
@@ -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;
}
}
+41
View File
@@ -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);
}
+963
View File
@@ -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;
}
}
+115
View File
@@ -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);
}
};
}
+632
View File
@@ -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);
}
};
}
+214
View File
@@ -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(&timestamp);
return timestamp.QuadPart;
}
extern const GUID TextureSharedHandleGUID;
extern const GUID VideoIndexGUID;
extern void PrintDebug(const wchar_t* fmt, ...);
}
+288
View File
@@ -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;
}
}
+51
View File
@@ -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);
}
};
}
+459
View File
@@ -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(&current_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, &current_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);
}
};
}
+40 -37
View File
@@ -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();
+1 -1
View File
@@ -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;
+57 -49
View File
@@ -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];
+510
View File
@@ -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;
}
+127
View File
@@ -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);