video: OGV support

This commit is contained in:
Thibault Deckers 2021-06-28 12:27:34 +09:00
parent dc4db4c4e3
commit 752e34b765
9 changed files with 44 additions and 6 deletions

View file

@ -100,7 +100,7 @@ class SourceEntry {
private val hasDuration: Boolean private val hasDuration: Boolean
get() = durationMillis ?: 0 > 0 get() = durationMillis ?: 0 > 0
private val isVideo: Boolean val isVideo: Boolean
get() = MimeTypes.isVideo(sourceMimeType) get() = MimeTypes.isVideo(sourceMimeType)
val isSvg: Boolean val isSvg: Boolean

View file

@ -65,7 +65,7 @@ internal class ContentImageProvider : ImageProvider() {
} }
val entry = SourceEntry(map).fillPreCatalogMetadata(context) val entry = SourceEntry(map).fillPreCatalogMetadata(context)
if (entry.isSized || entry.isSvg) { if (entry.isSized || entry.isSvg || entry.isVideo) {
callback.onSuccess(entry.toMap()) callback.onSuccess(entry.toMap())
} else { } else {
callback.onFailure(Exception("entry has no size")) callback.onFailure(Exception("entry has no size"))

View file

@ -27,7 +27,7 @@ internal class FileImageProvider : ImageProvider() {
} }
entry.fillPreCatalogMetadata(context) entry.fillPreCatalogMetadata(context)
if (entry.isSized || entry.isSvg) { if (entry.isSized || entry.isSvg || entry.isVideo) {
callback.onSuccess(entry.toMap()) callback.onSuccess(entry.toMap())
} else { } else {
callback.onFailure(Exception("entry has no size")) callback.onFailure(Exception("entry has no size"))

View file

@ -40,6 +40,7 @@ object MimeTypes {
private const val MP2T = "video/mp2t" private const val MP2T = "video/mp2t"
private const val MP2TS = "video/mp2ts" private const val MP2TS = "video/mp2ts"
const val MP4 = "video/mp4" const val MP4 = "video/mp4"
private const val OGV = "video/ogg"
private const val WEBM = "video/webm" private const val WEBM = "video/webm"
fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith(IMAGE) fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith(IMAGE)
@ -71,7 +72,7 @@ object MimeTypes {
// as of `metadata-extractor` v2.14.0 // as of `metadata-extractor` v2.14.0
fun isSupportedByMetadataExtractor(mimeType: String) = when (mimeType) { fun isSupportedByMetadataExtractor(mimeType: String) = when (mimeType) {
DJVU, WBMP, MP2T, MP2TS, WEBM -> false DJVU, WBMP, MP2T, MP2TS, OGV, WEBM -> false
else -> true else -> true
} }

View file

@ -5,6 +5,7 @@ import 'package:aves/model/entry_cache.dart';
import 'package:aves/model/favourites.dart'; import 'package:aves/model/favourites.dart';
import 'package:aves/model/metadata.dart'; import 'package:aves/model/metadata.dart';
import 'package:aves/model/settings/settings.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/ref/mime_types.dart';
import 'package:aves/services/geocoding_service.dart'; import 'package:aves/services/geocoding_service.dart';
import 'package:aves/services/service_policy.dart'; import 'package:aves/services/service_policy.dart';
@ -33,7 +34,7 @@ class AvesEntry {
// `dateModifiedSecs` can be missing in viewer mode // `dateModifiedSecs` can be missing in viewer mode
int? _dateModifiedSecs; int? _dateModifiedSecs;
final int? sourceDateTakenMillis; final int? sourceDateTakenMillis;
final int? durationMillis; int? durationMillis;
int? _catalogDateMillis; int? _catalogDateMillis;
CatalogMetadata? _catalogMetadata; CatalogMetadata? _catalogMetadata;
AddressDetails? _addressDetails; AddressDetails? _addressDetails;
@ -432,6 +433,11 @@ class AvesEntry {
} }
catalogMetadata = CatalogMetadata(contentId: contentId); catalogMetadata = CatalogMetadata(contentId: contentId);
} else { } 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); catalogMetadata = await metadataService.getCatalogMetadata(this, background: background);
} }
} }
@ -552,6 +558,8 @@ class AvesEntry {
if (width is int) this.width = width; if (width is int) this.width = width;
final height = newFields['height']; final height = newFields['height'];
if (height is int) this.height = height; if (height is int) this.height = height;
final durationMillis = newFields['durationMillis'];
if (durationMillis is int) this.durationMillis = durationMillis;
final dateModifiedSecs = newFields['dateModifiedSecs']; final dateModifiedSecs = newFields['dateModifiedSecs'];
if (dateModifiedSecs is int) this.dateModifiedSecs = dateModifiedSecs; if (dateModifiedSecs is int) this.dateModifiedSecs = dateModifiedSecs;

View file

@ -38,6 +38,7 @@ class Keys {
static const selectedAudioStream = 'audio'; static const selectedAudioStream = 'audio';
static const selectedTextStream = 'timedtext'; static const selectedTextStream = 'timedtext';
static const selectedVideoStream = 'video'; static const selectedVideoStream = 'video';
static const sourceOshash = 'source_oshash';
static const startMicros = 'start_us'; static const startMicros = 'start_us';
static const statisticsTags = '_statistics_tags'; static const statisticsTags = '_statistics_tags';
static const statisticsWritingApp = '_statistics_writing_app'; static const statisticsWritingApp = '_statistics_writing_app';

View file

@ -44,6 +44,31 @@ class VideoMetadataFormatter {
return info; return info;
} }
static Future<Map<String, int>> getCatalogMetadata(AvesEntry entry) async {
final mediaInfo = await getVideoMetadata(entry);
final fields = <String, int>{};
final streams = mediaInfo[Keys.streams];
if (streams is List) {
final allStreamInfo = streams.cast<Map>();
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' // pattern to extract optional language code suffix, e.g. 'location-eng'
static final keyWithLanguagePattern = RegExp(r'^(.*)-([a-z]{3})$'); static final keyWithLanguagePattern = RegExp(r'^(.*)-([a-z]{3})$');
@ -198,6 +223,9 @@ class VideoMetadataFormatter {
// skip common square pixels (1:1) // skip common square pixels (1:1)
if (sarNum != sarDen) save('SAR', '$sarNum:$sarDen'); if (sarNum != sarDen) save('SAR', '$sarNum:$sarDen');
break; break;
case Keys.sourceOshash:
save('Source OSHash', value);
break;
case Keys.startMicros: case Keys.startMicros:
if (value != 0) save('Start', formatPreciseDuration(Duration(microseconds: value))); if (value != 0) save('Start', formatPreciseDuration(Duration(microseconds: value)));
break; break;

View file

@ -218,7 +218,7 @@ packages:
description: description:
path: "." path: "."
ref: aves ref: aves
resolved-ref: "789162b567e2eaef4d6047cb85e77d9c915e1bed" resolved-ref: "40da97eb749d28c476601cb868a10e6c8bf7e569"
url: "git://github.com/deckerst/fijkplayer.git" url: "git://github.com/deckerst/fijkplayer.git"
source: git source: git
version: "0.9.0" version: "0.9.0"