From 1e6da9b5922fa422830968bfd7e98b218437b2bf Mon Sep 17 00:00:00 2001 From: 4aiman Date: Fri, 21 Aug 2026 13:27:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8B=D1=85=D0=BE=D0=B4=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D0=B6=D0=B4=D1=91=D1=82=20=D1=8F=D0=B4=D1=80=D0=BE=20mpv:?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=20Wine=20=D1=8D=D1=82=D0=BE=20=D0=BE?= =?UTF-8?q?=D0=B6=D0=B8=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=87=D0=B0=D0=BB=D0=BE=D1=81=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Журнал закрытия показал место точно: последняя запись «Закрытие: закрываю плеер», дальше ничего. Значит, зависание внутри mpv_terminate_destroy — она не просто отпускает клиента, а ждёт, пока ядро mpv свернёт работу и добьёт свои потоки. Под Linux это мгновенно, под Wine не кончалось вовсе, причём даже когда ничего не открывали. Теперь плеер отсоединяется через mpv_destroy: она отпускает клиента и возвращает управление сразу, а ядро без клиентов сворачивается само. Команда «завершиться» перед этим намеренно не посылается — её тоже выполняет ядро, и встать она могла бы точно так же. Если в библиотеке mpv_destroy не окажется, остаётся прежний путь с ожиданием, и в журнале будет видно, что пошли по нему. Отсюда следствие: libmpv больше не выгружается при выходе. Потоки ядра в этот момент могут ещё жить внутри неё, и выдёргивать её из-под них нельзя. Программа всё равно закрывается, память освободит система. Плеер получил свою строчку в журнале — теперь видно и отсоединение, и завершение, а не только шаги окна. --- src/uMainForm.pas | 10 ++++++++-- src/uMpvLib.pas | 6 ++++++ src/uMpvPlayer.pas | 43 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 5 deletions(-) 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