Android: czy można wyświetlać miniatury filmów?

94

Stworzyłem aplikację do nagrywania wideo z oknem biblioteki. W oknie dialogowym biblioteki wyświetlana jest lista nagranych filmów, gdzie każdy element składa się z ikony, tytułu wideo, tagów i informacji o lokalizacji w następujący sposób:

tekst alternatywny

Czy ktoś wie, czy można zamienić ikony na miniatury wideo (podgląd pojedynczej klatki)?

Dzięki!

Niko Gamulin
źródło
Jakakolwiek odpowiedź na to [ stackoverflow.com/questions/16190374/…
Make it Simple

Odpowiedzi:

71

Jeśli używasz API 2.0 lub nowszego, to zadziała.

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
źródło
9
Więc co to właściwie jest id?
phunehehe
1
Możesz zapytać MediaStore o filmy w telefonie. „Identyfikator” to tylko jedna z informacji, których szukasz. Więcej informacji na temat sklepu MediaStore można znaleźć pod adresem developer.android.com/reference/android/provider/ ...
Greg Zimmers
4
Wydaje się, że wszyscy są zaskoczeni, że to działa. Próbowałem tego, ale curThumb kończy się na zera.
BlueVoodoo
7
a co z filmem z adresu URL?
jayellos
odpowiedz na to [ stackoverflow.com/questions/16190374/…
Make it Simple
92

jeśli nie możesz lub nie możesz przejść przez kursor i jeśli masz tylko ścieżki lub obiekty File, możesz użyć od API poziomu 8 (2.2) public static Bitmap createVideoThumbnail (String filePath, int kind)

Dokumentacja systemu Android

Poniższy kod działa idealnie:

Bitmap bMap = ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(), MediaStore.Video.Thumbnails.MICRO_KIND);
Damien Praca
źródło
7
kiedy próbuję utworzyć miniaturę, otrzymuję wartość null. Myślę, że moja ścieżka może nie jest w porządku? myPath = "/ external / video / media / 14180"
haythem souissi
Działa jak magia. Nawet kiedy t have my video ID. For better quality use
włączam
Dziwne, to też nie działa ;-( wideo istnieje w bazie danych, mogę pobrać nazwę / rozmiar, ale nie miniaturę
Thomas Decaux
haythem souussi, ponieważ to nie jest ścieżka, to jest Uri, musisz ją przekonwertować na ścieżkę.
Nadir Novruzov
To działa, ale zwraca obraz z niewłaściwej części wideo. Chcę mieć pierwszą klatkę, ale w ciągu 5-6 sekund? Jakieś pomysły?
speedynomads
39

Korzystanie z zajęć:

import android.provider.MediaStore.Video.Thumbnails;

Możemy uzyskać dwa rozmiary miniatur podglądu z wideo:

Thumbnails.MICRO_KIND dla 96 x 96

Thumbnails.MINI_KIND dla 512 x 384 pikseli

Oto przykład kodu:

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
źródło
Jeśli mam taki link do ścieżki pliku, czy to nie zadziała, ponieważ próbuję ustawić go na widok obrazu i nic nie pokazuje ... to jest ścieżka do pliku "http: / /unknown.com/v3- 1aox9d1 .mp4 ”jest oczywiście prawdziwą domeną, ale czy ta ścieżka nie może być miniaturą?
Lion789
Aby użyć klasy ThumbnailUtils, musisz zapisać plik na dysku, używając metody: ThumbnailUtils.createVideoThumbnail ()
Jorgesys
Dzięki, w jaki sposób mogę uzyskać nową ścieżkę pliku dla kciuka tworzenia?
Lion789
Dlaczego Dose nie działa na Androidzie 4 i nowszych?
Criss
1
Cześć Cris, mam 3 urządzenia 4.1 4.2.2 i 5.0 i działa bez problemu, opublikuj pytanie dotyczące twojego problemu i trochę kodu, a mogę ci pomóc.
Jorgesys
22

Obecnie używam następującego kodu:

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

Ale znalazłem lepsze rozwiązanie dzięki bibliotece Glide z następującym kodem (buforuje również twój obraz i ma lepszą wydajność niż poprzednie podejście)

Glide.with(context)
                .load(uri)
                .placeholder(R.drawable.ic_video_place_holder)
                .into(imageView);
Amir
źródło
20

Naprawdę sugeruję użycie Glide biblioteki . Jest to jeden z najbardziej wydajnych sposobów generowania i wyświetlania miniatury wideo dla lokalnego pliku wideo.

Po prostu dodaj tę linię do swojego pliku gradle:

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

I stanie się to tak proste, jak:

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

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

Więcej informacji można znaleźć tutaj: https://futurestud.io/blog/glide-displaying-gifs-and-videos

Twoje zdrowie !

Edouard Brèthes
źródło
4
Glide działa tylko z lokalnymi filmami, a nie z
adresami
Niektóre urządzenia nie wyświetlają miniatur lokalnych filmów wideo
Sathish Gadde
7

Spróbuj tego, to działa dla mnie

RequestOptions requestOptions = new RequestOptions();
 Glide.with(getContext())
      .load("video_url")
      .apply(requestOptions)
      .thumbnail(Glide.with(getContext()).load("video_url"))
      .into("yourimageview");
karan brahmaxatriya
źródło
6

To rozwiązanie będzie działać dla każdej wersji Androida. Sprawdził się w wersji 1.5 i 2.2. To nie jest kolejne rozwiązanie typu „This is for Android 2.0+”. Znalazłem to za pośrednictwem strony kolekcji tablicy wiadomości e-mail i nie mogę znaleźć oryginalnego łącza. Wszystko zasługa oryginalnego plakatu.

W swojej aplikacji możesz tego użyć, dzwoniąc:

Bitmap bm = getVideoFrame(VideoStringUri);

Gdzieś w swojej własnej funkcji (poza OnCreate, itp.), Potrzebujesz:

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

W swoim folderze src potrzebujesz nowego podkatalogu android / media, który będzie zawierał klasę (skopiowaną z samego źródła androida), która umożliwia korzystanie z tej funkcji. Tej części nie należy zmieniać, zmieniać nazwy ani umieszczać nigdzie indziej. Aby to wszystko działało, MediaMetadataRetriever.java musi znajdować się w android.media w folderze źródłowym.

/*
 * 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...
}
Opuszczony wózek
źródło
To nie działa dla mnie .. błąd w System.loadLibrary ("media_jni");
DArkO
1
Mogę potwierdzić, że to nie działa. Potrzebowałem również tej możliwości. To nie zadziała, ponieważ używa natywnych wywołań systemowych, których zwykła aplikacja nie ma uprawnień.
Andy
MediaMetadataRetrieverjest obsługiwany od poziomu API 10
Asahi,
MediaMetadataRetriever to drugi blok kodu. Obecne jest, aby umożliwić API przed 10 (które nie było dostępne w czasie pisania) na dostęp do kodu z aplikacji zamiast z systemu. Natywne wywołania systemowe są możliwe, ale aby ich używać, będziesz potrzebować przybliżonego zrozumienia systemu. Wygląda na to, że duża część problemu polega na nieprawidłowej implementacji podanego źródła.
Opuszczony
@LoungeKatt czy można pozwolić mu na wielokrotne przechwytywanie wielu obrazów tego samego wideo?
programista android
5

Android 1.5 i 1.6 nie oferują tych miniatur, ale 2.0 tak, jak widać w oficjalnych informacjach o wydaniu :

Głoska bezdźwięczna

  • MediaScanner teraz generuje miniatury dla wszystkich obrazów po ich wstawieniu do MediaStore.
  • Nowy interfejs API miniatur do pobierania miniatur obrazów i filmów na żądanie.
Marc Climent
źródło
3

Odpowiadam na to pytanie późno, ale mam nadzieję, że pomoże to innemu kandydatowi w obliczu tego samego problemu.

Użyłem dwóch metod, aby załadować listę miniatur filmów. Pierwsza to

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

wygląda dobrze, ale wystąpił problem z tym rozwiązaniem, ponieważ podczas przewijania listy filmów wideo zawiesi się przez jakiś czas ze względu na duże przetwarzanie.

więc po tym znalazłem inne rozwiązanie, które działa doskonale przy użyciu Glide Library.

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

Poleciłem późniejsze rozwiązanie do wyświetlania miniatury z listą wideo. dzięki

Mudassir Khan
źródło
-4

To jest kod miniatury wideo na żywo.

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
źródło
2
dostaję wartość null Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);Uwaga, że ​​wszystkie parametry są ustawione
Chulo
1
wartość null w Bitmap bitmap = ThumbnailUtils.createVideoThumbnail (mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
Prasad