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
6 changes: 6 additions & 0 deletions core/libs/prx/libSceAvPlayer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ if(BUILD_TESTING)
configure_windows_unwind(guest_avplayer_tests)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/guest_avplayer)
add_test(NAME guest_avplayer COMMAND guest_avplayer_tests)
add_test(NAME guest_avplayer_callback_close COMMAND guest_avplayer_tests --callback-close)
set_tests_properties(guest_avplayer PROPERTIES
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/guest_avplayer
TIMEOUT 60)
set_tests_properties(guest_avplayer_callback_close PROPERTIES
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/guest_avplayer
TIMEOUT 20 RUN_SERIAL TRUE)
if(WIN32)
set_tests_properties(guest_avplayer PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAvPlayer>")
set_tests_properties(guest_avplayer_callback_close PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAvPlayer>")
endif()
endif()
3 changes: 2 additions & 1 deletion core/libs/prx/libSceAvPlayer/include/AvPlayer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Player final : public AvPlayerInternal, private ISourceEvents {
void OnWarning(std::int32_t code) override;
void OnError() override;
void queue(const Event& event);
void controllerLoop();
void controllerLoop(const bool& closed);
void deliver(const Event& event);
void autoStart();
void checkEndOfFile();
Expand Down Expand Up @@ -154,6 +154,7 @@ class Player final : public AvPlayerInternal, private ISourceEvents {
TimedWait::Condition eventCondition;
std::deque<Event> events;
bool quit = false;
std::shared_ptr<bool> controllerClosed = std::make_shared<bool>(false);
std::thread controller;
};

Expand Down
12 changes: 9 additions & 3 deletions core/libs/prx/libSceAvPlayer/src/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Player::Player(const AvPlayerInitData& init, std::uint32_t bufferCount)
if (init.default_language) {
for (std::size_t index = 0; index < 3 && init.default_language[index] != '\0'; ++index) language[index] = init.default_language[index];
}
controller = std::thread([this] { controllerLoop(); });
controller = std::thread([this, closed = controllerClosed] { controllerLoop(*closed); });
}

Player::~Player() {
Expand All @@ -79,7 +79,12 @@ Player::~Player() {
quit = true;
}
eventCondition.NotifyAll();
controller.join();
if (controller.get_id() == std::this_thread::get_id()) {
*controllerClosed = true;
controller.detach();
} else {
controller.join();
}
std::lock_guard lock(mutex);
source.reset();
}
Expand All @@ -100,7 +105,7 @@ void Player::OnError() {
queue({0, 0, false, true});
}

void Player::controllerLoop() {
void Player::controllerLoop(const bool& closed) {
for (;;) {
Event event{};
bool pending = false;
Expand All @@ -119,6 +124,7 @@ void Player::controllerLoop() {
} else {
checkEndOfFile();
}
if (closed) return;
}
}

Expand Down
61 changes: 60 additions & 1 deletion core/libs/tests/GuestAvPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,10 +1178,69 @@ void TestEofStopLatency() {
Check(median < std::chrono::milliseconds(10), "eof stop median " + std::to_string(medianMs) + " ms exceeds 10 ms");
}

struct ClosingEvents {
std::mutex mutex;
AvPlayerInternal* player = nullptr;
AvPlayerInternal* replacement = nullptr;
std::int32_t closeEvent;
bool closed = false;
};

void APS5_VABI CloseOnEvent(void* object, std::int32_t id, std::int32_t, void*) {
auto& events = *static_cast<ClosingEvents*>(object);
if (id != events.closeEvent) return;
std::lock_guard lock(events.mutex);
Check(sceAvPlayerClose(events.player) == 0, "close from event callback failed");
events.player = nullptr;
AvPlayerInitData init = InitData(nullptr);
events.replacement = sceAvPlayerInit(&init);
Check(events.replacement != nullptr, "replacement player init from callback failed");
events.closed = true;
}

int main() {
void TestCloseFromEventCallback(std::int32_t event) {
ClosingEvents events;
events.closeEvent = event;
AvPlayerInitData init = InitData(nullptr);
init.event_replacement = {&events, CloseOnEvent};
init.auto_start = event == EventStop;
{
std::lock_guard lock(events.mutex);
events.player = sceAvPlayerInit(&init);
Check(events.player != nullptr, "callback-close init failed");
Check(sceAvPlayerAddSource(events.player, "/app0/avplayer.mp4") == 0, "callback-close source failed");
}
Check(WaitFor([&] {
std::lock_guard lock(events.mutex);
if (events.closed) return true;
if (event == EventStop) {
AvPlayerFrameInfoEx video{};
AvPlayerFrameInfo audio{};
if (sceAvPlayerGetVideoDataEx(events.player, &video)) CheckVideoFrame(video);
if (sceAvPlayerGetAudioData(events.player, &audio)) CheckEnglishAudio(audio);
}
return false;
}), "event callback did not finish closing the player");
{
std::lock_guard lock(events.mutex);
Check(sceAvPlayerClose(events.replacement) == 0, "replacement player close failed");
}
std::lock_guard lock(allocations.mutex);
Check(allocations.blocks.empty(), "callback close leaked media allocations");
}

}

int main(int argc, char** argv) {
try {
if (argc == 2 && std::strcmp(argv[1], "--callback-close") == 0) {
WriteMovie();
TestCloseFromEventCallback(EventReady);
TestCloseFromEventCallback(EventStop);
std::puts("AvPlayer callback close tests passed");
return 0;
}
Check(argc == 1, "unknown AvPlayer test arguments");
WriteMovie();
TestPlayback();
TestChangeStream();
Expand Down
Loading