diff --git a/src/uMainForm.pas b/src/uMainForm.pas index 9ad2616..040a01b 100644 --- a/src/uMainForm.pas +++ b/src/uMainForm.pas @@ -399,8 +399,14 @@ begin FreeAndNil(FSettings); Log('Закрытие: закрываю плеер.'); FreeAndNil(FPlayer); - Log('Закрытие: выгружаю libmpv.'); - UnloadMpvLib; + + { Библиотеку не выгружаем. + + Плеер отсоединяется от mpv, не дожидаясь, пока ядро добьёт свои + потоки, — иначе под Wine ожидание не кончалось вовсе. Значит, в + момент выхода эти потоки могут ещё жить внутри библиотеки, и + выгружать её из-под них нельзя. Программа всё равно закрывается, + и система освободит всё сама. } Log('Закрытие: освобождаю проект.'); FreeAndNil(FProject); Log('Закрытие завершено.'); diff --git a/src/uMpvLib.pas b/src/uMpvLib.pas index 30b2606..03fc850 100644 --- a/src/uMpvLib.pas +++ b/src/uMpvLib.pas @@ -104,6 +104,10 @@ var mpv_create: function: Pmpv_handle; cdecl; mpv_initialize: function(ctx: Pmpv_handle): cint; cdecl; mpv_terminate_destroy: procedure(ctx: Pmpv_handle); cdecl; + { Необязательная: отсоединяет клиента, не дожидаясь, пока ядро добьёт + свои потоки. Есть в libmpv с давних версий, но если вдруг не найдётся, + работаем без неё. } + mpv_destroy: procedure(ctx: Pmpv_handle); cdecl; mpv_set_option_string: function(ctx: Pmpv_handle; name, data: PAnsiChar): cint; cdecl; mpv_set_property: function(ctx: Pmpv_handle; name: PAnsiChar; format: cint; data: Pointer): cint; cdecl; @@ -279,6 +283,8 @@ begin Need('mpv_request_log_messages', Pointer(mpv_request_log_messages)); Need('mpv_wait_event', Pointer(mpv_wait_event)); Need('mpv_event_name', Pointer(mpv_event_name)); + { Без Need: её отсутствие не повод отказываться от предпросмотра. } + Bind('mpv_destroy', Pointer(mpv_destroy)); if Missing <> '' then begin diff --git a/src/uMpvPlayer.pas b/src/uMpvPlayer.pas index 0329e61..8ab1d51 100644 --- a/src/uMpvPlayer.pas +++ b/src/uMpvPlayer.pas @@ -97,6 +97,9 @@ type вовсе — он приходил из видеофайла, а не с звуковой дорожки. } procedure SelectExternalAudio; { Цепочка смешивания звуковых дорожек с их громкостями. } + { Собственная запись в журнал программы. Идёт тем же путём, что и + сообщения самого mpv: у окна для них уже есть обработчик. } + procedure Note(const AText: string); procedure BuildAudioGraph; function SetOption(const AName, AValue: string): Integer; public @@ -377,16 +380,50 @@ begin FPump.OnTimer := @PumpEvents; end; +procedure TMpvPlayer.Note(const AText: string); +begin + if Assigned(FOnLog) then + FOnLog(Self, 'nlve', 'info', AText); +end; + +{ Закрытие плеера. + + Порядок здесь не случайный, и стоил зависания под Wine. + + mpv_terminate_destroy не просто отпускает клиента — она ждёт, пока ядро + mpv свернёт всю свою работу и добьёт свои потоки. Под Wine это ожидание + не кончалось никогда: журнал обрывался на «закрываю плеер», и дальше + ничего. Под Linux та же функция отрабатывает мгновенно. + + Поэтому отсоединяемся, не дожидаясь ядра: mpv_destroy отпускает клиента + и возвращает управление сразу, а ядро без клиентов сворачивается само. + Программа в этот момент и так закрывается — ждать её незачем. + + Команду «завершиться» перед этим не посылаем намеренно: она тоже + выполняется ядром и под Wine могла бы встать точно так же. Отсоединение + ни от чего не зависит. + + Окно, в которое рисовал mpv, убирается только после этого: в обратном + порядке он успел бы обратиться к уже несуществующему окну. } destructor TMpvPlayer.Destroy; begin FPump.Enabled := False; FreeAndNil(FPump); - { Сначала гасим mpv, и только потом убираем окно, в которое он рисовал: - в обратном порядке он успел бы обратиться к уже несуществующему окну. } if FHandle <> nil then begin - mpv_terminate_destroy(FHandle); + if Assigned(mpv_destroy) then + begin + Note('плеер: отсоединяюсь от mpv'); + mpv_destroy(FHandle); + end + else + begin + { Старая libmpv без mpv_destroy: другого способа нет, ждём. } + Note('плеер: закрываю mpv с ожиданием'); + mpv_terminate_destroy(FHandle); + end; FHandle := nil; + Note('плеер: закрыт'); end; {$IFDEF LCLGTK2} if (FVideoWin <> 0) and (FDisplay <> nil) then