本篇记录Android原生方法播放音视频和调用原生摄像头的使用方法。
- 使用MediaPlayer 来实现音视频的播放、控制。
- 使用CameraManager实现原生摄像头的调用。
音频播放
MediaPlayer 是 Android 提供的一个类,用于播放本地音频和视频文件,支持多种格式,包括 MP3、WAV、MP4 等。MediaPlayer 提供了丰富的控制接口,可以用来处理音频和视频的播放、暂停、停止、跳转、音量调节等操作。
1. 创建布局文件
布局文件中包括音频和视频两部分内容的控件,包括视频播放surfaceView、视频进度条SeekBar、开始/暂停/重播按钮、音量调节SeekBar。
<activity_main.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">
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<SeekBar
android:id="@+id/video_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start audio"/>
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop audio"/>
<Button
android:id="@+id/replay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="replay audio"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置音量"/>
<SeekBar
android:id="@+id/audio_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
2. 实现音频播放
(1)首先通过AudioManager实现音量调节功能,获取AudioManager后调用其中的getStreamVolume()和setStreamVolume获取和设置音频流的音量。
(2)播放音频首先要初始化MediaPlayer对象,本文使用的是本地音频资源文件,将.mp3文件添加到 res/raw文件夹中,然后在初始化MediaPlayer时传入。
(3)然后通过MediaPlayer的start()和stop()方法控制音频播放的开始和暂停,需要注意的时音频才播放前需要调用MediaPlayer的prepare()方法来加载媒体资源准备播放。
<MainActivity.java>
public class MainActivity extends AppCompatActivity {
private SeekBar videoSeekBar;
private SeekBar audioSeekBar;
private Button start;
private Button stop;
private AudioManager audioManager;
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Handler handler = new Handler();
private Runnable updateSeekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 播放音频测试:MediaPlayer:适用于播放本地文件或资源
initAudio();
}
private void initAudio(){
start = findViewById(R.id.start);
stop = findViewById(R.id.stop);
audioSeekBar = findViewById(R.id.audio_seek_bar);
// 初始化音量控制
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
audioSeekBar.setMax(maxVolume);
audioSeekBar.setProgress(currentVolume);
audioSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// 初始化 MediaPlayer 对象
try {
mediaPlayer = MediaPlayer.create(this, R.raw.audio); // mp3 音频
Log.d("=====", "init mediaPlayer! =====");
} catch (Exception e) {
e.printStackTrace();
// 处理初始化失败的情况
Log.d("=====", "mediaPlayer error! =====" + e);
}
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("=====", "start onClick! =====");
// 播放音频
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start(); // 播放音频
Log.d("=====", "start mediaPlayer! =====");
}
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("=====", "stop onClick! =====");
// 播放音频
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop(); // 停止音频
try {
mediaPlayer.prepare(); // 准备播放
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
// mediaPlayer状态监听
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// 播放完成后的操作
mp.release(); // 释放资源
Log.d("=====", "mediaPlayer onCompletion! =====");
}
});
}
@Override
protected void onPause() {
super.onPause();
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
视频播放
播放视频同样是使用MediaPlayer实现,在MainActivity.java中添加添加initVideo()即可。
(1)视频资源同样保存在 res/raw 文件夹下。
(2)通过MediaPlayer的setDataSource()设置视频资源、通过setDisplay()设置视频输出显示的位置、可以用过setLooping()控制视频是否循环播放、getCurrentPosition()可以获取视频播放进度,用于更新SeekBar。
(3)调用MediaPlayer的start()方法继续播放视频、调用pause()暂停播放、调用seekTo(int)跳转视频进度。
<MainActivity.java>
private void initVideo(){
Button start = findViewById(R.id.start);
Button stop = findViewById(R.id.stop);
Button replay = findViewById(R.id.replay);
// 初始化 SurfaceView
surfaceView = findViewById(R.id.surface_view);
surfaceHolder = surfaceView.getHolder();
// 继续播放
start.setOnClickListener(v -> {
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
mediaPlayer.start(); // 继续播放
}
});
// 暂停播放
stop.setOnClickListener(v -> {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause(); // 暂停播放
}
});
// 重新播放
replay.setOnClickListener(v -> {
if (mediaPlayer != null) {
mediaPlayer.seekTo(0); // 将播放位置重置到视频的开头
mediaPlayer.start(); // 重新开始播放
}
});
// 初始化 SeekBar
videoSeekBar = findViewById(R.id.video_seek_bar);
videoSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
Log.d("=====", "surfaceCreated! =====");
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d("=====", "surfaceChanged! =====");
// 初始化 MediaPlayer
mediaPlayer = new MediaPlayer();
try {
// 设置视频源(可以是本地文件或网络资源)
Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.video);
mediaPlayer.setDataSource(MainActivity.this, videoUri);
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.prepare();
mediaPlayer.setLooping(true);
mediaPlayer.start();
// 每秒更新 SeekBar
videoSeekBar.setMax(mediaPlayer.getDuration());
updateSeekBar = new Runnable() {
@Override
public void run() {
if (mediaPlayer != null) {
videoSeekBar.setProgress(mediaPlayer.getCurrentPosition());
handler.postDelayed(this, 1000);
}
}
};
handler.post(updateSeekBar);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
Log.d("=====", "surfaceDestroyed! =====");
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
});
}
调用原生摄像头
1. 创建布局
布局中添加了两个按钮,分别用于打开原生摄像头和关闭原生摄像头,还有一个TextureView用于展示摄像头画面。
2. 功能实现
(1)获取CameraManager实例和摄像头ID。
(2)判断Activity能够获取相机权限的话,调用CameraManager.openCamera()方法传入摄像头ID开启摄像头,该方法包含三个回调,当开启摄像头成功会走到onOpened()回调中,继续实现预览摄像头画面的逻辑。
(3)预览摄像头画面需要船舰捕获请求和预览会话,然后通过CameraCaptureSession的setRepeatingRequest()方法启动预览并展示在TextureView上。
<MainActivity.java>
private void initView() {
// 打开原生摄像头
findViewById(R.id.start_camera).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
startCamera();
} catch (CameraAccessException e) {
Log.d("=====startCamera=====", String.valueOf(e));
e.printStackTrace();
}
}
});
// 关闭原生摄像头
findViewById(R.id.stop_camera).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
releaseCamera();
}
});
mTextureView = findViewById(R.id.preview);
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
Log.d("=====mTextureView=====", "onSurfaceTextureAvailable");
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
// Surface大小变化时可以重新配置预览
Log.d("=====mTextureView=====", "onSurfaceTextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
// Surface销毁时,关闭摄像头
Log.d("=====mTextureView=====", "onSurfaceTextureDestroyed");
return false;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
// Surface更新时可以做处理
Log.d("=====mTextureView=====", "onSurfaceTextureUpdated");
}
});
}
// 打开原生摄像头
private void startCamera() throws CameraAccessException {
Log.d("=====startCamera=====", "");
mTextureView.setVisibility(View.VISIBLE);
CameraManager mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
String mCameraId = mCameraManager.getCameraIdList()[0]; // 获取默认的摄像头ID
Log.d("===cameraIdList===", "cameraIdList = " + Arrays.toString(mCameraManager.getCameraIdList()));
// CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
// 相机权限检查
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
Log.d("=====start_camera=====", "无相机权限!");
return;
}
try {
mCameraManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
// 摄像头已打开,开始预览
mCameraDevice = camera;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
camera.close();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("=====startCamera=====", "CameraManager.openCamera error : " + String.valueOf(e));
}
}
private void createCameraPreviewSession() {
try {
// 创建一个Surface,用于显示预览画面
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface surface = new Surface(texture);
// 创建捕获请求
CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
// 创建预览会话
mCameraDevice.createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (mCameraDevice == null) {
Log.d("=====onConfigured=====", "mCameraDevice is null!");
return;
}
// 启动预览
try {
session.setRepeatingRequest(previewRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("=====setRequest=====", String.valueOf(e));
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
// 配置失败处理
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("=====createSession=====", String.valueOf(e));
}
}
private void releaseCamera(){
mTextureView.setVisibility(View.GONE);
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}