android视频截屏&手机录屏实现代码
作者:金威JW 发布时间:2022-12-10 15:20:31
标签:android,视频,截屏
本文介绍了android视频截屏&手机录屏实现代码,分享给大家,希望对大家有帮助
问题
在android中有时候我们需要对屏幕进行截屏操作,单一的截屏操作好解决可以通过activity的顶层view DecorView获取一个bitmap,得到就是当前activity上面的全部视图。
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
Bitmap ret = Bitmap.createBitmap(bmp, 0, 0, dm.widthPixels, dm.heightPixels);
view.destroyDrawingCache();
如果activity中包含一些视频播放器比如SurfaceView GLSurfaceView TextureView,在调用截屏代码会发现播放视频的部分是黑屏的,原因是这几种视频渲染的view通过以上代码拿到的是缓冲区不是真正的图像。
解决办法
android5.0以上系统提供了一个 MediaProjectionManager类来对手机进行录屏操作,也支持获取手机的Image图像的操作,知道了这些我们就可以通过提供的api来进行截屏操作了。
这里通过Service来操作截屏和录屏的api
1.绑定截屏的Service
Intent intent = new Intent(this, ScreenService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
public void onServiceConnected(ComponentName className, IBinder service) {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
ScreenService.RecordBinder binder = (ScreenService.RecordBinder) service;
recordService = binder.getRecordService();
recordService.setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
mButton.setEnabled(true);
mButton.setText(recordService.isRunning() ? "结束" : "开始");
}
2.请求权限 onActivityResult 方法中回调。
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, RECORD_REQUEST_CODE);
成功后
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECORD_REQUEST_CODE && resultCode == RESULT_OK) {
//######## 截屏逻辑 ########
mediaProjection = projectionManager.getMediaProjection(resultCode, data);
recordService.setMediaProject(mediaProjection);
recordService.initImageReader();
}
}
3. 获取截屏
@Override
public void onClick(View view) {
//######## 截屏逻辑 ########
Bitmap bitmap = recordService.getBitmap();
mImageView.setImageBitmap(bitmap);
}
录屏
录屏需要初始化一些录屏参数,输入麦克风类型视频类型,保存路径等
private void initRecorder() {
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(
getSavePath() + System.currentTimeMillis() + ".mp4");
mediaRecorder.setVideoSize(width, height);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mediaRecorder.setVideoFrameRate(30);
try {
mediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
开始录屏
mediaRecorder.start();
保存路径
完整Service代码
public class ScreenService extends Service {
private MediaRecorder mediaRecorder;
private VirtualDisplay virtualDisplay;
private boolean running;
private int width = 720;
private int height = 1080;
private int dpi;
private ImageReader mImageReader;
private MediaProjection mediaProjection;
@Override
public IBinder onBind(Intent intent) {
return new RecordBinder();
}
@Override
public void onCreate() {
super.onCreate();
running = false;
mediaRecorder = new MediaRecorder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
public void setMediaProject(MediaProjection project) {
mediaProjection = project;
}
public boolean isRunning() {
return running;
}
public void setConfig(int width, int height, int dpi) {
this.width = width;
this.height = height;
this.dpi = dpi;
}
/**
* 开始录屏
*
* @return true
*/
public boolean startRecord() {
if (mediaProjection == null || running) {
return false;
}
initRecorder();
createVirtualDisplay();
mediaRecorder.start();
running = true;
return true;
}
/**
* 结束录屏
*
* @return true
*/
public boolean stopRecord() {
if (!running) {
return false;
}
running = false;
mediaRecorder.stop();
mediaRecorder.reset();
virtualDisplay.release();
mediaProjection.stop();
return true;
}
public void setMediaProjection(MediaProjection mediaProjection) {
this.mediaProjection = mediaProjection;
}
/**
* 初始化ImageRead参数
*/
public void initImageReader() {
if (mImageReader == null) {
int maxImages = 2;
mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, maxImages);
createImageVirtualDisplay();
}
}
/**
* 创建一个录屏 Virtual
*/
private void createVirtualDisplay() {
virtualDisplay = mediaProjection
.createVirtualDisplay("mediaprojection", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder
.getSurface(), null, null);
}
/**
* 创建一个ImageReader Virtual
*/
private void createImageVirtualDisplay() {
virtualDisplay = mediaProjection
.createVirtualDisplay("mediaprojection", width, height, dpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader
.getSurface(), null, null);
}
/**
* 初始化保存屏幕录像的参数
*/
private void initRecorder() {
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(
getSavePath() + System.currentTimeMillis() + ".mp4");
mediaRecorder.setVideoSize(width, height);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mediaRecorder.setVideoFrameRate(30);
try {
mediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取一个保存屏幕录像的路径
*
* @return path
*/
public String getSavePath() {
if (Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED)) {
String rootDir = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/" +
"ScreenRecord" + "/";
File file = new File(rootDir);
if (!file.exists()) {
if (!file.mkdirs()) {
return null;
}
}
return rootDir;
} else {
return null;
}
}
/**
* 请求完权限后马上获取有可能为null,可以通过判断is null来重复获取。
*/
public Bitmap getBitmap() {
Bitmap bitmap = cutoutFrame();
if (bitmap == null) {
getBitmap();
}
return bitmap;
}
/**
* 通过底层来获取下一帧的图像
*
* @return bitmap
*/
public Bitmap cutoutFrame() {
Image image = mImageReader.acquireLatestImage();
if (image == null) {
return null;
}
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width +
rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
return Bitmap.createBitmap(bitmap, 0, 0, width, height);
}
public class RecordBinder extends Binder {
public ScreenService getRecordService() {
return ScreenService.this;
}
}
demo下载
github
来源:http://www.jianshu.com/p/2a330e3cfdac


猜你喜欢
- ReferenceWhy using finalizers is a bad idea当在一个类中使用了另外一个实现了IDisposable
- 前言对象关系映射(ORM)已经被使用了很长时间,以解决在编程过程中对象模型与数据模型在关系数据库中不匹配的问题。Dapper是由Stack
- 一个中大型的 Java 项目往往包含若干 JAR 包,这些 JAR 包有着不同的版本号。如果这些 JAR 包单独发布,然后直接通过版本号引用
- SpringBoot 工厂模式自动注入Map一、建立工厂类public interface AnimalFactory { S
- 本文和大家一起做一个带箭头的圆角矩形菜单,大概长下面这个样子:要求顶上的箭头要对准菜单锚点,菜单项按压反色,菜单背景色和按压色可配置。最简单
- 方法一:通过Theme.Translucent@android:style/Theme.Translucent@android:style/
- JAVA用于开发图形界面应用的 SWING 组件包功能强大,使用方便。接下来我们就使用其写一个简单的图形界面小程序:加法计算器。第一步:首先
- SpringBoot整合第三方技术一、整合Junit新建一个SpringBoot项目使用@SpringBootTest标签在test测试包内
- 在使用各类App的时候,尤其是在发布朋友圈、微博的时候,都会选择配图,进入手机相册,选择自己想要的照片,作为发布内容的一部分,这里就简单介绍
- 树的结构说得差不多了,现在我们来说说一种数据结构叫做哈希表(hash table),哈希表有是干什么用的呢?我们知道树的操作的时间复杂度通常
- git忽略的原理:git设置本地忽略必须保证git的远程仓库分支上没有这个要忽略的文件,如果远程分支上存在这个文件,本地在设置ignore
- Spring Boot提供的监控接口,例如:/health、/info等等,实际上除了之前提到的信息,还有其他信息业需要监控:当前处于活跃状
- JVM内存组成结构JVM栈由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示:1)堆所有通过new创建的对象的内存都在堆中分配,其大小可
- 这篇文章主要介绍了Java并发编程预防死锁过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
- 场景描述单例模式对于我们来说一点也不模式,是一个常见的名称,单例模式在程序中的实际效果就是:确保一个程序中只有一个实例,并提供一个全局访问点
- 前言Graalvm通过静态分析提前编译来为Java应用程序构建高度优化的本机可执行文件,这就需要在编译时就知道所有的程序类型,而java中的
- 由于spring和es的集成并不是特别友好,es的高低版本兼容问题、api更新频率高等问题,所以我选择是官网提供的原生Client(Rest
- 在编写脚本的过程中我们会遇到一些小问题比如一个的变量 为了在其他脚本中可以调用 我们需要写成public类型的这样的话在Inspector面
- C#函数式程序设计之作用域在C#中,变量的作用域是严格确定的。其本质是所有代码生存在类的方法中、所有变量只生存于声明它们的模块中或者之后的代
- 一、开发环境:1、windows 7 企业版2、IDEA 143、JDK 1.84、Maven 3.5.25、MariaDB6、SQLYog