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