Problema al obtener metadatos de un video mediante URI en Android

En Android, al capturar un video con la cámara y especificar una ubicación de almacenamiento personalizada, se observa que el objeto Cursor devuelto por MediaStore solo contiene los campos DISPLAY_NAME y SIZE. Otros atributos, como la duración (DURATION) o incluso el identificador interno (_ID), no están disponibles. Al intentar acceder a ellos, se lanza una excepción:

java.lang.IllegalArgumentException

Este comportamiento ocurre específicamente cuando se ha definido una ruta de destino para el archivo. Si no se especifica una ubicación, todos los metadatos se obtienen correctamente. La causa principal es que, al guardar el video en un directorio arbitrario, el sistema no completa todas las columnas de la base de datos de MediaStore. A continuación se presenta una solución utilizable para recuperar la ruta absoluta del archivo a partir de su URI, la cual puede servir como base para extraer otros metadatos mediante consultas adicionales o la propia ruta del archivo.

Código de utilidad para obtener la ruta absoluta desde un URI

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class MediaPathResolver {

    public static String obtenerRutaAbsoluta(Context contexto, Uri uriVideo) {
        if (contexto == null || uriVideo == null) return null;

        final boolean esDocumento = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
                DocumentsContract.isDocumentUri(contexto, uriVideo);

        if (esDocumento) {
            // Documento de proveedor de almacenamiento externo
            if (esDocumentoDeAlmacenamientoExterno(uriVideo)) {
                final String idDoc = DocumentsContract.getDocumentId(uriVideo);
                final String[] partes = idDoc.split(":");
                if ("primary".equalsIgnoreCase(partes[0])) {
                    return Environment.getExternalStorageDirectory() + "/" + partes[1];
                }
            }
            // Documento de descargas
            else if (esDocumentoDeDescargas(uriVideo)) {
                final String id = DocumentsContract.getDocumentId(uriVideo);
                final Uri uriContenido = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return obtenerColumna(contexto, uriContenido, null, null);
            }
            // Documento multimedia (imagen, video, audio)
            else if (esDocumentoMultimedia(uriVideo)) {
                final String idDoc = DocumentsContract.getDocumentId(uriVideo);
                final String[] partes = idDoc.split(":");
                final String tipo = partes[0];
                Uri uriBase = null;
                switch (tipo) {
                    case "image":
                        uriBase = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                        break;
                    case "video":
                        uriBase = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                        break;
                    case "audio":
                        uriBase = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                        break;
                }
                if (uriBase != null) {
                    final String seleccion = MediaStore.Images.Media._ID + "=?";
                    final String[] args = new String[]{partes[1]};
                    return obtenerColumna(contexto, uriBase, seleccion, args);
                }
            }
        }
        // URI del tipo content:// (no documento)
        else if ("content".equals(uriVideo.getScheme())) {
            if (esDeGooglePhotos(uriVideo)) {
                return uriVideo.getLastPathSegment();
            }
            return obtenerColumna(contexto, uriVideo, null, null);
        }
        // URI del tipo file://
        else if ("file".equals(uriVideo.getScheme())) {
            return uriVideo.getPath();
        }
        return null;
    }

    private static String obtenerColumna(Context contexto, Uri uri, String seleccion, String[] args) {
        final String columna = MediaStore.Images.Media.DATA;
        final String[] proyeccion = {columna};
        Cursor cursor = null;
        try {
            cursor = contexto.getContentResolver().query(uri, proyeccion, seleccion, args, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int indice = cursor.getColumnIndexOrThrow(columna);
                return cursor.getString(indice);
            }
        } finally {
            if (cursor != null) cursor.close();
        }
        return null;
    }

    private static boolean esDocumentoDeAlmacenamientoExterno(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    private static boolean esDocumentoDeDescargas(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    private static boolean esDocumentoMultimedia(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    private static boolean esDeGooglePhotos(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
}

Esta función retorna la ruta absoluta del archivo. A partir de esa ruta, se puede reconstruir un File y, si es necesario, realizar una segunda consulta a MediaStore utilizando la columna DATA para obtener los metadatos faltantes, o bien acceder directamente al archivo para extraer información como la duración mediante MediaMetadataRetriever.

Etiquetas: Android MediaStore Uri Cursor ContentResolver

Publicado el 7-26 11:22