安卓 - 从URL显示视频缩略图

22

我需要根据一个URL显示视频缩略图到我的ListView项的ImageView视图中,我发现了这个帖子,但无法运行。

结果

输入图片描述

代码

        thumb_image.setImageBitmap(new LoadVideoThumbnail().execute(URLs.videos +"/"+videos.get(position).getId()+".mp4").get());

异步任务

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

        @Override
        protected Bitmap doInBackground(String... objectURL) {
            //return ThumbnailUtils.createVideoThumbnail(objectURL[0], Thumbnails.MINI_KIND);
            return ThumbnailUtils.extractThumbnail(ThumbnailUtils.createVideoThumbnail(objectURL[0], Thumbnails.MINI_KIND), 100, 100);
        }

        @Override
        protected void onPostExecute(Bitmap result){
             //img.setImageBitmap(result);
        }

    }

1
从提供视频的服务器获取缩略图。否则,我怀疑您需要下载整个视频才能获取缩略图。 - CommonsWare
1
我只是在评论,因为这不是一个真正的答案,但我们在工作中使用 Picasso。http://square.github.io/picasso/ 是一个开源库,易于使用,并且在 Android 上运行得非常好。 - Mike Docherty
1
然后使用Picasso或Ion或其他工具从服务器下载缩略图。 - CommonsWare
1
Glide 加载本地视频缩略图 @hasnain - Lutaaya Huzaifah Idris
1
可能是重复的问题:是否可以在Android中从视频URL生成缩略图 - Winter
显示剩余5条评论
8个回答

46

通过以下方法,您可以生成视频的缩略图而无需下载视频:

public static Bitmap retriveVideoFrameFromVideo(String videoPath)throws Throwable
{
    Bitmap bitmap = null;
    MediaMetadataRetriever mediaMetadataRetriever = null;
    try
    {
        mediaMetadataRetriever = new MediaMetadataRetriever();
        if (Build.VERSION.SDK_INT >= 14)
            mediaMetadataRetriever.setDataSource(videoPath, new HashMap<String, String>());
            else
                mediaMetadataRetriever.setDataSource(videoPath);
     //   mediaMetadataRetriever.setDataSource(videoPath);
        bitmap = mediaMetadataRetriever.getFrameAtTime(1, MediaMetadataRetriever.OPTION_CLOSEST);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        throw new Throwable("Exception in retriveVideoFrameFromVideo(String videoPath)"+ e.getMessage());
    }
    finally
    {
        if (mediaMetadataRetriever != null)
        {
            mediaMetadataRetriever.release();
        }
    }
    return bitmap;
}

@KishuDroid,我该如何将代码附加到ImageView上以加载缩略图?我对这段代码感到困惑,请帮帮我。 - Lutaaya Huzaifah Idris
@KishuDroid 你尝试过使用此方法获取https源吗? 呃,我无法弄清楚。 - Woppi
我建议您使用AsyncTask将进程保持在后台,以避免UI卡顿。 - olajide
1
这在使用RecyclerView时是不实际的,会导致延迟。 - Muhammed Haris
这是目前最好、最高效的解决方案,与需要应用程序在从第一帧提取位图之前下载视频的荒谬懒惰解决方案(Glide)相比,后者完全浪费时间等待缩略图的出现。在我的情况下,使用Glide加载缩略图需要15秒,而您使用MediaMetadataRetriever的解决方案平均只需要3秒! - user724861
显示剩余4条评论

13

使用Glide,我正在使用Glide 4.8.0版本

  long thumb = getLayoutPosition()*1000;
  RequestOptions options = new RequestOptions().frame(thumb);
  Glide.with(getBaseContext()).load(url).apply(options).into(mThumb);

2
比MediaMetadataRetriever解决方案好得多。 - Mete
6
你在哪里声明getLayoutPosition()方法?@WallaceRoberto - karan brahmaxatriya
比MediaMetadataRetriever解决方案好得多。 - Priyanka Thakkar
long thumb = holder.getLayoutPosition()*1000; RequestOptions options = new RequestOptions().frame(thumb); Glide.with(context).load(productPhotoUrl).apply(options).into(imageView); - TharakaNirmana

4

@CommonsWare,

并非针对每个视频存储单独的缩略图于服务器。但我们有一些库

fmmr.jar,使用jniLibs

它可以在10秒内直接从服务器url提供缩略图。

这里是获取缩略图的示例代码

FFmpegMediaMetadataRetriever fmmr = new FFmpegMediaMetadataRetriever();
        try {
            fmmr.setDataSource(videoPath);

            Bitmap b = fmmr.getFrameAtTime();

            if (b != null) {
                Bitmap b2 = fmmr.getFrameAtTime(4000000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST_SYNC);
                if (b2 != null) {
                    b = b2;
                }
            }

            if (b != null) {
                Log.i("Thumbnail", "Extracted frame");
                return b;
            } else {
                Log.e("Thumbnail", "Failed to extract frame");
            }
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } finally {
            fmmr.release();
        }

1
这个库的大小太大了,因为它是用本地语言编写的,大约有25MB! - Amir Hossein Ghasemi

1
使用Android的ThumbnailUtils类。
  public static Bitmap getThumblineImage(String videoPath) {
    return ThumbnailUtils.createVideoThumbnail(videoPath, MINI_KIND);
  }

导入静态的android.provider.MediaStore.Video.Thumbnails.MINI_KIND; - Anil Chandra Varma Dandu

1

你好,请尝试这个代码片段,它在我的情况下可以工作。

        Uri videoUri = data.getData();
        String selectedPathVideo="";
        selectedPathVideo = ImageFilePath.getPath(getApplicationContext(), videoUri);
        Log.i("Image File Path", ""+selectedPathVideo);

        try {
            Bitmap thumb = ThumbnailUtils.createVideoThumbnail(selectedPathVideo, MediaStore.Video.Thumbnails.MICRO_KIND);


            imgFarmerVideo.setImageBitmap(thumb);

        } catch (Exception e) {
            e.printStackTrace();
        }

支持类
public class ImageFilePath {
    /**
     * Method for return file path of Gallery image
     *
     * @param context
     * @param uri
     * @return path of the selected image file from gallery
     */
    public static String getPath(final Context context, final Uri uri)
    {

        //check here to KITKAT or new version
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
}

1
以下是用Kotlin编写的从视频路径获取缩略图的代码。
fun retrieveVideoFrameFromVideo(videoPath: String?): Bitmap? {
    var bitmap: Bitmap? = null
    var mediaMetadataRetriever: MediaMetadataRetriever? = null
    try {
        mediaMetadataRetriever = MediaMetadataRetriever()
        mediaMetadataRetriever.setDataSource(videoPath, HashMap<String, String>())
        bitmap = mediaMetadataRetriever.frameAtTime
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        mediaMetadataRetriever?.release()
    }
    return bitmap
}

请按以下方式调用方法。
val bm = retrieveVideoFrameFromVideo(model.FilePath)
imgPreview.setImageBitmap(bm)

我遇到了一个错误:getFrameAtTime: videoFrame是一个空指针 - Captain Jacky

0

您可以使用Glide来加载视频网址的缩略图。

  Glide.with(binding.getRoot().getContext())
                        .load("https://www.example.sample-mp4-file.mp4")
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .into(binding.ivThumbnail);

-3

要显示视频的位图预览,只需设置VideoView的视频路径并让流缓冲即可。

例如:

videoView.setVideoPath("/sdcard/myawesomevideo.mp4");

在几秒钟内,您将看到视频的第一帧。

setVideoPath也接受HTTP URL!只需将我的答案中的路径替换为实际的HTTP URL即可。 - Mattia Franchetto
对不起,我也想将它显示在 ImageView 中。 - TooCool
为什么在VideoView本身已经提供了预览的情况下,你还需要在ImageView中显示预览呢? - Mattia Franchetto
那你建议我在ListView中使用VideoView吗? - TooCool
抱歉,我不明白。我从来没有谈论过 WebView:WebView 用于 HTML 内容,而不是视频内容。 - Mattia Franchetto
抱歉,我的意思是VideoView。 - TooCool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接