[Android] Store covers in a cache on the sdcard for faster loading
Sébastien Toque
git at videolan.org
Mon Oct 8 23:18:21 CEST 2012
vlc-ports/android | branch: master | Sébastien Toque <xilasz at gmail.com> | Sat Oct 6 15:54:33 2012 +0200| [42d6c16caa8ec4e79ec12e5fc173cc007be3ad8b] | committer: Sébastien Toque
Store covers in a cache on the sdcard for faster loading
> http://git.videolan.org/gitweb.cgi/vlc-ports/android.git/?a=commit;h=42d6c16caa8ec4e79ec12e5fc173cc007be3ad8b
---
vlc-android/src/org/videolan/vlc/MurmurHash.java | 183 ++++++++++++++++++++
.../src/org/videolan/vlc/gui/audio/AudioUtil.java | 49 +++++-
2 files changed, 231 insertions(+), 1 deletion(-)
diff --git a/vlc-android/src/org/videolan/vlc/MurmurHash.java b/vlc-android/src/org/videolan/vlc/MurmurHash.java
new file mode 100644
index 0000000..bae3723
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/MurmurHash.java
@@ -0,0 +1,183 @@
+package org.videolan.vlc;
+
+/** Murmur hash 2.0.
+ *
+ * The murmur hash is a relative fast hash function from
+ * http://murmurhash.googlepages.com/ for platforms with efficient
+ * multiplication.
+ *
+ * This is a re-implementation of the original C code plus some
+ * additional features.
+ *
+ * Public domain.
+ *
+ * @author Viliam Holub
+ * @version 1.0.2
+ *
+ */
+public final class MurmurHash {
+
+ /** Generates 32 bit hash from byte array of the given length and
+ * seed.
+ *
+ * @param data byte array to hash
+ * @param length length of the array to hash
+ * @param seed initial seed value
+ * @return 32 bit hash of the given array
+ */
+ public static int hash32(final byte[] data, int length, int seed) {
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ final int m = 0x5bd1e995;
+ final int r = 24;
+ // Initialize the hash to a random value
+ int h = seed ^ length;
+ int length4 = length / 4;
+
+ for (int i = 0; i < length4; i++) {
+ final int i4 = i * 4;
+ int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8)
+ + ((data[i4 + 2] & 0xff) << 16) + ((data[i4 + 3] & 0xff) << 24);
+ k *= m;
+ k ^= k >>> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ }
+
+ // Handle the last few bytes of the input array
+ switch (length % 4) {
+ case 3:
+ h ^= (data[(length & ~3) + 2] & 0xff) << 16;
+ case 2:
+ h ^= (data[(length & ~3) + 1] & 0xff) << 8;
+ case 1:
+ h ^= (data[length & ~3] & 0xff);
+ h *= m;
+ }
+
+ h ^= h >>> 13;
+ h *= m;
+ h ^= h >>> 15;
+
+ return h;
+ }
+
+ /** Generates 32 bit hash from byte array with default seed value.
+ *
+ * @param data byte array to hash
+ * @param length length of the array to hash
+ * @return 32 bit hash of the given array
+ */
+ public static int hash32(final byte[] data, int length) {
+ return hash32(data, length, 0x9747b28c);
+ }
+
+ /** Generates 32 bit hash from a string.
+ *
+ * @param text string to hash
+ * @return 32 bit hash of the given string
+ */
+ public static int hash32(final String text) {
+ final byte[] bytes = text.getBytes();
+ return hash32(bytes, bytes.length);
+ }
+
+ /** Generates 32 bit hash from a substring.
+ *
+ * @param text string to hash
+ * @param from starting index
+ * @param length length of the substring to hash
+ * @return 32 bit hash of the given string
+ */
+ public static int hash32(final String text, int from, int length) {
+ return hash32(text.substring(from, from + length));
+ }
+
+ /** Generates 64 bit hash from byte array of the given length and seed.
+ *
+ * @param data byte array to hash
+ * @param length length of the array to hash
+ * @param seed initial seed value
+ * @return 64 bit hash of the given array
+ */
+ public static long hash64(final byte[] data, int length, int seed) {
+ final long m = 0xc6a4a7935bd1e995L;
+ final int r = 47;
+
+ long h = (seed & 0xffffffffl) ^ (length * m);
+
+ int length8 = length / 8;
+
+ for (int i = 0; i < length8; i++) {
+ final int i8 = i * 8;
+ long k = ((long) data[i8 + 0] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8)
+ + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24)
+ + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40)
+ + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56);
+
+ k *= m;
+ k ^= k >>> r;
+ k *= m;
+
+ h ^= k;
+ h *= m;
+ }
+
+ switch (length % 8) {
+ case 7:
+ h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48;
+ case 6:
+ h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40;
+ case 5:
+ h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32;
+ case 4:
+ h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24;
+ case 3:
+ h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16;
+ case 2:
+ h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8;
+ case 1:
+ h ^= (long) (data[length & ~7] & 0xff);
+ h *= m;
+ }
+ ;
+
+ h ^= h >>> r;
+ h *= m;
+ h ^= h >>> r;
+
+ return h;
+ }
+
+ /** Generates 64 bit hash from byte array with default seed value.
+ *
+ * @param data byte array to hash
+ * @param length length of the array to hash
+ * @return 64 bit hash of the given string
+ */
+ public static long hash64(final byte[] data, int length) {
+ return hash64(data, length, 0xe17a1465);
+ }
+
+ /** Generates 64 bit hash from a string.
+ *
+ * @param text string to hash
+ * @return 64 bit hash of the given string
+ */
+ public static long hash64(final String text) {
+ final byte[] bytes = text.getBytes();
+ return hash64(bytes, bytes.length);
+ }
+
+ /** Generates 64 bit hash from a substring.
+ *
+ * @param text string to hash
+ * @param from starting index
+ * @param length length of the substring to hash
+ * @return 64 bit hash of the given array
+ */
+ public static long hash64(final String text, int from, int length) {
+ return hash64(text.substring(from, from + length));
+ }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioUtil.java b/vlc-android/src/org/videolan/vlc/gui/audio/AudioUtil.java
index 00807eb..b21f0b9 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioUtil.java
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioUtil.java
@@ -19,13 +19,18 @@
*****************************************************************************/
package org.videolan.vlc.gui.audio;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.videolan.vlc.Media;
+import org.videolan.vlc.MurmurHash;
import org.videolan.vlc.R;
import org.videolan.vlc.Util;
import org.videolan.vlc.VLCApplication;
@@ -37,13 +42,17 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.MediaStore;
+import android.util.Log;
public class AudioUtil {
+ public final static String TAG = "VLC/AudioUtil";
+
public static void setRingtone( Media song, Activity activity){
File newringtone = Util.URItoFile(song.getLocation());
ContentValues values = new ContentValues();
@@ -138,9 +147,20 @@ public class AudioUtil {
return null;
}
- public static Bitmap getCover(Context context, Media media, int width) {
+ @SuppressLint("SdCardPath")
+ public synchronized static Bitmap getCover(Context context, Media media, int width) {
Bitmap cover = null;
+ String cachePath = null;
+
try {
+ // try to load from cache
+ int hash = MurmurHash.hash32(media.getArtist()+media.getAlbum());
+ cachePath = "/sdcard/Android/data/org.videolan.vlc/cache/covers/" +
+ (hash >= 0 ? "" + hash : "m" + (-hash)) + "_" + width;
+ cover = readBitmap(cachePath);
+ if (cover != null)
+ return cover;
+
// try to get the cover from android MediaStore
cover = getCoverFromMediaStore(context, media);
@@ -155,8 +175,35 @@ public class AudioUtil {
//scale down if requested
if (cover != null && width > 0)
cover = Util.scaleDownBitmap(context, cover, width);
+
+ //store cover into cache
+ if (cover != null)
+ writeBitmap(cover, cachePath);
+
} catch (Exception e) {
+ e.printStackTrace();
}
return cover;
}
+
+ private static void writeBitmap(Bitmap bitmap, String path) throws IOException {
+ OutputStream out = null;
+ try {
+ File file = new File(path);
+ out = new BufferedOutputStream(new FileOutputStream(file), 4096);
+ bitmap.compress(CompressFormat.JPEG, 90, out);
+ } catch (Exception e) {
+ Log.e(TAG, "writeBitmap failed : "+ e.getMessage());
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ private static Bitmap readBitmap(String path) {
+ File file = new File(path);
+ if (file == null || !file.exists()) return null;
+ return BitmapFactory.decodeFile(path);
+ }
}
More information about the Android
mailing list