[英]MP3 Decoding on Android
我們正在實施Android手機程序,播放從互聯網流式傳輸的音頻。 這是我們的大致工作:
到目前為止,我們的目標設備是Droid和Nexus One。 在Nexus One上一切都很棒,但在Droid上MP3解碼速度太慢了。 如果我們把Droid放在負載下,音頻播放就會開始跳過。 我們不允許將MP3數據解碼為SD卡,但我知道這不是我們的問題。
我們沒有編寫自己的MP3解碼器,而是使用了MPADEC( http://sourceforge.net/projects/mpadec/ )。 它是免費的,很容易與我們的程序集成。 我們用NDK編譯它。
在使用各種分析工具進行詳盡分析后,我們確信這個解碼器落后了。
以下是我們正在考慮的選項:
找到我們可以使用Android NDK編譯的另一個MP3解碼器。 此MP3解碼器必須經過優化才能在移動ARM設備上運行,或者可能使用僅整數數學或其他一些優化來提高性能。
由於內置的Android MediaPlayer服務將采用URL,我們可能能夠在我們的程序中實現一個小型HTTP服務器,並使用解密的MP3為MediaPlayer提供服務。 這樣我們就可以利用內置的MP3解碼器。
通過NDK訪問內置MP3解碼器。 我不知道這是否可行。
有沒有人對我們如何加速MP3解碼有任何建議?
- Rob Sz
正確的方法是構建自己的固件並將解密作為自定義OpenCORE編解碼器的一部分。 當然,這會將您限制在可以安裝該固件的設備上。
請記住,其余部分有點投機。 我實際上需要做一些類似你所描述的事情,但我沒有時間來解決這個問題幾個月。 所以,我將以我如何解決問題的方式來描述這一點。
一種解決方案是twk的答案中描述的解決方案。 您不必使用SD卡,但您可能必須在app-local文件存儲區中使用世界可讀的臨時文件( getFilesDir()
)。 下載第一個塊,解密它,將其寫成一個完整的世界可讀的MP3文件(但具有適當的模糊目錄/路徑),並通過setDataSource()
其傳遞給MediaPlayer
。 播放時,您下載/解密並設置第二個MediaPlayer
實例,該實例在第一個實例結束后立即開始播放,以實現盡可能無縫的轉換。 然后重置第一個MediaPlayer
並將其重新用於第三個塊,在兩者之間進行乒乓。
相關的解決方案將在jleedev的評論中。 它幾乎是一樣的,除了你通過ContentProvider
提供FileDescriptor
。 這有一個讓你使用套接字的選項,這可以讓你避免臨時文件。 但是, ContentProvider
本身必須是公共可訪問的,因此具有模糊目錄的臨時文件實際上可能更私密。
如果您擔心其他進程可以讀取這些內容這一事實,請理解MediaPlayer
本身(或者更確切地說,OpenCORE子系統)處於另一個進程中。 此外,您建議的HTTP服務器在設備上也是世界可讀的。 因此,如果您要讓MediaPlayer
進行解碼,那么默默無聞的安全性是您唯一可行的選擇。
AFAIK,NDK不授予訪問OpenCORE的權限,雖然我承認NDK體驗有限,所以我可能錯了。 當然還有其他的MP3解碼器( ffmpeg
/ mplayer
等),雖然這些轉換成NDK庫的方式還不清楚。
所以,它真的歸結為你想要防守的對象。 如果你試圖防御用戶,你可能不得不以某種方式自己解碼它。
MAD是一個僅僅是整數算術的解碼器庫,似乎很受歡迎。
(無論如何,將數據解碼到SD卡上都會減慢你的速度嗎?)
我沒試過這個,但我相信你可以給媒體播放器一個文件描述符:
public void setDataSource (FileDescriptor fd, long offset, long length)
也許您可以將解密的mp3寫入文件並使用文件描述符播放它。 假設您提前知道文件長度,這甚至可能適用於流式傳輸。 如果它工作,它將比做一個localhost網絡服務器簡單得多。
在播放之前解密加密的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);
ps: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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.