#838 cataloguing: detect HDR videos

This commit is contained in:
Thibault Deckers 2024-02-25 22:58:48 +01:00
parent df2d088ecf
commit 219bf1bf3f
7 changed files with 27 additions and 15 deletions

View file

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased]
### Added
- Cataloguing: detect/filter HDR videos
### Changed
- check Media Store changes when resuming app

View file

@ -1,6 +1,7 @@
package deckers.thibault.aves.channel.calls
import android.content.Context
import android.media.MediaFormat
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.os.Build
@ -559,7 +560,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
// identification of embedded gain map
if (xmpMeta.hasHdrGainMap()) {
flags = flags or MASK_HAS_HDR_GAIN_MAP
flags = flags or MASK_IS_HDR
}
} catch (e: XMPException) {
Log.w(LOG_TAG, "failed to read XMP directory for uri=$uri", e)
@ -797,6 +798,14 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_COLOR_TRANSFER) {
if (it == MediaFormat.COLOR_TRANSFER_ST2084 || it == MediaFormat.COLOR_TRANSFER_HLG) {
flags = flags or MASK_IS_HDR
}
}
}
metadataMap[KEY_FLAGS] = flags
} catch (e: Exception) {
Log.w(LOG_TAG, "failed to get catalog metadata by MediaMetadataRetriever for uri=$uri", e)
@ -1300,7 +1309,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
private const val MASK_IS_360 = 1 shl 3
private const val MASK_IS_MULTIPAGE = 1 shl 4
private const val MASK_IS_MOTION_PHOTO = 1 shl 5
private const val MASK_HAS_HDR_GAIN_MAP = 1 shl 6
private const val MASK_IS_HDR = 1 shl 6 // for images: embedded HDR gainmap, for videos: HDR color transfer
private const val XMP_SUBJECTS_SEPARATOR = ";"
// overlay metadata

View file

@ -242,6 +242,11 @@ class AvesEntry with AvesEntryBase {
return _bestDate;
}
@override
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
bool get isHdr => _catalogMetadata?.isHdr ?? false;
int get rating => _catalogMetadata?.rating ?? 0;
@override
@ -303,9 +308,6 @@ class AvesEntry with AvesEntryBase {
return d == null ? null : DateTime(d.year, d.month, d.day);
}
@override
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
@override
int? get durationMillis => _durationMillis;

View file

@ -13,8 +13,6 @@ extension ExtraAvesEntryMultipage on AvesEntry {
bool get isMotionPhoto => catalogMetadata?.isMotionPhoto ?? false;
bool get isHdr => catalogMetadata?.hasHdrGainMap ?? false;
String? getBurstKey(List<String> patterns) {
final key = BurstPatterns.getKeyForName(filenameWithoutExtension, patterns);
return key != null ? '$directory/$key' : null;

View file

@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
class CatalogMetadata {
final int id;
final int? dateMillis;
final bool isAnimated, isGeotiff, is360, isMultiPage, isMotionPhoto, hasHdrGainMap;
final bool isAnimated, isGeotiff, is360, isMultiPage, isMotionPhoto, isHdr;
bool isFlipped;
int? rotationDegrees;
final String? mimeType, xmpSubjects, xmpTitle;
@ -21,7 +21,7 @@ class CatalogMetadata {
static const _is360Mask = 1 << 3;
static const _isMultiPageMask = 1 << 4;
static const _isMotionPhotoMask = 1 << 5;
static const _hasHdrGainMapMask = 1 << 6;
static const _isHdr = 1 << 6; // for images: embedded HDR gainmap, for videos: HDR color transfer
CatalogMetadata({
required this.id,
@ -33,7 +33,7 @@ class CatalogMetadata {
this.is360 = false,
this.isMultiPage = false,
this.isMotionPhoto = false,
this.hasHdrGainMap = false,
this.isHdr = false,
this.rotationDegrees,
this.xmpSubjects,
this.xmpTitle,
@ -75,7 +75,7 @@ class CatalogMetadata {
is360: is360,
isMultiPage: isMultiPage ?? this.isMultiPage,
isMotionPhoto: isMotionPhoto,
hasHdrGainMap: hasHdrGainMap,
isHdr: isHdr,
rotationDegrees: rotationDegrees ?? this.rotationDegrees,
xmpSubjects: xmpSubjects,
xmpTitle: xmpTitle,
@ -97,7 +97,7 @@ class CatalogMetadata {
is360: flags & _is360Mask != 0,
isMultiPage: flags & _isMultiPageMask != 0,
isMotionPhoto: flags & _isMotionPhotoMask != 0,
hasHdrGainMap: flags & _hasHdrGainMapMask != 0,
isHdr: flags & _isHdr != 0,
// `rotationDegrees` should default to `sourceRotationDegrees`, not 0
rotationDegrees: map['rotationDegrees'],
xmpSubjects: map['xmpSubjects'] ?? '',
@ -112,7 +112,7 @@ class CatalogMetadata {
'id': id,
'mimeType': mimeType,
'dateMillis': dateMillis,
'flags': (isAnimated ? _isAnimatedMask : 0) | (isFlipped ? _isFlippedMask : 0) | (isGeotiff ? _isGeotiffMask : 0) | (is360 ? _is360Mask : 0) | (isMultiPage ? _isMultiPageMask : 0) | (isMotionPhoto ? _isMotionPhotoMask : 0) | (hasHdrGainMap ? _hasHdrGainMapMask : 0),
'flags': (isAnimated ? _isAnimatedMask : 0) | (isFlipped ? _isFlippedMask : 0) | (isGeotiff ? _isGeotiffMask : 0) | (is360 ? _is360Mask : 0) | (isMultiPage ? _isMultiPageMask : 0) | (isMotionPhoto ? _isMotionPhotoMask : 0) | (isHdr ? _isHdr : 0),
'rotationDegrees': rotationDegrees,
'xmpSubjects': xmpSubjects,
'xmpTitle': xmpTitle,

View file

@ -91,12 +91,12 @@ class GridThemeData {
if (located && showLocated) LocationIcon.located(),
if (!located && showUnlocated) LocationIcon.unlocated(),
if (entry.rating != 0 && showRating) RatingIcon(entry: entry),
if (entry.isHdr && showHdr) const HdrIcon(),
if (entry.isPureVideo)
VideoIcon(entry: entry)
else if (entry.isAnimated)
const AnimatedImageIcon()
else ...[
if (entry.isHdr && showHdr) const HdrIcon(),
if (entry.isRaw && showRaw) const RawIcon(),
if (entry.is360) const PanoramaIcon(),
],

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:math';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/viewer/controls/cast.dart';