android进阶4step2:Android音视频处理——音视频录制与播放

录音

MediaRecoder

Android有一个内置的麦克风,通过它可以捕获音频和存储,或在手机进行播放。

有很多方法可以做到这一点,但最常见的方法是通 过MediaRecorder类。

MediaRecoder常用方法

方法名

描述

setAudioSource()

指定声音源

setOutputFormat()

该方法规定了音频格式中的音频将被存储

setAudioEncoder()

该方法指定要使用的音频编码器 

setOutputFile()

该方法配置文件路径到其中记录的音频将被存储 

stop()

该方法停止记录处理

release()

录音结束后,释放

大致流程:

  • 长按事件:进行录音工作  初始化录音、设置参数、创建临时文件夹保存录音文件,将mic(麦克风录制)好的音频放到之前创建的临时文件中
  • touch抬手事件:录音自动播放(之前录制好的音频) 播放完毕停止、释放、删除资源
  • 单独点击 不起作用 添加标识符来控制

效果:

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/id_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="点击录音"
        android:layout_alignParentBottom="true" />

</RelativeLayout>

 清单文件: 添加权限

    <uses-permission android:name="android.permission.RECORD_AUDIO" />

java代码 重要的两个类 录制MediaRecoder和播放类MediaPlayer  注意:android 6.0 要动态申请权限 

了解更多权限请看:动态获取权限的封装

//长按下按钮,开始录制(文本会变成"请大声说话")
//松开按钮,停止录制(文本复原),播放刚刚录制的声音
public class MainActivity extends AppCompatActivity {

    private Button mRecordBtn;
    private MediaRecorder mRecord;//录音
    private MediaPlayer mPlayer;//播放声音
    private File voiceFile;
    private int REQ_CODE = 1001;
    private volatile boolean flag = false; //权限标识符

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecordBtn = findViewById(R.id.id_btn);
        initEvents();
    }

    private void initEvents() {
        //长按按键的监听事件
        mRecordBtn.setOnLongClickListener(new View.OnLongClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public boolean onLongClick(View v) {
                if (Build.VERSION.SDK_INT >= 23)
                    requestPermission();
                else {
                    flag = true;
                }
                //有权限才能操作
                if (flag) {
                    try {
                        //创建录音对象
                        mRecord = new MediaRecorder();
                        //设置声音来源  MIC 系统麦克风
                        mRecord.setAudioSource(MediaRecorder.AudioSource.MIC);
                        //设置声音格式  MPEG_4 音频、视频的标准格式
                        mRecord.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                        //设置声音的编码格式
                        mRecord.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
                        //准备临时文件用于承载录制的声音
                        voiceFile = File.createTempFile("myvoice", "MP3");
                        //将录制好的声音输出到创建好的临时文件中
                        mRecord.setOutputFile(voiceFile.getAbsoluteFile());

                        //准备录音
                        mRecord.prepare();
                        //开始录制
                        mRecord.start();
                        //更改文本
                        mRecordBtn.setText("录音中...");

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

        });

        //手向上抬起的按键
        mRecordBtn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(final View v, MotionEvent event) {
                if (flag) {
                    if (event.getAction() == MotionEvent.ACTION_UP) {
                        //停止录制
                        mRecord.stop();
                        //释放资源
                        mRecord.release();
                        //更改文本
                        mRecordBtn.setText("点击录音");

                        try {
                            //播放录音
                            mPlayer = new MediaPlayer();
                            //设置播放源
                            mPlayer.setDataSource(voiceFile.getAbsolutePath());
                            //准备
                            mPlayer.prepare();
                            //开始
                            mPlayer.start();
                            //播放结束后的监听
                            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                                @Override
                                public void onCompletion(MediaPlayer mp) {
                                    mp.stop();
                                    mp.release();
                                    //删掉录音的文件
                                    voiceFile.delete();
                                    //重置
                                    flag = false;
                                }

                            });


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

                return false;
            }
        });
    }

    /**
     * android 6.0之后动态申请权限
     */
    private void requestPermission() {
        //如果权限没有被申请
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, REQ_CODE);
        } else {
            //如果已经申请了
            flag = true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQ_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //权限申请成功
                flag = true;
            } else {
                //权限申请失败
                Toast.makeText(this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

简单的语音聊天案例:

  • 长按发送语音
  • 松开将语音保存起来
  • 点击对话框播放语音

布局文件省略了

注意要在清单文件中添加录音权限

    <uses-permission android:name="android.permission.RECORD_AUDIO" />

Msg.java 消息封装类

package com.demo.voicerecorddemo;


import java.io.File;

public class Msg {
    //内容、涉及到 语音、发送/接受的标志
    private String txt;
    private File f;
    private int flag;   //1:本人   -1:对方

    public Msg(String txt, File f, int flag) {
        this.txt = txt;
        this.f = f;
        this.flag = flag;
    }

    public String getTxt() {
        return txt;
    }

    public void setTxt(String txt) {
        this.txt = txt;
    }

    public File getF() {
        return f;
    }

    public void setF(File f) {
        this.f = f;
    }

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }
}

适配器类 ChattingAdapter.java

根据不同的标识来显示不同的布局

//适配器:处理聊天记录样式
public class ChattingAdapter extends BaseAdapter {
    private List<Msg> data;
    private Context ctx;

    public ChattingAdapter(List<Msg> data,Context ctx) {
        this.data = data;
        this.ctx = ctx;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        Msg g = data.get(i);
//        if(view == null){ }
            if(g.getFlag() == 1) {
                view = LayoutInflater.from(ctx).inflate(R.layout.to_msg,null);
            }else{
                view = LayoutInflater.from(ctx).inflate(R.layout.from_msg,null);
            }

        TextView msg = (TextView) view.findViewById(R.id.msg);
        msg.setText(g.getTxt());
        return view;
    }
}

注意聊天类: ChattingActivity.java

声音的录制 与播放 存储逻辑 和权限申请

public class ChattingActivity extends AppCompatActivity {

    private Button voiceBtn;
    private ListView msgList;
    private ChattingAdapter adapter;
    private List<Msg> datas = new ArrayList<>();
    private MediaRecorder recorder;
    private List<File> voices = new ArrayList<>();
    private int REQ_CODE = 1001;
    private volatile boolean flag = false; //权限标识符

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chatting);
        initView();
    }

    private void initView() {
        voiceBtn = findViewById(R.id.vice_btn);
        msgList = findViewById(R.id.msg_list);
        adapter = new ChattingAdapter(datas, this);
        msgList.setAdapter(adapter);

        //长按按钮,开始录音,松开按钮,录音结束
        //发送语音的效果
        //未来在点击了聊天气泡后才播放对应的语音
        voiceBtn.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                //android 6.0 之后才需要动态申请权限
                if (Build.VERSION.SDK_INT >= 23)
                    requestPermission();
                else {
                    flag = true;
                }
                if (flag) {
                    voiceBtn.setText("请开始说话...");
                    //录音
                    //一系列特性的设置
                    try {
                        recorder = new MediaRecorder();
                        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
                        //指定语音文件
                        File tempFile = File.createTempFile("temp", "mp3");
                        recorder.setOutputFile(tempFile.getAbsolutePath());

                        //保存语音文件
                        voices.add(tempFile);

                        //开始录音
                        recorder.prepare();
                        recorder.start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return false;
            }
        });
        voiceBtn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (flag) {//如果没授予权限
                    if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                        voiceBtn.setText("按住说话");
                        //停止录音
                        recorder.stop();
                        recorder.release();
                        flag = false;
                        //更新聊天界面
                        datas.add(new Msg("           ", voices.get(voices.size() - 1), 1));
                        datas.add(new Msg("哈哈哈哈", null, -1));        //伪造一条跟随着的收到的信息
                        adapter.notifyDataSetChanged();
                        //让listView一直滚动到底部
                        msgList.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
                    }
                } else {
                    if (ContextCompat.checkSelfPermission(ChattingActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
                        Toast.makeText(ChattingActivity.this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
                    else {
                        Toast.makeText(ChattingActivity.this, "长按说话", Toast.LENGTH_SHORT).show();
                    }
                }
                return false;
            }
        });

        msgList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Msg m = datas.get(i);
                try {
                    if (m.getFlag() == 1) {
                        final MediaPlayer player = new MediaPlayer();
                        player.setDataSource(m.getF().getAbsolutePath());
                        player.prepare();
                        player.start();

                        //录音播放完毕
                        player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                            @Override
                            public void onCompletion(MediaPlayer mediaPlayer) {
                                player.stop();
                                player.release();
                            }
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 销毁后删除之前保留的录音
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        for (int i = 0; i < voices.size(); i++) {
            voices.get(i).delete();
        }
    }

    /**
     * android 6.0之后动态申请权限
     */
    private void requestPermission() {
        //如果权限没有被申请
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, REQ_CODE);
        } else {
            //如果已经申请了
            flag = true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQ_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //权限申请成功
                flag = true;
            } else {
                //权限申请失败
                Toast.makeText(this, "需要打开录音权限", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

视频录制的简单实现

参考案例:

Android使用MediaRecorder和Camera实现视频录制及播放功能整理

Android多媒体录制--MediaRecorder视频录制

Android (系统+自定义)短视频录制(含暂停继续录制功能) 总结

1. 页面展示两个按钮,分别是开始和停止录制,SurfaceView 位置来展示视频,video_layout.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="9">

        <SurfaceView
            android:id="@+id/id_surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="开始录制" />

        <Button
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="停止录制" />
    </LinearLayout>

</LinearLayout>

2. VideoActivity.java 中为按钮绑定监听器,为 SurfaceView 准备回调接口等初始化工作


/**
 * 为按钮绑定监听器
 * 为SurfaceView 准备回调等初始化操作
 */
public class VideoActivity extends AppCompatActivity implements SurfaceHolder.Callback {

    private Button mBtnStart;//开始录制按钮
    private Button mBtnStop;//停止录制按钮
    private SurfaceView mSurfaceView;//显示视频控件
    private SurfaceHolder mHolder;
    private SurfaceHolder Holder;

    private MediaRecorder mRecorder;//录制视频的类

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉标题栏
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //设置全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //设置横屏显示
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        setContentView(R.layout.video_layout);
        initViews();
        initEvents();
    }

    /**
     * 初始化视图
     */
    private void initViews() {
        mBtnStart = findViewById(R.id.btn_start);
        mBtnStop = findViewById(R.id.btn_stop);
        mSurfaceView = findViewById(R.id.id_surfaceView);
    }

    /**
     * 初始化事件
     */
    private void initEvents() {
        mBtnStart.setOnClickListener(new VideoListener());
        mBtnStop.setOnClickListener(new VideoListener());
        mHolder = mSurfaceView.getHolder();//取得holder
        Log.e("TAG", "此时的mHolder是否为空" + mHolder);
        mHolder.addCallback(this);//添加surfaceView的回调
        // 设置Surface不需要维护自己的缓冲区
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    /**
     * 按钮点击事件
     */
    private class VideoListener implements View.OnClickListener {
        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void onClick(View v) {
            if (v == mBtnStart) {
                startVideo();
            } else {
                stopVideo();
            }
        }
    }
}

3. VideoActivity 中 MediaRecorder 录制与停止的工作

/**
     * 开始录制视频
     */
    private void startVideo() {
        //创建MediaRecorder对象
        mRecorder = new MediaRecorder();
        Camera camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
        camera.unlock();
        mRecorder.setCamera(camera);
        //设置视频录制源为相机
        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        //声音来源
        mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        //设置录制完成后视频的封装格式为THREE_GPP 3gp,MPEG_4 为mp4
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //设置录制视频编码为h263 h264
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        //设置视频录制的分辨率。必须放在设置 编码和格式的后面
        mRecorder.setVideoSize(1920, 1440);
        //设置录制视频的帧率,必须放在设置编码和格式的后面
        mRecorder.setVideoFrameRate(50);
        //设置编码率
        mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024);
        mRecorder.setOrientationHint(90);
        //设置记录会话的最大持续时间(毫秒)
        mRecorder.setMaxDuration(30 * 1000);
        File file = new File(Environment.getExternalStorageDirectory().getPath() + "/video/test.mp4");
        if (file.exists()) {
            file.delete();
        }
        //设置视频文件的输出路径
        mRecorder.setOutputFile(file.getAbsolutePath());
        //让录制的视频放在surfaceView上预览
        mRecorder.setPreviewDisplay(Holder.getSurface());

        try {
            //准备录制
            mRecorder.prepare();
            //开始录制
            mRecorder.start();

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

    }

    /**
     * 停止录制视频
     */
    private void stopVideo() {
        if (mRecorder != null) {
            try {
                mRecorder.stop();
                mRecorder.release();//释放资源
                mRecorder = null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

4. VideoActivity 中为视频展示相关的 SurfaceHolder.Callback


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //将holder ,这个holder为开始在initView里面取得的holder,将它赋给Holder
        Holder = holder;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //将holder ,这个holder为开始在initView里面取得的holder,将它赋给Holder
        Holder = holder;

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mSurfaceView = null;
        mHolder = null;
        mRecorder = null;
    }

5. 最后别忘记申请权限

   <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

android 6.0之后还需要动态申请权限:动态获取权限的封装

猜你喜欢

转载自blog.csdn.net/qq_17846019/article/details/85046076