#838 cataloguing: detect HDR videos
This commit is contained in:
parent
df2d088ecf
commit
219bf1bf3f
7 changed files with 27 additions and 15 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in a new issue