#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] ## <a id="unreleased"></a>[Unreleased]
### Added
- Cataloguing: detect/filter HDR videos
### Changed ### Changed
- check Media Store changes when resuming app - check Media Store changes when resuming app

View file

@ -1,6 +1,7 @@
package deckers.thibault.aves.channel.calls package deckers.thibault.aves.channel.calls
import android.content.Context import android.content.Context
import android.media.MediaFormat
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -559,7 +560,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
// identification of embedded gain map // identification of embedded gain map
if (xmpMeta.hasHdrGainMap()) { if (xmpMeta.hasHdrGainMap()) {
flags = flags or MASK_HAS_HDR_GAIN_MAP flags = flags or MASK_IS_HDR
} }
} catch (e: XMPException) { } catch (e: XMPException) {
Log.w(LOG_TAG, "failed to read XMP directory for uri=$uri", e) 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 metadataMap[KEY_FLAGS] = flags
} catch (e: Exception) { } catch (e: Exception) {
Log.w(LOG_TAG, "failed to get catalog metadata by MediaMetadataRetriever for uri=$uri", e) 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_360 = 1 shl 3
private const val MASK_IS_MULTIPAGE = 1 shl 4 private const val MASK_IS_MULTIPAGE = 1 shl 4
private const val MASK_IS_MOTION_PHOTO = 1 shl 5 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 = ";" private const val XMP_SUBJECTS_SEPARATOR = ";"
// overlay metadata // overlay metadata

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:aves/model/entry/entry.dart'; 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/services/common/services.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/viewer/controls/cast.dart'; import 'package:aves/widgets/viewer/controls/cast.dart';