Android: è possibile visualizzare le miniature dei video?


91

Ho creato un'applicazione di registrazione video con finestra di dialogo della libreria. La finestra di dialogo della libreria visualizza l'elenco dei video registrati in cui ogni elemento è costituito da icona, titolo del video, tag e informazioni sulla posizione nel modo seguente:

testo alternativo

Qualcuno sa se è possibile sostituire le icone con le miniature dei video (anteprima di un singolo fotogramma)?

Grazie!


Risposte:


71

Se stai usando API 2.0 o più recente, funzionerà.

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);

9
Allora cos'è esattamente id?
phunehehe

1
È possibile interrogare MediaStore per i video sul telefono. L '"id" è solo una delle informazioni che richiedi. Scopri di più su MediaStore su developer.android.com/reference/android/provider/…
Greg Zimmers,

4
Sorpresi tutti sembra che questo funzioni. Ho provato questo ma il curThumb finisce per essere nullo.
BlueVoodoo

7
che ne dici se il video da un URL?
jayellos

Si prega di rispondere a questa [ stackoverflow.com/questions/16190374/...
rendono semplice

91

se non puoi o non puoi passare attraverso il cursore e se hai solo percorsi o oggetti File, puoi usare dal livello API 8 (2.2) Bitmap statico pubblico createVideoThumbnail (String filePath, int kind)

Documentazione Android

Il codice seguente funziona perfettamente:

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

6
quando provo a creare una miniatura ottengo null. Penso che il mio percorso forse non va bene? myPath = "/ external / video / media / 14180"
haythem souissi,

Funziona come per magia. Anche quando t have my video ID. For better quality use indosso MediaStore.Video.Thumbnails.FULL_SCREEN_KIND`
Sami Eltamawy

Strano che non funzioni anche ;-( il video esiste nel DB, posso recuperare il nome / dimensione, ma non la miniatura
Thomas Decaux

haythem souussi perché questo non è un percorso questo è Uri, devi convertirlo in percorso.
Nadir Novruzov

Funziona ma restituisce un'immagine dalla parte sbagliata del video. Voglio il primissimo fotogramma ma ci vogliono 5-6 secondi? Qualche idea?
speedynomads

38

Utilizzando la classe:

import android.provider.MediaStore.Video.Thumbnails;

Possiamo ottenere due dimensioni di anteprima dal video:

Thumbnails.MICRO_KIND per 96 x 96

Thumbnails.MINI_KIND per 512 x 384 px

Questo è un esempio di codice:

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);

Se ho un collegamento come questo per il percorso del file non funzionerebbe, perché sto cercando di impostarlo su un'immagine e non mostra nulla ... questo è il percorso del file "http: / /unknown.com/v3- 1aox9d1 .mp4 "è ovviamente un dominio reale, ma quel percorso non è in grado di visualizzare le miniature?
Lion789

Per utilizzare la classe ThumbnailUtils, è necessario salvare il file su disco utilizzando il metodo: ThumbnailUtils.createVideoThumbnail ()
Jorgesys

Grazie, come ottengo quindi il nuovo percorso file per la miniatura di creazione?
Lion789

Perché la dose non funziona su Android 4 e versioni successive?
Criss

1
Ciao Cris, ho 3 dispositivi 4.1 4.2.2 e 5.0 e funziona senza problemi, posta una domanda con il tuo problema e un po 'di codice, e potrei aiutarti.
Jorgesys

19

Attualmente utilizzo il seguente codice:

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

Ma ho trovato una soluzione migliore con la libreria Glide con il seguente codice (memorizza anche la tua immagine e ha prestazioni migliori rispetto all'approccio precedente)

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

19

Ti consiglio davvero di usare la libreria Glide . È uno dei modi più efficienti per generare e visualizzare una miniatura video per un file video locale.

Basta aggiungere questa riga al file gradle:

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

E diventerà semplice come:

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

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

Puoi trovare maggiori informazioni qui: https://futurestud.io/blog/glide-displaying-gifs-and-videos

Saluti !


4
Glide funziona solo con video locali, non video Url, non esiste un modo semplice per
farlo

6

Questa soluzione funzionerà con qualsiasi versione di Android. Ha dimostrato di funzionare con 1.5 e 2.2 Questa non è un'altra soluzione "Questo è per Android 2.0+". L'ho trovato tramite una pagina di raccolta della bacheca di messaggi e-mail e non riesco a trovare il collegamento originale. Tutto il merito va al poster originale.

Nella tua app lo useresti chiamando:

Bitmap bm = getVideoFrame(VideoStringUri);

Da qualche parte nella sua funzione (al di fuori di OnCreate, ecc), avresti bisogno di:

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;
    }

Nella tua cartella src, hai bisogno di una nuova sottodirectory android / media che ospiterà la classe (copiata dalla sorgente Android stessa) che ti consente di utilizzare questa funzione. Questa parte non deve essere modificata, rinominata o posizionata altrove. MediaMetadataRetriever.java deve essere sotto android.media nella cartella dei sorgenti affinché tutto funzioni.

/*
 * 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...
}

Questo non funziona per me .. errore in System.loadLibrary ("media_jni");
DArkO

1
Posso confermare che questo non funziona. Avevo bisogno anche di questa capacità. Non funzionerà perché utilizza chiamate di sistema native che un'applicazione normale non dispone delle autorizzazioni per utilizzare.
Andy

MediaMetadataRetrieverè supportato dal livello API 10
Asahi

MediaMetadataRetriever è il secondo blocco di codice. È presente per consentire alle API precedenti alle 10 (che non erano disponibili al momento della scrittura) di accedere al codice dall'applicazione anziché dal sistema. Le chiamate di sistema native sono possibili, ma per utilizzarle è necessaria una conoscenza approssimativa del sistema. Sembra che gran parte del problema stia implementando in modo improprio la fonte fornita.
Carrello abbandonato

@LoungeKatt è possibile fargli catturare più immagini dello stesso video da più volte?
sviluppatore Android

6

Prova questo funziona per me

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

5

Android 1.5 e 1.6 non offrono queste miniature, ma 2.0 lo fa, come si vede nelle note di rilascio ufficiali :

Media

  • MediaScanner ora genera miniature per tutte le immagini quando vengono inserite in MediaStore.
  • Nuova API Thumbnail per il recupero di miniature di immagini e video su richiesta.

1

Rispondo tardi a questa domanda, ma spero che possa aiutare l'altro candidato ad affrontare lo stesso problema.

Ho usato due metodi per caricare la miniatura per l'elenco dei video il primo era

    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");
    }

sembra buono ma c'è stato un problema con questa soluzione perché quando scorro l'elenco dei video si blocca un po 'di tempo a causa della sua grande elaborazione.

quindi dopo questo ho trovato un'altra soluzione che funziona perfettamente usando Glide Library.

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

Ho consigliato la soluzione successiva per mostrare la miniatura con l'elenco dei video. Grazie


-4

Questo è il codice per la miniatura del video live.

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");
        }

    }

2
ottengo null a Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);Nota che tutti i parametri sono impostati
Chulo

1
valore nullo in Bitmap bitmap = ThumbnailUtils.createVideoThumbnail (mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
Prasad
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.