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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容