Skip to content

Instantly share code, notes, and snippets.

@nostra13
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nostra13/4e1df2d8fb7cde0e2722 to your computer and use it in GitHub Desktop.
Save nostra13/4e1df2d8fb7cde0e2722 to your computer and use it in GitHub Desktop.
VideoPreviewDecoder
/*******************************************************************************
* 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