2012年9月19日水曜日

Android Movie で File をデコードする

Movie を使うとアニメーション Gif を表示できるのですが、まぁバグがあるとかなんとかは自分でぐぐってください。 

よくあるサンプルは assets や raw にある Gif を読むやつばっかりです。 InputStream is = context.getResources().openRawResource(R.drawable.piggy); Movie movie = Movie.decodeStream(is); http://androidosbeginning.blogspot.jp/2010/09/gif-animation-in-android.html

とか InputStream is = context.getAssets().open("piggy.gif"); Movie movie = Movie.decodeStream(is); とか



SD にある Gif ファイルから読もうとして try { InputStream is = new FileInputStream(filePath); Movie movie = Movie.decodeStream(is); } catch (FileNotFoundException e) { e.printStackTrace(); } とすると Native 側で IOException が起こって表示されません。

LogCat で

09-19 15:38:37.828: W/System.err(7523): java.io.IOException 09-19 15:38:37.828: W/System.err(7523): at java.io.InputStream.reset(InputStream.java:218) 09-19 15:38:37.828: W/System.err(7523): at android.graphics.Movie.decodeStream(Native Method)

と出ていて、どうも InputStream の reset() で起こっていると。

InputStream のコードをみると、サブクラスが Override しないかぎり IOException が投げられるようになっています。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/InputStream.java#217 217 public synchronized void reset() throws IOException { 218 throw new IOException(); 219 }

FileInputStream は Override してませんでした。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/FileInputStream.java

AssetManager の open() で返ってくる InputStream は AssetInputStream です。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#288 288 public final InputStream open(String fileName) throws IOException { 289 return open(fileName, ACCESS_STREAMING); 290 } ... 309 public final InputStream open(String fileName, int accessMode) 310 throws IOException { 311 synchronized (this) { 312 if (!mOpen) { 313 throw new RuntimeException("Assetmanager has been closed"); 314 } 315 int asset = openAsset(fileName, accessMode); 316 if (asset != 0) { 317 AssetInputStream res = new AssetInputStream(asset); 318 incRefsLocked(res.hashCode()); 319 return res; 320 } 321 } 322 throw new FileNotFoundException("Asset file: " + fileName); 323 } この AssetInputStream は reset() を Override しています。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#538 538 public final class AssetInputStream extends InputStream { ... 569 public final void reset() throws IOException { 570 seekAsset(mAsset, mMarkPos, -1); 571 } ... 597 }

InputStream のサブクラスで reset() を継承しているクラスとして BufferedInputStream があります。

このクラスを使って、次のようにすると IOException が発生せずに読むことができます。 try { InputStream stream = new BufferedInputStream( new FileInputStream(filePath), 16 * 1024); stream.mark(16 * 1024); mMovie = Movie.decodeStream(stream); } catch (FileNotFoundException e) { e.printStackTrace(); } ポイントは mark() メソッドを呼ぶ必要がある点です。



ちなみに Movie には deocdeFile() というメソッドもあるのですが、なかみは FileInputStream 使ってるだけなので、使えません。。。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/graphics/java/android/graphics/Movie.java#51 51 public static Movie decodeFile(String pathName) { 52 InputStream is; 53 try { 54 is = new FileInputStream(pathName); 55 } 56 catch (java.io.FileNotFoundException e) { 57 return null; 58 } 59 return decodeTempStream(is); 60 }



おまけ

Movie.cpp
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/Movie.cpp#82 82 static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { 83 84 NPE_CHECK_RETURN_ZERO(env, istream); 85 86 // what is the lifetime of the array? Can the skstream hold onto it? 87 jbyteArray byteArray = env->NewByteArray(16*1024); 88 SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray); 89 if (NULL == strm) { 90 return 0; 91 } 92 93 SkMovie* moov = SkMovie::DecodeStream(strm); 94 strm->unref(); 95 return create_jmovie(env, moov); 96 } http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp#CreateJavaInputStreamAdaptor 12 class JavaInputStreamAdaptor : public SkStream { 13 public: 14 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) 15 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { 16 SkASSERT(ar); 17 fCapacity = env->GetArrayLength(ar); 18 SkASSERT(fCapacity > 0); 19 fBytesRead = 0; 20 } 21 22 virtual bool rewind() { 23 JNIEnv* env = fEnv; 24 25 fBytesRead = 0; 26 27 env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID); 28 if (env->ExceptionCheck()) { 29 env->ExceptionDescribe(); 30 env->ExceptionClear(); 31 SkDebugf("------- reset threw an exception\n"); 32 return false; 33 } 34 return true; 35 } ... 145 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 146 jbyteArray storage, int markSize) { 147 static bool gInited; 148 149 if (!gInited) { 150 jclass inputStream_Clazz = env->FindClass("java/io/InputStream"); 151 RETURN_NULL_IF_NULL(inputStream_Clazz); 152 153 gInputStream_resetMethodID = env->GetMethodID(inputStream_Clazz, 154 "reset", "()V"); 155 gInputStream_markMethodID = env->GetMethodID(inputStream_Clazz, 156 "mark", "(I)V"); 157 gInputStream_availableMethodID = env->GetMethodID(inputStream_Clazz, 158 "available", "()I"); 159 gInputStream_readMethodID = env->GetMethodID(inputStream_Clazz, 160 "read", "([BII)I"); 161 gInputStream_skipMethodID = env->GetMethodID(inputStream_Clazz, 162 "skip", "(J)J"); 163 164 RETURN_NULL_IF_NULL(gInputStream_resetMethodID); 165 RETURN_NULL_IF_NULL(gInputStream_markMethodID); 166 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 167 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 168 RETURN_NULL_IF_NULL(gInputStream_skipMethodID); 169 170 gInited = true; 171 } 172 173 if (markSize) { 174 env->CallVoidMethod(stream, gInputStream_markMethodID, markSize); 175 } 176 177 return new JavaInputStreamAdaptor(env, stream, storage); 178 }

0 件のコメント:

コメントを投稿