Подтвердить что ты не робот

Декодирование MP3 на Android

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

  • Загрузите собственный зашифрованный формат.
  • Расшифруйте, чтобы получить фрагменты обычных MP3-данных.
  • Декодировать данные MP3 для сырых данных PCM в буфере памяти.
  • Подключите исходные данные PCM к AudioTrack

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

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

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

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

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

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

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

Есть ли у кого-нибудь предложения о том, что мы можем сделать, чтобы ускорить наше декодирование MP3?

- Роб Sz

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

Чтобы дешифровать зашифрованный 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, для AudioTrack api, если вы используете:

  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;
    }
}