Show fps, bitrate and dim for video

This commit is contained in:
Neeraj Gupta
2024-07-16 17:38:52 +05:30
parent c28b4934c4
commit 95c92b0572
5 changed files with 56 additions and 34 deletions

View File

@@ -17,6 +17,20 @@ class FFProbeProps {
DateTime? creationTime;
String? bitrate;
String? majorBrand;
String? fps;
String? codecWidth;
String? codecHeight;
// dot separated bitrate, fps, codecWidth, codecHeight. Ignore null value
String get videoInfo {
final List<String> info = [];
if (bitrate != null) info.add('$bitrate');
if (fps != null) info.add('ƒ/$fps');
if (codecWidth != null && codecHeight != null) {
info.add('$codecWidth x $codecHeight');
}
return info.join(' * ');
}
// toString() method
@override
@@ -37,6 +51,7 @@ class FFProbeProps {
for (final key in json!.keys) {
final stringKey = key.toString();
switch (stringKey) {
case FFProbeKeys.bitrate:
case FFProbeKeys.bps:
@@ -95,8 +110,21 @@ class FFProbeProps {
parsedData[stringKey] = json[key];
}
}
// iterate through the streams
final List<dynamic> streams = json["streams"];
for (final stream in streams) {
final Map<String, dynamic> streamData = {};
for (final key in stream.keys) {
if (key == "r_frame_rate") {
result.fps = stream[key];
} else if (key == "coded_width") {
result.codecWidth = stream[key].toString();
} else if (key == "coded_height") {
result.codecHeight = stream[key].toString();
}
}
}
result.prodData = parsedData;
return result;
}

View File

@@ -14,6 +14,8 @@ extension FilePropsExtn on EnteFile {
bool get isOwner =>
(ownerID == null) || (ownerID == Configuration.instance.getUserID());
bool get isVideo => fileType == FileType.video;
bool get canEditMetaInfo => isUploaded && isOwner;
bool get isTrash => this is TrashFile;

View File

@@ -4,6 +4,7 @@ import "dart:io";
import "package:exif/exif.dart";
import "package:ffmpeg_kit_flutter_min/ffprobe_kit.dart";
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:logging/logging.dart";
import "package:photos/core/configuration.dart";
@@ -11,9 +12,11 @@ import "package:photos/core/event_bus.dart";
import "package:photos/events/people_changed_event.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/ffmpeg/ffprobe_props.dart";
import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import 'package:photos/models/file/file_type.dart';
import "package:photos/models/metadata/file_magic.dart";
import "package:photos/service_locator.dart";
import "package:photos/services/file_magic_service.dart";
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/components/buttons/icon_button_widget.dart';
@@ -101,7 +104,7 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
_exifData["exposureTime"] != null ||
_exifData["ISO"] != null;
});
} else {
} else if (flagService.internalUser && widget.file.isVideo) {
getMediaInfo();
}
getExif(widget.file).then((exif) {
@@ -115,9 +118,7 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
final File? originFile = await getFile(widget.file, isOrigin: true);
if (originFile == null) return;
final session = await FFprobeKit.getMediaInformation(originFile.path);
final mediaInfo = session.getMediaInformation();
if (mediaInfo == null) {
final failStackTrace = await session.getFailStackTrace();
final output = await session.getOutput();
@@ -128,11 +129,10 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
}
final properties = await FFProbeUtil.getProperties(mediaInfo);
_videoMetadataNotifier.value = properties;
// print all the properties
log("videoCustomProps ${properties.toString()}");
log("PropData ${properties.prodData.toString()}");
if (kDebugMode) {
log("videoCustomProps ${properties.toString()}");
log("PropData ${properties.prodData.toString()}");
}
setState(() {});
}
@@ -268,19 +268,17 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
},
),
]);
} else if (_videoMetadataNotifier.value != null) {
} else if (flagService.internalUser && widget.file.isVideo) {
fileDetailsTiles.addAll([
ValueListenableBuilder(
valueListenable: _videoMetadataNotifier,
builder: (context, value, _) {
return (value != null && value.prodData != null)
? Column(
children: [
VideoExifRowItem(file, value.prodData),
const FileDetailsDivider(),
],
)
: const SizedBox.shrink();
return Column(
children: [
VideoExifRowItem(file, value),
const FileDetailsDivider(),
],
);
},
),
]);

View File

@@ -79,7 +79,7 @@ class VideoExifDialog extends StatelessWidget {
Widget _buildStreamInfo(BuildContext context, Map<String, dynamic> stream) {
String titleString = stream['type']?.toString().toUpperCase() ?? '';
final codeName = stream['codec_name']?.toString().toUpperCase() ?? '';
if (codeName != 'NULL') {
if (codeName != 'NULL' && codeName.isNotEmpty) {
titleString += ' - $codeName';
}
return ExpansionTile(
@@ -87,7 +87,7 @@ class VideoExifDialog extends StatelessWidget {
titleString,
style: getEnteTextTheme(context).smallBold,
),
childrenPadding: const EdgeInsets.symmetric(vertical: 2, horizontal: 4),
childrenPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 4),
tilePadding: const EdgeInsets.symmetric(vertical: 4),
children: [
Column(

View File

@@ -1,19 +1,20 @@
import "package:flutter/material.dart";
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/ffmpeg/ffprobe_props.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/theme/colors.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/components/info_item_widget.dart";
import "package:photos/ui/viewer/file/vid_exif_dialog.dart";
import "package:photos/ui/viewer/file/video_exif_dialog.dart";
import "package:photos/utils/toast_util.dart";
class VideoExifRowItem extends StatefulWidget {
final EnteFile file;
final Map<String, dynamic>? exif;
final FFProbeProps? props;
const VideoExifRowItem(
this.file,
this.exif, {
this.props, {
super.key,
});
@@ -34,7 +35,8 @@ class _VideoProbeInfoState extends State<VideoExifRowItem> {
return InfoItemWidget(
leadingIcon: Icons.text_snippet_outlined,
title: "Video Info",
subtitleSection: _exifButton(context, widget.file, widget.exif),
subtitleSection:
_exifButton(context, widget.file, widget.props?.prodData),
onTap: _onTap,
);
}
@@ -46,24 +48,16 @@ class _VideoProbeInfoState extends State<VideoExifRowItem> {
) async {
late final String label;
late final VoidCallback? onTap;
final Map<String, dynamic> data = {};
if (exif != null) {
for (final key in exif.keys) {
if (exif[key] != null) {
data[key] = exif[key];
}
}
}
if (exif == null) {
label = S.of(context).loadingExifData;
onTap = null;
} else if (exif.isNotEmpty) {
label = "Tap to view more details";
label = "${widget.props?.videoInfo ?? ''} ..";
onTap = () => showBarModalBottomSheet(
context: context,
builder: (BuildContext context) {
return VideoExifDialog(
probeData: data,
probeData: exif,
);
},
shape: const RoundedRectangleBorder(