video: OGV support
This commit is contained in:
parent
dc4db4c4e3
commit
752e34b765
9 changed files with 44 additions and 6 deletions
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -44,6 +44,31 @@ class VideoMetadataFormatter {
|
|||
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'
|
||||
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;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue