Last active
August 29, 2015 14:02
-
-
Save nostra13/4e1df2d8fb7cde0e2722 to your computer and use it in GitHub Desktop.
VideoPreviewDecoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* Copyright 2011-2013 Sergey Tarasevich | |
* | |
* 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 com.nostra13.universalimageloader.core.decode; | |
import android.annotation.TargetApi; | |
import android.content.ContentResolver; | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import android.graphics.BitmapFactory.Options; | |
import android.graphics.Canvas; | |
import android.graphics.Rect; | |
import android.media.MediaMetadataRetriever; | |
import android.media.ThumbnailUtils; | |
import android.net.Uri; | |
import android.os.Build; | |
import android.provider.MediaStore; | |
import android.provider.MediaStore.Images.ImageColumns; | |
import android.text.TextUtils; | |
import android.util.LruCache; | |
import com.nostra13.universalimageloader.core.assist.ImageSize; | |
import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme; | |
import java.io.IOException; | |
import java.io.InputStream; | |
/** | |
* Initialization: | |
* <p/> | |
* ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) | |
* ... | |
* .imageDecoder(new ContentImageDecoder(getApplicationContext())) | |
* .build(); | |
* <p/> | |
* Credit: | |
* [Daniel Gabriel] (http://stackoverflow.com/questions/20931585/is-it-possible-to-display-video-thumbnails-using-universal-image-loader-and-how) | |
*/ | |
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) | |
public class ContentImageDecoder extends BaseImageDecoder { | |
private final Context mContext; | |
private ContentResolver mContentResolver; | |
private int mResourceId; | |
public static final int K = 1024; | |
private static LruCache<String, Integer> sRotationCache; | |
public ContentImageDecoder(Context context) { | |
this(false, context, 0); | |
} | |
public ContentImageDecoder(Context context, int resourceId) { | |
this(false, context, resourceId); | |
} | |
public ContentImageDecoder(boolean loggingEnabled, Context context, int resourceId) { | |
super(loggingEnabled); | |
mContext = context; | |
mResourceId = resourceId; | |
} | |
@Override | |
public Bitmap decode(ImageDecodingInfo info) throws IOException { | |
String cleanedUriString = info.getOriginalImageUri(); | |
Uri uri = Uri.parse(cleanedUriString); | |
if (isVideoUri(uri)) { | |
Bitmap thumbnail = makeVideoThumbnail(info.getTargetSize(), getMediaMetadataRetriever(uri)); | |
if (thumbnail == null) { | |
thumbnail = makeVideoThumbnail(info.getTargetSize(), getVideoFilePath(uri)); | |
} | |
if (thumbnail != null) { | |
overlayCenter(thumbnail, mContext, mResourceId); | |
} | |
return thumbnail; | |
} else { | |
return super.decode(info); | |
} | |
} | |
private boolean isVideoUri(Uri uri) { | |
String type = getContentResolver().getType(uri); | |
return !TextUtils.isEmpty(type) && type.startsWith("video/"); | |
} | |
private MediaMetadataRetriever getMediaMetadataRetriever(Uri uri) { | |
MediaMetadataRetriever retriever = new MediaMetadataRetriever(); | |
retriever.setDataSource(mContext, uri); | |
return retriever; | |
} | |
private String getVideoFilePath(Uri uri) { | |
String columnName = MediaStore.Video.VideoColumns.DATA; | |
Cursor cursor = getContentResolver().query(uri, new String[] {columnName}, null, null, null); | |
assert cursor != null; | |
try { | |
int colIndex = cursor.getColumnIndex(columnName); | |
if (colIndex != -1 && cursor.moveToFirst()) { | |
return cursor.getString(colIndex); | |
} | |
} finally { | |
cursor.close(); | |
} | |
return null; | |
} | |
private Bitmap makeVideoThumbnail(ImageSize imageSize, MediaMetadataRetriever retriever) { | |
if (retriever == null) return null; | |
Bitmap thumbnail = null; | |
byte[] picture = retriever.getEmbeddedPicture(); | |
if (picture != null) { | |
thumbnail = BitmapFactory.decodeByteArray(picture, 0, picture.length); | |
} | |
if (thumbnail == null) { | |
thumbnail = retriever.getFrameAtTime(); | |
} | |
if (thumbnail == null) { | |
return null; | |
} | |
Bitmap scaledThumb = scaleBitmap(thumbnail, imageSize); | |
thumbnail.recycle(); | |
return scaledThumb; | |
} | |
private Bitmap makeVideoThumbnail(ImageSize imageSize, String filePath) { | |
if (TextUtils.isEmpty(filePath)) return null; | |
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND); | |
if (thumbnail == null) return null; | |
Bitmap scaledThumb = scaleBitmap(thumbnail, imageSize); | |
thumbnail.recycle(); | |
return scaledThumb; | |
} | |
protected static void overlayCenter(Bitmap bitmap, Context context, int resourceId) { | |
if (resourceId <= 0) return; | |
Bitmap overlay = BitmapFactory.decodeResource(context.getResources(), resourceId); | |
final int x = bitmap.getWidth() / 2; | |
final int y = bitmap.getHeight() / 2; | |
final int w; | |
final int h; | |
if (bitmap.getWidth() >= bitmap.getHeight()) { | |
h = bitmap.getHeight() / 5; | |
w = overlay.getWidth() * h / overlay.getHeight(); | |
} else { | |
w = bitmap.getWidth() / 5; | |
h = overlay.getHeight() * w / overlay.getWidth(); | |
} | |
final Rect rect = new Rect(x - w / 2, y - h / 2, x + w / 2, y + h / 2); | |
final Canvas canvas = new Canvas(bitmap); | |
canvas.drawBitmap(overlay, null, rect, null); | |
overlay.recycle(); | |
overlay = null; | |
} | |
private ContentResolver getContentResolver() { | |
if (mContentResolver == null) { | |
mContentResolver = mContext.getContentResolver(); | |
} | |
return mContentResolver; | |
} | |
private Bitmap scaleBitmap(Bitmap origBitmap, ImageSize imageSize) { | |
float scale = Math.min(((float) imageSize.getWidth()) / ((float) origBitmap.getWidth()), | |
((float) imageSize.getHeight()) / ((float) origBitmap.getHeight())); | |
return Bitmap.createScaledBitmap(origBitmap, (int) (((float) origBitmap.getWidth()) * scale), | |
(int) (((float) origBitmap.getHeight()) * scale), false); | |
} | |
private static LruCache<String, Integer> getRotationCache() { | |
if (sRotationCache == null) { | |
sRotationCache = new LruCache<String, Integer>(256 * K); | |
} | |
return sRotationCache; | |
} | |
@Override | |
protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo) | |
throws IOException { | |
String imageUri = decodingInfo.getImageUri(); | |
if (Scheme.ofUri(imageUri) == Scheme.FILE) { | |
return super.defineImageSizeAndRotation(imageStream, decodingInfo); | |
} | |
Options options = new Options(); | |
options.inJustDecodeBounds = true; | |
BitmapFactory.decodeStream(imageStream, null, options); | |
ExifInfo exif; | |
Integer rotation = getRotationCache().get(imageUri); | |
if (rotation == null) { | |
if (decodingInfo.shouldConsiderExifParams()) { | |
exif = getExifInfo(imageUri); | |
getRotationCache().put(imageUri, exif.rotation); | |
} else { | |
exif = new ExifInfo(); | |
} | |
} else { | |
exif = new ExifInfo(rotation, false); | |
} | |
return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif); | |
} | |
protected ExifInfo getExifInfo(String imageUri) { | |
switch (Scheme.ofUri(imageUri)) { | |
case FILE: | |
return getExifInfoFromFile(imageUri); | |
case CONTENT: | |
return getExifInfoFromContent(imageUri); | |
default: | |
return new ExifInfo(); | |
} | |
} | |
protected ExifInfo getExifInfoFromContent(String imageUri) { | |
int rotation = 0; | |
final String[] PROJECTION = {ImageColumns.ORIENTATION}; | |
Cursor cursor = getContentResolver().query(Uri.parse(imageUri), PROJECTION, null, null, null); | |
assert cursor != null; | |
try { | |
if (cursor.moveToFirst()) { | |
int orientation = cursor.getInt(cursor.getColumnIndexOrThrow((ImageColumns.ORIENTATION))); | |
rotation = (orientation + 360) % 360; | |
} | |
} finally { | |
cursor.close(); | |
} | |
return new ExifInfo(rotation, false); | |
} | |
protected ExifInfo getExifInfoFromFile(String imageUri) { | |
return defineExifOrientation(imageUri); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment