Декодирование MP3 на Android - PullRequest
29 голосов
/ 22 марта 2010

Мы внедряем программу для телефонов Android, которая воспроизводит потоковое аудио из Интернета. Вот примерно то, что мы делаем:

  1. Скачать пользовательский зашифрованный формат.
  2. Расшифруйте, чтобы получить куски обычных данных MP3.
  3. Декодирование данных MP3 в необработанные данные PCM в буфере памяти.
  4. Передача необработанных данных PCM в AudioTrack

На данный момент нашими целевыми устройствами являются Droid и Nexus One. Все отлично работает на Nexus One, но декодирование MP3 слишком медленно на Droid. Воспроизведение аудио начинает пропадать, если мы загрузим Droid. Нам не разрешено декодировать данные MP3 на SD-карту, но я знаю, что это не наша проблема.

Мы не писали свой собственный MP3-декодер, но использовали MPADEC (http://sourceforge.net/projects/mpadec/). Это бесплатно и легко интегрируется с нашей программой. Мы компилируем его с помощью NDK.

После тщательного анализа с помощью различных инструментов профилирования мы убеждены, что именно этот декодер отстает.

Вот варианты, о которых мы думаем:

  1. Найдите другой MP3-декодер, который мы можем скомпилировать с Android NDK. Этот MP3-декодер должен быть либо оптимизирован для работы на мобильных устройствах ARM, либо, возможно, использовать только целочисленную математику или некоторые другие оптимизации для повышения производительности.

  2. Поскольку встроенная служба Android MediaPlayer будет принимать URL-адреса, возможно, мы сможем внедрить крошечный HTTP-сервер в нашу программу и предоставить MediaPlayer дешифрованные файлы MP3. Таким образом, мы можем воспользоваться встроенным MP3-декодером.

  3. Получите доступ к встроенному MP3-декодеру через NDK. Я не знаю, возможно ли это.

Есть ли у кого-нибудь предложения по ускорению декодирования MP3?

- Роб Сз

Ответы [ 4 ]

2 голосов
/ 25 марта 2010

Правильный способ сделать это - создать собственную прошивку и выполнить расшифровку как часть пользовательского кодека OpenCORE.Это, конечно, ограничит вас устройствами, на которые вы можете установить эту прошивку.

Имейте в виду, что остальное это несколько умозрительно.На самом деле мне нужно сделать что-то похожее на то, что вы описываете, но у меня нет времени на решение этой проблемы в течение нескольких месяцев.Итак, я опишу это так, как я подхожу к проблеме.

Одним из решений является решение, описанное в ответе twk.Вам не нужно использовать SD-карту, но вы, вероятно, должны иметь доступный для чтения временный файл в локальном хранилище файлов приложения (getFilesDir()).Загрузите первый блок, расшифруйте его, запишите его как полный читаемый миром файл MP3 (но с подходящим скрытым каталогом / путем) и передайте его в MediaPlayer через setDataSource().Пока он воспроизводится, вы загружаете / дешифруете и настраиваете второй экземпляр MediaPlayer, который начинает воспроизведение, как только заканчивается первый, для максимально плавного перехода, насколько это возможно.Затем вы сбрасываете первый MediaPlayer и повторно используете его с вашим третьим чанком, пинг-понг между ними.

Соответствующее решение будет в комментарии jleedev.Это почти то же самое, за исключением того, что вы предоставляете FileDescriptor через ContentProvider.У этого есть опция, позволяющая вам использовать сокет, который может позволит вам избежать временного файла.Однако ContentProvider сам по себе должен быть общедоступным, и поэтому временный файл с неясным каталогом может на самом деле быть более закрытым.

Если вы беспокоитесь о том, что эти вещи можно прочитатьпод другими процессами, пожалуйста, поймите, что MediaPlayer сам (или, скорее, подсистема OpenCORE) находится в другом процессе.Кроме того, ваш предложенный HTTP-сервер также доступен для чтения на устройстве.Таким образом, защита от неясности - ваш единственный жизнеспособный вариант, если вы собираетесь позволить MediaPlayer выполнить декодирование.

AFAIK, NDK не предоставляет доступ к OpenCORE, хотя я признаюсь, что ограниченный опыт NDK,так что я могу ошибатьсяКонечно, существуют и другие MP3-декодеры (ffmpeg / mplayer и т. Д.), Хотя насколько легко они могут быть преобразованы в библиотеку NDK, неясно.

Итак, все сводится к тому, кто выпытаемся защищаться от.Если вы пытаетесь защититься от пользователя, вам, вероятно, придется как-то его самостоятельно расшифровать.

1 голос
/ 26 марта 2010

MAD - это библиотека декодеров, предназначенная только для целочисленных арифметических вычислений, которая, кажется, хорошо ценится.

(Не замедлит ли вас декодирование данных на SD-карту?)

0 голосов
/ 23 октября 2017

Для расшифровки зашифрованного mp3-файла перед его воспроизведением. Есть 3 способа:

1, для использования MediaPlayer API:

После версии Android 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);

До версии Android 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, для API AudioTrack, если вы используете:

  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, используйте MediaExtractor, MediaCodec с AudioTrack, вы можете установить источник данных с помощью экстрактора:

            extractor.setDataSource(this.url);

Или

            extractor.setDataSource(this.Mediadatasource);

пс: Класс 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;
    }
}
0 голосов
/ 23 марта 2010

Я не пробовал это, но я думаю, что вы можете дать медиаплееру дескриптор файла:

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

Может быть, вы могли бы записать расшифрованный mp3 в файл и воспроизвести его, используя дескриптор файла. Это может даже работать для потоковой передачи, если вы заранее знаете длину файла. Если это сработает, это будет намного проще, чем создание веб-сервера localhost.

...