64 lines
1.8 KiB
Dart
64 lines
1.8 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
class PlayerFrameCapture {
|
|
const PlayerFrameCapture._();
|
|
|
|
static const int budgetMilliseconds = 32;
|
|
|
|
static Future<Uint8List?> capture({
|
|
required BuildContext context,
|
|
required GlobalKey boundaryKey,
|
|
required String source,
|
|
}) async {
|
|
final boundary = boundaryKey.currentContext?.findRenderObject();
|
|
if (boundary is! RenderRepaintBoundary) {
|
|
_log('[player-entry] phase=frame-capture-unavailable source=$source');
|
|
return null;
|
|
}
|
|
final stopwatch = Stopwatch()..start();
|
|
try {
|
|
final pixelRatio = MediaQuery.devicePixelRatioOf(
|
|
context,
|
|
).clamp(1.0, 2.0).toDouble();
|
|
final image = await boundary.toImage(pixelRatio: pixelRatio);
|
|
final data = await image.toByteData(format: ui.ImageByteFormat.png);
|
|
image.dispose();
|
|
if (stopwatch.elapsedMilliseconds > budgetMilliseconds) {
|
|
_log(
|
|
'[player-entry] phase=frame-capture-over-budget source=$source '
|
|
'ms=${stopwatch.elapsedMilliseconds} budget_ms=$budgetMilliseconds',
|
|
);
|
|
}
|
|
if (data == null) {
|
|
_log('[player-entry] phase=frame-capture-empty source=$source');
|
|
return null;
|
|
}
|
|
final bytes = data.buffer.asUint8List(
|
|
data.offsetInBytes,
|
|
data.lengthInBytes,
|
|
);
|
|
_log(
|
|
'[player-entry] phase=frame-captured source=$source '
|
|
'bytes=${bytes.length} ms=${stopwatch.elapsedMilliseconds}',
|
|
);
|
|
return bytes;
|
|
} catch (error) {
|
|
_log(
|
|
'[player-entry] phase=frame-capture-failed source=$source '
|
|
'error=$error',
|
|
);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static void _log(String message) {
|
|
if (kDebugMode) debugPrint(message);
|
|
}
|
|
}
|