Android: Có thể hiển thị hình thu nhỏ video không?


91

Tôi đã tạo một ứng dụng quay video với hộp thoại thư viện. Hộp thoại thư viện hiển thị danh sách các video đã ghi trong đó mỗi mục bao gồm biểu tượng, tiêu đề video, thẻ và thông tin vị trí theo cách sau:

văn bản thay thế

Có ai biết liệu có thể thay thế các biểu tượng bằng hình thu nhỏ video (xem trước khung hình đơn) không?

Cảm ơn!


Bất kỳ một câu trả lời cho [này stackoverflow.com/questions/16190374/...
Làm cho nó đơn giản

Câu trả lời:


71

Nếu bạn đang sử dụng API 2.0 hoặc mới hơn, điều này sẽ hoạt động.

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
Vì vậy, những gì chính xác là id?
phunehehe 29/04

1
Bạn có thể truy vấn MediaStore cho Video trên điện thoại. "Id" chỉ là một trong những phần thông tin bạn truy vấn. Xem thêm về MediaStore tại developer.android.com/reference/android/provider/…
Greg Zimmers

4
Mọi người ngạc nhiên dường như làm việc này. Tôi đã thử điều này nhưng curThumb kết thúc bằng rỗng.
BlueVoodoo

7
làm thế nào về nếu video từ một URL?
jayellos


91

nếu bạn không hoặc không thể đi qua con trỏ và nếu bạn chỉ có đường dẫn hoặc đối tượng Tệp, bạn có thể sử dụng vì API cấp 8 (2.2) Bitmap tĩnh công khai createVideoThumbnail (String filePath, int kind)

Tài liệu Android

Đoạn mã sau chạy hoàn hảo:

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

6
khi tôi cố gắng tạo một hình thu nhỏ, tôi nhận được giá trị rỗng. Tôi thnik con đường của tôi có lẽ là không ổn? myPath = "/ bên ngoài / video / media / 14.180"
haythem souissi

Nó hoạt động như một phép thuật. Ngay cả khi tôi không có t have my video ID. For better quality use MediaStore.Video.Thumbnails.FULL_SCREEN_KIND`
Sami Eltamawy

Kỳ lạ là nó cũng không hoạt động ;- (video tồn tại trong DB, tôi có thể truy xuất tên / kích thước, nhưng không phải hình thu nhỏ
Thomas Decaux

haythem souussi vì đây không phải là đường dẫn mà đây là Uri, bạn cần chuyển nó thành đường dẫn.
Nadir Novruzov 13/1214

Điều này hoạt động nhưng trả về hình ảnh từ phần sai của video. Tôi muốn khung hình đầu tiên nhưng nhận được như 5-6 giây trong? Bất kỳ ý tưởng?
speedynomads

38

Sử dụng lớp học:

import android.provider.MediaStore.Video.Thumbnails;

Chúng tôi có thể nhận được hai kích thước hình thu nhỏ xem trước từ video:

Thumbnails.MICRO_KIND cho 96 x 96

Thumbnails.MINI_KIND cho 512 x 384 px

Đây là một ví dụ mã:

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

Nếu tôi có một liên kết như vậy cho đường dẫn tệp, điều này sẽ không hoạt động, vì tôi đang cố đặt nó thành chế độ xem hình ảnh và nó không hiển thị bất kỳ thứ gì ... đây là đường dẫn tệp "http://unknown.com/v3- 1aox9d1 .mp4 "rõ ràng là một miền thực, nhưng đường dẫn đó không có hình thu nhỏ?
Lion789

Để sử dụng lớp ThumbnailUtils, bạn phải lưu các tập tin vào đĩa việc sử dụng phương pháp này: ThumbnailUtils.createVideoThumbnail ()
Jorgesys

Cảm ơn, làm cách nào để lấy đường dẫn tệp mới cho ngón tay cái tạo?
Lion789,

Tại sao Dose không hoạt động trên Android 4 trở lên?
Criss

1
Xin chào Cris, tôi có 3 thiết bị 4.1 4.2.2 và 5.0 và nó hoạt động không có vấn đề gì, hãy đăng câu hỏi về sự cố của bạn và một số mã, và tôi có thể giúp bạn.
Jorgesys

19

Hiện tại tôi sử dụng mã sau:

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

Nhưng tôi đã tìm thấy giải pháp tốt hơn với thư viện Glide với mã sau (Nó cũng lưu vào bộ nhớ cache hình ảnh của bạn và có hiệu suất tốt hơn so với cách tiếp cận trước đây)

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

19

Tôi thực sự đề nghị bạn sử dụng thư viện Glide . Đây là một trong những cách hiệu quả nhất để tạo và hiển thị hình thu nhỏ video cho tệp video cục bộ.

Chỉ cần thêm dòng này vào tệp gradle của bạn:

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

Và nó sẽ trở nên đơn giản như:

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

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

Bạn có thể tìm thêm thông tin tại đây: https://futurestud.io/blog/glide-displaying-gifs-and-videos

Chúc mừng!


4
Glide chỉ làm việc với video địa phương, không phải là video Url, isnt Có cách nào đơn giản cho điều này
Lutaaya Huzaifah Idris

6

Giải pháp này sẽ hoạt động với mọi phiên bản Android. Nó đã được chứng minh là hoạt động trong 1.5 và 2.2 Đây không phải là một giải pháp "Đây là dành cho Android 2.0+". Tôi tìm thấy điều này thông qua một trang thu thập bảng tin email và không thể tìm thấy liên kết ban đầu. Tất cả tín dụng được chuyển đến áp phích gốc.

Trong ứng dụng của mình, bạn sẽ sử dụng điều này bằng cách gọi:

Bitmap bm = getVideoFrame(VideoStringUri);

Ở đâu đó trong chức năng riêng của nó (ngoài OnCreate, v.v.), bạn sẽ cần:

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

Trong thư mục src của bạn, bạn cần một thư mục con android / media mới sẽ chứa lớp (được sao chép từ chính nguồn android) cho phép bạn sử dụng chức năng này. Phần này không được thay đổi, đổi tên hoặc đặt ở bất kỳ nơi nào khác. MediaMetadataRetriever.java cần phải nằm trong android.media trong thư mục nguồn của bạn để tất cả điều này hoạt động.

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

Điều này không hiệu quả với tôi .. lỗi tại System.loadLibrary ("media_jni");
DArkO

1
Tôi có thể xác nhận rằng điều này không hoạt động. Tôi cũng cần khả năng này. Nó sẽ không hoạt động vì nó sử dụng các lệnh gọi hệ thống gốc mà một ứng dụng thông thường không có quyền sử dụng.
Andy

MediaMetadataRetrieverđược hỗ trợ từ API cấp 10
Asahi

MediaMetadataRetriever là khối mã thứ hai. Nó hiện tại cho phép các API trước 10 (không có sẵn tại thời điểm viết bài) truy cập mã từ ứng dụng thay vì hệ thống. Có thể gọi hệ thống gốc, nhưng bạn cần hiểu sơ bộ về hệ thống để sử dụng chúng. Có vẻ như rất nhiều vấn đề là triển khai không đúng nguồn được cung cấp.
Giỏ hàng bị bỏ rơi vào

@LoungeKatt có thể cho phép nó chụp nhiều hình ảnh của cùng một video từ nhiều lần không?
nhà phát triển android

6

Hãy thử cái này nó làm việc cho tôi

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 và 1.6 không cung cấp hình thu nhỏ này, nhưng 2.0 thì có, như đã thấy trên ghi chú phát hành chính thức :

Phương tiện truyền thông

  • MediaScanner hiện tạo hình thu nhỏ cho tất cả hình ảnh khi chúng được chèn vào MediaStore.
  • API hình thu nhỏ mới để truy xuất hình thu nhỏ hình ảnh và video theo yêu cầu.

1

Tôi trả lời câu hỏi này muộn nhưng hy vọng nó sẽ giúp các ứng viên khác đang đối mặt với vấn đề tương tự.

Tôi đã sử dụng hai phương pháp để tải hình thu nhỏ cho danh sách video, phương pháp đầu tiên là

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

Nó trông đẹp nhưng có một vấn đề với giải pháp này vì khi tôi cuộn danh sách video, nó sẽ bị đóng băng một lúc do quá trình xử lý lớn.

vì vậy sau điều này tôi đã tìm thấy một giải pháp khác hoạt động hoàn hảo bằng cách sử dụng Thư viện Glide.

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

Tôi đã đề xuất giải pháp sau để hiển thị hình thu nhỏ với danh sách video. cảm ơn


-4

Đây là mã cho hình thu nhỏ Video trực tiếp.

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
tôi nhận được null tại Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);Lưu ý rằng tất cả các thông số đã được đặt
Chulo

1
giá trị null tại Bitmap bitmap = ThumbnailUtils.createVideoThumbnail (mMediaPath, MediaStore.Video.Thumbnails.MICRO_KIND);
Prasad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.