2010-03-22 3 views
29

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

  1. Загрузите собственный зашифрованный формат.
  2. Расшифруйте, чтобы получить куски обычных данных MP3.
  3. Декодировать данные MP3 для сырых данных PCM в буфере памяти.
  4. Pipe исходных данных 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. Поскольку встроенная служба MediaPlayer для Android будет использовать URL-адреса, мы могли бы реализовать крошечный HTTP-сервер в нашей программе и обслуживать MediaPlayer с расшифрованными MP3-файлами. Таким образом, мы можем использовать встроенный MP3-декодер.

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

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

- Роб Sz

+0

В вашей опции # 2 , Я ожидал бы, что HTTP-накладные расходы увеличит прибыль, получаемую от использования встроенной поддержки потоковой передачи MediaPlayer. – CommonsWare

+2

Если я не ошибаюсь, встроенный 'MediaPlayer' также может передавать из любого' content: // 'URI, который по существу дает вам канал для записи. –

+0

@jleedev: Не могли бы вы рассказать об этом. Звучит очень интересно! – Peterdk

ответ

0

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

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

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

+0

Возможно, вы не заметили его замечания: «Нам не разрешено расшифровывать данные MP3 на SD-карту». По причинам авторского права и управлению цифровыми правами (я думаю). –

+0

Возможно, вы не заметили, что twk ничего не сказал о SD-картах. – CommonsWare

+1

А, да, я пропустил это. Тем не менее, возможно, вы можете использовать pipe(), чтобы получить некоторые связанные файловые дескрипторы. – twk

2

Правильный способ сделать это - создать собственную прошивку и выполнить дешифрование как часть пользовательского кодека 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, неясно.

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

+0

MediaPlayer.create (context, uri) ?? – TacB0sS

1

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

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

+3

В качестве примечания, MAD находится под GPL. – yincrash

0

Для decrypte зашифрованного 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); 

Перед андроида версии 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, для аудиодорожки апи, если вы используете:

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 с аудиодорожки, вы можете установить источник данных с вытяжкой:

  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; 
    } 
} 
Смежные вопросы