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) { if (dateString != null) {
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString); 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) { if (dateMillis > 0) {
metadataMap.put("dateMillis", dateMillis); metadataMap.put("dateMillis", dateMillis);
} }

View file

@ -2,7 +2,9 @@ package deckers.thibault.aves.model.provider;
import android.app.Activity; import android.app.Activity;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException; import com.drew.imaging.ImageProcessingException;
@ -17,39 +19,92 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import deckers.thibault.aves.utils.Constants;
import deckers.thibault.aves.utils.MetadataHelper;
import static deckers.thibault.aves.utils.MetadataHelper.getOrientationDegreesForExifCode; import static deckers.thibault.aves.utils.MetadataHelper.getOrientationDegreesForExifCode;
class UnknownContentImageProvider extends ImageProvider { class UnknownContentImageProvider extends ImageProvider {
@Override @Override
public void fetchSingle(final Activity activity, final Uri uri, final String mimeType, final ImageOpCallback callback) { public void fetchSingle(final Activity activity, final Uri uri, final String mimeType, final ImageOpCallback callback) {
int width = 0, height = 0, orientationDegrees = 0; int width = 0, height = 0;
Long sourceDateTakenMillis = null; Integer orientationDegrees = null;
Long sourceDateTakenMillis = null, durationMillis = null;
String title = null;
// check metadata first // check first metadata with MediaMetadataRetriever
try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
Metadata metadata = ImageMetadataReader.readMetadata(is); try {
JpegDirectory jpegDir = metadata.getFirstDirectoryOfType(JpegDirectory.class); MediaMetadataRetriever retriever = new MediaMetadataRetriever();
if (jpegDir != null) { retriever.setDataSource(activity, uri);
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH)) {
width = jpegDir.getInt(JpegDirectory.TAG_IMAGE_WIDTH); title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
} String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
if (jpegDir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) { long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
height = jpegDir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT); // 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) { String widthString = null, heightString = null, rotationString = null, durationMillisString = null;
if (exifDir.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { if (mimeType.startsWith(Constants.MIME_IMAGE)) {
orientationDegrees = getOrientationDegreesForExifCode(exifDir.getInt(ExifIFD0Directory.TAG_ORIENTATION)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
} widthString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
if (exifDir.containsTag(ExifIFD0Directory.TAG_DATETIME)) { heightString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
sourceDateTakenMillis = exifDir.getDate(ExifIFD0Directory.TAG_DATETIME, null, TimeZone.getDefault()).getTime(); 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 // 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 // fallback to decoding the image bounds
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
try (InputStream is = activity.getContentResolver().openInputStream(uri)) { try (InputStream is = activity.getContentResolver().openInputStream(uri)) {
@ -75,13 +130,13 @@ class UnknownContentImageProvider extends ImageProvider {
entry.put("mimeType", mimeType); entry.put("mimeType", mimeType);
entry.put("width", width); entry.put("width", width);
entry.put("height", height); entry.put("height", height);
entry.put("orientationDegrees", orientationDegrees); entry.put("orientationDegrees", orientationDegrees != null ? orientationDegrees : 0);
entry.put("sizeBytes", null); entry.put("sizeBytes", null);
entry.put("title", null); entry.put("title", title);
entry.put("dateModifiedSecs", null); entry.put("dateModifiedSecs", null);
entry.put("sourceDateTakenMillis", sourceDateTakenMillis); entry.put("sourceDateTakenMillis", sourceDateTakenMillis);
entry.put("bucketDisplayName", null); entry.put("bucketDisplayName", null);
entry.put("durationMillis", null); entry.put("durationMillis", durationMillis);
callback.onSuccess(entry); callback.onSuccess(entry);
} }
} }

View file

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

View file

@ -28,7 +28,7 @@ class BasicSection extends StatelessWidget {
InfoRow('Resolution', resolutionText), InfoRow('Resolution', resolutionText),
InfoRow('Size', entry.sizeBytes != null ? formatFilesize(entry.sizeBytes) : '?'), InfoRow('Size', entry.sizeBytes != null ? formatFilesize(entry.sizeBytes) : '?'),
InfoRow('URI', entry.uri ?? '?'), 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'), child: SectionRow('Location'),
), ),
ImageMap( ImageMap(
markerId: entry.path, markerId: entry.uri ?? entry.path,
latLng: LatLng( latLng: LatLng(
entry.latLng.item1, entry.latLng.item1,
entry.latLng.item2, entry.latLng.item2,