Android: ¿es posible mostrar miniaturas de videos?

91

Creé una aplicación de grabación de video con diálogo de biblioteca. El cuadro de diálogo de la biblioteca muestra la lista de videos grabados donde cada elemento consta de un icono, título de video, etiquetas e información de ubicación de la siguiente manera:

texto alternativo

¿Alguien sabe si es posible reemplazar los íconos con miniaturas de video (vista previa de un solo cuadro)?

¡Gracias!

Niko Gamulin
fuente
Cualquier respuesta a esta [ stackoverflow.com/questions/16190374/…
Simplifique las cosas

Respuestas:

71

Si está utilizando API 2.0 o más reciente, esto funcionará.

int id = **"The Video's ID"**
ImageView iv = (ImageView ) convertView.findViewById(R.id.imagePreview);
ContentResolver crThumb = getContentResolver();
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap curThumb = MediaStore.Video.Thumbnails.getThumbnail(crThumb, id, MediaStore.Video.Thumbnails.MICRO_KIND, options);
iv.setImageBitmap(curThumb);
Greg Zimmers
fuente
9
Entonces, ¿qué es exactamente id?
phunehehe
1
Puede consultar MediaStore en busca de videos en el teléfono. La "identificación" es solo una de las piezas de información que consulta. Más información sobre MediaStore en developer.android.com/reference/android/provider/…
Greg Zimmers
4
Sorprendido, todo el mundo parece conseguir que esto funcione. Intenté esto pero el curThumb termina siendo nulo.
BlueVoodoo
7
¿Qué tal si el video procede de una URL?
jayellos
por favor responda a esto [ stackoverflow.com/questions/16190374/…
Make it Simple
91

si no puede o no puede pasar por el cursor y si solo tiene rutas u objetos de archivo, puede usar desde API nivel 8 (2.2) el mapa de bits estático público createVideoThumbnail (String filePath, int kind)

Documentación de Android

El siguiente código se ejecuta perfectamente:

Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);
Damien Praca
fuente
6
cuando intento crear una miniatura, obtengo un valor nulo. Creo que mi camino tal vez no esté bien. myPath = "/ external / video / media / 14180"
haythem souissi
Funciona como por arte de magia. Incluso cuando me t have my video ID. For better quality use pongo MediaStore.Video.Thumbnails.FULL_SCREEN_KIND`
Sami Eltamawy
Extraño, tampoco funciona ;-( el video existe en la base de datos, puedo recuperar el nombre / tamaño, pero no la miniatura
Thomas Decaux
haythem souussi porque este no es un camino, esto es Uri, necesitas convertirlo en camino.
Nadir Novruzov
Esto funciona pero devuelve una imagen de la parte incorrecta del video. Quiero el primer fotograma, pero ¿me quedan 5-6 segundos? ¿Algunas ideas?
speedynomads
38

Usando la clase:

import android.provider.MediaStore.Video.Thumbnails;

Podemos obtener dos tamaños de miniatura de vista previa del video:

Thumbnails.MICRO_KIND para 96 ​​x 96

Thumbnails.MINI_KIND para 512 x 384 px

Este es un ejemplo de código:

String filePath = "/sdcard/DCIM/Camera/my_video.mp4"; //change the location of your file!

ImageView imageview_mini = (ImageView)findViewById(R.id.thumbnail_mini);
ImageView imageview_micro = (ImageView)findViewById(R.id.thumbnail_micro);

Bitmap bmThumbnail;

//MICRO_KIND, size: 96 x 96 thumbnail
bmThumbnail = ThumbnailUtils.createVideoThumbnail(filePath, Thumbnails.MICRO_KIND);
imageview_micro.setImageBitmap(bmThumbnail);
     
// MINI_KIND, size: 512 x 384 thumbnail 
bmThumbnail = ThumbnailUtils.createVideoThumbnail(filePath, Thumbnails.MINI_KIND);
imageview_mini.setImageBitmap(bmThumbnail);
Jorgesys
fuente
Si tengo un enlace como este para la ruta del archivo, ¿no funcionaría? Porque estoy tratando de configurarlo en una vista de imagen y no muestra nada ... esta es la ruta del archivo "http: / /unknown.com/v3- 1aox9d1 .mp4 "es un dominio real, obviamente, pero ¿esa ruta no es compatible con miniaturas?
Lion789
Para usar la clase ThumbnailUtils, debe guardar el archivo en el disco y usar el método: ThumbnailUtils.createVideoThumbnail ()
Jorgesys
Gracias, ¿cómo obtengo la nueva ruta de archivo para crear el pulgar?
Lion789
¿Por qué Dose no funciona en Android 4 y superior?
Criss
1
Hola Cris, tengo 3 dispositivos 4.1 4.2.2 y 5.0 y funciona sin problemas, publique una pregunta con su problema y algo de código, y podría ayudarlo.
Jorgesys
19

Actualmente utilizo el siguiente código:

Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);

Pero encontré una mejor solución con la biblioteca Glide con el siguiente código (también almacena en caché su imagen y tiene un mejor rendimiento que el enfoque anterior)

Glide.with(context)
                .load(uri)
                .placeholder(R.drawable.ic_video_place_holder)
                .into(imageView);
Amir
fuente
19

Realmente te sugiero que uses el Glide biblioteca . Es una de las formas más eficaces de generar y mostrar una miniatura de video para un archivo de video local.

Simplemente agregue esta línea a su archivo gradle:

compile 'com.github.bumptech.glide:glide:3.7.0'

Y será tan simple como:

String filePath = "/storage/emulated/0/Pictures/example_video.mp4";

Glide  
    .with( context )
    .load( Uri.fromFile( new File( filePath ) ) )
    .into( imageViewGifAsBitmap );

Puede encontrar más información aquí: https://futurestud.io/blog/glide-displaying-gifs-and-videos

Salud !

Edouard Brèthes
fuente
4
Glide solo funciona con videos locales, no videos de URL, ¿no hay una manera simple de
hacerlo?
6

Esta solución funcionará para cualquier versión de Android. Se ha demostrado que funciona en 1.5 y 2.2 Esta no es otra solución "Esto es para Android 2.0+". Encontré esto a través de una página de colección de foros de mensajes de correo electrónico y no puedo encontrar el enlace original. Todo el crédito es para el póster original.

En su aplicación, usaría esto llamando a:

Bitmap bm = getVideoFrame(VideoStringUri);

En algún lugar de su propia función (fuera de OnCreate, etc.), necesitaría:

private Bitmap getVideoFrame(String uri) {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
            retriever.setDataSource(uri);
            return retriever.captureFrame();
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (RuntimeException ex) {
            ex.printStackTrace();
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
            }
        }
        return null;
    }

En su carpeta src, necesita un nuevo subdirectorio android / media que albergará la clase (copiada de la fuente de Android) que le permite usar esta función. Esta pieza no debe cambiarse, cambiarse de nombre ni colocarse en ningún otro lugar. MediaMetadataRetriever.java debe estar en android.media en su carpeta de origen para que todo esto funcione.

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;

/**
 * MediaMetadataRetriever class provides a unified interface for retrieving
 * frame and meta data from an input media file. {@hide}
 */
public class MediaMetadataRetriever {
    static {
        System.loadLibrary("media_jni");
        native_init();
    }

    // The field below is accessed by native methods
    private int mNativeContext;

    public MediaMetadataRetriever() {
        native_setup();
    }

    /**
     * Call this method before setDataSource() so that the mode becomes
     * effective for subsequent operations. This method can be called only once
     * at the beginning if the intended mode of operation for a
     * MediaMetadataRetriever object remains the same for its whole lifetime,
     * and thus it is unnecessary to call this method each time setDataSource()
     * is called. If this is not never called (which is allowed), by default the
     * intended mode of operation is to both capture frame and retrieve meta
     * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY). Often,
     * this may not be what one wants, since doing this has negative performance
     * impact on execution time of a call to setDataSource(), since both types
     * of operations may be time consuming.
     * 
     * @param mode
     *            The intended mode of operation. Can be any combination of
     *            MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY: 1.
     *            MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: For neither
     *            frame capture nor meta data retrieval 2.
     *            MODE_GET_METADATA_ONLY: For meta data retrieval only 3.
     *            MODE_CAPTURE_FRAME_ONLY: For frame capture only 4.
     *            MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: For both
     *            frame capture and meta data retrieval
     */
    public native void setMode(int mode);

    /**
     * @return the current mode of operation. A negative return value indicates
     *         some runtime error has occurred.
     */
    public native int getMode();

    /**
     * Sets the data source (file pathname) to use. Call this method before the
     * rest of the methods in this class. This method may be time-consuming.
     * 
     * @param path
     *            The path of the input media file.
     * @throws IllegalArgumentException
     *             If the path is invalid.
     */
    public native void setDataSource(String path)
            throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use. It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd
     *            the FileDescriptor for the file you want to play
     * @param offset
     *            the offset into the file where the data to be played starts,
     *            in bytes. It must be non-negative
     * @param length
     *            the length in bytes of the data to be played. It must be
     *            non-negative.
     * @throws IllegalArgumentException
     *             if the arguments are invalid
     */
    public native void setDataSource(FileDescriptor fd, long offset, long length)
            throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use. It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd
     *            the FileDescriptor for the file you want to play
     * @throws IllegalArgumentException
     *             if the FileDescriptor is invalid
     */
    public void setDataSource(FileDescriptor fd)
            throws IllegalArgumentException {
        // intentionally less than LONG_MAX
        setDataSource(fd, 0, 0x7ffffffffffffffL);
    }

    /**
     * Sets the data source as a content Uri. Call this method before the rest
     * of the methods in this class. This method may be time-consuming.
     * 
     * @param context
     *            the Context to use when resolving the Uri
     * @param uri
     *            the Content URI of the data you want to play
     * @throws IllegalArgumentException
     *             if the Uri is invalid
     * @throws SecurityException
     *             if the Uri cannot be used due to lack of permission.
     */
    public void setDataSource(Context context, Uri uri)
            throws IllegalArgumentException, SecurityException {
        if (uri == null) {
            throw new IllegalArgumentException();
        }

        String scheme = uri.getScheme();
        if (scheme == null || scheme.equals("file")) {
            setDataSource(uri.getPath());
            return;
        }

        AssetFileDescriptor fd = null;
        try {
            ContentResolver resolver = context.getContentResolver();
            try {
                fd = resolver.openAssetFileDescriptor(uri, "r");
            } catch (FileNotFoundException e) {
                throw new IllegalArgumentException();
            }
            if (fd == null) {
                throw new IllegalArgumentException();
            }
            FileDescriptor descriptor = fd.getFileDescriptor();
            if (!descriptor.valid()) {
                throw new IllegalArgumentException();
            }
            // Note: using getDeclaredLength so that our behavior is the same
            // as previous versions when the content provider is returning
            // a full file.
            if (fd.getDeclaredLength() < 0) {
                setDataSource(descriptor);
            } else {
                setDataSource(descriptor, fd.getStartOffset(),
                        fd.getDeclaredLength());
            }
            return;
        } catch (SecurityException ex) {
        } finally {
            try {
                if (fd != null) {
                    fd.close();
                }
            } catch (IOException ioEx) {
            }
        }
        setDataSource(uri.toString());
    }

    /**
     * Call this method after setDataSource(). This method retrieves the meta
     * data value associated with the keyCode.
     * 
     * The keyCode currently supported is listed below as METADATA_XXX
     * constants. With any other value, it returns a null pointer.
     * 
     * @param keyCode
     *            One of the constants listed below at the end of the class.
     * @return The meta data value associate with the given keyCode on success;
     *         null on failure.
     */
    public native String extractMetadata(int keyCode);

    /**
     * Call this method after setDataSource(). This method finds a
     * representative frame if successful and returns it as a bitmap. This is
     * useful for generating a thumbnail for an input media source.
     * 
     * @return A Bitmap containing a representative video frame, which can be
     *         null, if such a frame cannot be retrieved.
     */
    public native Bitmap captureFrame();

    /**
     * Call this method after setDataSource(). This method finds the optional
     * graphic or album art associated (embedded or external url linked) the
     * related data source.
     * 
     * @return null if no such graphic is found.
     */
    public native byte[] extractAlbumArt();

    /**
     * Call it when one is done with the object. This method releases the memory
     * allocated internally.
     */
    public native void release();

    private native void native_setup();

    private static native void native_init();

    private native final void native_finalize();

    @Override
    protected void finalize() throws Throwable {
        try {
            native_finalize();
        } finally {
            super.finalize();
        }
    }

    public static final int MODE_GET_METADATA_ONLY = 0x01;
    public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;

    /*
     * Do not change these values without updating their counterparts in
     * include/media/mediametadataretriever.h!
     */
    public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
    public static final int METADATA_KEY_ALBUM = 1;
    public static final int METADATA_KEY_ARTIST = 2;
    public static final int METADATA_KEY_AUTHOR = 3;
    public static final int METADATA_KEY_COMPOSER = 4;
    public static final int METADATA_KEY_DATE = 5;
    public static final int METADATA_KEY_GENRE = 6;
    public static final int METADATA_KEY_TITLE = 7;
    public static final int METADATA_KEY_YEAR = 8;
    public static final int METADATA_KEY_DURATION = 9;
    public static final int METADATA_KEY_NUM_TRACKS = 10;
    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
    public static final int METADATA_KEY_CODEC = 12;
    public static final int METADATA_KEY_RATING = 13;
    public static final int METADATA_KEY_COMMENT = 14;
    public static final int METADATA_KEY_COPYRIGHT = 15;
    public static final int METADATA_KEY_BIT_RATE = 16;
    public static final int METADATA_KEY_FRAME_RATE = 17;
    public static final int METADATA_KEY_VIDEO_FORMAT = 18;
    public static final int METADATA_KEY_VIDEO_HEIGHT = 19;
    public static final int METADATA_KEY_VIDEO_WIDTH = 20;
    public static final int METADATA_KEY_WRITER = 21;
    public static final int METADATA_KEY_MIMETYPE = 22;
    public static final int METADATA_KEY_DISCNUMBER = 23;
    public static final int METADATA_KEY_ALBUMARTIST = 24;
    // Add more here...
}
Carro abandonado
fuente
Esto no funciona para mí .. error en System.loadLibrary ("media_jni");
DArkO
1
Puedo confirmar que esto no funciona. También necesitaba esta capacidad. No funcionará porque usa llamadas nativas al sistema que una aplicación normal no tiene permisos para usar.
Andy
MediaMetadataRetrieveres compatible con API nivel 10
Asahi
MediaMetadataRetriever es el segundo bloque de código. Está presente para permitir que las API anteriores a 10 (que no estaban disponibles en el momento de escribir este artículo) accedan al código desde la aplicación en lugar del sistema. Las llamadas al sistema nativo son posibles, pero necesitará una comprensión aproximada del sistema para usarlas. Parece que gran parte del problema está implementando incorrectamente la fuente proporcionada.
Carrito abandonado
@LoungeKatt ¿es posible permitirle capturar múltiples imágenes del mismo video de varias veces?
desarrollador de Android
6

Prueba esto, me está funcionando

RequestOptions requestOptions = new RequestOptions();
 Glide.with(getContext())
      .load("video_url")
      .apply(requestOptions)
      .thumbnail(Glide.with(getContext()).load("video_url"))
      .into("yourimageview");
karan brahmaxatriya
fuente
5

Android 1.5 y 1.6 no ofrecen estas miniaturas, pero 2.0 sí, como se ve en las notas de la versión oficial :

Medios de comunicación

  • MediaScanner ahora genera miniaturas para todas las imágenes cuando se insertan en MediaStore.
  • Nueva API de miniaturas para recuperar miniaturas de imágenes y vídeos bajo demanda.
Marc Climent
fuente
1

Estoy respondiendo esta pregunta tarde, pero espero que ayude al otro candidato que enfrenta el mismo problema.

He usado dos métodos para cargar la miniatura de la lista de videos, el primero fue

    Bitmap bmThumbnail;
    bmThumbnail = ThumbnailUtils.createVideoThumbnail(FILE_PATH
                    + videoList.get(position),
            MediaStore.Video.Thumbnails.MINI_KIND);

    if (bmThumbnail != null) {
        Log.d("VideoAdapter","video thumbnail found");
        holder.imgVideo.setImageBitmap(bmThumbnail);
    } else {
        Log.d("VideoAdapter","video thumbnail not found");
    }

se ve bien, pero hubo un problema con esta solución porque cuando desplazo la lista de videos, se congelará un tiempo debido a su gran procesamiento.

así que después de esto encontré otra solución que funciona perfectamente usando Glide Library.

 Glide
            .with( mContext )
            .load( Uri.fromFile( new File( FILE_PATH+videoList.get(position) ) ) )
            .into( holder.imgVideo );

Recomendé la solución posterior para mostrar la miniatura con la lista de videos. Gracias

Mudassir Khan
fuente
-4

Este es el código para la miniatura de video en vivo.

public class LoadVideoThumbnail extends AsyncTask<Object, Object, Bitmap>{

        @Override
        protected Bitmap doInBackground(Object... params) {try {

            String mMediaPath = "http://commonsware.com/misc/test2.3gp";
            Log.e("TEST Chirag","<< thumbnail doInBackground"+ mMediaPath);
            FileOutputStream out;
            File land=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()
                            +"/portland.jpg");

                Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
                        byte[] byteArray = stream.toByteArray();

                        out=new  FileOutputStream(land.getPath());
                        out.write(byteArray);
                        out.close();
                 return bitmap;

            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        return null;
            }
        @Override
        protected void onPostExecute(Bitmap result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            if(result != null){
                 ((ImageView)findViewById(R.id.imageView1)).setImageBitmap(result);
            }
            Log.e("TEST Chirag","====> End");
        }

    }
Chirag
fuente
2
obtengo nulo en Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);Tenga en cuenta que todos los parámetros están configurados
Chulo
1
valor nulo en Bitmap bitmap = ThumbnailUtils.createVideoThumbnail (mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
Prasad