video: fixed aspect ratio, handle thumbnail loading error

This commit is contained in:
Thibault Deckers 2019-08-04 17:49:05 +09:00
parent cc0283d393
commit a7f9163ec3
6 changed files with 30 additions and 33 deletions

View file

@ -71,22 +71,22 @@ class ImageEntry {
bool get isVideo => mimeType.startsWith(MimeTypes.MIME_VIDEO); bool get isVideo => mimeType.startsWith(MimeTypes.MIME_VIDEO);
int getMegaPixels() { double get aspectRatio => height == 0 ? 1 : width / height;
return ((width * height) / 1000000).round();
}
DateTime getBestDate() { int get megaPixels => (width * height / 1000000).round();
DateTime get bestDate {
if (sourceDateTakenMillis != null && sourceDateTakenMillis > 0) return DateTime.fromMillisecondsSinceEpoch(sourceDateTakenMillis); if (sourceDateTakenMillis != null && sourceDateTakenMillis > 0) return DateTime.fromMillisecondsSinceEpoch(sourceDateTakenMillis);
if (dateModifiedSecs != null && dateModifiedSecs > 0) return DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs * 1000); if (dateModifiedSecs != null && dateModifiedSecs > 0) return DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs * 1000);
return null; return null;
} }
DateTime getMonthTaken() { DateTime get monthTaken {
final d = getBestDate(); final d = bestDate;
return d == null ? null : DateTime(d.year, d.month); return d == null ? null : DateTime(d.year, d.month);
} }
String getDurationText() { String get durationText {
final d = Duration(milliseconds: durationMillis); final d = Duration(milliseconds: durationMillis);
String twoDigits(int n) { String twoDigits(int n) {

View file

@ -55,7 +55,7 @@ class ThumbnailState extends State<Thumbnail> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fontSize = (widget.extent / 8).roundToDouble(); final fontSize = min(14.0, (widget.extent / 8).roundToDouble());
final iconSize = fontSize * 2; final iconSize = fontSize * 2;
return DefaultTextStyle( return DefaultTextStyle(
style: TextStyle( style: TextStyle(
@ -86,12 +86,14 @@ class ThumbnailState extends State<Thumbnail> {
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
constraints: BoxConstraints.tight(Size(dim, dim)), constraints: BoxConstraints.tight(Size(dim, dim)),
child: Image.memory( child: bytes.length > 0
? Image.memory(
bytes, bytes,
width: dim, width: dim,
height: dim, height: dim,
fit: BoxFit.cover, fit: BoxFit.cover,
), )
: Icon(Icons.error),
); );
}), }),
), ),
@ -123,7 +125,7 @@ class VideoTag extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.all(1), margin: EdgeInsets.all(1),
padding: EdgeInsets.only(right: 4), padding: EdgeInsets.only(right: iconSize / 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color(0xBB000000), color: Color(0xBB000000),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
@ -139,9 +141,7 @@ class VideoTag extends StatelessWidget {
size: iconSize, size: iconSize,
), ),
SizedBox(width: 2), SizedBox(width: 2),
Text( Text(entry.durationText)
entry.getDurationText(),
)
], ],
), ),
); );

View file

@ -16,7 +16,7 @@ class ThumbnailCollection extends StatelessWidget {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
ThumbnailCollection({Key key, this.entries, this.done}) ThumbnailCollection({Key key, this.entries, this.done})
: sections = groupBy(entries, (entry) => entry.getMonthTaken()), : sections = groupBy(entries, (entry) => entry.monthTaken),
super(key: key); super(key: key);
@override @override

View file

@ -213,15 +213,9 @@ class ImagePageState extends State<ImagePage> with AutomaticKeepAliveClientMixin
builder: (galleryContext, index) { builder: (galleryContext, index) {
final entry = widget.entries[index]; final entry = widget.entries[index];
if (entry.isVideo) { if (entry.isVideo) {
final screenSize = MediaQuery.of(galleryContext).size;
final videoAspectRatio = entry.width / entry.height;
final childWidth = min(screenSize.width, entry.width);
final childHeight = childWidth / videoAspectRatio;
debugPrint('ImagePageState video path=${entry.path} childWidth=$childWidth childHeight=$childHeight var=$videoAspectRatio car=${childWidth / childHeight}');
return PhotoViewGalleryPageOptions.customChild( return PhotoViewGalleryPageOptions.customChild(
child: AvesVideo(entry: entry), child: AvesVideo(entry: entry),
childSize: Size(childWidth, childHeight), childSize: MediaQuery.of(galleryContext).size,
// no heroTag because `Chewie` already internally builds one with the videoController // no heroTag because `Chewie` already internally builds one with the videoController
minScale: PhotoViewComputedScale.contained, minScale: PhotoViewComputedScale.contained,
initialScale: PhotoViewComputedScale.contained, initialScale: PhotoViewComputedScale.contained,
@ -264,15 +258,18 @@ class AvesVideoState extends State<AvesVideo> {
VideoPlayerController videoPlayerController; VideoPlayerController videoPlayerController;
ChewieController chewieController; ChewieController chewieController;
ImageEntry get entry => widget.entry;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
videoPlayerController = VideoPlayerController.file( videoPlayerController = VideoPlayerController.file(
File(widget.entry.path), File(entry.path),
// ensure the first frame is shown after the video is initialized );
)..initialize().then((_) => setState(() {}));
chewieController = ChewieController( chewieController = ChewieController(
videoPlayerController: videoPlayerController, videoPlayerController: videoPlayerController,
aspectRatio: entry.aspectRatio,
autoInitialize: true,
); );
} }

View file

@ -37,9 +37,9 @@ class InfoPageState extends State<InfoPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final date = entry.getBestDate(); final date = entry.bestDate;
final dateText = '${DateFormat.yMMMd().format(date)} ${DateFormat.Hm().format(date)}'; final dateText = '${DateFormat.yMMMd().format(date)} ${DateFormat.Hm().format(date)}';
final resolutionText = '${entry.width} × ${entry.height}${entry.isVideo ? '' : ' (${entry.getMegaPixels()} MP)'}'; final resolutionText = '${entry.width} × ${entry.height}${entry.isVideo ? '' : ' (${entry.megaPixels} MP)'}';
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: IconButton(
@ -75,7 +75,7 @@ class InfoPageState extends State<InfoPage> {
SectionRow('File'), SectionRow('File'),
InfoRow('Title', entry.title), InfoRow('Title', entry.title),
InfoRow('Date', dateText), InfoRow('Date', dateText),
if (entry.isVideo) InfoRow('Duration', entry.getDurationText()), if (entry.isVideo) InfoRow('Duration', entry.durationText),
InfoRow('Resolution', resolutionText), InfoRow('Resolution', resolutionText),
InfoRow('Size', formatFilesize(entry.sizeBytes)), InfoRow('Size', formatFilesize(entry.sizeBytes)),
InfoRow('Path', entry.path), InfoRow('Path', entry.path),

View file

@ -150,7 +150,7 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final subRowWidth = min(400.0, maxWidth); final subRowWidth = min(400.0, maxWidth);
final date = entry.getBestDate(); final date = entry.bestDate;
return DefaultTextStyle( return DefaultTextStyle(
style: TextStyle( style: TextStyle(
shadows: [ shadows: [