web-dev-qa-db-ger.com

MediaPlayer.setDataSource (String) funktioniert nicht mit lokalen Dateien

Ich kann ein lokales MP3 abspielen, wenn ich die statische Methode MediaPlayer.create (Kontext, ID) verwende, aber es funktioniert nicht, wenn ich die nicht statische Methode MediaPlayer.setDataSource (String) verwende. Beim Aufruf von MediaPlayer.prepare () erhalte ich eine synchrone Ausnahme:

prepare exceptionjava.io.IOException: Prepare failed.: status=0x1

Hier ist mein Code (Protokollierung weggelassen):

String filename = "Android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(filename); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();

Bitte beachten Sie, dass ich keine Fehlermeldung bekomme, dass eine Datei nicht gefunden wurde. Der vollständige Name der Datei lautet test0.mp3 und ich platziere sie im Verzeichnis/res/raw/in Eclipse.

Ich gehe davon aus, dass ich den Pfad falsch eingestellt habe, aber alle Beispiele, die ich online finde, verwenden die FileDescriptor-Version von setDataPath anstelle der String-Version von setDataPath.

BEARBEITEN: Ich kann auch ein lokales MP3 abspielen, wenn ich die Methode MediaPlayer.setDataSource (FileDescriptor) verwende und die Dateien im Verzeichnis/assets/in Eclipse ablege.

EDIT 2: Ich akzeptierte die Antwort, dass dies nicht möglich ist, erkannte jedoch, dass die von mir verwendete Bibliothek (openFrameworks) tatsächlich die Methode String verwendet, um eine Datei zu laden. Siehe hier: 

https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.Java

23
user755921

Alternative Lösung 1: Verwenden von Resources.getIdentifier ()

Verwenden Sie getResources (). GetIdentifier (), um die ID der Ressource abzurufen, und verwenden Sie den statischen MediaPlayer.create () wie üblich. 

public int getIdentifier (String name, String defType, String defPackage)

getIdentifier () nimmt Ihren Ressourcennamen (test0), den Ressourcentyp (raw), Ihren Paketnamen und gibt die tatsächliche Ressourcen-ID zurück.

 MediaPlayer mp;
 //String filename = "Android.resource://" + this.getPackageName() + "/raw/test0";
 mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
 mp.start();

Ich habe diesen Code getestet und funktioniert.


Update Nr. 1:

Alternative Lösung # 2: Verwenden von Uri.parse ()

Ich habe diesen Code auch getestet und funktioniert auch. Übergeben Sie Ihren Ressourcenpfad als URI an setDataSource (). Ich habe diese Änderung nur an Ihrem Code vorgenommen, damit er funktioniert.

String filename = "Android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(this,Uri.parse(filename)); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();


Update Nr. 2: Antwort ist NEIN

Über den Aufruf von setDataSource (String)

Nachdem Sie Ihren Kommentar gesehen haben, sieht es so aus, als würden Sie genau wünschen, dass setDataSource (string) für Ihren Zweck verwendet wird. Ich verstehe nicht warum. Aber ich gehe davon aus, dass Sie aus irgendeinem Grund versuchen, den "Kontext" zu vermeiden. Wenn dies nicht der Fall ist, sollten die beiden oben genannten Lösungen perfekt für Sie funktionieren oder wenn Sie versuchen, den Kontext zu vermeiden. Leider ist dies mit der Funktion mit dem Aufruf der Funktion setDataSource (String) nicht möglich. Der Grund ist wie folgt:

Die MediaPlayer-Funktion setDataSource () enthält die folgenden Optionen, von denen Sie nur an setDataSource (String) interessiert sind.

 setDataSource Functions

setDataSource (String) ruft intern die Funktion setDataSource (String path, String [], String [] - Werte) auf. Wenn Sie seine source überprüfen können,

public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);
    }

wenn Sie den setDataSource-Code (String-Pfad, String [] -Schlüssel, String [] -Werte) überprüfen, wird die folgende Bedingung angezeigt, die den Pfad anhand seines Schemas filtert, insbesondere wenn es sich um ein "Datei" -Schema handelt, das setDataSource (FileDescriptor) oder aufgerufen wird Wenn das Schema keine "Datei" ist, wird die native JNI-Medienfunktion aufgerufen. 

{
        final Uri uri = Uri.parse(path);
        final String scheme = uri.getScheme();
        if ("file".equals(scheme)) {
            path = uri.getPath();
        } else if (scheme != null) {
            // handle non-file sources
            nativeSetDataSource(
                MediaHTTPService.createHttpServiceBinderIfNecessary(path),
                path,
                keys,
                values);
            return;
        }
        final File file = new File(path);
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            setDataSource(fd);
            is.close();
        } else {
            throw new IOException("setDataSource failed.");
        }
}

Im obigen Code ist das URI-Schema der Ressourcendatei nicht null (Android.resource: //), und setDataSource (String)) versucht, die native JNI-Funktion nativeSetDataSource () zu verwenden, wobei der Pfad http/https/rtsp ist und natürlich schlägt dieser Aufruf ebenfalls fehl, ohne dass eine Ausnahme ausgelöst wird. Deshalb wird Ihr Aufruf von setDataSource (String) ohne Ausnahme abgebrochen und kann mit der folgenden Ausnahme () vorbereitet werden. 

Prepare failed.: status=0x1

Daher kann setDataSource (String) nicht mit Ihrer Ressourcendatei umgehen. Sie müssen eine andere Überschreibung wählen.

Auf der anderen Seite überprüfen Sie setDataSource (Kontext-Kontext, Uri-Uri, Map-Header), der von SetDataSource (Kontext-Kontext, Uri-Uri) verwendet wird. Er verwendet AssetFileDescriptor, ContentResolver aus Ihrem Kontext und OpenAssetFileDescriptor (OpenAssetFileDescriptor). ) kann Ihre Ressourcendatei öffnen, und schließlich wird das resultierende fd verwendet, um die setDataSource (FileDescriptor) Überschreibung aufzurufen.

    AssetFileDescriptor fd = null;
    try {
        ContentResolver resolver = context.getContentResolver();
        fd = resolver.openAssetFileDescriptor(uri, "r");
        //  :
        //  :
        //  :
        if (fd.getDeclaredLength() < 0) {
                setDataSource(fd.getFileDescriptor());
            } else {
                setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
            }

Zum Abschluss können Sie die setDataSource (String) Überschreibung nicht wie Ihre Ressourcen-MP3-Datei verwenden. Wenn Sie zur Wiedergabe Ihrer Ressourcendatei einen String verwenden möchten, können Sie entweder die statische Funktion MediaPlayer.create () mit getIdentifier () wie oben angegeben verwenden oder setDataSource (Kontext, URI) wie in Update Nr. 1 angegeben.

Weitere Informationen finden Sie im vollständigen Quellcode: Android MediaPlayer


Update 3:

openFrameworks setDataSource (String):

Wie ich in den Kommentaren unten erwähnt habe, verwendet openFrameworks Android MediaPlayer-Code asis. Wenn Sie sich auf Zeile Nr. 4 beziehen können,

import Android.media.MediaPlayer;

und Zeile Nr. 26, 27, 28 und 218

        player = new MediaPlayer();       //26
        player.setDataSource(fileName);   //27
        player.prepare();                 //28

        private MediaPlayer player;       //218

Wenn Sie also versuchen, ardroid.resource // + this.getPackageName () + "raw/test0" mit openFrameworks an setDataSource () zu übergeben, erhalten Sie immer noch die gleiche Ausnahme, die ich in Update # erläutert habe. 2 Nachdem ich das gesagt habe, habe ich gerade mein Glück bei der Suche nach Google versucht, um das, was ich sage, zu verdoppeln, und fand diesen openFrameworks-Forum-Link wo einer der openFrameworks-Kernentwickler arturo sagt: 

ich weiß nicht genau, wie der mediaPlayer funktioniert, aber alles in res/raw oder bin/data wird nach /sdcard/cc.openframeworks.packagename kopiert

Basierend auf diesem Kommentar können Sie versuchen, den kopierten Pfad in setDataSource () zu verwenden. Die Verwendung der Ressourcendatei in setDataSource (String) von MediaPlayer ist nicht möglich, da der Ressourcendateipfad nicht akzeptiert werden kann. Bitte beachten Sie, dass "Ressourcenpfad" mit dem Schema Android.resource // beginnt, bei dem es sich tatsächlich um einen jar-Speicherort (in Ihrer apk) und nicht um einen physischen Speicherort handelt. Die lokale Datei funktioniert mit setDataSource (String), das mit dem Schema file: // beginnt.

Um Ihnen klar zu machen, was Sie mit einer Ressourcendatei tun möchten, führen Sie den folgenden Code aus und sehen Sie das Ergebnis in logcat.

    try{
      Log.d("RESURI", this.getClass().getClassLoader().getResource("res/raw/test0").toURI().toString());
    } 
    catch(Exception e) {

    }

Sie erhalten das Ergebnis als

jar:file:/data/app/<packagename>/<apkname>.apk!/res/raw/test0

das heißt, um Ihnen zu zeigen, dass die Ressourcendatei, auf die Sie zugreifen möchten, nicht tatsächlich eine Datei im physischen Pfad ist, sondern eine JAR-Position (in apk), auf die Sie nicht mit der Methode setDataSource (String) zugreifen können. (Versuchen Sie es mit 7Zip, um Ihre apk-Datei zu extrahieren, und Sie sehen darin die Datei res/raw/test0).

Hoffentlich hilft das.

PS: Ich kenne seine etwas langwierige Antwort, aber ich hoffe, das erklärt es im Detail. Lassen Sie die alternativen Lösungen oben, wenn dies anderen helfen kann.

34

Unterhalb des Codes, der für mich arbeitet, denke ich, dass dieser Code Ihnen helfen wird

    player = MediaPlayer.create(this,R.raw.test0);
    player.setLooping(true); // Set looping

    player.setVolume(100,100);
    player.start();

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
player.stop();
player.release();
}
2
Ravi Vaghela

Wie die Android Dokumentation sagte

Beliebige Dateien, die in ihrer Rohform gespeichert werden sollen. Um diese Ressourcen zu öffnen, __. Rufen Sie mit einem rohen InputStream Resources.openRawResource () mit der .__ auf. Ressourcen-ID, dh R.raw.filename.

Wenn Sie jedoch auf die ursprünglichen Dateinamen und die Dateiehierarchie zugreifen müssen, muss Vielleicht möchten Sie einige Ressourcen im Verzeichnis assets/speichern (statt res/raw /). Dateien in Assets/erhalten keine Ressourcen-ID Sie können sie daher nur mit AssetManager lesen.

Sie müssen also einen InputStream verwenden, um die Audiodatei zu lesen, bevor Sie sie auf den Media Player einstellen.

Ich empfehle Ihnen, die Audiodatei in den Assets-Ordner zu legen, wie Sie gesagt haben

:)

2
DeKoServidoni

Beim Umgang mit einer Rohressource sollten Sie sich auf den folgenden Konstruktor verlassen:

public static MediaPlayer create (Kontextkontext, int resid)

Praktische Methode zum Erstellen eines MediaPlayer für eine bestimmte Ressourcen-ID. Bei Erfolg wird prepare () bereits aufgerufen und darf nicht erneut aufgerufen werden.

Der Code sieht so aus

mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();

Vergiss nicht, mediaPlayer.release() anzurufen, wenn du damit fertig bist.

( Quelle )

2
Sebastiano

Von hier ,

Wenn der Pfad auf eine lokale Datei verweist, wird die Datei möglicherweise von einem anderen Prozess als der aufrufenden Anwendung geöffnet. Dies bedeutet, dass der Pfadname ein absoluter Pfad sein sollte (da jeder andere Prozess mit einem nicht angegebenen aktuellen Arbeitsverzeichnis ausgeführt wird) und dass der Pfadname auf eine weltweit lesbare Datei verweist. Alternativ kann die Anwendung zuerst die Datei zum Lesen öffnen und dann das Dateideskriptor-Formular setDataSource (FileDescriptor) verwenden. 

Versuchen,

String filePath = "path/file.mp3";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
mediaPlayer.setDataSource(inputStream.getFD());
inputStream.close();

Ich hoffe es hilft!

1
Akhunzaada

Sie müssen setDataSource(@NonNull Context context, @NonNull Uri uri) anstelle von setDataSource(String path) verwenden. 

Da Sie interne Ressourcen für Ressourcenressourcen auflösen möchten, müssen Sie Context angeben, das zur Auflösung dieses Materials verwendet wird

Wenn Sie einen Blick in diese beiden Methoden werfen, werden Sie feststellen, dass sie unterschiedliche Strategien verwenden, um die resultierende Ressource zu finden.

0
Dmitry