MediaMetadataRetriever.java のソース コードをベースとして、Android の MediaMetadataRetriever の独自のバージョンを作成しました。私のバージョンでは、FFmpeg を使用してメタデータを取得しています。このアプローチは機能しますが、JNI 呼び出し間で状態を保持するために C コードの静的変数に依存しています。これは、一度にこのクラスのインスタンスを 1 つしか使用できないことを意味します。そうしないと、状態が破損する可能性があります。2 つの Java 関数は、次のように定義されています。
public class MediaMetadataRetriever
{
static {
System.loadLibrary("metadata_retriever_jni");
}
public MediaMetadataRetriever() {
}
public native void setDataSource(String path) throws IllegalArgumentException;
public native String extractMetadata(String key);
}
対応する C (JNI) コード コードは次のとおりです。
const char *TAG = "Java_com_example_metadataexample_MediaMetadataRetriever";
static AVFormatContext *pFormatCtx = NULL;
JNIEXPORT void JNICALL
Java_com_example_metadataexample_MediaMetadataRetriever_setDataSource(JNIEnv *env, jclass obj, jstring jpath) {
if (pFormatCtx) {
avformat_close_input(&pFormatCtx);
}
char duration[30] = "0";
const char *uri;
uri = (*env)->GetStringUTFChars(env, jpath, NULL);
if (avformat_open_input(&pFormatCtx, uri, NULL, NULL) != 0) {
__android_log_write(ANDROID_LOG_INFO, TAG, "Metadata could not be retrieved");
(*env)->ReleaseStringUTFChars(env, jpath, uri);
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
__android_log_write(ANDROID_LOG_INFO, TAG, "Metadata could not be retrieved");
avformat_close_input(&pFormatCtx);
(*env)->ReleaseStringUTFChars(env, jpath, uri);
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
(*env)->ReleaseStringUTFChars(env, jpath, uri);
}
JNIEXPORT jstring JNICALL
Java_com_example_metadataexample_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jclass obj, jstring jkey) {
const char *key;
jstring value = NULL;
key = (*env)->GetStringUTFChars(env, jkey, NULL) ;
if (!pFormatCtx) {
goto fail;
}
if (key) {
if (av_dict_get(pFormatCtx->metadata, key, NULL, AV_DICT_IGNORE_SUFFIX)) {
value = (*env)->NewStringUTF(env, av_dict_get(pFormatCtx->metadata, key, NULL, AV_DICT_IGNORE_SUFFIX)->value);
}
}
fail:
(*env)->ReleaseStringUTFChars(env, jkey, key);
return value;
}
私の問題を概説する使用例は次のとおりです。
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource("one.mp3");
MediaMetadataRetriever mmr2 = new MediaMetadataRetriever();
// This line resets the data source to two.mp3
mmr2.setDataSource("two.mp3");
// should retrieve the artist from one.mp3 but retrieves it from two.mp3 due to the static
// variable being reset in the previous statement
String artist = mmr.extractMetadata(MediaMetadataRetriever.ARTIST);
MediaMetadataRetriever の複数のインスタンスを互いに干渉させずに使用できるように、このコードをどのように構成するかを誰かが説明できますか? 私はコードを C++ に切り替えたくありません。このコードは Android フレームワークから行ごとに取得されるため、MediaMetadataRetriever.java を変更する必要はないと確信しています (複数のインスタンスを許可します。以下の例を参照してください)。 )。C コードを再構築する必要があるようですが、静的変数を使用せずに JNI 呼び出し間で状態を保持する方法がわかりません。前もって感謝します。
File file1 = new File(Environment.getExternalStorageDirectory(), "Music/one.mp3");
File file2 = new File(Environment.getExternalStorageDirectory(), "Music/two.mp3");
android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();
mmr.setDataSource(file1.toString());
android.media.MediaMetadataRetriever mmr2 = new android.media.MediaMetadataRetriever();
mmr2.setDataSource(file2.toString());
// Returns the artist of one.mp3, not two.mp3, as expected. This is the expected behavior
// and confirms that multiple instances of MediaMetadataRetriever can be used simultaneously
mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_ARTIST));