Skip to content

Commit

Permalink
fix: Implementing software zooming (bluecherrydvr#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Mar 21, 2024
2 parents d26096e + 7b92c5e commit 77091c7
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 113 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/flutter_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install Flutter
uses: subosito/[email protected]
with:
channel: master
channel: stable

- run: flutter upgrade
- run: flutter pub get
Expand All @@ -41,7 +41,7 @@ jobs:
- name: Install Flutter
uses: subosito/[email protected]
with:
channel: master
channel: stable

- run: flutter upgrade
- run: flutter pub get
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ lib
├───firebase_options.dart [auto-generated firebase configuration.]
└───main.dart [entry-point of the application.]
packages
├───unity_multi_window [multi-window support for desktop platforms.]
├───unity_video_player
│ ├───unity_video_player [the core video player logic.]
│ ├───unity_video_player_flutter [video player used as fallback, used on embedded platforms.]
│ ├───unity_video_player_main [main video player logic, used on most platforms.]
│ ├───unity_video_player_platform_interface [the platform interface for the video player.]
│ └───unity_video_player_web [web specific video player logic, used on web.]
```

Feel free to send any pull-requests to add any features you wish or fix any bugs you notice.
Expand Down
8 changes: 4 additions & 4 deletions lib/models/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ class Device {

/// The type of zoom matrix of this device.
///
/// Defaults to a 4x4 matrix.
final MatrixType matrixType;
/// If not provided, used from the settings.
final MatrixType? matrixType;

/// A list of text overlays that will be rendered over the video.
final List<VideoOverlay> overlays;
Expand All @@ -124,7 +124,7 @@ class Device {
required this.server,
this.hasPTZ = false,
this.url,
this.matrixType = MatrixType.t16,
this.matrixType,
this.overlays = const [],
this.preferredStreamingType,
this.externalData,
Expand Down Expand Up @@ -372,7 +372,7 @@ class Device {
'server': server.toJson(devices: false),
'hasPTZ': hasPTZ,
'url': url,
'matrixType': matrixType.index,
if (matrixType != null) 'matrixType': matrixType!.index,
'overlays': overlays.map((e) => e.toMap()).toList(),
'preferredStreamingType': preferredStreamingType?.name,
'externalData': externalData?.toMap(),
Expand Down
42 changes: 41 additions & 1 deletion lib/providers/settings_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'dart:io';

import 'package:bluecherry_client/providers/app_provider_interface.dart';
import 'package:bluecherry_client/providers/downloads_provider.dart';
import 'package:bluecherry_client/screens/layouts/desktop/external_stream.dart';
import 'package:bluecherry_client/utils/storage.dart';
import 'package:bluecherry_client/utils/video_player.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand All @@ -40,13 +42,15 @@ class _SettingsOption<T> {

late final String Function(T value) saveAs;
late final T Function(String value) loadFrom;
final ValueChanged<T>? onChanged;

late T _value;

T get value => _value;
set value(T newValue) {
SettingsProvider.instance.updateProperty(() {
_value = newValue;
onChanged?.call(value);
});
}

Expand All @@ -61,6 +65,7 @@ class _SettingsOption<T> {
T Function(String value)? loadFrom,
this.min,
this.max,
this.onChanged,
}) {
Future.microtask(() async {
_value = getDefault != null ? await getDefault!() : def;
Expand Down Expand Up @@ -341,6 +346,17 @@ class SettingsProvider extends UnityProvider {
loadFrom: (value) => MatrixType.values[int.parse(value)],
saveAs: (value) => value.index.toString(),
);
final kSoftwareZooming = _SettingsOption<bool>(
def: Platform.isMacOS ? true : false,
key: 'other.software_zoom',
onChanged: (value) {
for (final player in UnityPlayers.players.values) {
player
..resetCrop()
..softwareZoom = value;
}
},
);
final kShowDebugInfo = _SettingsOption(
def: kDebugMode,
key: 'other.show_debug_info',
Expand Down Expand Up @@ -405,6 +421,7 @@ class SettingsProvider extends UnityProvider {
kShowReleaseNotes.loadData(data),
kDefaultBetaMatrixedZoomEnabled.loadData(data),
kMatrixSize.loadData(data),
kSoftwareZooming.loadData(data),
kShowDebugInfo.loadData(data),
kShowNetworkUsage.loadData(data),
]);
Expand Down Expand Up @@ -460,6 +477,29 @@ class SettingsProvider extends UnityProvider {
kTimelineInitialPoint.saveAs(kTimelineInitialPoint.value),
kThemeMode.key: kThemeMode.saveAs(kThemeMode.value),
kLanguageCode.key: kLanguageCode.saveAs(kLanguageCode.value),
kDateFormat.key: kDateFormat.saveAs(kDateFormat.value),
kTimeFormat.key: kTimeFormat.saveAs(kTimeFormat.value),
kLaunchAppOnStartup.key:
kLaunchAppOnStartup.saveAs(kLaunchAppOnStartup.value),
kMinimizeToTray.key: kMinimizeToTray.saveAs(kMinimizeToTray.value),
kAnimationsEnabled.key:
kAnimationsEnabled.saveAs(kAnimationsEnabled.value),
kHighContrast.key: kHighContrast.saveAs(kHighContrast.value),
kLargeFont.key: kLargeFont.saveAs(kLargeFont.value),
kAllowDataCollection.key:
kAllowDataCollection.saveAs(kAllowDataCollection.value),
kAllowCrashReports.key:
kAllowCrashReports.saveAs(kAllowCrashReports.value),
kAutoUpdate.key: kAutoUpdate.saveAs(kAutoUpdate.value),
kShowReleaseNotes.key:
kShowReleaseNotes.saveAs(kShowReleaseNotes.value),
kDefaultBetaMatrixedZoomEnabled.key: kDefaultBetaMatrixedZoomEnabled
.saveAs(kDefaultBetaMatrixedZoomEnabled.value),
kMatrixSize.key: kMatrixSize.saveAs(kMatrixSize.value),
kSoftwareZooming.key: kSoftwareZooming.saveAs(kSoftwareZooming.value),
kShowDebugInfo.key: kShowDebugInfo.saveAs(kShowDebugInfo.value),
kShowNetworkUsage.key:
kShowNetworkUsage.saveAs(kShowNetworkUsage.value),
});
} catch (e) {
debugPrint('Error saving settings: $e');
Expand Down
32 changes: 3 additions & 29 deletions lib/screens/layouts/desktop/external_stream.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,12 @@ import 'package:bluecherry_client/utils/extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:unity_video_player/unity_video_player.dart';
import 'package:uuid/uuid.dart';

enum MatrixType {
t16(4),
t9(3),
t4(2),
t1(1);

final int size;

const MatrixType(this.size);

@override
String toString() {
return switch (this) {
MatrixType.t16 => '4x4',
MatrixType.t9 => '3x3',
MatrixType.t4 => '2x2',
MatrixType.t1 => '1x1',
};
}
export 'package:unity_video_player/unity_video_player.dart' show MatrixType;

extension MatrixTypeExtension on MatrixType {
Widget get icon {
return switch (this) {
MatrixType.t16 => const Icon(Icons.grid_4x4),
Expand All @@ -58,16 +42,6 @@ enum MatrixType {
MatrixType.t1 => const Icon(Icons.square_outlined),
};
}

MatrixType get next {
return switch (this) {
MatrixType.t16 => MatrixType.t9,
MatrixType.t9 => MatrixType.t4,
MatrixType.t4 => MatrixType.t16,
// ideally, t1 is never reached
MatrixType.t1 => MatrixType.t16,
};
}
}

class AddExternalStreamDialog extends StatefulWidget {
Expand Down
7 changes: 3 additions & 4 deletions lib/screens/layouts/desktop/multicast_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class _MulticastViewportState extends State<MulticastViewport> {
return const SizedBox.shrink();
}

final size = widget.device.matrixType.size;
final matrixType = widget.device.matrixType ?? settings.kMatrixSize.value;
final size = matrixType.size;
if (view.player.isCropped) {
return Listener(
onPointerSignal: (event) async {
Expand Down Expand Up @@ -155,9 +156,7 @@ class _MulticastViewportState extends State<MulticastViewport> {
onDoubleTap: () {
views.updateDevice(
widget.device,
widget.device.copyWith(
matrixType: widget.device.matrixType.next,
),
widget.device.copyWith(matrixType: matrixType.next),
);
},
onPressed: () {
Expand Down
3 changes: 2 additions & 1 deletion lib/screens/layouts/desktop/stream_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ class _StreamDataState extends State<StreamData> {
Center(
child: ToggleButtons(
isSelected: MatrixType.values.map((type) {
return type.index == matrixType.index;
return type.index ==
(matrixType?.index ?? settings.kMatrixSize.value);
}).toList(),
onPressed: (type) => setState(() {
matrixType = MatrixType.values[type];
Expand Down
96 changes: 67 additions & 29 deletions lib/screens/settings/advanced_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'dart:io';

import 'package:bluecherry_client/providers/settings_provider.dart';
import 'package:bluecherry_client/screens/layouts/desktop/external_stream.dart';
import 'package:bluecherry_client/screens/settings/settings_desktop.dart';
Expand Down Expand Up @@ -52,37 +54,59 @@ class AdvancedOptionsSettings extends StatelessWidget {
value: settings.kDefaultBetaMatrixedZoomEnabled.value,
onChanged: (value) {
if (value != null) {
settings.updateProperty(() {
settings.kDefaultBetaMatrixedZoomEnabled.value = value;
});
settings.kDefaultBetaMatrixedZoomEnabled.value = value;
}
},
),
if (kDebugMode)
OptionsChooserTile<MatrixType>(
title: 'Default Matrix Size',
icon: Icons.view_quilt,
value: MatrixType.t4,
values: MatrixType.values.map((size) {
return Option(
value: size,
text: size.toString(),
);
}),
onChanged: (v) {
settings.updateProperty(() {
settings.kMatrixSize.value = v;
});
},
OptionsChooserTile<MatrixType>(
title: 'Default Matrix Size',
icon: Icons.view_quilt,
value: settings.kMatrixSize.value,
values: MatrixType.values.map((size) {
return Option(
value: size,
text: size.toString(),
);
}),
onChanged: (v) {
settings.kMatrixSize.value = v;
},
),
CheckboxListTile.adaptive(
contentPadding: DesktopSettings.horizontalPadding,
secondary: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.center_focus_strong),
),
title: const Text('Software zoom'),
subtitle: Text(
'When enabled, the matrix zoom will not happen in the GPU. This is '
'useful when the hardware zoom is not working properly. '
'${Platform.isMacOS ? 'On macOS, this can not be disabled.' : ''}',
),
value: Platform.isMacOS ? true : settings.kSoftwareZooming.value,
onChanged: Platform.isMacOS
? null
: (v) {
if (v != null) {
settings.kSoftwareZooming.value = v;
}
},
dense: false,
),
const SubHeader('Developer Options'),
if (!kIsWeb) ...[
FutureBuilder(
future: getLogFile(),
builder: (context, snapshot) {
return ListTile(
contentPadding: DesktopSettings.horizontalPadding,
leading: const Icon(Icons.bug_report),
leading: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.bug_report),
),
title: const Text('Open log file'),
subtitle: Text(snapshot.data?.path ?? loc.loading),
trailing: const Icon(Icons.navigate_next),
Expand All @@ -100,7 +124,11 @@ class AdvancedOptionsSettings extends StatelessWidget {
builder: (context, snapshot) {
return ListTile(
contentPadding: DesktopSettings.horizontalPadding,
leading: const Icon(Icons.home),
leading: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.home),
),
title: const Text('Open app data'),
subtitle: Text(snapshot.data?.path ?? loc.loading),
trailing: const Icon(Icons.navigate_next),
Expand All @@ -113,9 +141,13 @@ class AdvancedOptionsSettings extends StatelessWidget {
);
},
),
CheckboxListTile(
CheckboxListTile.adaptive(
contentPadding: DesktopSettings.horizontalPadding,
secondary: const Icon(Icons.adb),
secondary: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.adb),
),
title: const Text('Debug info'),
subtitle: const Text(
'Display useful information for debugging, such as video metadata '
Expand All @@ -124,17 +156,19 @@ class AdvancedOptionsSettings extends StatelessWidget {
value: settings.kShowDebugInfo.value,
onChanged: (v) {
if (v != null) {
settings.updateProperty(() {
settings.kShowDebugInfo.value = v;
});
settings.kShowDebugInfo.value = v;
}
},
dense: false,
),
if (kDebugMode)
CheckboxListTile(
CheckboxListTile.adaptive(
contentPadding: DesktopSettings.horizontalPadding,
secondary: const Icon(Icons.network_check),
secondary: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.network_check),
),
title: const Text('Network Usage'),
subtitle: const Text(
'Display network usage information over playing videos.',
Expand All @@ -151,7 +185,11 @@ class AdvancedOptionsSettings extends StatelessWidget {
),
ListTile(
contentPadding: DesktopSettings.horizontalPadding,
leading: const Icon(Icons.restore),
leading: CircleAvatar(
backgroundColor: Colors.transparent,
foregroundColor: theme.iconTheme.color,
child: const Icon(Icons.restore),
),
title: const Text('Restore Defaults'),
subtitle: const Text(
'Restore all settings to their default values. This will not '
Expand Down
Loading

0 comments on commit 77091c7

Please sign in to comment.