fix(mpv): harden native initialization and logging
This commit is contained in:
@@ -727,6 +727,7 @@
|
|||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.edde746.plezy;
|
PRODUCT_BUNDLE_IDENTIFIER = com.edde746.plezy;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@@ -387,10 +387,12 @@ class MpvPlayerCoreBase: NSObject {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEMP: always "v" (tvOS builds in release) so the avfoundation VO's
|
#if DEBUG
|
||||||
// osd-perf timing lines show up — restore the DEBUG/warn split after the
|
let defaultLogLevel = "v"
|
||||||
// subtitle-timing investigation.
|
#else
|
||||||
checkError(mpv_request_log_messages(mpv, "v"))
|
let defaultLogLevel = "warn"
|
||||||
|
#endif
|
||||||
|
checkError(mpv_request_log_messages(mpv, defaultLogLevel))
|
||||||
|
|
||||||
configure()
|
configure()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async' show Completer;
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:plezy/mpv/models.dart';
|
import 'package:plezy/mpv/models.dart';
|
||||||
@@ -16,6 +18,41 @@ void main() {
|
|||||||
await SettingsService.getInstance();
|
await SettingsService.getInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('MPV coalesces concurrent Dart initialization requests', () async {
|
||||||
|
final initialize = Completer<bool>();
|
||||||
|
final calls = <MethodCall>[];
|
||||||
|
|
||||||
|
await withMockPlayerChannels(
|
||||||
|
methodChannelName: 'com.plezy/mpv_player',
|
||||||
|
eventChannelName: 'com.plezy/mpv_player/events',
|
||||||
|
methodHandler: (call) {
|
||||||
|
calls.add(call);
|
||||||
|
if (call.method == 'initialize') return initialize.future;
|
||||||
|
return Future.value(null);
|
||||||
|
},
|
||||||
|
testBody: () async {
|
||||||
|
final player = PlayerNative();
|
||||||
|
try {
|
||||||
|
final logLevel = player.setLogLevel('warn');
|
||||||
|
final command = player.command(['stop']);
|
||||||
|
await Future<void>.delayed(Duration.zero);
|
||||||
|
|
||||||
|
expect(calls.where((call) => call.method == 'initialize'), hasLength(1));
|
||||||
|
|
||||||
|
initialize.complete(true);
|
||||||
|
await Future.wait([logLevel, command]);
|
||||||
|
|
||||||
|
expect(calls.where((call) => call.method == 'initialize'), hasLength(1));
|
||||||
|
expect(calls.where((call) => call.method == 'setLogLevel'), hasLength(1));
|
||||||
|
expect(calls.where((call) => call.method == 'command'), hasLength(1));
|
||||||
|
} finally {
|
||||||
|
if (!initialize.isCompleted) initialize.complete(true);
|
||||||
|
await player.dispose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Android command failure reaches seek recovery', () async {
|
test('Android command failure reaches seek recovery', () async {
|
||||||
await withMockPlayerChannels(
|
await withMockPlayerChannels(
|
||||||
methodChannelName: 'com.plezy/mpv_player',
|
methodChannelName: 'com.plezy/mpv_player',
|
||||||
@@ -64,6 +101,28 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('audio setLogLevel uses the dedicated native channel', () async {
|
||||||
|
MethodCall? logLevelCall;
|
||||||
|
await withMockPlayerChannels(
|
||||||
|
methodChannelName: 'com.plezy/mpv_audio_player',
|
||||||
|
eventChannelName: 'com.plezy/mpv_audio_player/events',
|
||||||
|
methodHandler: (call) async {
|
||||||
|
if (call.method == 'initialize') return true;
|
||||||
|
if (call.method == 'setLogLevel') logLevelCall = call;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
testBody: () async {
|
||||||
|
final player = PlayerNative.audio();
|
||||||
|
try {
|
||||||
|
await player.setLogLevel('v');
|
||||||
|
expect(logLevelCall?.arguments, {'level': 'v'});
|
||||||
|
} finally {
|
||||||
|
await player.dispose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Android mpv end-file error preserves native diagnostic message', () async {
|
test('Android mpv end-file error preserves native diagnostic message', () async {
|
||||||
await withMockPlayerChannels(
|
await withMockPlayerChannels(
|
||||||
methodChannelName: 'com.plezy/mpv_player',
|
methodChannelName: 'com.plezy/mpv_player',
|
||||||
|
|||||||
@@ -862,6 +862,7 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = com.edde746.plezy;
|
PRODUCT_BUNDLE_IDENTIFIER = com.edde746.plezy;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ void MpvPlayerPlugin::HandleMethodCall(
|
|||||||
const auto& method = method_call.method_name();
|
const auto& method = method_call.method_name();
|
||||||
|
|
||||||
if (method == "initialize") {
|
if (method == "initialize") {
|
||||||
|
// Method-channel calls are serialized on the platform thread. Keep an
|
||||||
|
// already-live core instead of replacing it (which would synchronously
|
||||||
|
// join its event thread before MpvPlayer::Initialize can see its guard).
|
||||||
|
if (player_ && player_->IsInitialized()) {
|
||||||
|
result->Success(flutter::EncodableValue(true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (proc_id_) {
|
if (proc_id_) {
|
||||||
registrar_->UnregisterTopLevelWindowProcDelegate(proc_id_.value());
|
registrar_->UnregisterTopLevelWindowProcDelegate(proc_id_.value());
|
||||||
proc_id_ = std::nullopt;
|
proc_id_ = std::nullopt;
|
||||||
|
|||||||
Reference in New Issue
Block a user