mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
avplayer: signal when playback ends
This commit is contained in:
1 parent
303a0203b6
commit
35a275e97d
1 file changed
+80
-54
+80
-54
@@ -117,6 +117,13 @@ struct AvPlayerEventReplacement {
|
|||||||
void* object_pointer = nullptr;
|
void* object_pointer = nullptr;
|
||||||
AvPlayerEventCallback event_callback = nullptr;
|
AvPlayerEventCallback event_callback = nullptr;
|
||||||
};
|
};
|
||||||
|
static void emit_event(AvPlayerEventReplacement event, int32_t id, void* data = nullptr) {
|
||||||
|
if (event.event_callback != nullptr) {
|
||||||
|
LOGF("\t event_id = %d\n", id);
|
||||||
|
event.event_callback(event.object_pointer, id, 0, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AvPlayerUri {
|
struct AvPlayerUri {
|
||||||
const char* name = nullptr;
|
const char* name = nullptr;
|
||||||
uint32_t length = 0;
|
uint32_t length = 0;
|
||||||
@@ -639,9 +646,9 @@ private:
|
|||||||
|
|
||||||
class Source {
|
class Source {
|
||||||
public:
|
public:
|
||||||
Source(AvPlayerMemAllocator m, AvPlayerFileReplacement f, int32_t video_buffers, bool vdec2,
|
Source(AvPlayerMemAllocator m, AvPlayerFileReplacement f, AvPlayerEventReplacement e,
|
||||||
uint32_t demux_buffer_size)
|
int32_t video_buffers, bool vdec2, uint32_t demux_buffer_size)
|
||||||
: mem(m), file(f),
|
: mem(m), file(f), event(e),
|
||||||
max_video_buffers(std::clamp(video_buffers <= 0 ? 2 : video_buffers, 2, 16)),
|
max_video_buffers(std::clamp(video_buffers <= 0 ? 2 : video_buffers, 2, 16)),
|
||||||
use_vdec2(vdec2),
|
use_vdec2(vdec2),
|
||||||
video_packets(
|
video_packets(
|
||||||
@@ -817,21 +824,26 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int Stop() {
|
int Stop() {
|
||||||
std::scoped_lock lifecycle_lock(lifecycle_mutex);
|
bool was_playing_video = false;
|
||||||
bool was_playing_video = false;
|
AvPlayerEventReplacement stop_event;
|
||||||
{
|
{
|
||||||
std::lock_guard lock(mutex);
|
std::scoped_lock lifecycle_lock(lifecycle_mutex);
|
||||||
was_playing_video = !stopped && video_id.has_value();
|
{
|
||||||
stopped = true;
|
std::lock_guard lock(mutex);
|
||||||
}
|
was_playing_video = !stopped && video_id.has_value();
|
||||||
StopWorkers();
|
stopped = true;
|
||||||
{
|
}
|
||||||
std::lock_guard lock(mutex);
|
StopWorkers();
|
||||||
ResetNoLock();
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
ResetNoLock();
|
||||||
|
}
|
||||||
|
stop_event = TakeStopEvent();
|
||||||
}
|
}
|
||||||
if (was_playing_video) {
|
if (was_playing_video) {
|
||||||
::printf("AvPlayer video stopped\n");
|
::printf("AvPlayer video stopped\n");
|
||||||
}
|
}
|
||||||
|
emit_event(stop_event, AVPLAYER_EVENT_STATE_STOP);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void Pause() {
|
void Pause() {
|
||||||
@@ -860,9 +872,7 @@ public:
|
|||||||
}
|
}
|
||||||
bool Active() const {
|
bool Active() const {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
return !stopped && !pipeline_failed &&
|
return !stopped && !pipeline_failed && !DrainedNoLock();
|
||||||
(!demux_eof || !video_done || !audio_done || !video_frames.Empty() ||
|
|
||||||
!audio_frames.Empty());
|
|
||||||
}
|
}
|
||||||
uint64_t CurrentTime() const {
|
uint64_t CurrentTime() const {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
@@ -945,6 +955,7 @@ public:
|
|||||||
current_video = std::move(*frame);
|
current_video = std::move(*frame);
|
||||||
*out = current_video->info;
|
*out = current_video->info;
|
||||||
RecordLoopBoundary(*current_video);
|
RecordLoopBoundary(*current_video);
|
||||||
|
completion_changed.notify_all();
|
||||||
if (deliver_seek_frame) {
|
if (deliver_seek_frame) {
|
||||||
seek_video_frame_pending = false;
|
seek_video_frame_pending = false;
|
||||||
}
|
}
|
||||||
@@ -976,6 +987,7 @@ public:
|
|||||||
current_audio->info.details.audio.language_code, 4);
|
current_audio->info.details.audio.language_code, 4);
|
||||||
last_audio_ts = out->time_stamp;
|
last_audio_ts = out->time_stamp;
|
||||||
RecordLoopBoundary(*current_audio);
|
RecordLoopBoundary(*current_audio);
|
||||||
|
completion_changed.notify_all();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::optional<int32_t> TakeWarning() {
|
std::optional<int32_t> TakeWarning() {
|
||||||
@@ -988,6 +1000,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool DrainedNoLock() const {
|
||||||
|
return demux_eof && video_done && audio_done && video_frames.Empty() &&
|
||||||
|
audio_frames.Empty();
|
||||||
|
}
|
||||||
|
AvPlayerEventReplacement TakeStopEvent() {
|
||||||
|
return stop_event_sent.exchange(true) ? AvPlayerEventReplacement {} : event;
|
||||||
|
}
|
||||||
void RecordLoopBoundary(const ReadyFrame& frame) {
|
void RecordLoopBoundary(const ReadyFrame& frame) {
|
||||||
if (frame.timestamp_offset > last_output_loop_offset) {
|
if (frame.timestamp_offset > last_output_loop_offset) {
|
||||||
last_output_loop_offset = frame.timestamp_offset;
|
last_output_loop_offset = frame.timestamp_offset;
|
||||||
@@ -1002,23 +1021,17 @@ private:
|
|||||||
streamer.reset();
|
streamer.reset();
|
||||||
}
|
}
|
||||||
void ResetNoLock(bool retain_output_buffers = false) {
|
void ResetNoLock(bool retain_output_buffers = false) {
|
||||||
std::optional<ReadyFrame> delivered_video;
|
|
||||||
std::optional<ReadyFrame> delivered_audio;
|
|
||||||
std::deque<std::unique_ptr<GuestBuffer>> in_flight_video;
|
|
||||||
if (retain_output_buffers) {
|
|
||||||
delivered_video = std::move(current_video);
|
|
||||||
delivered_audio = std::move(current_audio);
|
|
||||||
in_flight_video = std::move(retired_video);
|
|
||||||
}
|
|
||||||
video_packets.Clear();
|
video_packets.Clear();
|
||||||
audio_packets.Clear();
|
audio_packets.Clear();
|
||||||
video_frames.Clear();
|
video_frames.Clear();
|
||||||
audio_frames.Clear();
|
audio_frames.Clear();
|
||||||
video_buffers.Clear();
|
video_buffers.Clear();
|
||||||
audio_buffers.Clear();
|
audio_buffers.Clear();
|
||||||
retired_video.clear();
|
if (!retain_output_buffers) {
|
||||||
current_video.reset();
|
retired_video.clear();
|
||||||
current_audio.reset();
|
current_video.reset();
|
||||||
|
current_audio.reset();
|
||||||
|
}
|
||||||
if (sws != nullptr) {
|
if (sws != nullptr) {
|
||||||
sws_freeContext(sws);
|
sws_freeContext(sws);
|
||||||
sws = nullptr;
|
sws = nullptr;
|
||||||
@@ -1042,9 +1055,6 @@ private:
|
|||||||
last_audio_ts = 0;
|
last_audio_ts = 0;
|
||||||
last_output_loop_offset = 0;
|
last_output_loop_offset = 0;
|
||||||
pending_loop_warnings = 0;
|
pending_loop_warnings = 0;
|
||||||
current_video = std::move(delivered_video);
|
|
||||||
current_audio = std::move(delivered_audio);
|
|
||||||
retired_video = std::move(in_flight_video);
|
|
||||||
}
|
}
|
||||||
void AutoEnable() {
|
void AutoEnable() {
|
||||||
for (uint32_t i = 0; i < fmt->nb_streams; i++) {
|
for (uint32_t i = 0; i < fmt->nb_streams; i++) {
|
||||||
@@ -1174,6 +1184,7 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool StartWorkers() {
|
bool StartWorkers() {
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
worker_stop = false;
|
worker_stop = false;
|
||||||
interrupt_io = false;
|
interrupt_io = false;
|
||||||
pipeline_failed = false;
|
pipeline_failed = false;
|
||||||
@@ -1183,20 +1194,26 @@ private:
|
|||||||
if ((video_id && !CreateWorker(video_thread, VideoDecoderEntry, "AvPlayerVideoDecoder")) ||
|
if ((video_id && !CreateWorker(video_thread, VideoDecoderEntry, "AvPlayerVideoDecoder")) ||
|
||||||
(audio_id && !CreateWorker(audio_thread, AudioDecoderEntry, "AvPlayerAudioDecoder")) ||
|
(audio_id && !CreateWorker(audio_thread, AudioDecoderEntry, "AvPlayerAudioDecoder")) ||
|
||||||
!CreateWorker(demux_thread, DemuxEntry, "AvPlayerDemuxer")) {
|
!CreateWorker(demux_thread, DemuxEntry, "AvPlayerDemuxer")) {
|
||||||
|
lock.unlock();
|
||||||
StopWorkers();
|
StopWorkers();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
stop_event_sent = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void NotifyWorkers() {
|
void NotifyWorkers() {
|
||||||
|
completion_changed.notify_all();
|
||||||
video_packets.Notify();
|
video_packets.Notify();
|
||||||
audio_packets.Notify();
|
audio_packets.Notify();
|
||||||
video_buffers.Notify();
|
video_buffers.Notify();
|
||||||
audio_buffers.Notify();
|
audio_buffers.Notify();
|
||||||
}
|
}
|
||||||
void StopWorkers() {
|
void StopWorkers() {
|
||||||
worker_stop = true;
|
{
|
||||||
interrupt_io = true;
|
std::lock_guard lock(mutex);
|
||||||
|
worker_stop = true;
|
||||||
|
interrupt_io = true;
|
||||||
|
}
|
||||||
NotifyWorkers();
|
NotifyWorkers();
|
||||||
if (demux_thread != nullptr) {
|
if (demux_thread != nullptr) {
|
||||||
LibKernel::PthreadJoin(demux_thread, nullptr);
|
LibKernel::PthreadJoin(demux_thread, nullptr);
|
||||||
@@ -1212,9 +1229,12 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void FailPipeline() {
|
void FailPipeline() {
|
||||||
pipeline_failed = true;
|
{
|
||||||
worker_stop = true;
|
std::lock_guard lock(mutex);
|
||||||
interrupt_io = true;
|
pipeline_failed = true;
|
||||||
|
worker_stop = true;
|
||||||
|
interrupt_io = true;
|
||||||
|
}
|
||||||
NotifyWorkers();
|
NotifyWorkers();
|
||||||
}
|
}
|
||||||
void Demux() {
|
void Demux() {
|
||||||
@@ -1269,6 +1289,13 @@ private:
|
|||||||
demux_eof = true;
|
demux_eof = true;
|
||||||
video_packets.Notify();
|
video_packets.Notify();
|
||||||
audio_packets.Notify();
|
audio_packets.Notify();
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
completion_changed.wait(lock, [&] { return worker_stop || DrainedNoLock(); });
|
||||||
|
if (!worker_stop) {
|
||||||
|
const auto stop_event = TakeStopEvent();
|
||||||
|
lock.unlock();
|
||||||
|
emit_event(stop_event, AVPLAYER_EVENT_STATE_STOP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void VideoDecoder() {
|
void VideoDecoder() {
|
||||||
Decoder(video_packets, video_ctx, video_buffers, video_frames, video_done,
|
Decoder(video_packets, video_ctx, video_buffers, video_frames, video_done,
|
||||||
@@ -1316,7 +1343,11 @@ private:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done = true;
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
completion_changed.notify_all();
|
||||||
}
|
}
|
||||||
bool DecodePacket(AVCodecContext* codec, AVPacket* packet, uint64_t timestamp_offset,
|
bool DecodePacket(AVCodecContext* codec, AVPacket* packet, uint64_t timestamp_offset,
|
||||||
WorkQueue<std::unique_ptr<GuestBuffer>>& buffers,
|
WorkQueue<std::unique_ptr<GuestBuffer>>& buffers,
|
||||||
@@ -1607,6 +1638,7 @@ private:
|
|||||||
}
|
}
|
||||||
AvPlayerMemAllocator mem;
|
AvPlayerMemAllocator mem;
|
||||||
AvPlayerFileReplacement file;
|
AvPlayerFileReplacement file;
|
||||||
|
AvPlayerEventReplacement event;
|
||||||
int max_video_buffers = 2;
|
int max_video_buffers = 2;
|
||||||
bool use_vdec2 = false;
|
bool use_vdec2 = false;
|
||||||
AvPlayerSourceType source_type = AvPlayerSourceUnknown;
|
AvPlayerSourceType source_type = AvPlayerSourceUnknown;
|
||||||
@@ -1640,6 +1672,8 @@ private:
|
|||||||
std::atomic_bool demux_eof {true};
|
std::atomic_bool demux_eof {true};
|
||||||
std::atomic_bool video_done {true};
|
std::atomic_bool video_done {true};
|
||||||
std::atomic_bool audio_done {true};
|
std::atomic_bool audio_done {true};
|
||||||
|
std::atomic_bool stop_event_sent {true};
|
||||||
|
std::condition_variable completion_changed;
|
||||||
std::mutex lifecycle_mutex;
|
std::mutex lifecycle_mutex;
|
||||||
mutable std::mutex mutex;
|
mutable std::mutex mutex;
|
||||||
bool stopped = true;
|
bool stopped = true;
|
||||||
@@ -1673,19 +1707,13 @@ struct AvPlayerInternal {
|
|||||||
static bool valid_allocators(const AvPlayerMemAllocator& m) {
|
static bool valid_allocators(const AvPlayerMemAllocator& m) {
|
||||||
return m.allocate && m.deallocate && m.allocate_texture && m.deallocate_texture;
|
return m.allocate && m.deallocate && m.allocate_texture && m.deallocate_texture;
|
||||||
}
|
}
|
||||||
static void emit_event(AvPlayerInternal* h, int32_t id, void* data = nullptr) {
|
|
||||||
if (h != nullptr && h->event.event_callback != nullptr) {
|
|
||||||
LOGF("\t event_id = %d\n", id);
|
|
||||||
h->event.event_callback(h->event.object_pointer, id, 0, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void pump_warnings(AvPlayerInternal* h) {
|
static void pump_warnings(AvPlayerInternal* h) {
|
||||||
if (h == nullptr || h->source == nullptr) {
|
if (h == nullptr || h->source == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (auto w = h->source->TakeWarning()) {
|
while (auto w = h->source->TakeWarning()) {
|
||||||
int32_t warning = *w;
|
int32_t warning = *w;
|
||||||
emit_event(h, AVPLAYER_EVENT_WARNING_ID, &warning);
|
emit_event(h->event, AVPLAYER_EVENT_WARNING_ID, &warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static AvPlayerInternal* create_player(const AvPlayerMemAllocator& mem,
|
static AvPlayerInternal* create_player(const AvPlayerMemAllocator& mem,
|
||||||
@@ -1713,17 +1741,17 @@ static int add_source(AvPlayerInternal* h, const std::string& filename, AvPlayer
|
|||||||
}
|
}
|
||||||
bool vdec2 =
|
bool vdec2 =
|
||||||
h->post_init.video_decoder_init.decoder_type.video_type == AvPlayerVideoDecoderSoftware2;
|
h->post_init.video_decoder_init.decoder_type.video_type == AvPlayerVideoDecoderSoftware2;
|
||||||
auto s = std::make_unique<Source>(h->mem, h->file, h->video_buffers, vdec2,
|
auto s = std::make_unique<Source>(h->mem, h->file, h->event, h->video_buffers, vdec2,
|
||||||
h->post_init.demux_video_buffer_size);
|
h->post_init.demux_video_buffer_size);
|
||||||
if (auto rc = s->Init(filename, type); rc < 0) {
|
if (auto rc = s->Init(filename, type); rc < 0) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
h->source = std::move(s);
|
h->source = std::move(s);
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_READY);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_READY);
|
||||||
if (h->auto_start) {
|
if (h->auto_start) {
|
||||||
auto rc = h->source->Start();
|
auto rc = h->source->Start();
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1872,7 +1900,7 @@ int KYTY_SYSV_ABI AvPlayerStart(AvPlayerInternal* h) {
|
|||||||
}
|
}
|
||||||
auto rc = h->source->Start();
|
auto rc = h->source->Start();
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1887,7 +1915,7 @@ int KYTY_SYSV_ABI AvPlayerStartEx(AvPlayerInternal* h, const void* start_info_ex
|
|||||||
: static_cast<const AvPlayerStartInfoEx*>(start_info_ex)->start_time_milliseconds;
|
: static_cast<const AvPlayerStartInfoEx*>(start_info_ex)->start_time_milliseconds;
|
||||||
auto rc = h->source->Start(ms);
|
auto rc = h->source->Start(ms);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1896,9 +1924,7 @@ int KYTY_SYSV_ABI AvPlayerStop(AvPlayerInternal* h) {
|
|||||||
if (h == nullptr || h->source == nullptr) {
|
if (h == nullptr || h->source == nullptr) {
|
||||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
auto rc = h->source->Stop();
|
return h->source->Stop();
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_STOP);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
int KYTY_SYSV_ABI AvPlayerPause(AvPlayerInternal* h) {
|
int KYTY_SYSV_ABI AvPlayerPause(AvPlayerInternal* h) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
@@ -1906,7 +1932,7 @@ int KYTY_SYSV_ABI AvPlayerPause(AvPlayerInternal* h) {
|
|||||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
h->source->Pause();
|
h->source->Pause();
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_PAUSE);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_PAUSE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int KYTY_SYSV_ABI AvPlayerResume(AvPlayerInternal* h) {
|
int KYTY_SYSV_ABI AvPlayerResume(AvPlayerInternal* h) {
|
||||||
@@ -1915,7 +1941,7 @@ int KYTY_SYSV_ABI AvPlayerResume(AvPlayerInternal* h) {
|
|||||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||||
}
|
}
|
||||||
h->source->Resume();
|
h->source->Resume();
|
||||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int KYTY_SYSV_ABI AvPlayerSetLooping(AvPlayerInternal* h, Bool loop) {
|
int KYTY_SYSV_ABI AvPlayerSetLooping(AvPlayerInternal* h, Bool loop) {
|
||||||
@@ -2017,7 +2043,7 @@ int KYTY_SYSV_ABI AvPlayerJumpToTime(AvPlayerInternal* h, uint64_t time_ms) {
|
|||||||
auto rc = h->source->Jump(time_ms);
|
auto rc = h->source->Jump(time_ms);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
int32_t w = AVPLAYER_WARNING_JUMP_COMPLETE;
|
int32_t w = AVPLAYER_WARNING_JUMP_COMPLETE;
|
||||||
emit_event(h, AVPLAYER_EVENT_WARNING_ID, &w);
|
emit_event(h->event, AVPLAYER_EVENT_WARNING_ID, &w);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user