I have a special video when i want to get this video's width code returns different value for Samsung galaxy s6 and Samsung note 3. I tested many different codes and libraries, but result is the same.
in Samsung galaxy s6: 640x480
in Samsung note 3: 853x480
when I open this video with Gspot program it shows:
Recommended Display Size: 853x480
and this is the same value is returned by our IOS app tested in Iphone 7. aspect radio is not the same and this is big problem.
here is some of codes I tested:
(1)
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(path);
Bitmap bmp = metaRetriever.getFrameAtTime(-1);
int height = bmp.getHeight();
int width = bmp.getWidth();
(2)
MediaPlayer myMediaPlayer= MediaPlayer.create(ApplicationLoader.applicationContext,
Uri.fromFile(new File(path)));
width = myMediaPlayer.getVideoWidth();
height = myMediaPlayer.getVideoHeight();
(3)
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(path);
String heights = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
String widths = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
int height = Integer.valueOf(heights);
int width = Integer.valueOf(widths);
(4)
MediaPlayer myMediaPlayer= MediaPlayer.create(ApplicationLoader.applicationContext, Uri.fromFile(new File(path)));
myMediaPlayer.setOnVideoSizeChangedListener((mp, width, height) -> {
int videoWidth = width;
int videoHeight = height;
);
myMediaPlayer.setDataSource(path);
myMediaPlayer.prepare();
FFmpegMediaMetadataRetriever, FFmpeg Java, ExoPlayer and some other libraries returns the same result.
try to scale images with DPI scale of device, i had same problem with premade bitmaps original pixel width was scaled like that.
I want to retrieve height and width of video, I am using MediaMetadataRetriever class for this. It is working correct for most of the case, but for few case height and width are interchanged.
I think this might be happening because of orientation change.
My current code:
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(videoPath);
videoHeight = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
videoWidth = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
How can i get correct values? Thank you
I've been trying to figure this out myself for the last day or so, I eventually had to solve it experimentally.
File file = new File(path);
if (file.exists()) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(file.getAbsolutePath());
String metaRotation = retriever.extractMetadata(METADATA_KEY_VIDEO_ROTATION);
int rotation = metaRotation == null ? 0 : Integer.parseInt(metaRotation);
Log.i("Test", "Rotation = " + rotation);
}
If the rotation is 90 or 270 the width and height will be transposed.
I would like to confirm that what I am doing is indeed the correct way as some elements behave unexpected.
First, I have a landscape and portrait layout, as I understand, doing this will automatically detect if the phone is in portrait/landscape mode:
- layout
- activity_video_player.xml
- layout-land
- activity_video_player.xml
Then when the user selects a video from the gallery, I check if the video was taking in landscape or portrait, by doing this (inside OnCreate):
int w;
int h;
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(this, videoURI);
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
w = Integer.parseInt(width);
h = Integer.parseInt(height);
if (w > h) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
I have tested this and it works fine, but I noticed some of my xml elements (play button) is placed incorrectly.
So my app flow is:
MainActivity --> SelectvidButton --> Gallery Intent --> VideoPlayActivity
My Question
Is this the correct way of doing this and if it is, is there any reason why some of the xml elements get placed incorrectly?
EDIT 1:
I noticed that this only happens when the activity is launched for the first time, if I press the back button and select the same video again, the layout is perfectly like I want it to be.
EDIT 2:
I have also noticed that this only happens if the previous activity (MainActivity) was in the same orientation than what the selected video is.
Here is what I ended up doing.
Instead of using MediaMetadataRetriever to get the width and height, I first retrieve a Bitmap from the video file and then setting the orientation according to the width and hight of the Bitmap, as shown below:
private void rotateScreen() {
try {
//Create a new instance of MediaMetadataRetriever
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
//Declare the Bitmap
Bitmap bmp;
//Set the video Uri as data source for MediaMetadataRetriever
retriever.setDataSource(this, mVideoUri);
//Get one "frame"/bitmap - * NOTE - no time was set, so the first available frame will be used
bmp = retriever.getFrameAtTime();
//Get the bitmap width and height
videoWidth = bmp.getWidth();
videoHeight = bmp.getHeight();
//If the width is bigger then the height then it means that the video was taken in landscape mode and we should set the orientation to landscape
if (videoWidth > videoHeight) {
//Set orientation to landscape
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
//If the width is smaller then the height then it means that the video was taken in portrait mode and we should set the orientation to portrait
if (videoWidth < videoHeight) {
//Set orientation to portrait
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
} catch (RuntimeException ex) {
//error occurred
Log.e("MediaMetadataRetriever", "- Failed to rotate the video");
}
}
Fellow developer, i seek your help.
I have a problem which is memory related. I don't really know how to tackle this so i will just present my code snippets. Bear in mind that, although it is on Xamarin.Android, it will also apply to normal Android.
I use a library which just starts a camera intent. The library (or component) i am using is : http://components.xamarin.com/view/xamarin.mobile. That is not really relevant, but maybe you can point me to other insights why i should or shouldn't be using this library.
Anyway, to start the camera and capture the input. I use the following code :
private void StartCamera() {
var picker = new MediaPicker (this);
if (! picker.IsCameraAvailable) {
Toast.MakeText (this, "No available camera found!", ToastLength.Short).Show ();
} else {
var intent = picker.GetTakePhotoUI (new StoreCameraMediaOptions{
Name = "photo.jpg",
Directory = "photos"
});
StartActivityForResult (intent, 1);
}
}
The onActivityForResult() method is called when i return from this camera intent. In this method, i do the following :
protected override async void OnActivityResult (int requestCode, Result resultCode, Intent data)
{
// User canceled
if (resultCode == Result.Canceled)
return;
System.GC.Collect ();
dialog = new ProgressDialog (this);
dialog.SetProgressStyle (ProgressDialogStyle.Spinner);
dialog.SetIconAttribute (Android.Resource.Attribute.DialogIcon);
dialog.SetTitle (Resources.GetString(Resource.String.dialog_picture_sending_title));
dialog.SetMessage (Resources.GetString(Resource.String.dialog_picture_sending_text));
dialog.SetCanceledOnTouchOutside (false);
dialog.SetCancelable (false);
dialog.Show ();
MediaFile file = await data.GetMediaFileExtraAsync (this);
await ConvertingAndSendingTask ( file );
dialog.Hide();
await SetupView ();
}
Then, in my ConvertingAndSendingTask() i convert the picture into the desired dimensions with a scaled bitmap. The code is as follows :
public async Task ConvertingAndSendingTask(MediaFile file) {
try{
System.GC.Collect();
int targetW = 1600;
int targetH = 1200;
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
Bitmap b = BitmapFactory.DecodeFile (file.Path, options);
int photoW = options.OutWidth;
int photoH = options.OutHeight;
int scaleFactor = Math.Min(photoW/targetW, photoH/targetH);
options.InJustDecodeBounds = false;
options.InSampleSize = scaleFactor;
options.InPurgeable = true;
Bitmap bitmap = BitmapFactory.DecodeFile(file.Path, options);
float resizeFactor = CalculateInSampleSize (options, 1600, 1200);
Bitmap bit = Bitmap.CreateScaledBitmap(bitmap, (int)(bitmap.Width/resizeFactor),(int)(bitmap.Height/resizeFactor), false);
bitmap.Recycle();
System.GC.Collect();
byte[] data = BitmapToBytes(bit);
bit.Recycle();
System.GC.Collect();
await app.api.SendPhoto (data, app.ChosenAlbum.foreign_id);
bitmap.Recycle();
System.GC.Collect();
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine (e.StackTrace);
}
}
Well, this method sends it good on newer devices with more memory but on lower end devices it ends up in a Out of memory error. Or anyway a nearly OOM. When Somethimes i goes well but when i want to take the second or third picture, it always ends up in OOM errors.
I realize that what i am doing is memory intensive. For example :
First i want the initial Width and Height of the original image.
Then it is sampled down (i don't really know if it is done well).
Then i load the sampled Bitmap into the memory.
When i loaded it into memory, my scaled bitmap has to be loaded in memory aswell prior to Recycle() the first Bitmap.
Ultimately i need a byte[] for sending the Bitmap over the web. But i need to convert it first prior to releasing my scaled Bitmap.
Then i release my scaled Bitmap and send the byte[].
Then as a final step, the byte[] needs to be released from memory aswell. I already do that on my BitmapToBytes() method as shown below, but i wanted to include it for maybe other insights.
static byte[] BitmapToBytes(Bitmap bitmap) {
byte[] data = new byte[0];
using (MemoryStream stream = new MemoryStream ())
{
bitmap.Compress (Bitmap.CompressFormat.Jpeg, 90, stream);
stream.Close ();
data = stream.ToArray ();
}
return data;
}
Does somebody sees any good parts where i can optimize this process? I know i am loading to much into memory but i can't think of another way.
It should be mentioned that i always want my images to be 1600x1200(Landscape) or 1200x1600(Portrait). I calculate that value in the following way :
public static float CalculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
float inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
float heightRatio = ((float) height / (float) reqHeight);
float widthRatio = ((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
if(height < reqHeight || width < reqWidth) {
// Calculate ratios of height and width to requested height and
// width
float heightRatio = ((float) reqHeight / (float) height);
float widthRatio = ((float) reqWidth / (float) width);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Does anybody has any recommendations or an alternative workflow?
I would be so much helped with this!
This might be a very delayed reply but may helpful to somebody who got the same issue.
Use the the calculated InSampleSize value (option)to decode file to
bitmap. this itself generates the scaled down bitmap instead of using in CreateScaledBitmap.
If you are expecting High resolution image as output then it is
difficult to handle OutOfMemory issue. Because An image with a higher
resolution does not provide any visible benefit, but still takes up
precious memory and incurs additional performance overhead due to
additional scaling performed by the view.[xamarin doc]
Calculate the bitmap target width and height relating to the imageview height and width. you can calculate this by MeasuredHeight and MeasuredWidth Property. [Note: This works only after complete image draw]
Consider using async method to decode file instead running on main thread [DecodeFileAsync]
For more detail follow this http://appliedcodelog.blogspot.in/2015/07/avoiding-imagebitmap.html
I'm loading a bitmap into an ImageView, and seeing this error. I gather this limit relates to a size limit for OpenGL hardware textures (2048x2048). The image I need to load is a pinch-zoom image of about 4,000 pixels high.
I've tried turning off hardware acceleration in the manifest, but no joy.
<application
android:hardwareAccelerated="false"
....
>
Is it possible to load an image larger than 2048 pixels into an ImageView?
This isn't a direct answer to the question (loading images >2048), but a possible solution for anyone experiencing the error.
In my case, the image was smaller than 2048 in both dimensions (1280x727 to be exact) and the issue was specifically experienced on a Galaxy Nexus. The image was in the drawable folder and none of the qualified folders. Android assumes drawables without a density qualifier are mdpi and scales them up or down for other densities, in this case scaled up 2x for xhdpi. Moving the culprit image to drawable-nodpi to prevent scaling solved the problem.
I have scaled down the image in this way:
ImageView iv = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);
All rendering is based on OpenGL, so no you can't go over this limit (GL_MAX_TEXTURE_SIZE depends on the device, but the minimum is 2048x2048, so any image lower than 2048x2048 will fit).
With such big images, if you want to zoom in out, and in a mobile, you should setup a system similar to what you see in google maps for example. With the image split in several pieces, and several definitions.
Or you could scale down the image before displaying it (see user1352407's answer on this question).
And also, be careful to which folder you put the image into, Android can automatically scale up images. Have a look at Pilot_51's answer below on this question.
Instead of spending hours upon hours trying to write/debug all this downsampling code manually, why not use Picasso? It was made for dealing with bitmaps of all types and/or sizes.
I have used this single line of code to remove my "bitmap too large...." problem:
Picasso.load(resourceId).fit().centerCrop().into(imageView);
Addition of the following 2 attributes in (AndroidManifest.xml) worked for me:
android:largeHeap="true"
android:hardwareAccelerated="false"
Changing the image file to drawable-nodpi folder from drawable folder worked for me.
I used Picasso and had the same problem. image was too large at least in on size, width or height. finally I found the solution here. you can scale the large image down according to display size and also keep the aspect ratio:
public Point getDisplaySize(Display display) {
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
} else {
int width = display.getWidth();
int height = display.getHeight();
size = new Point(width, height);
}
return size;
}
and use this method for loading image by Picasso:
final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
Picasso.with(this)
.load(urlSource)
.resize(size, size)
.centerInside()
.into(imageViewd);
also for better performance you can download the image according to width and height of the display screen, not whole the image:
public String reviseImageUrl(final Integer displayWidth, final Integer displayHeight,
final String originalImageUrl) {
final String revisedImageUrl;
if (displayWidth == null && displayHeight == null) {
revisedImageUrl = originalImageUrl;
} else {
final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();
if (displayWidth != null && displayWidth > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
}
if (displayHeight != null && displayHeight > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
}
revisedImageUrl = uriBuilder.toString();
}
return revisedImageUrl;
}
final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);
and then:
Picasso.with(this)
.load(newImageUlr)
.resize(size, size)
.centerInside()
.into(imageViewd);
EDIT: getDisplaySize()
display.getWidth()/getHeight() is deprecated. Instead of Display use DisplayMetrics.
public Point getDisplaySize(DisplayMetrics displayMetrics) {
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
return new Point(width, height);
}
BitmapRegionDecoder does the trick.
You can override onDraw(Canvas canvas), start a new Thread and decode the area visible to the user.
As pointed by Larcho, starting from API level 10, you can use BitmapRegionDecoder to load specific regions from an image and with that, you can accomplish to show a large image in high resolution by allocating in memory just the needed regions. I've recently developed a lib that provides the visualisation of large images with touch gesture handling. The source code and samples are available here.
View level
You can disable hardware acceleration for an individual view at runtime with the following code:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I ran through same problem, here is my solution. set the width of image same as android screen width and then scales the height
Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);
This is better for any size android screen. let me know if it works for you.
Scale down image:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);
int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
pow += 1;
options.inSampleSize = 1 << pow;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);
The image will be scaled down at the size of reqHeight and reqWidth. As I understand inSampleSize only take in a power of 2 values.
Use Glide library instead of directly loading into imageview
Glide : https://github.com/bumptech/glide
Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
I tried all the solutions above, one-after-the-other, for quite many hours, and none seemed to work! Finally, I decided to look around for an official example concerning capturing images with Android's camera, and displaying them. The official example (here), finally gave me the only method that worked. Below I present the solution I found in that example app:
public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = imgView.getWidth();
int targetH = imgView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
/* Associate the Bitmap to the ImageView */
imgView.setImageBitmap(bitmap);
imgView.setVisibility(View.VISIBLE);
}
NOTE FOR THOSE WHO WANT TO PUT IMAGES OF SMALL SIZE:
Pilot_51's solution (moving your images to drawable-nodpi folder) works, but has another problem:
It makes images TOO SMALL on screen unless the images are resized to a very large (like 2000 x 3800) resolution to fit screen -- then it makes your app heavier.
SOLUTION: put your image files in drawable-hdpi -- It worked like a charm for me.
Using the correct drawable subfolder solved it for me. My solution was to put my full resolution image (1920x1200) into the drawable-xhdpi folder, instead of the drawable folder.
I also put a scaled down image (1280x800) into the drawable-hdpi folder.
These two resolutions match the 2013 and 2012 Nexus 7 tablets I'm programming. I also tested the solution on some other tablets.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
///*
if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){
uri = data.getData();
String[] prjection ={MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(prjection[0]);
ImagePath = cursor.getString(columnIndex);
cursor.close();
FixBitmap = BitmapFactory.decodeFile(ImagePath);
ShowSelectedImage = (ImageView)findViewById(R.id.imageView);
// FixBitmap = new BitmapDrawable(ImagePath);
int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);
// ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));
ShowSelectedImage.setImageBitmap(FixBitmap);
}
}
This code is work