I have to play a video in one of my screens of Android and I am using Video View intent for the same. The video gets played but there is no thumbnail appearing on the launch of the screen.
My code is like this
#OnClick(R.id.icon_play)
protected void playVideo(){
String videoUrl="https://someUrl/Video/v07.mp4";
if(!videoUrl.isEmpty()) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(videoUrl));
intent.setDataAndType(Uri.parse(videoUrl), "video/mp4");
startActivity(intent);
}
}
By thumbnail, I mean that when the screen is launched each video should have an image of its own. (something like YouTube)
I tried seekTo() for attaching the thumbnail, but it didn't work.
Please Help. Thanks.
I solve the problem using MediaMetaDataRetriever.
The code goes like this-
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;
}
Note that : Because my video link was in the form of server URL, that's why createThumnailUtils was returning a null when video Url was passed through it.
The below code works fine when the video is coming from local storage.
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail("URL", MediaStore.Images.Thumbnails.MINI_KIND);
BitmapDrawable bitmapD = new BitmapDrawable(thumbnail);
VideoView.setBackground(Drawable bitmapD);
Hope this helps someone!!
Just looked at another example. Not 100% sure if it's going to work but worth a try.
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail("URL", MediaStore.Images.Thumbnails.MINI_KIND);
BitmapDrawable bitmapD = new BitmapDrawable(thumbnail);
VideoView.setBackground(Drawable bitmapD);
Please note, I have written this over phone so there might be spelling errors.
Let me know if this works or if you find another alternative
this may be used i am using this method for thumbnail image of video on my list view of video player..
Cursor cursor = mContext.getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, proj,
MediaStore.Video.Media.DISPLAY_NAME + "=?",
new String[]{localItem._display_name}, null);
cursor.moveToFirst();
long ids = cursor.getLong(cursor
.getColumnIndex(MediaStore.Video.Media._ID));
ContentResolver crThumb = mContext.getContentResolver();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap curThumb = MediaStore.Video.Thumbnails.getThumbnail(
crThumb, ids, MediaStore.Video.Thumbnails.MICRO_KIND,
options);
itemHolder.thumbImage.setImageBitmap(curThumb);
curThumb = null;
cursor.close();
try this way it may be helpful
above ans is now deprecated.
new updated answer with fast one solution
we just have to give the video data path
Bitmap bmThumbnail;
bmThumbnail = ThumbnailUtils.createVideoThumbnail(arraylist.get(i)._data, MediaStore.Video.Thumbnails.MICRO_KIND);
itemHolder.thumbImage.setImageBitmap(bmThumbnail);
where arraylist.get(i)._data => path of video.
and best way is to use Glide or any other image lodging async library for smooth scrolling of listview.
Related
I am developing a module for which i want to show all the user's videos from sd card into a Gridview. I have grabbed video file paths in usual way (Checking if file or directory and save if its a file) in a arraylist and grabbed its bitmap thumbnail with following code:
Bitmap bmThumbnail = ThumbnailUtils.createVideoThumbnail(VideoValues.get(position).getAbsolutePath(),
Thumbnails.MINI_KIND);
Obviously this code runs in a background thread. But the only problem is that the gribview still freezes a lot while scrolling. According to me the main problem is extracting the bitmap from video, which takes a lot of time. Can anyone suggest me a different way to get bitmap from video and how it in a grid ? I have seen the smooth behavior in other apps like Facebook, etc. But I cannot figure out as to how that can be done.
please use below method for retrive video thumbnail from video
#SuppressLint("NewApi")
public static Bitmap retriveVideoFrameFromVideo(String videoPath)
throws Throwable
{
Bitmap bitmap = null;
MediaMetadataRetriever mediaMetadataRetriever = null;
try
{
mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(videoPath);
bitmap = mediaMetadataRetriever.getFrameAtTime();
}
catch (Exception e)
{
throw new Throwable(
"Exception in retriveVideoFrameFromVideo(String videoPath)"
+ e.getMessage());
}
finally
{
if (mediaMetadataRetriever != null)
{
mediaMetadataRetriever.release();
}
}
return bitmap;
}
Recently Google added the Photos app for Google+ (plus) and it shows up when you launch an Intent to choose an image. However, if I select an image from Google+ Photos and try to use it in my application none of my current logic is able to return a usable URI or URL to actually get an image that I can download and manipulate. I'm currently using the "common" methods to try to manipulate the URI that can be found here on Stack Overflow and elsewhere. I can provide code if needed, but at this point I think it's kind of irrelevant since it works well for everything else except this new app. Any ideas on how to get a usable image?
The URI looks something like the following:
content://com.google.android.apps.photos.content/0/https%3A%2F%2Flh5.googleusercontent.com%<a bunch of letters and numbers here>
The MediaColumns.DATA info always returns null and the MediaColumns.DISPLAY_NAME always returns image.jpg no matter what I select from the Google Photos app. If I try to paste everything from https to the end in my browser, nothing comes up. Not sure how to get usable info from this.
When receiving the data intent, you should use the contentResolver to get the photos.
Here's what you should do:
String url = intent.getData().toString();
Bitmap bitmap = null;
InputStream is = null;
if (url.startsWith("content://com.google.android.apps.photos.content")){
is = getContentResolver().openInputStream(Uri.parse(url));
bitmap = BitmapFactory.decodeStream(is);
}
I did faced issues selecting images from new Google Photos app. I was able to resolve it by below code.
It works for me, basically what i did is i am checking if there is any authority is there or not in content URI. If it is there i am writing to temporary file and returning path of that temporary image. You can skip compression part while writing to temporary image
public static String getImageUrlWithAuthority(Context context, Uri uri) {
InputStream is = null;
if (uri.getAuthority() != null) {
try {
is = context.getContentResolver().openInputStream(uri);
Bitmap bmp = BitmapFactory.decodeStream(is);
return writeToTempImageAndGetPathUri(context, bmp).toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
P.S. : I have answered a similar question here
You have to use projection in order to get ImageColumns.DATA (or MediaColumns.DATA):
private String getRealPathFromURI(Uri contentURI) {
// Projection makes ContentResolver to get needed columns only
String[] medData = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(contentURI, medData, null, null, null);
// this is how you can simply get Bitmap
Bitmap bmp = MediaStore.Images.Media.getBitmap(getContentResolver(), contentURI);
// After using projection cursor will have needed DATA column
cursor.moveToFirst();
final int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
So I have the following code in an AsyncTask. The AsyncTask takes in a url to an image file, downloads it into a Bitmap, saves the Bitmap off to disk somewhere, and then displays the Bitmap in an existing ImageView.
Here's the implementation of the doInBackground() call for my AsyncTask:
protected Bitmap doInBackground(String... urls) {
try {
URL image_url = new URL(urls[0]);
String image_url_prefix_regex = "http://www\\.somewebsite\\.com";
if (externalStorageIsAvailable()) {
String file_path = getExternalFilesDir(null).getPath() + image_url.toString().replaceAll(image_url_prefix_regex, "");
File target_file = new File(file_path);
if (!target_file.getParentFile().exists()) {
target_file.getParentFile().mkdirs();
}
BitmapFactory.Options bitmap_options = new BitmapFactory.Options();
bitmap_options.inScaled = false;
bitmap_options.inDither = false;
bitmap_options.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap_options.inPreferQualityOverSpeed = true;
bitmap_options.inSampleSize = 1;
Bitmap image = BitmapFactory.decodeStream(image_url.openStream(), null, bitmap_options);
image.compress(CompressFormat.JPEG, 100, new FileOutputStream(target_file));
return image;
}
}
catch (MalformedURLException e) {
Log.v(DEBUG_TAG, "Error: Caught MalformedURLException");
}
catch (IOException e) {
Log.v(DEBUG_TAG, "Error: Caught IOException");
}
return null;
}
Then later in the onPostExecute() call I have this:
protected void onPostExecute(Bitmap image) {
ImageView mImageView = (ImageView) findViewById(R.id.main_image);
mImageView.setImageBitmap(image);
}
Yet when the code downloads and displays the image, the image is reduced in size and quality. How do I make it so that the resulting image is full quality? Those BitmapFactory.Options settings are the things I've tried thus far, but they did not seem to work.
Note that I'm not asking about the image that gets saved to external storage. I think that one will likely be of lower quality due to getting compressed again, but that shouldn't affect the image I'm sending to my ImageView, which is what I'm asking about. Of course, if there's anything wrong with these assumptions please point them out.
Why you are using Bitmap factory options while decoding bitmap Stream ?
Just use the
Bitmap image = BitmapFactory.decodeStream(image_url.openStream());
instead of
Bitmap image = BitmapFactory.decodeStream(image_url.openStream(), null, bitmap_options);
For getThumbnail, the android documentation has:
public static Bitmap getThumbnail (ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options)
I have absolutely no idea how to get origId (The ID of the original image to perform getThumbnail on) when taking a picture with Camera.TakePicture.
My current attempt, based on various other questions I've read is:
String[] projection = { MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA };
String sort = MediaStore.Images.ImageColumns._ID + " DESC";
Log.d("getting IDs:",sort);
Cursor myCursor = managedQuery(imagesUri, projection, null, null, sort);
myCursor.moveToFirst();
thumbBitmap = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), myCursor.getLong(myCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)), MediaStore.Images.Thumbnails.MINI_KIND, null );
However, my log is outputting the string "_ID" for what should be the actual ID, and it then gives me a null pointer exception on the line where I try and create myCursor.
I also read as the answer to somebody else's similar question that images on the SD card don't have IDs, in which case I guess origID would actually be a URI and the docs are just messed up? I am extremely confused, and any explanation would be very very welcome.
I ended up not being able to use getThumbnail, as I could not find any working way to use the path to the location of the image succsessfully, and (at the time at least, I believe there have been reports submitted) it had issues with devices not storing their thumbnails in the expected location.
My solution to this ended up being what I had hoped I could avoid, writing my own little thumbnail generator instead of using Android's getThumbnail.
public class CreateThumbnail extends Activity {
Bitmap imageBitmap;
public Bitmap notTheBestThumbnail(String file) {
byte[] imageData = null;
try
{
final int THUMBNAIL_SIZE = 95;
FileInputStream fis = new FileInputStream(file); //file is the path to the image-to-be-thumbnailed.
imageBitmap = BitmapFactory.decodeStream(fis);
imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 10, baos); //What image format and level of compression to use.
imageData = baos.toByteArray();
}
catch(Exception ex) {
Log.e("Something did not work", "True");
}
return imageBitmap;
}
}
I use the class like:
CreateThumbnail thumb = new CreateThumbnail();
thumb.notTheBestThumbnail(Environment.getExternalStorageDirectory() + "/exampleDir" + "/" + exampleVar + "/example_img.jpg");
Bitmap mBitmap = thumb.imageBitmap; //Assigns the thumbnail to a bitmap variable, for manipulation.
While I didn't actually figure out how to get the ID, hopefully this will help anybody facing similar problems with getThumbnail.
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();
If you are on android-8 (Froyo) or above, you can use ThumbnailUtils.createVideoThumbnail:
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
MediaStore.Images.Thumbnails.MINI_KIND);
Use Glide it will fetch the thumbnail in async.
Glide.with(context)
.load(videoFilePath) // or URI/path
.into(imgView); //imageview to set thumbnail to
3 ways to get a thumbnail from a video:
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
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);
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.
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;
}
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..
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);
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;
}
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;
}
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"/>
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));
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();
}
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();
}