29 Stimmen

MP3-Dekodierung auf Android

Wir implementieren ein Programm für Android-Telefone, das Audio-Streams aus dem Internet abspielt. Hier ist ungefähr, was wir tun:

  1. Laden Sie ein benutzerdefiniertes verschlüsseltes Format herunter.
  2. Entschlüsseln, um Teile der regulären MP3-Daten zu erhalten.
  3. Dekodierung von MP3-Daten in PCM-Rohdaten in einem Speicherpuffer.
  4. Weiterleitung der PCM-Rohdaten an eine Audiospur

Unsere Zielgeräte sind bisher Droid und Nexus One. Auf dem Nexus One funktioniert alles prima, aber die MP3-Dekodierung ist auf dem Droid zu langsam. Die Audiowiedergabe beginnt zu springen, wenn wir den Droid unter Last setzen. Es ist uns nicht erlaubt, die MP3-Daten auf die SD-Karte zu dekodieren, aber ich weiß, dass das sowieso nicht unser Problem ist.

Wir haben keinen eigenen MP3-Decoder geschrieben, sondern verwenden MPADEC ( http://sourceforge.net/projects/mpadec/ ). Es ist kostenlos und ließ sich leicht in unser Programm integrieren. Wir kompilieren es mit dem NDK.

Nach eingehender Analyse mit verschiedenen Profiling-Tools sind wir davon überzeugt, dass es dieser Decoder ist, der zurückbleibt.

Hier sind die Optionen, die wir in Betracht ziehen:

  1. Finden Sie einen anderen MP3-Decoder, den wir mit dem Android NDK kompilieren können. Dieser MP3-Decoder müsste entweder für die Ausführung auf mobilen ARM-Geräten optimiert sein oder vielleicht nur Integer-Mathematik oder andere Optimierungen zur Leistungssteigerung verwenden.

  2. Da der eingebaute Android MediaPlayer-Dienst URLs akzeptiert, könnten wir in unserem Programm einen kleinen HTTP-Server implementieren und den MediaPlayer mit den entschlüsselten MP3s versorgen. Auf diese Weise können wir die Vorteile des eingebauten MP3-Decoders nutzen.

  3. Zugriff auf den eingebauten MP3-Decoder durch das NDK. Ich weiß nicht, ob dies möglich ist.

Hat jemand einen Vorschlag, wie wir unsere MP3-Dekodierung beschleunigen können?

-- Rob Sz

0 Stimmen

Bei Ihrer Option #2 würde ich erwarten, dass der HTTP-Overhead die Vorteile, die Sie durch die Verwendung der integrierten MediaPlayer Streaming-Unterstützung.

2 Stimmen

Wenn ich mich nicht irre, ist die eingebaute MediaPlayer kann auch von jedem beliebigen content:// URI, die Ihnen im Wesentlichen eine Pipe zum Schreiben zur Verfügung stellt.

0 Stimmen

@jleedev: Können Sie das näher erläutern. Klingt sehr interessant!

2voto

CommonsWare Punkte 950864

Der richtige Weg ist, eine eigene Firmware zu entwickeln und die Entschlüsselung als Teil eines eigenen OpenCORE-Codecs durchzuführen. Damit sind Sie natürlich auf Geräte beschränkt, auf denen Sie diese Firmware installieren können.

Bitte beachten Sie, dass der Rest dieser Ausführungen eher spekulativ ist. Ich habe tatsächlich das Bedürfnis, etwas Ähnliches zu tun, wie Sie es beschreiben, aber ich habe erst in ein paar Monaten Zeit, das Problem anzugehen. Ich beschreibe es also so, wie ich das Problem angehen würde.

Eine Lösung ist die, die in der Antwort von twk beschrieben wird. Sie müssen die SD-Karte nicht verwenden, aber Sie müssen wahrscheinlich eine weltweit lesbare temporäre Datei in Ihrem app-lokalen Dateispeicher haben ( getFilesDir() ). Laden Sie das erste Stück herunter, entschlüsseln Sie es, schreiben Sie es als vollständige, weltweit lesbare MP3-Datei heraus (aber mit einem entsprechend obskuren Verzeichnis/Pfad), und geben Sie es an einen MediaPlayer via setDataSource() . Während das abgespielt wird, laden Sie eine zweite Datei herunter, entschlüsseln sie und richten sie ein. MediaPlayer Instanz, die mit der Wiedergabe beginnt, sobald die erste Instanz endet, um einen möglichst nahtlosen Übergang zu gewährleisten. Anschließend setzen Sie die erste MediaPlayer und verwenden Sie es erneut mit Ihrem dritten Stück, indem Sie zwischen den beiden hin- und herwechseln.

Eine verwandte Lösung wäre in jleedevs Kommentar zu finden. Es ist so ziemlich das Gleiche, außer dass Sie eine FileDescriptor über eine ContentProvider . Es gibt eine Option, mit der Sie ein Socket verwenden können, das Mai können Sie die temporäre Datei vermeiden. Allerdings ist die ContentProvider muss selbst öffentlich zugänglich sein, so dass die temporäre Datei mit einem obskuren Verzeichnis vielleicht sogar privater ist.

Wenn Sie sich Sorgen darüber machen, dass diese Dinge von anderen Prozessen gelesen werden können, sollten Sie wissen, dass MediaPlayer selbst (oder besser gesagt, das OpenCORE-Subsystem) befindet sich in einem anderen Prozess. Außerdem ist der von Ihnen vorgeschlagene HTTP-Server auch auf dem Gerät weltweit lesbar. Sicherheit durch Unklarheit ist also Ihre einzige praktikable Option, wenn Sie die MediaPlayer die Dekodierung vornehmen.

Soweit ich weiß, gewährt das NDK keinen Zugriff auf OpenCORE, obwohl ich zugeben muss, dass ich nur begrenzte Erfahrung mit NDKs habe, ich kann mich also irren. Es gibt sicherlich andere MP3-Decoder ( ffmpeg / mplayer usw.), obwohl unklar ist, wie leicht diese in eine NDK-Bibliothek umgewandelt werden können.

Es kommt also wirklich darauf an, gegen wen man sich verteidigen will. Wenn Sie versuchen, sich gegen den Benutzer zu verteidigen, müssen Sie es wahrscheinlich irgendwie selbst entschlüsseln.

0 Stimmen

MediaPlayer.create(kontext, uri) ??

1voto

crazyscot Punkte 11509

MAD ist eine rein ganzzahlig-arithmetische Decoder-Bibliothek, die gut angesehen zu sein scheint.

(Würde die Dekodierung der Daten auf die SD-Karte nicht ohnehin die Arbeit verlangsamen?)

3 Stimmen

Hinweis: MAD steht unter der GPL.

0voto

twk Punkte 15970

Ich habe das nicht ausprobiert, aber ich glaube, Sie können dem Media Player einen Dateideskriptor geben:

public void setDataSource (FileDescriptor fd, long offset, long length)

Vielleicht können Sie das entschlüsselte mp3 in eine Datei schreiben und mit dem Dateideskriptor abspielen. Dies könnte sogar für das Streaming funktionieren, vorausgesetzt, Sie kennen die Dateilänge im Voraus. Wenn es funktioniert, wäre es viel einfacher als ein Localhost-Webserver zu tun.

0 Stimmen

Vielleicht haben Sie seine Bemerkung nicht bemerkt: "Wir dürfen die MP3-Daten nicht auf die SD-Karte entschlüsseln". Aus Gründen des Urheberrechts und der digitalen Rechteverwaltung (denke ich).

0 Stimmen

Vielleicht haben Sie nicht bemerkt, dass Twk nichts über SD-Karten gesagt hat.

1 Stimmen

Ah, richtig - das habe ich übersehen. Trotzdem könnten Sie vielleicht pipe() verwenden, um einige verknüpfte Dateideskriptoren zu erhalten.

0voto

Arthur Zhixin Liu Punkte 328

So entschlüsseln Sie eine verschlüsselte mp3-Datei, bevor Sie sie abspielen. Es gibt 3 Möglichkeiten:

1,Für die Verwendung von MediaPlayer api:

Nach Android Version M:

you can play mp3 data in byte array directly by:

//Read encrypted mp3:
byte[] bytesAudioData =  Utils.readFile(path); // or from a url.

final byte[] bytesAudioDataDecrypted = YourUtils.decryptFileToteArray(bytesAudioData);

                ByteArrayMediaDataSource bds = new ByteArrayMediaDataSource(bytesAudioDataDecrypted) ;
                mediaPlayer.setDataSource(bds);

Vor Android Version M:

//Read encrypted mp3:
byte[] bytesAudioData =  Utils.readFile(path); // or from a url.

final byte[] bytesAudioDataDecrypted = YourUtils.decryptFileToteArray(bytesAudioData);

 File file =new File(dirofyouraudio.mp3");

                FileOutputStream fos = new FileOutputStream(file);
                fos.write(bytesAudioDataDecrypted);
                Log.d(TAG, "playSound :: file write to temp :: System. nanoTime() ="+System. nanoTime());
                fos.close();

                FileInputStream fis = new FileInputStream(file);
                mediaPlayer.setDataSource(fis.getFD());

2,Für AudioTrack api, wenn Sie verwenden:

  int minBufferSize = AudioTrack.getMinBufferSize(sampleRate,
            AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT);

   AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
            AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT, minBufferSize,
            AudioTrack.MODE_STREAM);

    int i = 0;
    byte[] tempMinBufferToRead = new byte[minBufferSize];

    try {

        byte[] bytesAudioData = Utils.readFile( lastRecordedAudiofilePath);

        bytesAudioData = yourUtils.decryptYourFileToByteArray(bytesAudioData);

        try {
            fileInputStremForPlay = new FileInputStream( lastRecordedAudiofilePath);

            dataInputStreamForPlay = new DataInputStream(new ByteArrayInputStream(bytesAudioData));

            track.play();
            while ((i = dataInputStreamForPlay.read(tempMinBufferToRead, 0, minBufferSize)) > -1) {
                Log.d(LOG_TAG, "mThreadExitFlag= " + mThreadExitFlag);

                if ((mThreadExitFlag == true) || (!audioFilePathInThisThread.equals(lastRecordedAudiofilePath))) {

                    m_handler.post(new Runnable() {
                        public void run() {
                            Log.d(LOG_TAG, "startPlaying: break and reset play button ");

                            startPlayBtnInToolbar.setEnabled(true);
                            stopPlayBtnInToolbar.setEnabled(false);
                        }
                    });

                    break;
                }

                track.write(tempMinBufferToRead, 0, i);
            }

            Log.d(LOG_TAG, "===== Playing Audio Completed ===== ");
            track.stop();

            track.release();

            dataInputStreamForPlay.close();

            fileInputStremForPlay.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

3, Verwenden Sie MediaExtractor, MediaCodec mit AudioTrack, können Sie Datenquelle mit Extraktor eingestellt:

            extractor.setDataSource(this.url);

Oder

            extractor.setDataSource(this.Mediadatasource);

ps: Die Klasse ByteArrayMediaDataSource:

import android.annotation.TargetApi;
import android.media.MediaDataSource;
import android.os.Build;
import java.io.IOException;

import android.content.res.AssetFileDescriptor;
 import android.media.MediaDataSource;
import android.util.Log;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

@TargetApi(Build.VERSION_CODES.M)
public class ByteArrayMediaDataSource extends MediaDataSource {

    private static final String TAG = ByteArrayMediaDataSource.class.getSimpleName();

    private   byte[] data;

    public ByteArrayMediaDataSource(byte []data) {
//        assert data != null;
        this.data = data;
    }
    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {

        if (position >= data.length) {
            return -1; // -1 indicates EOF
        }
        if (position + size > data.length) {
            size -= (position + size) - data.length;
        }

        Log.d(TAG, "MediaDataSource size = "+size);

        System.arraycopy(data, (int)position, buffer, offset, size);
        return size;

    }

    @Override
    public long getSize() throws IOException {

        Log.d(TAG, "MediaDataSource data.length = "+data.length);

        return data.length;
    }

    @Override
    public void close() throws IOException {
        // Nothing to do here
        data =null;
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X