将 AMediaExtractor 与位于媒体存储中的音频文件一起使用时,设置提取器数据源时出错,错误 -10002

Error setting extractor data source, err -10002 when using AMediaExtractor with audio file located in media store

我正在尝试从 MediaStore 获取音频文件,从中提取音频数据,对其进行解码,然后在 Android 10 上播放。

我在 MediaExtractor 实例上调用 setDataSource 时出现以下错误:

Error setting extractor data source, err -10002

重现:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val path = getSong().get(0).path
        stringToJNI("file://"+ path)

        val URI = Uri.parse(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString() + "/" + getSong().get(0).id)
        stringToJNI(URI!!.toString())
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringToJNI(URI: String)

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
        }
    }

    fun getSong() : MutableList<Songs> {
        val SONGS_PROJECTION = arrayOf(
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.DATA
        )

        val cursor = contentResolver.query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            SONGS_PROJECTION,
            null,
             null,
            MediaStore.Audio.Media._ID +
                    " ASC LIMIT 100"
        )

        val items: MutableList<Songs> = mutableListOf()
        cursor?.let {
            if (cursor.count > 0) {
                cursor.moveToFirst()
                while (!cursor.isAfterLast) {
                    val s0 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[0]))
                    val s1 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[1]))
                    val s2 = cursor.getString(cursor.getColumnIndex(SONGS_PROJECTION[2]))
                    items.add(Songs(s0, s1, s2))
                    cursor.moveToNext()
                }
            }
            cursor.close()
        }
        return items
    }
}

MainActivity中,我将一次路径传递给本机端,第二次传递URI,每次都没有运气。

#include <jni.h>
#include <string>
#include <media/NdkMediaExtractor.h>
#include <android/log.h>
#include <bitset>

#define APP_NAME "MediaStoreToNativeAudio"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__))

extern "C" JNIEXPORT void JNICALL
Java_com_example_mediastoretonativeaudio_MainActivity_stringToJNI(
    JNIEnv *env,
    jobject jobj,
    jstring URI) {

const char *uri = env->GetStringUTFChars(URI, NULL);
std::string s(uri);

AMediaExtractor *extractor = AMediaExtractor_new();
media_status_t amresult = AMediaExtractor_setDataSource(extractor, uri);
if (amresult != AMEDIA_OK) {
    LOGE("AMediaExtractor_setDataSource called with: [%s]", s.c_str());
    LOGE("Error setting extractor data source, err %d", amresult);
}

return;
}

来自日志:

2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/NdkMediaExtractor: can't create http service
2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: AMediaExtractor_setDataSource called with: [file:///storage/emulated/0/Music/Thank you for the drum machine/01 - Everything Moves.mp3]
2019-11-20 01:09:03.519 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: Error setting extractor data source, err -10002
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/NdkMediaExtractor: can't create http service
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: AMediaExtractor_setDataSource called with: [content://media/external/audio/media/472]
2019-11-20 01:09:03.543 8270-8270/com.example.mediastoretonativeaudio E/MediaStoreToNativeAudio: Error setting extractor data source, err -10002

我的清单:

安装应用程序后,我也通过设置授予权限。

编辑:

带有测试代码的 public 存储库:https://github.com/AndrewBloom/MediaStoreToNativeAudioSample

相同的行为结果使用:

android:requestLegacyExternalStorage="true"

这对我来说似乎是 Android 10 上的错误。似乎 android:requestLegacyExternalStorage="true" 并没有改变这种情况。您可能必须在清单上请求并在运行时请求相同的权限。 AMediaExtractor_setDataSource 函数必须在附加到 Java 的线程上调用。正确地完成所有这些将允许你让它在其他版本的 Android 上工作,但不能在 Android 10 上工作。我已经在 Android Bug Tracker 上报告了这个问题:https://issuetracker.google.com/144837266 As per google answer, it seems that all the app using native libraries that require file access through path can be affected and they know the issue https://www.youtube.com/watch?v=UnJ3amzJM94.

我的解决方法是使用 AMediaExtractor_setDataSourceFd,通过 contentResolver 及其方法 openFileDescriptor 获取 Java 级别的文件描述符。