viewer: improved fetch for unknown content

This commit is contained in:
Thibault Deckers 2020-03-17 14:24:56 +09:00
parent 8d798c6f08
commit e011efe6a9
5 changed files with 84 additions and 27 deletions

View file

@ -222,7 +222,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
if (dateString != null) {
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
// some videos have an invalid default date (19040101T000000.000Z) that is before Epoch time
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
if (dateMillis > 0) {
metadataMap.put("dateMillis", dateMillis);
}

View file

@ -2,7 +2,9 @@ package deckers.thibault.aves.model.provider;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
@ -17,39 +19,92 @@ import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import deckers.thibault.aves.utils.Constants;
import deckers.thibault.aves.utils.MetadataHelper;
import static deckers.thibault.aves.utils.MetadataHelper.getOrientationDegreesForExifCode;
class UnknownContentImageProvider extends ImageProvider {
@Override
public void fetchSingle(final Activity activity, final Uri uri, final String mimeType, final ImageOpCallback callback) {
int width = 0, height = 0, orientationDegrees = 0;
Long sourceDateTakenMillis = null;
int width = 0, height = 0;
Integer orientationDegrees = null;
Long sourceDateTakenMillis = null, durationMillis = null;
String title = null;
// check metadata first
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
Metadata metadata = ImageMetadataReader.readMetadata(is);
JpegDirectory jpegDir = metadata.getFirstDirectoryOfType(JpegDirectory.class);
if (jpegDir != null) {
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
width = jpegDir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
}
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) {
height = jpegDir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
}
// check first metadata with MediaMetadataRetriever
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(activity, uri);
title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
if (dateMillis > 0) {
sourceDateTakenMillis = dateMillis;
}
ExifIFD0Directory exifDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
if (exifDir != null) {
if (exifDir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
orientationDegrees = getOrientationDegreesForExifCode(exifDir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
}
if (exifDir.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
sourceDateTakenMillis = exifDir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime();
String widthString = null, heightString = null, rotationString = null, durationMillisString = null;
if (mimeType.startsWith(Constants.MIME_IMAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
widthString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
heightString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
}
} else if (mimeType.startsWith(Constants.MIME_VIDEO)) {
widthString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
heightString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
durationMillisString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
}
} catch (IOException | ImageProcessingException | MetadataException e) {
if (widthString != null) {
width = Integer.parseInt(widthString);
}
if (heightString != null) {
height = Integer.parseInt(heightString);
}
if (rotationString != null) {
orientationDegrees = Integer.parseInt(rotationString);
}
if (durationMillisString != null) {
durationMillis = Long.parseLong(durationMillisString);
}
retriever.release();
} catch (Exception e) {
// ignore
}
// fallback to metadata-extractor for known types
if (width <= 0 || height <= 0 || orientationDegrees == null || sourceDateTakenMillis == null) {
if (Constants.MIME_JPEG.equals(mimeType)) {
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
Metadata metadata = ImageMetadataReader.readMetadata(is);
JpegDirectory jpegDir = metadata.getFirstDirectoryOfType(JpegDirectory.class);
if (jpegDir != null) {
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
width = jpegDir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
}
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) {
height = jpegDir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
}
}
ExifIFD0Directory exifDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
if (exifDir != null) {
if (exifDir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
orientationDegrees = getOrientationDegreesForExifCode(exifDir.getInt(ExifIFD0Directory.TAG_ORIENTATION));
}
if (exifDir.containsTag(ExifIFD0Directory.TAG_DATETIME)) {
sourceDateTakenMillis = exifDir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime();
}
}
} catch (IOException | ImageProcessingException | MetadataException e) {
// ignore
}
}
}
// fallback to decoding the image bounds
if (width <= 0 || height <= 0) {
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
@ -75,13 +130,13 @@ class UnknownContentImageProvider extends ImageProvider {
entry.put("mimeType", mimeType);
entry.put("width", width);
entry.put("height", height);
entry.put("orientationDegrees", orientationDegrees);
entry.put("orientationDegrees", orientationDegrees != null ? orientationDegrees : 0);
entry.put("sizeBytes", null);
entry.put("title", null);
entry.put("title", title);
entry.put("dateModifiedSecs", null);
entry.put("sourceDateTakenMillis", sourceDateTakenMillis);
entry.put("bucketDisplayName", null);
entry.put("durationMillis", null);
entry.put("durationMillis", durationMillis);
callback.onSuccess(entry);
}
}

View file

@ -244,6 +244,8 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
if (entry == null || !entry.isVideo) return;
final path = entry.path;
if (path == null) return;
var controllerEntry = _videoControllers.firstWhere((kv) => kv.item1 == entry.path, orElse: () => null);
if (controllerEntry != null) {
_videoControllers.remove(controllerEntry);

View file

@ -28,7 +28,7 @@ class BasicSection extends StatelessWidget {
InfoRow('Resolution', resolutionText),
InfoRow('Size', entry.sizeBytes != null ? formatFilesize(entry.sizeBytes) : '?'),
InfoRow('URI', entry.uri ?? '?'),
InfoRow('Path', entry.path ?? '?'),
if (entry.path != null) InfoRow('Path', entry.path),
],
);
}

View file

@ -79,7 +79,7 @@ class _LocationSectionState extends State<LocationSection> {
child: SectionRow('Location'),
),
ImageMap(
markerId: entry.path,
markerId: entry.uri ?? entry.path,
latLng: LatLng(
entry.latLng.item1,
entry.latLng.item2,