簡體   English   中英

Android上的MP3解碼

[英]MP3 Decoding on Android

我們正在實施Android手機程序,播放從互聯網流式傳輸的音頻。 這是我們的大致工作:

  1. 下載自定義加密格式。
  2. 解密得到大量常規MP3數據。
  3. 將MP3數據解碼為內存緩沖區中的原始PCM數據。
  4. 將原始PCM數據傳輸到AudioTrack

到目前為止,我們的目標設備是Droid和Nexus One。 在Nexus One上一切都很棒,但在Droid上MP3解碼速度太慢了。 如果我們把Droid放在負載下,音頻播放就會開始跳過。 我們不允許將MP3數據解碼為SD卡,但我知道這不是我們的問題。

我們沒有編寫自己的MP3解碼器,而是使用了MPADEC( http://sourceforge.net/projects/mpadec/ )。 它是免費的,很容易與我們的程序集成。 我們用NDK編譯它。

在使用各種分析工具進行詳盡分析后,我們確信這個解碼器落后了。

以下是我們正在考慮的選項:

  1. 找到我們可以使用Android NDK編譯的另一個MP3解碼器。 此MP3解碼器必須經過優化才能在移動ARM設備上運行,或者可能使用僅整數數學或其他一些優化來提高性能。

  2. 由於內置的​​Android MediaPlayer服務將采用URL,我們可能能夠在我們的程序中實現一個小型HTTP服務器,並使用解密的MP3為MediaPlayer提供服務。 這樣我們就可以利用內置的MP3解碼器。

  3. 通過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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM