Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

485 changes: 228 additions & 257 deletions mediaplayer/src/jvmMain/native/windows/AudioManager.cpp

Large diffs are not rendered by default.

60 changes: 11 additions & 49 deletions mediaplayer/src/jvmMain/native/windows/AudioManager.h
Original file line number Diff line number Diff line change
@@ -1,68 +1,30 @@
#pragma once

#include "ErrorCodes.h"
#include <windows.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <mfapi.h>
#include <mfidl.h>

// Error code definitions
#define OP_E_NOT_INITIALIZED ((HRESULT)0x80000001L)
#define OP_E_ALREADY_INITIALIZED ((HRESULT)0x80000002L)
#define OP_E_INVALID_PARAMETER ((HRESULT)0x80000003L)

// Forward declarations
struct VideoPlayerInstance;

namespace AudioManager {

/**
* @brief Initializes WASAPI for audio playback.
* @param pInstance Pointer to the video player instance.
* @param pSourceFormat Optional source audio format.
* @return S_OK on success, or an error code.
*/
HRESULT InitWASAPI(VideoPlayerInstance* pInstance, const WAVEFORMATEX* pSourceFormat = nullptr);

/**
* @brief Audio processing thread procedure.
* @param lpParam Pointer to the video player instance.
* @return Thread exit code.
*/
DWORD WINAPI AudioThreadProc(LPVOID lpParam);

/**
* @brief Starts the audio thread for a video player instance.
* @param pInstance Pointer to the video player instance.
* @return S_OK on success, or an error code.
*/
/**
* @brief Pre-fills the WASAPI buffer before Start() to avoid gaps after seek.
*/
// InitWASAPI does NOT take ownership of pSourceFormat. The caller is
// responsible for freeing it (or transferring ownership to the instance
// via VideoPlayerInstance::pSourceAudioFormat) after the call returns.
HRESULT InitWASAPI(VideoPlayerInstance* pInstance, const WAVEFORMATEX* pSourceFormat);
HRESULT PreFillAudioBuffer(VideoPlayerInstance* pInstance);

HRESULT StartAudioThread(VideoPlayerInstance* pInstance);
void StopAudioThread(VideoPlayerInstance* pInstance);

/**
* @brief Stops the audio thread for a video player instance.
* @param pInstance Pointer to the video player instance.
*/
void StopAudioThread(VideoPlayerInstance* pInstance);

/**
* @brief Sets the audio volume for a video player instance.
* @param pInstance Pointer to the video player instance.
* @param volume Volume level (0.0 to 1.0).
* @return S_OK on success, or an error code.
*/
HRESULT SetVolume(VideoPlayerInstance* pInstance, float volume);

/**
* @brief Gets the audio volume for a video player instance.
* @param pInstance Pointer to the video player instance.
* @param volume Pointer to receive the volume level.
* @return S_OK on success, or an error code.
*/
HRESULT GetVolume(const VideoPlayerInstance* pInstance, float* volume);

// Called by the video player when playback is resumed/paused so the audio
// thread can block efficiently instead of busy-waiting.
void SignalResume(VideoPlayerInstance* pInstance);
void SignalPause(VideoPlayerInstance* pInstance);

} // namespace AudioManager
41 changes: 30 additions & 11 deletions mediaplayer/src/jvmMain/native/windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.15)
project(NativeVideoPlayer LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Find JNI
find_package(JNI REQUIRED)

# Check target architecture
if(DEFINED ENV{NATIVE_LIBS_OUTPUT_DIR})
set(BASE_OUTPUT_DIR "$ENV{NATIVE_LIBS_OUTPUT_DIR}")
else()
Expand All @@ -16,23 +16,21 @@ endif()
if(CMAKE_GENERATOR_PLATFORM STREQUAL "x64" OR CMAKE_GENERATOR_PLATFORM STREQUAL "")
set(TARGET_ARCH "x64")
set(OUTPUT_DIR "${BASE_OUTPUT_DIR}/win32-x86-64")
add_compile_options("/arch:AVX2")
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
set(TARGET_ARCH "ARM64")
set(OUTPUT_DIR "${BASE_OUTPUT_DIR}/win32-arm64")
add_compile_options("/arch:arm64")
else()
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_GENERATOR_PLATFORM}")
endif()

# Ensure output directory exists
file(MAKE_DIRECTORY ${OUTPUT_DIR})

# Define the target
add_library(NativeVideoPlayer SHARED
NativeVideoPlayer.cpp
NativeVideoPlayer.h
VideoPlayerInstance.h
ErrorCodes.h
ComHelpers.h
Utils.cpp
Utils.h
MediaFoundationManager.cpp
Expand All @@ -44,17 +42,40 @@ add_library(NativeVideoPlayer SHARED
jni_bridge.cpp
)

# JNI include directories
target_include_directories(NativeVideoPlayer PRIVATE ${JNI_INCLUDE_DIRS})

# Compilation definitions
target_compile_definitions(NativeVideoPlayer PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
NATIVEVIDEOPLAYER_EXPORTS
_CRT_SECURE_NO_WARNINGS
)

# Linked libraries
# Baseline is SSE2 (guaranteed on x64). AVX2 codepaths are runtime-detected
# via __cpuid in ForceAlphaOpaque — MSVC allows AVX2 intrinsics without
# /arch:AVX2, so we keep the binary runnable on older CPUs.
if(MSVC)
target_compile_options(NativeVideoPlayer PRIVATE
/W4
/permissive-
/Zc:__cplusplus
/Zc:preprocessor
/EHsc
/MP # parallel compilation
/wd4245 # MF_SOURCE_READER_* macros are unsigned-cast-from-signed
/wd4505 # unreferenced inline helpers
)
# Release-only: disable RTTI & enable whole-program optimization.
target_compile_options(NativeVideoPlayer PRIVATE
$<$<CONFIG:Release>:/GR->
$<$<CONFIG:Release>:/GL>
$<$<CONFIG:Release>:/Oi>
)
target_link_options(NativeVideoPlayer PRIVATE
$<$<CONFIG:Release>:/LTCG>
)
endif()

target_link_libraries(NativeVideoPlayer PRIVATE
mf
mfplat
Expand All @@ -71,7 +92,6 @@ target_link_libraries(NativeVideoPlayer PRIVATE
evr
)

# Configure output directory
set_target_properties(NativeVideoPlayer PROPERTIES
OUTPUT_NAME "NativeVideoPlayer"
LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}"
Expand All @@ -82,6 +102,5 @@ set_target_properties(NativeVideoPlayer PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIR}"
)

# Display target architecture and output directory
message(STATUS "Target architecture: ${TARGET_ARCH}")
message(STATUS "Output directory: ${OUTPUT_DIR}")
76 changes: 76 additions & 0 deletions mediaplayer/src/jvmMain/native/windows/ComHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once

// Small RAII helpers around Win32 primitives used throughout the native
// player. Kept header-only to stay dependency-free.

#include <windows.h>
#include <wrl/client.h>
#include <utility>

namespace VideoPlayerUtils {

// RAII wrapper for CRITICAL_SECTION with spin count tuned for tight audio/video
// feed loops (reduces context switches under contention).
class CriticalSection {
public:
CriticalSection() {
InitializeCriticalSectionAndSpinCount(&cs_, 4000);
}
~CriticalSection() { DeleteCriticalSection(&cs_); }

CriticalSection(const CriticalSection&) = delete;
CriticalSection& operator=(const CriticalSection&) = delete;

void Enter() { EnterCriticalSection(&cs_); }
void Leave() { LeaveCriticalSection(&cs_); }

CRITICAL_SECTION* Raw() { return &cs_; }

private:
CRITICAL_SECTION cs_{};
};

class ScopedLock {
public:
explicit ScopedLock(CriticalSection& cs) : cs_(&cs) { cs_->Enter(); }
~ScopedLock() { cs_->Leave(); }
ScopedLock(const ScopedLock&) = delete;
ScopedLock& operator=(const ScopedLock&) = delete;
private:
CriticalSection* cs_;
};

// RAII wrapper for Win32 HANDLEs representing events/timers.
class UniqueHandle {
public:
UniqueHandle() = default;
explicit UniqueHandle(HANDLE h) : h_(h) {}
~UniqueHandle() { Reset(); }

UniqueHandle(const UniqueHandle&) = delete;
UniqueHandle& operator=(const UniqueHandle&) = delete;

UniqueHandle(UniqueHandle&& other) noexcept : h_(other.h_) { other.h_ = nullptr; }
UniqueHandle& operator=(UniqueHandle&& other) noexcept {
if (this != &other) {
Reset();
h_ = other.h_;
other.h_ = nullptr;
}
return *this;
}

void Reset(HANDLE h = nullptr) {
if (h_ && h_ != INVALID_HANDLE_VALUE) CloseHandle(h_);
h_ = h;
}

HANDLE Get() const { return h_; }
HANDLE Release() { HANDLE h = h_; h_ = nullptr; return h; }
explicit operator bool() const { return h_ != nullptr && h_ != INVALID_HANDLE_VALUE; }

private:
HANDLE h_ = nullptr;
};

} // namespace VideoPlayerUtils
8 changes: 8 additions & 0 deletions mediaplayer/src/jvmMain/native/windows/ErrorCodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <windows.h>

// Custom error codes (single source of truth).
#define OP_E_NOT_INITIALIZED ((HRESULT)0x80000001L)
#define OP_E_ALREADY_INITIALIZED ((HRESULT)0x80000002L)
#define OP_E_INVALID_PARAMETER ((HRESULT)0x80000003L)
Loading