Skip to content

Commit 7a47043

Browse files
authored
Merge pull request #204 from kdroidFilter/refactor/windows-native-player
refactor(windows): rewrite native player
2 parents 3bae71b + 2758a2f commit 7a47043

15 files changed

Lines changed: 1912 additions & 2441 deletions

File tree

mediaplayer/src/jvmMain/kotlin/io/github/kdroidfilter/composemediaplayer/windows/WindowsVideoPlayerState.kt

Lines changed: 371 additions & 258 deletions
Large diffs are not rendered by default.

mediaplayer/src/jvmMain/native/windows/AudioManager.cpp

Lines changed: 228 additions & 257 deletions
Large diffs are not rendered by default.
Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,30 @@
11
#pragma once
22

3+
#include "ErrorCodes.h"
34
#include <windows.h>
45
#include <audioclient.h>
56
#include <mmdeviceapi.h>
67
#include <mfapi.h>
78
#include <mfidl.h>
89

9-
// Error code definitions
10-
#define OP_E_NOT_INITIALIZED ((HRESULT)0x80000001L)
11-
#define OP_E_ALREADY_INITIALIZED ((HRESULT)0x80000002L)
12-
#define OP_E_INVALID_PARAMETER ((HRESULT)0x80000003L)
13-
14-
// Forward declarations
1510
struct VideoPlayerInstance;
1611

1712
namespace AudioManager {
1813

19-
/**
20-
* @brief Initializes WASAPI for audio playback.
21-
* @param pInstance Pointer to the video player instance.
22-
* @param pSourceFormat Optional source audio format.
23-
* @return S_OK on success, or an error code.
24-
*/
25-
HRESULT InitWASAPI(VideoPlayerInstance* pInstance, const WAVEFORMATEX* pSourceFormat = nullptr);
26-
27-
/**
28-
* @brief Audio processing thread procedure.
29-
* @param lpParam Pointer to the video player instance.
30-
* @return Thread exit code.
31-
*/
32-
DWORD WINAPI AudioThreadProc(LPVOID lpParam);
33-
34-
/**
35-
* @brief Starts the audio thread for a video player instance.
36-
* @param pInstance Pointer to the video player instance.
37-
* @return S_OK on success, or an error code.
38-
*/
39-
/**
40-
* @brief Pre-fills the WASAPI buffer before Start() to avoid gaps after seek.
41-
*/
14+
// InitWASAPI does NOT take ownership of pSourceFormat. The caller is
15+
// responsible for freeing it (or transferring ownership to the instance
16+
// via VideoPlayerInstance::pSourceAudioFormat) after the call returns.
17+
HRESULT InitWASAPI(VideoPlayerInstance* pInstance, const WAVEFORMATEX* pSourceFormat);
4218
HRESULT PreFillAudioBuffer(VideoPlayerInstance* pInstance);
43-
4419
HRESULT StartAudioThread(VideoPlayerInstance* pInstance);
20+
void StopAudioThread(VideoPlayerInstance* pInstance);
4521

46-
/**
47-
* @brief Stops the audio thread for a video player instance.
48-
* @param pInstance Pointer to the video player instance.
49-
*/
50-
void StopAudioThread(VideoPlayerInstance* pInstance);
51-
52-
/**
53-
* @brief Sets the audio volume for a video player instance.
54-
* @param pInstance Pointer to the video player instance.
55-
* @param volume Volume level (0.0 to 1.0).
56-
* @return S_OK on success, or an error code.
57-
*/
5822
HRESULT SetVolume(VideoPlayerInstance* pInstance, float volume);
59-
60-
/**
61-
* @brief Gets the audio volume for a video player instance.
62-
* @param pInstance Pointer to the video player instance.
63-
* @param volume Pointer to receive the volume level.
64-
* @return S_OK on success, or an error code.
65-
*/
6623
HRESULT GetVolume(const VideoPlayerInstance* pInstance, float* volume);
6724

25+
// Called by the video player when playback is resumed/paused so the audio
26+
// thread can block efficiently instead of busy-waiting.
27+
void SignalResume(VideoPlayerInstance* pInstance);
28+
void SignalPause(VideoPlayerInstance* pInstance);
29+
6830
} // namespace AudioManager

mediaplayer/src/jvmMain/native/windows/CMakeLists.txt

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.15)
22
project(NativeVideoPlayer LANGUAGES CXX)
33

44
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
set(CMAKE_CXX_EXTENSIONS OFF)
57

6-
# Find JNI
78
find_package(JNI REQUIRED)
89

9-
# Check target architecture
1010
if(DEFINED ENV{NATIVE_LIBS_OUTPUT_DIR})
1111
set(BASE_OUTPUT_DIR "$ENV{NATIVE_LIBS_OUTPUT_DIR}")
1212
else()
@@ -16,23 +16,21 @@ endif()
1616
if(CMAKE_GENERATOR_PLATFORM STREQUAL "x64" OR CMAKE_GENERATOR_PLATFORM STREQUAL "")
1717
set(TARGET_ARCH "x64")
1818
set(OUTPUT_DIR "${BASE_OUTPUT_DIR}/win32-x86-64")
19-
add_compile_options("/arch:AVX2")
2019
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
2120
set(TARGET_ARCH "ARM64")
2221
set(OUTPUT_DIR "${BASE_OUTPUT_DIR}/win32-arm64")
23-
add_compile_options("/arch:arm64")
2422
else()
2523
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_GENERATOR_PLATFORM}")
2624
endif()
2725

28-
# Ensure output directory exists
2926
file(MAKE_DIRECTORY ${OUTPUT_DIR})
3027

31-
# Define the target
3228
add_library(NativeVideoPlayer SHARED
3329
NativeVideoPlayer.cpp
3430
NativeVideoPlayer.h
3531
VideoPlayerInstance.h
32+
ErrorCodes.h
33+
ComHelpers.h
3634
Utils.cpp
3735
Utils.h
3836
MediaFoundationManager.cpp
@@ -44,17 +42,40 @@ add_library(NativeVideoPlayer SHARED
4442
jni_bridge.cpp
4543
)
4644

47-
# JNI include directories
4845
target_include_directories(NativeVideoPlayer PRIVATE ${JNI_INCLUDE_DIRS})
4946

50-
# Compilation definitions
5147
target_compile_definitions(NativeVideoPlayer PRIVATE
5248
WIN32_LEAN_AND_MEAN
5349
NOMINMAX
5450
NATIVEVIDEOPLAYER_EXPORTS
51+
_CRT_SECURE_NO_WARNINGS
5552
)
5653

57-
# Linked libraries
54+
# Baseline is SSE2 (guaranteed on x64). AVX2 codepaths are runtime-detected
55+
# via __cpuid in ForceAlphaOpaque — MSVC allows AVX2 intrinsics without
56+
# /arch:AVX2, so we keep the binary runnable on older CPUs.
57+
if(MSVC)
58+
target_compile_options(NativeVideoPlayer PRIVATE
59+
/W4
60+
/permissive-
61+
/Zc:__cplusplus
62+
/Zc:preprocessor
63+
/EHsc
64+
/MP # parallel compilation
65+
/wd4245 # MF_SOURCE_READER_* macros are unsigned-cast-from-signed
66+
/wd4505 # unreferenced inline helpers
67+
)
68+
# Release-only: disable RTTI & enable whole-program optimization.
69+
target_compile_options(NativeVideoPlayer PRIVATE
70+
$<$<CONFIG:Release>:/GR->
71+
$<$<CONFIG:Release>:/GL>
72+
$<$<CONFIG:Release>:/Oi>
73+
)
74+
target_link_options(NativeVideoPlayer PRIVATE
75+
$<$<CONFIG:Release>:/LTCG>
76+
)
77+
endif()
78+
5879
target_link_libraries(NativeVideoPlayer PRIVATE
5980
mf
6081
mfplat
@@ -71,7 +92,6 @@ target_link_libraries(NativeVideoPlayer PRIVATE
7192
evr
7293
)
7394

74-
# Configure output directory
7595
set_target_properties(NativeVideoPlayer PROPERTIES
7696
OUTPUT_NAME "NativeVideoPlayer"
7797
LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}"
@@ -82,6 +102,5 @@ set_target_properties(NativeVideoPlayer PROPERTIES
82102
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIR}"
83103
)
84104

85-
# Display target architecture and output directory
86105
message(STATUS "Target architecture: ${TARGET_ARCH}")
87106
message(STATUS "Output directory: ${OUTPUT_DIR}")
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#pragma once
2+
3+
// Small RAII helpers around Win32 primitives used throughout the native
4+
// player. Kept header-only to stay dependency-free.
5+
6+
#include <windows.h>
7+
#include <wrl/client.h>
8+
#include <utility>
9+
10+
namespace VideoPlayerUtils {
11+
12+
// RAII wrapper for CRITICAL_SECTION with spin count tuned for tight audio/video
13+
// feed loops (reduces context switches under contention).
14+
class CriticalSection {
15+
public:
16+
CriticalSection() {
17+
InitializeCriticalSectionAndSpinCount(&cs_, 4000);
18+
}
19+
~CriticalSection() { DeleteCriticalSection(&cs_); }
20+
21+
CriticalSection(const CriticalSection&) = delete;
22+
CriticalSection& operator=(const CriticalSection&) = delete;
23+
24+
void Enter() { EnterCriticalSection(&cs_); }
25+
void Leave() { LeaveCriticalSection(&cs_); }
26+
27+
CRITICAL_SECTION* Raw() { return &cs_; }
28+
29+
private:
30+
CRITICAL_SECTION cs_{};
31+
};
32+
33+
class ScopedLock {
34+
public:
35+
explicit ScopedLock(CriticalSection& cs) : cs_(&cs) { cs_->Enter(); }
36+
~ScopedLock() { cs_->Leave(); }
37+
ScopedLock(const ScopedLock&) = delete;
38+
ScopedLock& operator=(const ScopedLock&) = delete;
39+
private:
40+
CriticalSection* cs_;
41+
};
42+
43+
// RAII wrapper for Win32 HANDLEs representing events/timers.
44+
class UniqueHandle {
45+
public:
46+
UniqueHandle() = default;
47+
explicit UniqueHandle(HANDLE h) : h_(h) {}
48+
~UniqueHandle() { Reset(); }
49+
50+
UniqueHandle(const UniqueHandle&) = delete;
51+
UniqueHandle& operator=(const UniqueHandle&) = delete;
52+
53+
UniqueHandle(UniqueHandle&& other) noexcept : h_(other.h_) { other.h_ = nullptr; }
54+
UniqueHandle& operator=(UniqueHandle&& other) noexcept {
55+
if (this != &other) {
56+
Reset();
57+
h_ = other.h_;
58+
other.h_ = nullptr;
59+
}
60+
return *this;
61+
}
62+
63+
void Reset(HANDLE h = nullptr) {
64+
if (h_ && h_ != INVALID_HANDLE_VALUE) CloseHandle(h_);
65+
h_ = h;
66+
}
67+
68+
HANDLE Get() const { return h_; }
69+
HANDLE Release() { HANDLE h = h_; h_ = nullptr; return h; }
70+
explicit operator bool() const { return h_ != nullptr && h_ != INVALID_HANDLE_VALUE; }
71+
72+
private:
73+
HANDLE h_ = nullptr;
74+
};
75+
76+
} // namespace VideoPlayerUtils
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
#include <windows.h>
4+
5+
// Custom error codes (single source of truth).
6+
#define OP_E_NOT_INITIALIZED ((HRESULT)0x80000001L)
7+
#define OP_E_ALREADY_INITIALIZED ((HRESULT)0x80000002L)
8+
#define OP_E_INVALID_PARAMETER ((HRESULT)0x80000003L)

0 commit comments

Comments
 (0)