mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-22 10:10:08 +08:00
89
lib/plugin/pl_player/view/simple_video_texture.dart
Normal file
89
lib/plugin/pl_player/view/simple_video_texture.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
|
||||
class SimpleVideoTexture extends StatefulWidget {
|
||||
final Color fill;
|
||||
final VideoController controller;
|
||||
final double? aspectRatio;
|
||||
final FilterQuality filterQuality;
|
||||
|
||||
const SimpleVideoTexture({
|
||||
super.key,
|
||||
this.fill = Colors.black,
|
||||
required this.controller,
|
||||
this.aspectRatio,
|
||||
this.filterQuality = FilterQuality.low,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SimpleVideoTexture> createState() => SimpleVideoTextureState();
|
||||
}
|
||||
|
||||
class SimpleVideoTextureState extends State<SimpleVideoTexture> {
|
||||
late double _devicePixelRatio;
|
||||
late bool _visible =
|
||||
widget.controller.player.state.width > 0 &&
|
||||
widget.controller.player.state.height > 0;
|
||||
|
||||
late final StreamSubscription<(int, int)> _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// --------------------------------------------------
|
||||
// Do not show the video frame until width & height are available.
|
||||
// Since [ValueNotifier<Rect?>] inside [VideoController] only gets updated by the render loop (i.e. it will not fire when video's width & height are not available etc.), it's important to handle this separately here.
|
||||
_subscription = widget.controller.player.stream.size.listen((value) {
|
||||
final visible = value.$1 > 0 && value.$2 > 0;
|
||||
if (_visible != visible) {
|
||||
_visible = visible;
|
||||
// ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
|
||||
widget.controller.rect.notifyListeners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ctr = widget.controller;
|
||||
return ListenableBuilder(
|
||||
listenable: Listenable.merge([ctr.id, ctr.rect]),
|
||||
builder: (context, _) {
|
||||
final id = ctr.id.value;
|
||||
final rect = ctr.rect.value;
|
||||
if (id != null && rect != null && _visible) {
|
||||
return SizedBox(
|
||||
width: widget.aspectRatio == null
|
||||
? rect.width / _devicePixelRatio
|
||||
: rect.height / _devicePixelRatio * widget.aspectRatio!,
|
||||
height: rect.height / _devicePixelRatio,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (rect.width <= 1.0 &&
|
||||
rect.height <= 1.0 &&
|
||||
widget.fill != Colors.transparent)
|
||||
Positioned.fill(child: ColoredBox(color: widget.fill)),
|
||||
Texture(textureId: id, filterQuality: widget.filterQuality),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,5 @@
|
||||
part of 'view.dart';
|
||||
|
||||
Widget buildDmChart(
|
||||
Color color,
|
||||
List<double> dmTrend,
|
||||
VideoDetailController videoDetailController, [
|
||||
double offset = 0,
|
||||
]) {
|
||||
return IgnorePointer(
|
||||
child: Container(
|
||||
height: 12,
|
||||
margin: EdgeInsets.only(
|
||||
bottom:
|
||||
videoDetailController.viewPointList.isNotEmpty &&
|
||||
videoDetailController.showVP.value
|
||||
? 19.25 + offset
|
||||
: 4.25 + offset,
|
||||
),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
titlesData: const FlTitlesData(show: false),
|
||||
lineTouchData: const LineTouchData(enabled: false),
|
||||
gridData: const FlGridData(show: false),
|
||||
borderData: FlBorderData(show: false),
|
||||
minX: 0,
|
||||
maxX: (dmTrend.length - 1).toDouble(),
|
||||
minY: 0,
|
||||
maxY: dmTrend.max,
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: List.generate(
|
||||
dmTrend.length,
|
||||
(index) => FlSpot(
|
||||
index.toDouble(),
|
||||
dmTrend[index],
|
||||
),
|
||||
),
|
||||
isCurved: true,
|
||||
barWidth: 1,
|
||||
color: color,
|
||||
dotData: const FlDotData(show: false),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
color: color.withValues(alpha: 0.4),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSeekPreviewWidget(
|
||||
PlPlayerController plPlayerController,
|
||||
double maxWidth,
|
||||
@@ -291,94 +240,6 @@ class _VideoShotImageState extends State<VideoShotImage> {
|
||||
}
|
||||
}
|
||||
|
||||
const double _triangleHeight = 5.6;
|
||||
|
||||
class _DanmakuTip extends SingleChildRenderObjectWidget {
|
||||
const _DanmakuTip({
|
||||
this.offset = 0,
|
||||
super.child,
|
||||
});
|
||||
|
||||
final double offset;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _RenderDanmakuTip(offset: offset);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderDanmakuTip renderObject,
|
||||
) {
|
||||
renderObject.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderDanmakuTip extends RenderProxyBox {
|
||||
_RenderDanmakuTip({
|
||||
required double offset,
|
||||
}) : _offset = offset;
|
||||
|
||||
double _offset;
|
||||
double get offset => _offset;
|
||||
set offset(double value) {
|
||||
if (_offset == value) return;
|
||||
_offset = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
final paint = Paint()
|
||||
..color = const Color(0xB3000000)
|
||||
..style = .fill;
|
||||
|
||||
final radius = size.height / 2;
|
||||
const triangleBase = _triangleHeight * 2 / 3;
|
||||
|
||||
final triangleCenterX = (size.width / 2 + _offset).clamp(
|
||||
radius + triangleBase,
|
||||
size.width - radius - triangleBase,
|
||||
);
|
||||
final path = Path()
|
||||
// triangle (exceed)
|
||||
..moveTo(triangleCenterX - triangleBase, 0)
|
||||
..lineTo(triangleCenterX, -_triangleHeight)
|
||||
..lineTo(triangleCenterX + triangleBase, 0)
|
||||
// top
|
||||
..lineTo(size.width - radius, 0)
|
||||
// right
|
||||
..arcToPoint(
|
||||
Offset(size.width - radius, size.height),
|
||||
radius: Radius.circular(radius),
|
||||
)
|
||||
// bottom
|
||||
..lineTo(radius, size.height)
|
||||
// left
|
||||
..arcToPoint(
|
||||
Offset(radius, 0),
|
||||
radius: Radius.circular(radius),
|
||||
)
|
||||
..close();
|
||||
|
||||
context.canvas
|
||||
..save()
|
||||
..translate(offset.dx, offset.dy)
|
||||
..drawPath(path, paint)
|
||||
..drawPath(
|
||||
path,
|
||||
paint
|
||||
..color = const Color(0x7EFFFFFF)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.25,
|
||||
)
|
||||
..restore();
|
||||
|
||||
super.paint(context, offset);
|
||||
}
|
||||
}
|
||||
|
||||
class _VideoTime extends LeafRenderObjectWidget {
|
||||
const _VideoTime({
|
||||
required this.position,
|
||||
@@ -465,12 +326,6 @@ class _RenderVideoTime extends RenderBox {
|
||||
return Size(paragraph.maxIntrinsicWidth, paragraph.height * 2);
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
config.label = 'position:$_position\nduration:$_duration';
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
size = computeDryLayout(constraints);
|
||||
|
||||
Reference in New Issue
Block a user