How to get thumbnail for video in my /sdcard/Android/data/mypackage/files folder?

AndroidVideoThumbnails

Android Problem Overview


Query to MediaStore.Video.Media.EXTERNAL_CONTENT_URI returns only video in /sdcard/DCIM/100MEDIA

But I want to get thumbnails for video in my /sdcard/Android/data/mypackage/files folder. Is it possible ?

Here is part of my code:

        ContentResolver cr = getContentResolver();
    String[] proj = {
                    BaseColumns._ID
    };
     
    Cursor c = cr.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, proj, null, null, null);
    if (c.moveToFirst()) {
    	do
    	{
            int id = c.getInt(0);
            Bitmap b = MediaStore.Video.Thumbnails.getThumbnail(cr, id, MediaStore.Video.Thumbnails.MINI_KIND, null);
            Log.d("*****My Thumbnail*****", "onCreate bitmap " + b);
            ImageView iv = (ImageView) findViewById(R.id.img_thumbnail);
            iv.setImageBitmap(b);
    	}
    	while( c.moveToNext() );
    }         
    c.close();

Android Solutions


Solution 1 - Android

If you are on android-8 (Froyo) or above, you can use ThumbnailUtils.createVideoThumbnail:

Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
    MediaStore.Images.Thumbnails.MINI_KIND);

Solution 2 - Android

Use Glide it will fetch the thumbnail in async.

 Glide.with(context)
                    .load(videoFilePath) // or URI/path
                    .into(imgView); //imageview to set thumbnail to

Solution 3 - Android

3 ways to get a thumbnail from a video:

  1. The best way is to use Glide. It will do all the work in the background, load the thumbnail right into the ImageView and even can show animation when loading. It can work with Uri, byte[] and many other sources. As @Ajji mentioned:

     Glide.with(context)
                 .load(videoFilePath) // or URI/path
                 .into(imgView); //imageview to set thumbnail to
    
  2. If you just need a bitmap in the most efficient way - use ThumbnailUtils. In my case, it produced a bitmap with a size of 294 912 bytes (video taken with a camera of Nexus5X - 1280x720) and the quality was the same as in the next approach. After you compress into JPEG with 90 it will generate a jpeg file of ~30Kb.

     Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
     MediaStore.Images.Thumbnails.MINI_KIND);
    
  3. The last approach is to use MediaMetadataRetriever. But in my case, it produced a bitmap with size more than 6 times bigger than you got with ThumbnailUtils (with the same quality). So consider it as a last resort.

     MediaMetadataRetriever mMMR = new MediaMetadataRetriever();
                         mMMR.setDataSource(mContext, mAttachment.getUri());
                         bmp = mMMR.getFrameAtTime();
    

P.S.: Don't forget that images in Bitmap, byte[] and real file .jpeg formats can be easily converted in any direction within these types. In case of Uri's you often don't have real path to the source file but you can always get the byte stream from it like this:

    InputStream in =  mContext.getContentResolver().openInputStream(uri);

and with this input stream you can do whatever you want.

Solution 4 - Android

You can just use FFmpegMediaMetadataRetriever and forget the reflection:

/**
 *
 * @param path
 *            the path to the Video
 * @return a thumbnail of the video or null if retrieving the thumbnail failed.
 */
public static Bitmap getVideoThumbnail(String path) {
    Bitmap bitmap = null;
    
    FFmpegMediaMetadataRetriever fmmr = new FFmpegMediaMetadataRetriever();  

    try {
        fmmr.setDataSource(path);            

        final byte[] data = fmmr.getEmbeddedPicture();

        if (data != null) {
            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        }
     
        if (bitmap == null) {
            bitmap = fmmr.getFrameAtTime();   
        }
    } catch (Exception e) {
        bitmap = null;
    } finally {
        fmmr.release();
    }
    return bitmap;
}

Solution 5 - Android

see @Ajji 's answer :

 Glide.with(context)
                    .load(videoFilePath) // or URI/path
                    .into(imgView); //imageview to set thumbnail to

It sometimes returns black image, this issue is already mentioned in Glide library's issues

Use this code:

 BitmapPool bitmapPool = Glide.get(activity).getBitmapPool();
                int microSecond = 6000000;// 6th second as an example
                VideoBitmapDecoder videoBitmapDecoder = new VideoBitmapDecoder(microSecond);
                FileDescriptorBitmapDecoder fileDescriptorBitmapDecoder = new FileDescriptorBitmapDecoder(videoBitmapDecoder, bitmapPool, DecodeFormat.PREFER_ARGB_8888);
                Glide.with(activity)
                        .load(videoPath)
                        .asBitmap()
                        .override(50,50)// Example
                        .videoDecoder(fileDescriptorBitmapDecoder)
                        .into(holder.ivFirstUpload);

Solution 6 - Android

BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;

Bitmap bitmapThumb = MediaStore.Video.Thumbnails.getThumbnail(mActivity.getContentResolver(),
     Long.parseLong(video_id), 
     Images.Thumbnails.MINI_KIND, 
     options);

Use Options to load bitmap of decrease the bitmap size..

Solution 7 - Android

Get video thumbnail from VIDEO_ID:

public static Drawable getVideoThumbnail(Context context, int videoID) {
		try {
			String[] projection = {
					MediaStore.Video.Thumbnails.DATA,
			};
			ContentResolver cr = context.getContentResolver();
			Cursor cursor = cr.query(
					MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
					projection, 
					MediaStore.Video.Thumbnails.VIDEO_ID + "=?", 
					new String[] { String.valueOf(videoID) }, 
					null);
			cursor.moveToFirst();
			return Drawable.createFromPath(cursor.getString(0));
		} catch (Exception e) {
		}
		return null;
	}

Solution 8 - Android

Here is a similar answer to Matthew Willis but with added reflection. Why? because science.

/**
 *
 * @param path
 *            the path to the Video
 * @return a thumbnail of the video or null if retrieving the thumbnail failed.
 */
public static Bitmap getVidioThumbnail(String path) {
    Bitmap bitmap = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
        bitmap = ThumbnailUtils.createVideoThumbnail(path, Thumbnails.MICRO_KIND);
        if (bitmap != null) {
            return bitmap;
        }
    }
    // MediaMetadataRetriever is available on API Level 8 but is hidden until API Level 10
    Class<?> clazz = null;
    Object instance = null;
    try {
        clazz = Class.forName("android.media.MediaMetadataRetriever");
        instance = clazz.newInstance();
        final Method method = clazz.getMethod("setDataSource", String.class);
        method.invoke(instance, path);
        // The method name changes between API Level 9 and 10.
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) {
            bitmap = (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
        } else {
            final byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
            if (data != null) {
                bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            }
            if (bitmap == null) {
                bitmap = (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
            }
        }
    } catch (Exception e) {
        bitmap = null;
    } finally {
        try {
            if (instance != null) {
                clazz.getMethod("release").invoke(instance);
            }
        } catch (final Exception ignored) {
        }
    }
    return bitmap;
}

Solution 9 - Android

If you are directly creating thumbnails as follows

Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
    MediaStore.Images.Thumbnails.MINI_KIND);

>Then there is a problem with this method if your are creating thumbnails for large video set(for large number of videos). the application will freeze until all the thumbnails are loaded because all the process are executing in the main thread.


Use SuziLoader

This loader will load the thumbnails for the videos which is locally stored on your filesystem in background.

String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/video.mp4";
ImageView mThumbnail = (ImageView) findViewById(R.id.thumbnail);

SuziLoader loader = new SuziLoader(); //Create it for once
loader.with(MainActivity.this) //Context
	.load(path) //Video path
	.into(mThumbnail) // imageview to load the thumbnail
	.type("mini") // mini or micro
	.show(); // to show the thumbnail

>To get this dependency use the following steps

Step 1. Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

Step 2. Add the dependency

dependencies {
	compile 'com.github.sushinpv:SuziVideoThumbnailLoader:0.1.0'
}

ADD READ EXTERNAL STORAGE Permission in manifest

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

Solution 10 - Android

Try something similar to this code snippet:

img.setImageBitmap(ThumbnailUtils.createVideoThumbnail(
                   Environment.getExternalStorageDirectory().getPath() + "/WhatsApp/Media/WhatsApp Video/"+getItem(position),
                   MediaStore.Video.Thumbnails.FULL_SCREEN_KIND));

                   

Solution 11 - Android

Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        String[] projection = { MediaStore.Video.VideoColumns.DATA };
        Cursor c = getContentResolver().query(uri, projection, null, null, null);
        int vidsCount = 0;
        if (c != null) {
            vidsCount = c.getCount();
            while (c.moveToNext()) {
                String path = c.getString(0);
                Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
                        MediaStore.Images.Thumbnails.MINI_KIND);
            }
            c.close();
        }

Solution 12 - Android

You can use this method with any Uri:

public static Bitmap getVideoFrame(Uri uri, Context context) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(context, uri);
    return retriever.getFrameAtTime();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVladimir BerezkinView Question on Stackoverflow
Solution 1 - AndroidMatthew WillisView Answer on Stackoverflow
Solution 2 - AndroidAjjiView Answer on Stackoverflow
Solution 3 - AndroidKirill KarmazinView Answer on Stackoverflow
Solution 4 - AndroidWilliam SeemannView Answer on Stackoverflow
Solution 5 - AndroidTapanHPView Answer on Stackoverflow
Solution 6 - AndroidVaibhav JainView Answer on Stackoverflow
Solution 7 - Androidtuantv.devView Answer on Stackoverflow
Solution 8 - AndroidJared RummlerView Answer on Stackoverflow
Solution 9 - AndroidSushin PvView Answer on Stackoverflow
Solution 10 - Androiduser8085762View Answer on Stackoverflow
Solution 11 - AndroidKKumar TechnologiesView Answer on Stackoverflow
Solution 12 - AndroidisabsentView Answer on Stackoverflow