Android 多媒体开发 MediaPlayer|SoundPool |MediaRecorder |Camera2| MediaProjectionManager

  android支持音频格式:  mp3、wav、3gp  视频: MP4、g3p 等等....

1. MediaPlayer|SoundPool 使用

  1.1. MediaPlayer 加载资源

/**
     * 1.MediaPlayer create(Context context, Uri uri)
     * MediaPlayer create(Context context, int resid)
     *  上面2个方法每次调用都会 返回 MediaPlayer 对象,如果使用 MediaPlayer 循环播放,使用setDataSource
     *
     * 2.  播放本地assert下文件
     * 
     * 3.  播放网络文件
     *
     * @param view
     */
    public void mediaTest(View view) {
        // 1. 使用
//        MediaPlayer mediaPlayer=  MediaPlayer.create(this,R.raw.hello);
//        mediaPlayer.start();



        //2.  使用setDataSource 循环播放
        MediaPlayer mediaPlayer=new MediaPlayer();
        mediaPlayer.reset();
        // 装载下一首歌曲
        try {
            mediaPlayer.setDataSource("/sdcard/1moviestest/235319.mp3");
            // 准备
      //      mediaPlayer.prepare();
             // 播放
      //      mediaPlayer.start();
            // 播放监听
//            mediaPlayer.setOnCompletionListener();      //播放完  成
//            mediaPlayer.setOnErrorListener();   //出 错
//            mediaPlayer.setOnPreparedListener();
//            mediaPlayer.setOnSeekCompleteListener();   //media调用seek()或seekTo() 方法的时候触发
        } catch (IOException e) {
            e.printStackTrace();
        }


   //3.  播放本地assert下文件
        try {
            MediaPlayer mediaPlayer2=new MediaPlayer();
            // 播放assert目录下
            AssetManager assetManager= getAssets();
            // 打开音乐文
            AssetFileDescriptor afd= assetManager.openFd("abc.mp3");
            //  使用 MediaPlayer 装载指定声音文件
            mediaPlayer2.setDataSource(afd.getFileDescriptor(),
                    afd.getStartOffset(),
                    afd.getLength());
            // 准备声音
            mediaPlayer2.prepare();
            // 播放
            mediaPlayer2.start();

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


        // 4. 播放网络文件
        /***
         * MediaPlayer create(Context context, Uri uri)
         * setDataSource(@NonNull Context context, @NonNull Uri uri)
         */
//         Uri music= Uri.parse("http://localhost:8080/abc.mp3");
//        try {
//            mediaPlayer.setDataSource(MediaPlayer1.this,music);
//            mediaPlayer.prepare();
//            mediaPlayer.start();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

    }

1.2  SoundPool 播放不同音效 

如果需要 播放密集、短促音效的时候,MediaPlayer不合适,
缺点: 1.   资源占用过大 ,延迟时间比较长       2. 不支持同时播放 多个音效   
案例1: 使用  SoundPool 的

public class MainActivity extends AppCompatActivity {
 
    SoundPool.Builder soundPoolBuild=null;
    int soundId;
    SoundPool soundPool;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sv= (SurfaceView) findViewById(R.id.sv);
        // 声音池
        soundPoolBuild=new SoundPool.Builder();
        soundPool= soundPoolBuild.build();
        // 加载声音到声音池
        soundId = soundPool.load(this,R.raw.abc,1);
 
    }
	 public void playSound(View view) {
  /**  
   *   play(int soundID, float leftVolume, float rightVolume,
            int priority, int loop, float rate)
   * 
   *   播放哪个声音
   *   leftVolume、rightVolume 指定左、右的音量
   *   priority  : 指定播放声音优先级,数值越大,优先级越高
   *   loop : 指定是否循环     0不循环 -1循环
   *   rate: 播放比率  数值从0.5到 2   1为正常 
   */
 soundPool.play(soundId,1.0f,1.0f,0,-1,1.0f);
 
    }
	
}

案例2 : 使用  SoundPool 的 load多个资源, 点击哪个按钮播放哪个音效,同时点击多个播放多个

  比如可以同时加载load 20个音效,以后再程序中按照音效ID播放

    // 定义一个SoundPool
    private SoundPool soundPool;
    private HashMap<Integer, Integer> soundMap = new HashMap<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_player1);

        Button bombBn = findViewById(R.id.bomb);
        Button shotBn = findViewById(R.id.shot);
        Button arrowBn = findViewById(R.id.arrow);
        AudioAttributes attr = new AudioAttributes.Builder().setUsage(
                AudioAttributes.USAGE_GAME) // 设置音效使用场景
                // 设置音效的类型
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();
        soundPool = new SoundPool.Builder().setAudioAttributes(attr) // 设置音效池的属性
                .setMaxStreams(10) // 设置最多可容纳10个音频流
                .build();  // ①
        // 使用load方法加载指定的音频文件,并返回所加载的音频ID
        // 此处使用HashMap来管理这些音频流
        soundMap.put(1, soundPool.load(this, R.raw.bomb, 1));  // ②
        soundMap.put(2, soundPool.load(this, R.raw.shot, 1));
        soundMap.put(3, soundPool.load(this, R.raw.arrow, 1));
        // 定义一个按钮的单击监听器
        View.OnClickListener listener = source -> {
            // 判断哪个按钮被单击
            switch (source.getId())
            {
                case R.id.bomb:
                    soundPool.play(soundMap.get(1), 1f, 1f, 0, 0, 1f);  // ③
                    break;
                case R.id.shot:
                    soundPool.play(soundMap.get(2), 1f, 1f, 0, 0, 1f);
                    break;
                case R.id.arrow:
                    soundPool.play(soundMap.get(3), 1f, 1f, 0, 0, 1f);
                    break;
            }
        };
        bombBn.setOnClickListener(listener);
        shotBn.setOnClickListener(listener);
        arrowBn.setOnClickListener(listener);

    }

布局文件: 

<?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"
    tools:context=".MediaPlayer1"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MediaPalyer的create播放"
        android:textAllCaps="false"
        android:onClick="mediaTest"
        />

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/bomb"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/bomb"
            />
        <Button
            android:id="@+id/shot"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/shot"
            />
        <Button
            android:id="@+id/arrow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/arrow"
            />
    </LinearLayout>


</LinearLayout>

3.  录制音频Android

package com.denganzhi.camera2;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

/***
 *  录制音频Android
 */
public class MediaRecorder1Activity extends AppCompatActivity {

    // 定义界面上的两个按钮
    private ImageButton recordBn;
    private ImageButton stopBn;
    // 系统的音频文件
    private File soundFile;
    private MediaRecorder mRecorder;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_recorder1);
        // 获取程序界面中的两个按钮
        recordBn = findViewById(R.id.record);
        stopBn = findViewById(R.id.stop);
        stopBn.setEnabled(false);
        requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO,
                Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x123);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        if (requestCode == 0x123 && grantResults.length == 2
                && grantResults[0] == PackageManager.PERMISSION_GRANTED
                && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
            View.OnClickListener listener = source ->
            {
                switch (source.getId()) {
                    // 单击录音按钮
                    case R.id.record:
                        if (!Environment.getExternalStorageState()
                                .equals(Environment.MEDIA_MOUNTED)) {
                            Toast.makeText(MediaRecorder1Activity.this,
                                    "SD卡不存在,请插入SD卡!",
                                    Toast.LENGTH_SHORT).show();
                            return;
                        }
                        // 创建保存录音的音频文件, 把改成mp3 就可以用酷狗打开了
                        soundFile = new File(Environment.getExternalStorageDirectory()
                                .toString() + "/1sound.amr");
                        mRecorder = new MediaRecorder();
                        // 设置录音的声音来源
                        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        // 设置录制的声音的输出格式(必须在设置声音编码格式之前设置)
                        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                        // 设置声音编码格式
                        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                        mRecorder.setOutputFile(soundFile.getAbsolutePath());
                        try {
                            mRecorder.prepare();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        // 开始录音
                        mRecorder.start();  // ①
                        recordBn.setEnabled(false);
                        stopBn.setEnabled(true);
                        break;
                    // 单击停止录制按钮
                    case R.id.stop:
                        if (soundFile != null && soundFile.exists()) {


                            // 停止录音
                            mRecorder.stop();  // ②
                            // 释放资源
                            mRecorder.release();  // ③
                            mRecorder = null;
                            recordBn.setEnabled(true);
                            stopBn.setEnabled(false);
                        }
                        break;
                }
            };
            // 为两个按钮的单击事件绑定监听器
            recordBn.setOnClickListener(listener);
            stopBn.setOnClickListener(listener);
        }
    }

    @Override
    public void onDestroy()
    {
        if (soundFile != null && soundFile.exists()) {
            // 停止录音
            mRecorder.stop();
            // 释放资源
            mRecorder.release();
            mRecorder = null;
        }
        super.onDestroy();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="horizontal">
    <ImageButton
        android:id="@+id/record"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/record"
        />
    <ImageButton
        android:id="@+id/stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/stop"
        />
</LinearLayout>

4.    相机预览拍照

1.    textureView.setSurfaceTextureListener(mSurfaceTextureListener);
就表示可以接收外界的绘制指令了,SurfaceTexture会以GL纹理信息更新到TextureView对应的HardwareLayer中
  回调方法 :onSurfaceTextureAvailable
2. 打开相机:    manager.openCamera(mCameraId, stateCallback, null); // ①
stateCallback:  摄像头打开、断开、错误监听 
  摄像头被打开的时候获取  cameraDevice       
3. cameraDevice.createCaptureSession: 表示  CameraCaptureSession 创建成功,开始预览   CameraCaptureSession   Android设备 和  摄像头建立 pipe 
          通过 Android设备通过 CaptureRequest.Builder  设置
         1.  把是需要拍照 、录屏、  预览、
         2. 对焦、曝光 模式传递给相机
           CaptureRequest 代表一次捕获  
   previewRequestBuilder.addTarget(new Surface(texture));  // previewRequestBuilder 获取图形被显示在texture上  预览
   
        captureRequestBuilder.addTarget(imageReader.getSurface());    // 获取捕获图片到 ImageReader 上
4.    captureSession
captureSession.setRepeatingRequest(previewRequest, null, null);  // ④   表示预览 
captureSession.capture()  :   表示进行拍照

package com.denganzhi.camera2;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *  相机预览拍照
 */
public class Camera2PreView extends AppCompatActivity {

        private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

        static {
            ORIENTATIONS.append(Surface.ROTATION_0, 90);
            ORIENTATIONS.append(Surface.ROTATION_90, 0);
            ORIENTATIONS.append(Surface.ROTATION_180, 270);
            ORIENTATIONS.append(Surface.ROTATION_270, 180);
        }
        // 定义界面上根布局管理器
        private FrameLayout rootLayout;
        // 定义自定义的AutoFitTextureView组件,用于预览摄像头照片
        private AutoFitTextureView textureView;
        // 摄像头ID(通常0代表后置摄像头,1代表前置摄像头)
        private String mCameraId = "0";
        // 定义代表摄像头的成员变量
        private CameraDevice cameraDevice;
        // 预览尺寸
        private Size previewSize;
        private CaptureRequest.Builder previewRequestBuilder;
        // 定义用于预览照片的捕获请求
        private CaptureRequest previewRequest;
        // 定义CameraCaptureSession成员变量
        private CameraCaptureSession captureSession;
        private ImageReader imageReader;
        private final TextureView.SurfaceTextureListener mSurfaceTextureListener
                = new TextureView.SurfaceTextureListener()
        {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture texture
                    , int width, int height)
            {
                // 当TextureView可用时,打开摄像头
                openCamera(width, height);
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture texture
                    , int width, int height)
            {
                configureTransform(width, height);
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture texture)
            {
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture texture)
            {
            }
        };
        private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback()
        {
            //  摄像头被打开时激发该方法
            @Override
            public void onOpened(@NonNull CameraDevice cameraDevice)
            {
                Camera2PreView.this.cameraDevice = cameraDevice;
                // 开始预览
                createCameraPreviewSession();  // ②
            }

            // 摄像头断开连接时激发该方法
            @Override
            public void onDisconnected(CameraDevice cameraDevice)
            {
                cameraDevice.close();
                Camera2PreView.this.cameraDevice = null;
            }

            // 打开摄像头出现错误时激发该方法
            @Override
            public void onError(CameraDevice cameraDevice, int error)
            {
                cameraDevice.close();
                Camera2PreView.this.cameraDevice = null;
                Camera2PreView.this.finish();
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            rootLayout = findViewById(R.id.root);
            requestPermissions(new String[]{Manifest.permission.CAMERA}, 0x123);

            //  获取设备头列表 与摄像头 特征

        }

        @Override
        public void onRequestPermissionsResult(int requestCode,
                                               @NonNull String[] permissions, @NonNull int[] grantResults)
        {
            if (requestCode == 0x123 && grantResults.length == 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 创建预览摄像头图片的TextureView组件
                textureView = new AutoFitTextureView(Camera2PreView.this, null);
                // 为TextureView组件设置监听器
                textureView.setSurfaceTextureListener(mSurfaceTextureListener);
                rootLayout.addView(textureView);
                findViewById(R.id.capture).setOnClickListener(view -> captureStillPicture());
            }
        }

        private void captureStillPicture()
        {
            try {
                if (cameraDevice == null) {
                    return;
                }
                // 创建作为拍照的CaptureRequest.Builder
                CaptureRequest.Builder captureRequestBuilder = cameraDevice
                        .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                // 将imageReader的surface作为CaptureRequest.Builder的目标
                captureRequestBuilder.addTarget(imageReader.getSurface());
                // 设置自动对焦模式
                captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                // 设置自动曝光模式
                captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                // 获取设备方向
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                // 根据设备方向计算设置照片的方向
                captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                        ORIENTATIONS.get(rotation));
                // 停止连续取景
                captureSession.stopRepeating();
                // 捕获静态图像
                captureSession.capture(captureRequestBuilder.build(),
                        new CameraCaptureSession.CaptureCallback()  // ⑤
                        {
                            // 拍照完成时激发该方法
                            @Override
                            public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                                           @NonNull CaptureRequest request, @NonNull TotalCaptureResult result)
                            {
                                try {
                                    // 重设自动对焦模式
                                    previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                                            CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
                                    // 设置自动曝光模式
                                    previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                                            CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                                    // 打开连续取景模式
                                    captureSession.setRepeatingRequest(previewRequest, null, null);
                                } catch (CameraAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        }, null);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
        // 根据手机的旋转方向确定预览图像的方向
        private void configureTransform(int viewWidth, int viewHeight) {
            if (null == previewSize) {
                return;
            }
            // 获取手机的旋转方向
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            Matrix matrix = new Matrix();
            RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
            RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
            float centerX = viewRect.centerX();
            float centerY = viewRect.centerY();
            // 处理手机横屏的情况
            if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
                bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
                matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
                float scale = Math.max(
                        (float) viewHeight / previewSize.getHeight(),
                        (float) viewWidth / previewSize.getWidth());
                matrix.postScale(scale, scale, centerX, centerY);
                matrix.postRotate(90 * (rotation - 2), centerX, centerY);
            }
            // 处理手机倒置的情况
            else if (Surface.ROTATION_180 == rotation)
            {
                matrix.postRotate(180, centerX, centerY);
            }
            textureView.setTransform(matrix);
        }
        // 打开摄像头
        private void openCamera(int width, int height)
        {
            setUpCameraOutputs(width, height);
            configureTransform(width, height);
            CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);


            // 创建一个空的CameraInfo对象,用于获取摄像头信息
//            try {
//                Camera.CameraInfo cameraInfo= new Camera.CameraInfo();
//                String[] ids= manager.getCameraIdList();
//                for (int i=0;i<ids.length;i++){
//
//                }
//            } catch (CameraAccessException e) {
//                e.printStackTrace();
//            }


            try {
                // 如果用户没有授权使用摄像头,直接返回
                if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                // 打开摄像头
                /**
                 *
                 *  mCameraId  为 0 表示打开后置摄像头
                 *  hander参数作用: 打开摄像头、创建cameraSession、预览、拍照 都没有传入handler参数,这意味着直接在主线程中完成相应 Callback任务
                 *   这可能导致程序响应变慢, 实际开发中传入 Handler参数, 让handler来执行 callback 任务,提高 响应效率
                 */
                manager.openCamera(mCameraId, stateCallback, null); // ①
            }
            catch (CameraAccessException e)
            {
                e.printStackTrace();
            }
        }
        private void createCameraPreviewSession()
        {
            try
            {
                SurfaceTexture texture = textureView.getSurfaceTexture();
                texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
                Surface surface = new Surface(texture);
                // 创建作为预览的CaptureRequest.Builder
                // 改方法支持    TEMPLATE_PREVIEW  预览
                //  TEMPLATE_RECORD  拍摄视频
                //  TEMPLATE_STILL_CAPTURE  拍照
                previewRequestBuilder = cameraDevice
                        .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

                // 将textureView的surface作为CaptureRequest.Builder的目标
                previewRequestBuilder.addTarget(new Surface(texture));
                // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
                cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()),
                        new CameraCaptureSession.StateCallback() // ③
                        {
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession)
                            {
                                // 表示  CameraCaptureSession 创建成功,开始预览
                                // 如果摄像头为null,直接结束方法
                                if (null == cameraDevice)
                                {
                                    return;
                                }
                                // 当摄像头已经准备好时,开始显示预览
                                captureSession = cameraCaptureSession;
                                // 设置自动对焦模式
                                previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                // 设置自动曝光模式
                                previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                                // 开始显示相机预览
                                previewRequest = previewRequestBuilder.build();
                                try {
                                    // 设置预览时连续捕获图像数据
                                    captureSession.setRepeatingRequest(previewRequest, null, null);  // ④
                                }
                                catch (CameraAccessException e)
                                {
                                    e.printStackTrace();
                                }
                            }
                            @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession)
                            {
                                Toast.makeText(Camera2PreView.this, "配置失败!",
                                        Toast.LENGTH_SHORT).show();
                            }
                        }, null);
            }
            catch (CameraAccessException e)
            {
                e.printStackTrace();
            }
        }

        // 获取相机参数
    //  获取摄像头支持的最大尺寸 、 获取最佳的预览尺寸
        private void setUpCameraOutputs(int width, int height)
        {
            CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            try {
                // 获取指定摄像头的特性
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId);
                // 获取摄像头支持的配置属性
                // 这里的 代替 Camera1 的  CameInfo获取摄像头信息


                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.
                        SCALER_STREAM_CONFIGURATION_MAP);

                // 获取摄像头支持的最大尺寸
                Size largest = Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                        new CompareSizesByArea());
                // 创建一个ImageReader对象,用于获取摄像头的图像数据
                imageReader = ImageReader.newInstance(largest.getWidth(),
                        largest.getHeight(), ImageFormat.JPEG, 2);
                imageReader.setOnImageAvailableListener(reader -> {
                    // 当照片数据可用时激发该方法
                    // 获取捕获的照片数据
                    Image image = reader.acquireNextImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[buffer.remaining()];
                    // 使用IO流将照片写入指定文件
                    File file = new File(getExternalFilesDir(null), "pic.jpg");
                    buffer.get(bytes);
                    try (
                            FileOutputStream output = new FileOutputStream(file))
                    {
                        output.write(bytes);
                        Toast.makeText(Camera2PreView.this, "保存: "
                                + file, Toast.LENGTH_SHORT).show();
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        image.close();
                    }
                },null);
                // 获取最佳的预览尺寸
                previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                        width, height, largest);
                // 根据选中的预览尺寸来调整预览组件(TextureView的)的长宽比
                int orientation = getResources().getConfiguration().orientation;
                if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    textureView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight());
                } else {
                    textureView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());
                }
            }
            catch (CameraAccessException e)
            {
                e.printStackTrace();
            }
            catch (NullPointerException e)
            {
                System.out.println("出现错误。");
            }
        }
        private static Size chooseOptimalSize(Size[] choices
                , int width, int height, Size aspectRatio)
        {
            // 收集摄像头支持的打过预览Surface的分辨率
            List<Size> bigEnough = new ArrayList<>();
            int w = aspectRatio.getWidth();
            int h = aspectRatio.getHeight();
            for (Size option : choices)
            {
                if (option.getHeight() == option.getWidth() * h / w &&
                        option.getWidth() >= width && option.getHeight() >= height)
                {
                    bigEnough.add(option);
                }
            }
            // 如果找到多个预览尺寸,获取其中面积最小的。
            if (bigEnough.size() > 0)
            {
                return Collections.min(bigEnough, new CompareSizesByArea());
            }
            else
            {
                System.out.println("找不到合适的预览尺寸!!!");
                return choices[0];
            }
        }
        // 为Size定义一个比较器Comparator
        static class CompareSizesByArea implements Comparator<Size>
        {
            @Override
            public int compare(Size lhs, Size rhs)
            {
                // 强转为long保证不会发生溢出
                return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                        (long) rhs.getWidth() * rhs.getHeight());
            }
        }
    }

自定义控件用于渲染预览画面:

package com.denganzhi.camera2;

import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;

public class AutoFitTextureView extends TextureView
{
    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    public AutoFitTextureView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    void setAspectRatio(int width, int height)
    {
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight)
        {
            setMeasuredDimension(width, height);
        }
        else
        {
            if (width < height * mRatioWidth / mRatioHeight)
            {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            }
            else
            {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }
}

布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/root">

     <com.denganzhi.camera2.AutoFitTextureView
         android:id="@+id/texture"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />

     <ImageButton
         android:id="@+id/capture"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="end|bottom"
         android:src="@drawable/capture" />
</FrameLayout>

5.   打开设置头屏幕录制  音频|视频

 MediaRecorder:
   1. 调用  MediaRecorder 设置  
                        // 设置从麦克风采集声音
                        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        // 设置从摄像头采集图像
                        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                        //设置视频文件的输出格式 
                        // 设置声音编码格式
                        // 设置图像编码格式 .....
    2.      // 指定使用SurfaceView来预览视频
                        mRecorder.setPreviewDisplay(sView.getHolder().getSurface()) ; // ①

package com.denganzhi.camera2;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

/***
 *  打开设置头屏幕录制  音频|视频
 */
public class MediaRecorderActivity extends AppCompatActivity {

    private ImageButton recordBn;
    private ImageButton stopBn;
    // 系统的视频文件
    private File videoFile;
    private MediaRecorder mRecorder;
    // 显示视频预览的SurfaceView
    private SurfaceView sView;
    // 记录是否正在进行录制
    private boolean isRecording;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_recorder);
        // 获取程序界面中的两个按钮
        recordBn = findViewById(R.id.record);
        stopBn = findViewById(R.id.stop);
        // 让stopBn按钮不可用
        stopBn.setEnabled(false);
        requestPermissions(new String[]{Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO,
                Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x123);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        if (requestCode == 0x123 && grantResults.length == 3
                && grantResults[0] == PackageManager.PERMISSION_GRANTED
                && grantResults[1] == PackageManager.PERMISSION_GRANTED
                && grantResults[2] == PackageManager.PERMISSION_GRANTED) {
            View.OnClickListener listener = source ->
            {
                switch (source.getId()) {
                    // 单击录制按钮
                    case R.id.record:
                        if (!Environment.getExternalStorageState().equals(
                                android.os.Environment.MEDIA_MOUNTED)) {
                            Toast.makeText(MediaRecorderActivity.this,"SD卡不存在,请插入SD卡!",
                                    Toast.LENGTH_SHORT).show();
                            return;
                        }
                        // 创建保存录制视频的视频文件
                        videoFile = new File(Environment.getExternalStorageDirectory()
                                .toString() + "/myvideo.mp4");
                        // 创建MediaRecorder对象
                        mRecorder = new MediaRecorder();
                        mRecorder.reset();
                        // 设置从麦克风采集声音
                        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        // 设置从摄像头采集图像
                        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                        // 设置视频文件的输出格式
                        // 必须在设置声音编码格式、图像编码格式之前设置
                        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                        // 设置声音编码格式
                        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
                        // 设置图像编码格式
                        mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
                        // 设置视频尺寸,此处要求摄像头本身支持该尺寸,否则会在调用MediaRecorder的play方法时出现异常
//						mRecorder.setVideoSize(640, 560);
                        mRecorder.setVideoSize(1920, 1080);
                        // 每秒 12帧
                        mRecorder.setVideoFrameRate(12);
                        mRecorder.setOutputFile(videoFile.getAbsolutePath());
                        // 指定使用SurfaceView来预览视频
                        mRecorder.setPreviewDisplay(sView.getHolder().getSurface()) ; // ①
                        try {
                            mRecorder.prepare();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        // 开始录制
                        mRecorder.start();
                        System.out.println("---recording---");
                        // 让recordBn按钮不可用
                        recordBn.setEnabled(false);
                        // 让stopBn按钮可用
                        stopBn.setEnabled(true);
                        isRecording = true;
                        break;
                    // 单击停止按钮
                    case R.id.stop:
                        // 如果正在进行录制
                        if (isRecording) {
                            // 停止录制
                            mRecorder.stop();
                            // 释放资源
                            mRecorder.release();
                            mRecorder = null;
                            // 让recordBn按钮可用
                            recordBn.setEnabled(true);
                            // 让stopBn按钮不可用
                            stopBn.setEnabled(false);
                        }
                        break;
                }
            };
            // 为两个按钮的单击事件绑定监听器
            recordBn.setOnClickListener(listener);
            stopBn.setOnClickListener(listener);
            // 获取程序界面中的SurfaceView
            sView = findViewById(R.id.sView);
            // 设置分辨率
            sView.getHolder().setFixedSize(320, 280);
            // 设置该组件让屏幕不会自动关闭
            sView.getHolder().setKeepScreenOn(true);
        }
    }
}

activity_media_recorder 布局文件代码如下 :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 显示视频预览的SurfaceView -->
    <SurfaceView
        android:id="@+id/sView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center|bottom"
        android:gravity="center_horizontal"
        android:orientation="horizontal">
        <ImageButton
            android:id="@+id/record"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/record" />
        <ImageButton
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/stop" />
    </LinearLayout>
</FrameLayout>

6.   屏幕捕捉,    就是录屏功能,这里什么也没有实现

1. projectionManager = (MediaProjectionManager) getSystemService(
                Context.MEDIA_PROJECTION_SERVICE);   
            屏幕捕捉核心 api :MediaProjectionManager
2.     Intent intent = projectionManager.createScreenCaptureIntent();  // ②   创建intent
            startActivityForResult(intent, CAPTURE_CODE);  // ③  开始捕捉
            
    3. mediaProjection = projectionManager.getMediaProjection(resultCode, data);  // ④
            virtualDisplay = mediaProjection.createVirtualDisplay("屏幕捕捉",
                    displayWidth, displayHeight, screenDensity,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    surface, null /* Callback */, null /*Handler*/);
                    
                // 捕捉图像显示在surfaceView上 

package com.denganzhi.camera2;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import android.widget.ToggleButton;

/**
 *  就是录屏功能,这里什么也没有实现
 *  屏幕捕捉
 */
public class CaptureScreenActivity extends AppCompatActivity {

    public static final int CAPTURE_CODE = 0x123;
    private MediaProjectionManager projectionManager;
    private int screenDensity;
    private int displayWidth = 720;
    private int displayHeight = 1080;
    private boolean screenSharing;
    private MediaProjection mediaProjection;
    private VirtualDisplay virtualDisplay;
    private Surface surface;
    private SurfaceView surfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_capture_screen);
        // 获取屏幕分辨率
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        screenDensity = metrics.densityDpi;
        // 获取应用界面上的SurfaceView组件
        surfaceView = findViewById(R.id.surface);
        surface = surfaceView.getHolder().getSurface();
        // 控制界面上的SurfaceView组件的宽度和高度
        ViewGroup.LayoutParams lp = surfaceView.getLayoutParams();
        lp.height = displayHeight;
        lp.width = displayWidth;
        surfaceView.setLayoutParams(lp);
        // 获取MediaProjectionManager管理器
        projectionManager = (MediaProjectionManager) getSystemService(
                Context.MEDIA_PROJECTION_SERVICE); // ①
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (mediaProjection != null) {
            mediaProjection.stop();
            mediaProjection = null;
        }
    }

    // 当用户单击开关按钮时激发该方法
    public void onToggleScreenShare(View view)
    {
        if (((ToggleButton)view).isChecked())
        {
            shareScreen();
        }
        else
        {
            stopScreenSharing();
        }
    }

    private void shareScreen()
    {
        screenSharing = true;
        if (surface == null) {
            return;
        }
        if (mediaProjection == null) {
            Intent intent = projectionManager.createScreenCaptureIntent();  // ②
            startActivityForResult(intent, CAPTURE_CODE);  // ③
        }
    }

    private void stopScreenSharing()
    {
        screenSharing = false;
        if (virtualDisplay == null) {
            return;
        }
        virtualDisplay.release();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == CAPTURE_CODE) {
            // 如果resultCode不等于RESULT_OK,表明用户拒绝了屏幕捕捉
            if (resultCode != Activity.RESULT_OK) {
                Toast.makeText(this, "用户取消了屏幕捕捉",
                        Toast.LENGTH_SHORT).show();
                return;
            }
            mediaProjection = projectionManager.getMediaProjection(resultCode, data);  // ④
            virtualDisplay = mediaProjection.createVirtualDisplay("屏幕捕捉",
                    displayWidth, displayHeight, screenDensity,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    surface, null /* Callback */, null /*Handler*/);
        }
    }
}

布局文件xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal|top"
    android:orientation="vertical">

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onToggleScreenShare"
        android:text="屏幕捕捉"
        android:textColor="#ff0000"/>

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

需要权限: 

   <!-- 授予该程序录制声音的权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- 授予该程序使用摄像头的权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- 授予使用外部存储器的权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 源码地址:https://download.csdn.net/download/dreams_deng/12457830

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值