From 120f9cd4e4819cdbe2e21a225aafd2a5dbeea09b Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Fri, 9 Oct 2020 13:47:07 +0900 Subject: [PATCH] info: improved media descriptions & minor fixes --- .../aves/channel/calls/MetadataHandler.kt | 19 ++--- .../utils/MediaMetadataRetrieverHelper.kt | 71 ++++++++++++++++++- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt index 39937bcd7..a46db0d68 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt @@ -7,7 +7,6 @@ import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build import android.provider.MediaStore -import android.text.format.Formatter import android.util.Log import androidx.exifinterface.media.ExifInterface import com.adobe.internal.xmp.XMPException @@ -28,6 +27,7 @@ import deckers.thibault.aves.utils.* import deckers.thibault.aves.utils.ExifInterfaceHelper.describeAll import deckers.thibault.aves.utils.ExifInterfaceHelper.getSafeDateMillis import deckers.thibault.aves.utils.ExifInterfaceHelper.getSafeInt +import deckers.thibault.aves.utils.MediaMetadataRetrieverHelper.getSafeDescription import deckers.thibault.aves.utils.MediaMetadataRetrieverHelper.getSafeInt import deckers.thibault.aves.utils.Metadata.getRotationDegreesForExifCode import deckers.thibault.aves.utils.Metadata.isFlippedForExifCode @@ -151,17 +151,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { val retriever = StorageUtils.openMetadataRetriever(context, uri) ?: return dirMap try { for ((code, name) in MediaMetadataRetrieverHelper.allKeys) { - val value = retriever.extractMetadata(code) - if (value != null) { - when (code) { - MediaMetadataRetriever.METADATA_KEY_BITRATE -> Formatter.formatFileSize(context, value.toLong()) + "/sec" - MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION -> "$value°" - MediaMetadataRetriever.METADATA_KEY_DURATION -> "$value ms" - MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH -> "$value pixels" - MediaMetadataRetriever.METADATA_KEY_LOCATION, MediaMetadataRetriever.METADATA_KEY_MIMETYPE -> null - else -> value - }?.let { dirMap[name] = it } - } + retriever.getSafeDescription(code, context) { dirMap[name] = it } } } catch (e: Exception) { Log.w(LOG_TAG, "failed to get video metadata by MediaMetadataRetriever for uri=$uri", e) @@ -333,7 +323,8 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { try { val latitude = latitudeString.toDoubleOrNull() ?: 0 val longitude = longitudeString.toDoubleOrNull() ?: 0 - if (latitude != 0 && longitude != 0) { + // keep `0.0` as `0.0`, not `0` + if (latitude != 0.0 || longitude != 0.0) { metadataMap[KEY_LATITUDE] = latitude metadataMap[KEY_LONGITUDE] = longitude } @@ -379,7 +370,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { val num = it.numerator val denom = it.denominator metadataMap[KEY_EXPOSURE_TIME] = when { - num > denom -> it.toSimpleString(true) + "″" + num >= denom -> it.toSimpleString(true) + "″" num != 1L && num != 0L -> Rational(1, (denom / num.toDouble()).roundToLong()).toString() else -> it.toString() } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MediaMetadataRetrieverHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MediaMetadataRetrieverHelper.kt index ca06f0bed..dd5fafbf3 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MediaMetadataRetrieverHelper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MediaMetadataRetrieverHelper.kt @@ -1,7 +1,12 @@ package deckers.thibault.aves.utils +import android.content.Context +import android.media.MediaFormat import android.media.MediaMetadataRetriever import android.os.Build +import android.text.format.Formatter +import java.text.SimpleDateFormat +import java.util.* object MediaMetadataRetrieverHelper { @JvmField @@ -21,8 +26,8 @@ object MediaMetadataRetrieverHelper { MediaMetadataRetriever.METADATA_KEY_DATE to "Date", MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER to "Disc Number", MediaMetadataRetriever.METADATA_KEY_DURATION to "Duration", - MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH to "EXIF Length", - MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET to "EXIF Offset", + MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH to "Exif Length", + MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET to "Exif Offset", MediaMetadataRetriever.METADATA_KEY_GENRE to "Genre", MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO to "Has Audio", MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO to "Has Video", @@ -49,6 +54,8 @@ object MediaMetadataRetrieverHelper { } } + private val durationFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("UTC") } + // extensions fun MediaMetadataRetriever.getSafeString(tag: Int, save: (value: String) -> Unit) { @@ -72,4 +79,64 @@ object MediaMetadataRetrieverHelper { // some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time if (dateMillis > 0) save(dateMillis) } + + fun MediaMetadataRetriever.getSafeDescription(tag: Int, context: Context, save: (value: String) -> Unit) { + val value = this.extractMetadata(tag) + if (value != null) { + when (tag) { + // format + MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION, + MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION -> "$value°" + MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT, MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH, + MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH -> "$value pixels" + MediaMetadataRetriever.METADATA_KEY_BITRATE -> { + val bitrate = value.toLongOrNull() ?: 0 + if (bitrate > 0) Formatter.formatFileSize(context, bitrate) + "/sec" else null + } + MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE -> { + val framerate = value.toDoubleOrNull() ?: 0.0 + if (framerate > 0.0) "$framerate" else null + } + MediaMetadataRetriever.METADATA_KEY_DURATION -> { + val dateMillis = value.toLongOrNull() ?: 0 + if (dateMillis > 0) durationFormat.format(Date(dateMillis)) else null + } + MediaMetadataRetriever.METADATA_KEY_COLOR_RANGE -> { + when (value.toIntOrNull()) { + MediaFormat.COLOR_RANGE_FULL -> "Full" + MediaFormat.COLOR_RANGE_LIMITED -> "Limited" + else -> value + } + } + MediaMetadataRetriever.METADATA_KEY_COLOR_STANDARD -> { + when (value.toIntOrNull()) { + MediaFormat.COLOR_STANDARD_BT709 -> "BT.709" + MediaFormat.COLOR_STANDARD_BT601_PAL -> "BT.601 625 (PAL)" + MediaFormat.COLOR_STANDARD_BT601_NTSC -> "BT.601 525 (NTSC)" + MediaFormat.COLOR_STANDARD_BT2020 -> "BT.2020" + else -> value + } + } + MediaMetadataRetriever.METADATA_KEY_COLOR_TRANSFER -> { + when (value.toIntOrNull()) { + MediaFormat.COLOR_TRANSFER_LINEAR -> "Linear" + MediaFormat.COLOR_TRANSFER_SDR_VIDEO -> "SMPTE 170M" + MediaFormat.COLOR_TRANSFER_ST2084 -> "SMPTE ST 2084" + MediaFormat.COLOR_TRANSFER_HLG -> "ARIB STD-B67 (HLG)" + else -> value + } + } + // hide `0` values + MediaMetadataRetriever.METADATA_KEY_COMPILATION, + MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, + MediaMetadataRetriever.METADATA_KEY_YEAR -> if (value != "0") value else null + MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER -> if (value != "0/0") value else null + // hide + MediaMetadataRetriever.METADATA_KEY_LOCATION, + MediaMetadataRetriever.METADATA_KEY_MIMETYPE -> null + // as is + else -> value + }?.let { save(it) } + } + } } \ No newline at end of file