美文网首页编程
Android音频录制&倒放

Android音频录制&倒放

作者: 禄眠 | 来源:发表于2019-12-04 15:20 被阅读0次

介绍

最近经常看到一些倒放挑战,非常有意思,所以也想尝试着做一个。音频录制和播放需要用到MediaRecorderMediaPlayer,其中最麻烦的就是如何实现音频倒放。百度了一大圈,搜到的结果都不尽人意

然后在酷安看到了这个 skkily/audioReversal

其中倒放的功能是使用Rxffmpeg实现的

官方介绍:

RxFFmpeg 是基于 ( FFmpeg 4.0 + X264 + mp3lame + fdk-aac ) 编译的适用于 Android 平台的音视频编辑、视频剪辑的快速处理框架,包含以下功能(视频拼接,转码,压缩,裁剪,片头片尾,分离音视频,变速,添加静态贴纸和gif动态贴纸,添加字幕,添加滤镜,添加背景音乐,加速减速视频,倒放音视频,音频裁剪,##百变魔音##,混音,图片合成视频,视频解码图片等主流特色功能……

这是开源地址:microshow/RxFFmpeg

这么强大的工具虽然有点大材小用,但是他方便啊
使用方式非常简单,安按照文档配置即可,其中倒放的关键命令是:

ffmpeg -i 文件地址 -vf reverse -af areverse -preset superfast 生成文件地址

使用

为了方便使用及管理,我把音频的操作封装成了一个工具类方便使用

package com.wzl.reversalchallenge;

import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaRecorder;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import io.microshow.rxffmpeg.RxFFmpegInvoke;
import io.microshow.rxffmpeg.RxFFmpegSubscriber;

public class MediaUtil {

    private MediaRecorder recorder = null;
    private MediaPlayer player = null;
    private String path;
    private Context mContext;
    private String fileName;
    private String newFileName;
    private Date currentDate;

    // 创建需要传入Context,用于获取Android外部缓存路径
    MediaUtil(Context context) {
        mContext = context;
        path = mContext.getExternalCacheDir().getAbsolutePath() + "/";
    }

    // 开始录音
    void startRecord() {

        // 以时间作为录音文件名
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
        currentDate = Calendar.getInstance().getTime();
        fileName = dateFormat.format(currentDate) + ".mp3";
        recorder = new MediaRecorder();
        // 设置音频源为麦克风
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        // 设置媒体输出格式
        recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        // 设置媒体输出路径
        recorder.setOutputFile(path + fileName);
        // 设置媒体编码格式
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        try {
            recorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        recorder.start();
    }

    // 停止录音
    void stopRecord() {

        recorder.stop();
        recorder.release();
        recorder = null;
        // 生成倒放音频文件
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
        // 为了区分源文件,添加_reverse后缀
        newFileName = dateFormat.format(currentDate) + "_reverse.mp3";
        reverseAudio(fileName, newFileName);
    }

    // 开始播放音频
    void startPlay() {

        player = new MediaPlayer();
        try {
            player.setDataSource(path + newFileName);
            player.prepare();
            player.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 停止播放
    void stopPlay() {
        player.stop();
        player.release();
        player = null;
    }

    // 释放资源,可在onStop()中调用
    void closeAll() {
        if (recorder != null) {
            recorder.release();
            recorder = null;
        }
        if (player != null) {
            player.release();
            player = null;
        }
    }

    // 音频反转
    private void reverseAudio(String fileName, String newFileName) {
        // 这里拼接要注意空格
        String text = "ffmpeg -i " + path + fileName + " -vf reverse -af areverse -preset superfast " + path + newFileName;
        String[] commands = text.split(" ");

        RxFFmpegInvoke.getInstance().runCommandRxJava(commands).subscribe(new RxFFmpegSubscriber() {
            @Override
            public void onFinish() {

            }

            @Override
            public void onProgress(int progress, long progressTime) {

            }

            @Override
            public void onCancel() {

            }

            @Override
            public void onError(String message) {

            }
        });
    }
}

功能全部完成以后,只需要在Activity中调用即可

package com.wzl.reversalchallenge;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;

import com.wzl.reversalchallenge.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ActivityMainBinding binding;
    private MediaUtil mediaUtil;
    private String[] permissions = {Manifest.permission.RECORD_AUDIO};
    private int REQUEST_RECORD_AUDIO_PERMISSION  = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);

        // 申请权限
        checkPermissions();

        // 使用了DataBinding
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mediaUtil = new MediaUtil(this);
        initView();
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.start_record:
                Toast.makeText(this, "点击 start_record", Toast.LENGTH_SHORT).show();
                if (mediaUtil != null) {
                    mediaUtil.startRecord();
                }
                break;
            case R.id.stop_record:
                Toast.makeText(this, "点击 stop_record", Toast.LENGTH_SHORT).show();
                if (mediaUtil != null) {
                    mediaUtil.stopRecord();
                }
                break;
            case R.id.start_play:
                Toast.makeText(this, "点击 start_play", Toast.LENGTH_SHORT).show();
                if (mediaUtil != null) {
                    mediaUtil.startPlay();
                }
                break;
            case R.id.stop_play:
                Toast.makeText(this, "点击 stop_play", Toast.LENGTH_SHORT).show();
                if (mediaUtil != null) {
                    mediaUtil.stopPlay();
                }
                break;
        }
    }

    private void initView() {

        binding.startRecord.setOnClickListener(this);
        binding.stopRecord.setOnClickListener(this);
        binding.startPlay.setOnClickListener(this);
        binding.stopPlay.setOnClickListener(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 释放资源
        mediaUtil.closeAll();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "权限授权成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "权限授权失败,请重新授权!", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 检查权限是否申请
    private void checkPermissions() {
        if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
        }
    }
}

至于界面就不贴了,随便写的,只要功能完成了都好说
我把代码放到Github上了,后续可能还会再修改下界面
rianlu/ReversalChallenge

相关文章

网友评论

    本文标题:Android音频录制&倒放

    本文链接:https://www.haomeiwen.com/subject/odklgctx.html