Initial commit

This commit is contained in:
admin1
2026-07-14 11:11:36 +08:00
commit 656499cf94
604 changed files with 119518 additions and 0 deletions
@@ -0,0 +1,155 @@
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smplayer/core/contracts/cancellation.dart';
import 'package:smplayer/core/infra/player_engine/player_engine.dart';
import 'package:smplayer/features/player/session/player_engine_holder.dart';
import 'package:smplayer/features/player/session/player_engine_resolution.dart';
void main() {
group('PlayerEngineHolder', () {
test('同 kind 复用:第二次 acquire 不再调 create', () async {
final holder = PlayerEngineHolder();
var createCalls = 0;
_FakeEngine factory(String tag) {
createCalls++;
return _FakeEngine(tag);
}
final first = await holder.acquire(
PlayerEngineKind.media3,
() => factory('a'),
);
final second = await holder.acquire(
PlayerEngineKind.media3,
() => factory('b'),
);
expect(createCalls, 1);
expect(identical(first, second), isTrue);
expect((second as _FakeEngine).tag, 'a');
});
test('异 kind dispose+create:旧引擎先 dispose 再新建', () async {
final holder = PlayerEngineHolder();
final disposeOrder = <String>[];
final acquireOrder = <String>[];
final fvp = _FakeEngine(
'fvp',
onDispose: () async {
await Future<void>.delayed(const Duration(milliseconds: 10));
disposeOrder.add('fvp');
},
);
await holder.acquire(PlayerEngineKind.fvp, () => fvp);
acquireOrder.add('fvp');
final media3 = _FakeEngine('media3');
final second = await holder.acquire(PlayerEngineKind.media3, () {
acquireOrder.add('media3-create');
return media3;
});
expect(disposeOrder, ['fvp']);
expect(acquireOrder, ['fvp', 'media3-create']);
expect(second, media3);
expect(fvp.disposed, isTrue);
expect(media3.disposed, isFalse);
});
});
}
class _FakeEngine implements PlayerEngine {
final String tag;
final Future<void> Function()? onDispose;
bool disposed = false;
_FakeEngine(this.tag, {this.onDispose});
@override
Future<void> dispose() async {
if (disposed) return;
if (onDispose != null) await onDispose!();
disposed = true;
}
@override
String get displayName => tag;
@override
bool get usesPlatformSurface => false;
@override
Future<int?> totalRxBytes() async => null;
@override
bool isNetworkStreamError(String error) => false;
@override
void configureHardwareDecoding(String mode) {}
@override
Future<void> open(
String url, {
Duration start = Duration.zero,
bool play = true,
Map<String, String>? headers,
CancellationToken? cancel,
}) async {}
@override
Future<void> play() async {}
@override
Future<void> pause() async {}
@override
Future<void> stop() async {}
@override
Future<void> seek(Duration position) async {}
@override
Future<void> setRate(double rate) async {}
@override
Future<void> setVolume(double volume) async {}
@override
Future<void> toggleMute() async {}
@override
Future<void> setAudioTrack(AudioTrack track) async {}
@override
Future<void> setSubtitleTrack(SubtitleTrack track) async {}
@override
void setNativeSubtitleRendering(bool enabled) {}
@override
void setSubtitleStyle({
required double fontSize,
required double bgOpacity,
required int position,
}) {}
@override
Widget buildVideoView(BuildContext context, ValueListenable<BoxFit> fit) =>
const SizedBox.shrink();
Future<String> getProperty(String name) async => '';
@override
PlayerState get state => PlayerState();
@override
PlayerStreams get stream => PlayerStreams();
@override
ValueListenable<int?> get textureId => ValueNotifier<int?>(null);
@override
ValueListenable<int> get textureVersion => ValueNotifier<int>(0);
@override
ValueNotifier<ui.Size?> get videoSize => ValueNotifier<ui.Size?>(null);
@override
ValueListenable<PlaybackStats?> get debugStats =>
ValueNotifier<PlaybackStats?>(null);
}