Android: é possível exibir miniaturas de vídeo?

91

Criei um aplicativo de gravação de vídeo com diálogo de biblioteca. A caixa de diálogo da biblioteca exibe a lista de vídeos gravados, onde cada item consiste em ícone, título do vídeo, tags e informações de localização da seguinte maneira:

texto alternativo

Alguém sabe se é possível substituir ícones por miniaturas de vídeo (visualização de quadro único)?

Obrigado!

Niko Gamulin
fonte
Qualquer uma resposta a esta [ stackoverflow.com/questions/16190374/...
torná-lo simples

Respostas:

71

Se você estiver usando API 2.0 ou mais recente, isso 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
fonte
9
Então, o que exatamente é id?
phunehehe
1
Você pode consultar a MediaStore para obter vídeos no telefone. O "id" é apenas uma das informações que você consulta. Veja mais sobre a MediaStore em developer.android.com/reference/android/provider/…
Greg Zimmers
4
Todos parecem estar surpresos com isso. Tentei fazer isso, mas o curThumb acabou sendo nulo.
BlueVoodoo
7
que tal se o vídeo for de um URL?
Jayellos
Por favor, responda a este [ stackoverflow.com/questions/16190374/...
torná-lo simples
91

se você não pode ou não pode passar pelo cursor e se você tem apenas caminhos ou objetos File, você pode usar desde API nível 8 (2.2) public static Bitmap createVideoThumbnail (String filePath, int kind)

Documentação Android

O código a seguir funciona perfeitamente:

Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);
Damien Praca
fonte
6
quando tento criar uma miniatura, fico nulo. Acho que meu caminho talvez não esteja ok? myPath = "/ external / video / media / 14180"
haythem souissi
Funciona como mágica. Mesmo quando eu uso t have my video ID. For better quality use MediaStore.Video.Thumbnails.FULL_SCREEN_KIND`
Sami Eltamawy
Estranho que não funcione tão bem ;-( o vídeo existe no banco de dados, posso recuperar o nome / tamanho, mas não a miniatura
Thomas Decaux
haythem souussi porque este não é um caminho, este é Uri, você precisa convertê-lo em caminho.
Nadir Novruzov
Isso funciona, mas retorna uma imagem da parte errada do vídeo. Eu quero o primeiro quadro, mas tenho de 5 a 6 segundos em? Alguma ideia?
speedynomads
38

Usando a classe:

import android.provider.MediaStore.Video.Thumbnails;

Podemos obter dois tamanhos de miniatura de visualização do vídeo:

Thumbnails.MICRO_KIND para 96 ​​x 96

Thumbnails.MINI_KIND para 512 x 384 px

Este é um exemplo 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
fonte
Se eu tiver um link como esse para o caminho do arquivo, isso não funcionaria, porque estou tentando configurá-lo para uma visualização de imagem e não está mostrando nada ... este é o caminho do arquivo "http: / /unknown.com/v3- 1aox9d1 .mp4 "sendo um domínio real obviamente, mas esse caminho não pode ser miniaturizado?
Lion789
Para usar a classe ThumbnailUtils, você deve salvar o arquivo em disco e usar o método: ThumbnailUtils.createVideoThumbnail ()
Jorgesys
Obrigado, como faço para obter o novo caminho de arquivo para o polegar de criação?
Lion789
Por que a dose não funciona no Android 4 e superior?
Criss de
1
Oi Cris, Tenho 3 aparelhos 4.1 4.2.2 e 5.0 e funciona sem problemas, poste uma dúvida com seu problema e algum código, e posso te ajudar.
Jorgesys de
19

Atualmente eu uso o seguinte código:

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

Mas eu encontrei uma solução melhor com a biblioteca Glide com o seguinte código (ela também armazena sua imagem em cache e tem melhor desempenho do que a abordagem anterior)

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

Eu realmente sugiro que você use a biblioteca Glide . É uma das maneiras mais eficientes de gerar e exibir uma miniatura de vídeo para um arquivo de vídeo local.

Basta adicionar esta linha ao seu arquivo Gradle:

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

E se tornará tão simples quanto:

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

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

Você pode encontrar mais informações aqui: https://futurestud.io/blog/glide-displaying-gifs-and-videos

Felicidades !

Edouard Brèthes
fonte
4
O Glide só funciona com vídeos locais, não vídeos em URL, não há uma maneira simples de fazer isso
Lutaaya Huzaifah Idris
6

Esta solução funcionará para qualquer versão do Android. Ele provou funcionar em 1.5 e 2.2. Esta não é outra solução "Isto é para Android 2.0+". Eu encontrei isso através de uma página de coleção de quadro de mensagens de e-mail e não consigo encontrar o link original. Todo o crédito vai para o autor do pôster original.

Em seu aplicativo, você usaria isso chamando:

Bitmap bm = getVideoFrame(VideoStringUri);

Em algum lugar em sua própria função (fora do OnCreate, ect), você precisaria:

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

Em sua pasta src, você precisa de um novo subdiretório android / media que abrigará a classe (copiada da própria fonte do Android) que permite usar essa função. Esta parte não deve ser alterada, renomeada ou colocada em qualquer outro lugar. MediaMetadataRetriever.java precisa estar em android.media em sua pasta de origem para que tudo 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...
}
Carrinho Abandonado
fonte
Isso não funciona para mim .. erro em System.loadLibrary ("media_jni");
DArkO de
1
Posso confirmar que isso não funciona. Eu precisava dessa capacidade também. Não funcionará porque usa chamadas de sistema nativas que um aplicativo regular não tem permissão para usar.
Andy,
MediaMetadataRetrieveré compatível com API de nível 10
Asahi
O MediaMetadataRetriever é o segundo bloco de código. Ele está presente para permitir que APIs anteriores a 10 (que não estava disponível no momento da escrita) acessem o código do aplicativo em vez do sistema. Chamadas de sistema nativo são possíveis, mas você precisará ter um conhecimento aproximado do sistema para usá-las. Parece que grande parte do problema está na implementação incorreta da fonte fornecida.
Carrinho abandonado em
@LoungeKatt é possível deixá-lo capturar várias imagens do mesmo vídeo de várias vezes?
desenvolvedor Android de
6

Tente isso está funcionando para mim

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

O Android 1.5 e 1.6 não oferece miniaturas, mas o 2.0 oferece, conforme visto nas notas de lançamento oficiais :

meios de comunicação

  • O MediaScanner agora gera miniaturas para todas as imagens quando elas são inseridas no MediaStore.
  • Nova API de miniaturas para recuperar miniaturas de imagens e vídeos sob demanda.
Marc Climent
fonte
1

Estou respondendo a esta pergunta tarde, mas espero que ajude o outro candidato que está enfrentando o mesmo problema.

Eu usei dois métodos para carregar miniaturas para lista de vídeos, o primeiro foi

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

parece bom, mas houve um problema com esta solução porque quando eu rolar a lista de vídeos, ele irá congelar algum tempo devido ao seu grande processamento.

Então, depois disso, encontrei outra solução que funciona perfeitamente usando a Biblioteca Glide.

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

Eu recomendei a solução posterior para mostrar miniaturas com lista de vídeos. obrigado

Mudassir Khan
fonte
-4

Este é o código para miniatura de vídeo ao 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
fonte
2
recebo null em Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);Observe que todos os parâmetros estão definidos
Chulo
1
valor nulo no bitmap de bitmap = ThumbnailUtils.createVideoThumbnail (mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
Prasad de