安卓音频播放,用原生mediaplayer边缓冲边播放

1,需流媒体服务器才能支持快进到任意位置断点开始缓冲播放,不然只能在缓冲好的进度内播放。如需要实现快进效果,可在拖动进度到未缓冲区域的时候暂停播放,在缓冲回调中判断缓冲进度和进度条进度再行播放。

2,mediaplayer内部缓冲大小写死了,不可能更改。播放到一定位置才会继续缓冲下面部分。断网后无法继续缓冲

3,后台播放可以加唤醒锁防止休眠停止播放,wifi锁防止wifi断开停止缓冲

4,焦点,监听音频焦点控制暂停和恢复播放

5,最后播放完毕的时间偶尔会达不到总时长,需要在完成监听里纠正

下面是播放服务:

public class MusicService extends Service {

    private MyBinder mBinder = new MyBinder();
    public MediaPlayer mMediaPlayer;
    private final VideoProgressUpdater updater = new VideoProgressUpdater();
    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener;
    private AudioManager mAudioMgr;
    private boolean mIsCached;
    private AudioAsyncTask mAudioAsyncTask;

    public interface OnProgressListener {
        void onProgress(int progress);
    }

    public interface OnCompletionCallBack {
        void OnCompletionCallBack(MediaPlayer mp);
    }

    public MusicService() {

    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (null == mMediaPlayer) {
            mMediaPlayer = new MediaPlayer();
        }

    }

    private void initServiceAudioListener() {

        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ECLAIR_MR1) {

            mAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {

                @Override
                public void onAudioFocusChange(int focusChange) {

                    switch (focusChange) {
                        case AudioManager.AUDIOFOCUS_LOSS:
                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                            //失去焦点之后的操作
                            mBinder.pauseMusic();
                            break;
                        case AudioManager.AUDIOFOCUS_GAIN:
                        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
                        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
                        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
                            //获得焦点之后的操作
                            mBinder.playMusic();
                            break;
                    }
                }
            };
        }
    }

    private void requestAudioFocus() {
        if (mAudioMgr == null)
            mAudioMgr = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        int ret = mAudioMgr.requestAudioFocus(mAudioFocusChangeListener,
                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        if (ret != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        }

    }

    private void abandonAudioFocus() {
        if (mAudioMgr != null) {
            mAudioMgr.abandonAudioFocus(mAudioFocusChangeListener);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public class MyBinder extends Binder {

        public OnProgressListener onProgressListener;
        private OnCompletionCallBack mOnCompletionCallBack;

        public void setOnPreparedListener(MediaPlayer.OnPreparedListener onPreparedListener) {
            mMediaPlayer.setOnPreparedListener(onPreparedListener);
        }

//        public void setOnCompletionListener(MediaPlayer.OnCompletionListener onCompletionListener) {
//            mMediaPlayer.setOnCompletionListener(onCompletionListener);
//        }

        public void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener) {
            mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener);
        }

        public void setOnProgressListener(OnProgressListener onProgressListener) {
            this.onProgressListener = onProgressListener;
        }

        public void setCompletionCallBack(OnCompletionCallBack onCompletionCallBack) {

            this.mOnCompletionCallBack = onCompletionCallBack;

            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    updater.stop();

                    if (null != mOnCompletionCallBack) {
                        mOnCompletionCallBack.OnCompletionCallBack(mp);
                    }
                }
            });
        }

        public void initMusic(String url) {
            initMediaPlay(url);
        }

        public void playMusic() {
            if (null != mMediaPlayer && !mMediaPlayer.isPlaying()) {
                mMediaPlayer.start();
                updater.start();
                requestAudioFocus();
            }
        }

        public void pauseMusic() {
            if (null != mMediaPlayer && mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
                updater.stop();
                abandonAudioFocus();
            }
        }

        public boolean isPlaying() {
            return mMediaPlayer.isPlaying();
        }

        public void resetMusic() {
            if (null != mMediaPlayer && !mMediaPlayer.isPlaying()) {
                mMediaPlayer.reset();
                updater.stop();
            }
        }

        public void closeMedia() {
            if (mMediaPlayer != null) {
                mMediaPlayer.stop();
                mMediaPlayer.release();
                mMediaPlayer = null;
                updater.stop();
            }
        }

        public boolean isCached() {
            return mIsCached;
        }

        public int getDuration() {
            return mMediaPlayer.getDuration();
        }

        public void initAudioListener() {
            initServiceAudioListener();
        }

        public int getCurrentPosition() {
            return mMediaPlayer.getCurrentPosition();
        }

        public void seekToPositon(int msec) {
            mMediaPlayer.seekTo(msec);
        }
    }

    private void initMediaPlay(String url) {

        VoiceFileUtils fileUtils = new VoiceFileUtils(this);

        try {

            if (url.startsWith("https")) {
                url = url.replace("https", "http");
            }

            String path = fileUtils.exists(url);
            mMediaPlayer.reset();
            mMediaPlayer.setScreenOnWhilePlaying(true);
            if (path != null) { // 存在缓存文件
                mMediaPlayer.setDataSource(path);
                mIsCached = true;
            } else {
                mAudioAsyncTask = new AudioAsyncTask(fileUtils);
                mAudioAsyncTask.execute(url);

                mMediaPlayer.setDataSource(this, Uri.parse(url));
                mIsCached = false;
            }
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.prepareAsync();

            mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
                @Override
                public void onSeekComplete(MediaPlayer mp) {
                }
            });

            mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    return false;
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private final class VideoProgressUpdater extends Handler {

        public void start() {
            sendEmptyMessage(0);
        }

        public void stop() {
            removeMessages(0);
        }

        @Override
        public void handleMessage(Message msg) {
            mBinder.onProgressListener.onProgress(mMediaPlayer.getCurrentPosition());
            sendEmptyMessageDelayed(0, 1000);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        if (mAudioAsyncTask != null && mAudioAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            mAudioAsyncTask.checkDownFile();
            mAudioAsyncTask.cancel(true);
            mAudioAsyncTask = null;
        }

        if (null != mMediaPlayer) {
            mBinder.closeMedia();
        }
    }
}

下载线程工具类:

public class AudioAsyncTask extends AsyncTask<String, Void, Void> {
    private VoiceFileUtils audioFile;

    public AudioAsyncTask(VoiceFileUtils audioFile) {
        this.audioFile = audioFile;
    }

    public void checkDownFile(){
        audioFile.checkDownFile();
    }

    @Override
    protected Void doInBackground(String... params) {
        HttpURLConnection conn = null;
        try {
            URL url = new URL(params[0]); // 构建URL
            // 构造网络连接
            conn = (HttpURLConnection) url.openConnection();
            // 保存音频文件
            audioFile.exists(params[0]);
            audioFile.saveFile(conn.getInputStream(),params[0]);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            assert conn != null;
            conn.disconnect(); // 断开网络连接
        }
        return null;
    }
}

 文件处理工具类:

/**
 * 文件处理工具类
 */
public class VoiceFileUtils {

public interface DeleteListener {
    public void success();
    public void failed(String error);
}


    private final Context mContext;
    private File file; // 文件根目录
    private File tempFile; // 临时文件,保存下载的文件,每次下载过后,清空

    public VoiceFileUtils(Context context) {
        mContext = context;
        // 构造文件根目录
        file = new File(getSdcardPath());
        if (!file.exists()) { // 判断文件夹是否存在
            // 不存在,则创建文件夹
            file.mkdirs();
        }
    }

    /**
     * 判断本地是否有指定url音频文件的缓存文件
     *
     * @param url 音频文件url
     * @return 缓存文件路径, 如果不存在则返回null
     */
    public String exists(String url) throws IOException {
        // 截取地址, 获取缓存音频文件的名称
        String fileName;
        if (url.startsWith("http://")) {
            fileName = getMediaID(url) == null ? url.substring(url.lastIndexOf("/")) : getMediaID(url);
            // 构建文件
            tempFile = new File(file, fileName);
            Log.i("AUDIO_TOOLS", "缓存路径 = " + tempFile.getAbsolutePath());
            // 如果文件存在,则返回文件路径,如果文件不存在则返回null并且创建文件
            if (tempFile.exists()) {
                return tempFile.getAbsolutePath();
            } else {
                tempFile.createNewFile(); // 创建音频文件
                return null;
            }
        } else {
            fileName = url;
            tempFile = new File(fileName);
            // 如果文件存在,则返回文件路径,如果文件不存在则返回null并且创建文件
            if (tempFile.exists()) {
                return tempFile.getAbsolutePath();
            } else {
                tempFile.createNewFile(); // 创建音频文件
                return null;
            }
        }
    }


    /**
     * 得到下载内容的大小
     *
     * @param downloadUrl
     * @return
     */
    private long getContentLength(String downloadUrl) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(downloadUrl).build();
        try {
            Response response = client.newCall(request).execute();
            if (response != null && response.isSuccessful()) {
                long contentLength = response.body().contentLength();
                response.body().close();
                return contentLength;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    private String getMediaID(String url) {
//        String mediaID = url.split("/media/")[1];
//        return mediaID.split("/")[0];
        String mediaID = url.replaceAll("\\.", "").replaceAll("/", "").replaceAll(":", "") + ".aud";
        Log.i("AUDIO_TOOLS", "缓存id = " + mediaID);
        return mediaID;
    }


    boolean isDownSucess;

    public void checkDownFile() {

        if (null != tempFile && tempFile.exists() && !isDownSucess) {
            tempFile.delete();
        }
    }

    /**
     * 保存音频文件
     *
     * @param is InputStream
     */
    public void saveFile(InputStream is, String url) throws IOException {
        // 构建文件输出流
        FileOutputStream fos = new FileOutputStream(tempFile);
        byte[] b = new byte[1024]; // 构建缓冲区
        // 循环读取数据
        int byteCount; // 每次读取的长度
        while ((byteCount = is.read(b)) != -1) {
            fos.write(b, 0, byteCount); // 将每次读取的数据保存到文件当中
        }
        fos.close(); // 关闭文件输出流
        is.close(); // 关闭输入流

        if (tempFile.exists()) {
            //如果文件存在的话,得到文件的大小
            long downloadLength = tempFile.length();
            long contentLength = getContentLength(url);
            if (contentLength != 0 && contentLength == downloadLength) {
                isDownSucess = true;
            }
        }

    }

    /**
     * 删除文件(多个文件)
     *
     * @param fileNames 文件名的集合
     */
    public void deleteFiles(List<String> fileNames) {
        if (fileNames != null && fileNames.size() > 0) {
            // 循环依次删除每一个文件
            for (String fileName : fileNames) {
                // 构建文件
                File f = new File(file, fileName);
                if (f.exists()) { // 文件存在
                    f.delete(); // 删除文件
                }
            }
        }
    }

    /**
     * 合并所有的音频文件为一个音频文件(后缀名为.amr)
     * 由于.amr的文件的头文件大小固定都是6个字节
     * 所以合并的时候,只需要把除去第一个文件外的头文件(6个字节)然后拼接在一起
     *
     * @param fileNames 文件名称集合
     * @return 合并后的文件File
     */
    public File mergeAudioFiles(List<String> fileNames) throws IOException {
        // 创建临时文件保存合并后的文件
        tempFile = new File(file, System.currentTimeMillis() + "_temp.amr");
        if (tempFile.exists()) { // 如果文件存在,则删除文件
            tempFile.delete(); // 删除文件
        }
        tempFile.createNewFile(); // 重新创建新文件
        // 构建文件输出流,用来写数据到文件中
        FileOutputStream os = new FileOutputStream(tempFile);
        RandomAccessFile raf;
        // 循环依次读取每一个文件的音频信息
        for (int i = 0; i < fileNames.size(); i++) {
            // 以只读模式打开文件流
            raf = new RandomAccessFile(new File(file, fileNames.get(i)), "r");
            // 如果不是第一个文件,则跳过文件头,直接读取音频帧信息
            if (i != 0) {
                raf.seek(6); // 跳过文件头,.amr文件头为固定的6个字节
            }
            // 构建临时缓存数组
            byte[] bytes = new byte[1024];
            int len; // 保存每次读取的数据长度
            // 循环依次读取数据
            while ((len = raf.read(bytes)) != -1) {
                // 将读取的数据写入文件
                os.write(bytes, 0, len);
            }
            raf.close(); // 关闭流
        }
        // 数据写入完成后
        os.close(); // 关闭流
        return tempFile;
    }

    public String audioFileCache = "/audio_cache";

    /**
     * 获取SDCARD的绝对路径
     *
     * @return Environment.getExternalStorageDirectory().getAbsolutePath()
     */
    private String getSdcardPath() {
        return getExternDir(audioFileCache);
    }

    /**
     * 获取保存文件的根目录
     *
     * @return String file.getAbsolutePath()
     */
    public String getFileDirector() {
        return file.getAbsolutePath();
    }

    public File getFile() {
        return file;
    }

    public String getExternDir(String dir) {
        String path = mContext.getCacheDir().getPath();
        if (dir != null) {
            path += dir;
        }

        return path;
    }

    public void recursionDeleteFile(DeleteListener mDeleteListener) {
        File file = new File(getSdcardPath());
        if (file.isFile()) {
            mDeleteListener.failed("error : the path is a file");
            return;
        }
        if (file.isDirectory()) {
            File[] childFile = file.listFiles();
            if (childFile == null || childFile.length == 0) {
                mDeleteListener.failed("the directory is already empty");
                return;
            }
            for (File f : childFile) {
                if (f.exists()) { // 文件存在
                    f.delete(); // 删除文件
                }
            }
        }
        mDeleteListener.success();
    }

}

猜你喜欢

转载自blog.csdn.net/m0_38058826/article/details/86589432