繁体   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