diff --git a/android/app/libs/fijkplayer-full-release.aar b/android/app/libs/fijkplayer-full-release.aar index 27a06d6e1..11f0fb30f 100644 Binary files a/android/app/libs/fijkplayer-full-release.aar and b/android/app/libs/fijkplayer-full-release.aar differ diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt index bfbdc737c..07efb26fe 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt @@ -100,7 +100,7 @@ class SourceEntry { private val hasDuration: Boolean get() = durationMillis ?: 0 > 0 - private val isVideo: Boolean + val isVideo: Boolean get() = MimeTypes.isVideo(sourceMimeType) val isSvg: Boolean diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt index 03ac091b8..1f1829db5 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt @@ -65,7 +65,7 @@ internal class ContentImageProvider : ImageProvider() { } val entry = SourceEntry(map).fillPreCatalogMetadata(context) - if (entry.isSized || entry.isSvg) { + if (entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) } else { callback.onFailure(Exception("entry has no size")) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt index 5ce04ce10..eb0eb3138 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt @@ -27,7 +27,7 @@ internal class FileImageProvider : ImageProvider() { } entry.fillPreCatalogMetadata(context) - if (entry.isSized || entry.isSvg) { + if (entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) } else { callback.onFailure(Exception("entry has no size")) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt index eb8a24780..5deae04ed 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt @@ -40,6 +40,7 @@ object MimeTypes { private const val MP2T = "video/mp2t" private const val MP2TS = "video/mp2ts" const val MP4 = "video/mp4" + private const val OGV = "video/ogg" private const val WEBM = "video/webm" fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith(IMAGE) @@ -71,7 +72,7 @@ object MimeTypes { // as of `metadata-extractor` v2.14.0 fun isSupportedByMetadataExtractor(mimeType: String) = when (mimeType) { - DJVU, WBMP, MP2T, MP2TS, WEBM -> false + DJVU, WBMP, MP2T, MP2TS, OGV, WEBM -> false else -> true } diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 297756c47..7b3f1698b 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -5,6 +5,7 @@ import 'package:aves/model/entry_cache.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/metadata.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/model/video/metadata.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/geocoding_service.dart'; import 'package:aves/services/service_policy.dart'; @@ -33,7 +34,7 @@ class AvesEntry { // `dateModifiedSecs` can be missing in viewer mode int? _dateModifiedSecs; final int? sourceDateTakenMillis; - final int? durationMillis; + int? durationMillis; int? _catalogDateMillis; CatalogMetadata? _catalogMetadata; AddressDetails? _addressDetails; @@ -432,6 +433,11 @@ class AvesEntry { } catalogMetadata = CatalogMetadata(contentId: contentId); } else { + if (isVideo && (!isSized || durationMillis == 0)) { + // exotic video that is not sized during loading + final fields = await VideoMetadataFormatter.getCatalogMetadata(this); + await _applyNewFields(fields, persist: persist); + } catalogMetadata = await metadataService.getCatalogMetadata(this, background: background); } } @@ -552,6 +558,8 @@ class AvesEntry { if (width is int) this.width = width; final height = newFields['height']; if (height is int) this.height = height; + final durationMillis = newFields['durationMillis']; + if (durationMillis is int) this.durationMillis = durationMillis; final dateModifiedSecs = newFields['dateModifiedSecs']; if (dateModifiedSecs is int) this.dateModifiedSecs = dateModifiedSecs; diff --git a/lib/model/video/keys.dart b/lib/model/video/keys.dart index 773f1809e..47a84bc84 100644 --- a/lib/model/video/keys.dart +++ b/lib/model/video/keys.dart @@ -38,6 +38,7 @@ class Keys { static const selectedAudioStream = 'audio'; static const selectedTextStream = 'timedtext'; static const selectedVideoStream = 'video'; + static const sourceOshash = 'source_oshash'; static const startMicros = 'start_us'; static const statisticsTags = '_statistics_tags'; static const statisticsWritingApp = '_statistics_writing_app'; diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart index 477120572..dd8a4f01e 100644 --- a/lib/model/video/metadata.dart +++ b/lib/model/video/metadata.dart @@ -44,6 +44,31 @@ class VideoMetadataFormatter { return info; } + static Future> getCatalogMetadata(AvesEntry entry) async { + final mediaInfo = await getVideoMetadata(entry); + final fields = {}; + + final streams = mediaInfo[Keys.streams]; + if (streams is List) { + final allStreamInfo = streams.cast(); + final sizedStream = allStreamInfo.firstWhereOrNull((stream) => stream.containsKey(Keys.width) && stream.containsKey(Keys.height)); + if (sizedStream != null) { + final width = sizedStream[Keys.width]; + final height = sizedStream[Keys.height]; + if (width is int && height is int) { + fields['width'] = width; + fields['height'] = height; + } + } + } + + final durationMicros = mediaInfo[Keys.durationMicros]; + if (durationMicros is num) { + fields['durationMillis'] = (durationMicros / 1000).round(); + } + return fields; + } + // pattern to extract optional language code suffix, e.g. 'location-eng' static final keyWithLanguagePattern = RegExp(r'^(.*)-([a-z]{3})$'); @@ -198,6 +223,9 @@ class VideoMetadataFormatter { // skip common square pixels (1:1) if (sarNum != sarDen) save('SAR', '$sarNum:$sarDen'); break; + case Keys.sourceOshash: + save('Source OSHash', value); + break; case Keys.startMicros: if (value != 0) save('Start', formatPreciseDuration(Duration(microseconds: value))); break; diff --git a/pubspec.lock b/pubspec.lock index 1ec97be1d..d838b9b18 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -218,7 +218,7 @@ packages: description: path: "." ref: aves - resolved-ref: "789162b567e2eaef4d6047cb85e77d9c915e1bed" + resolved-ref: "40da97eb749d28c476601cb868a10e6c8bf7e569" url: "git://github.com/deckerst/fijkplayer.git" source: git version: "0.9.0"