Android:截屏/视频截图

需求描述

实现截取Android应用当前界面的功能,需包含界面中视频(此博客的参考代码以存储在设备本地的视频为例,未检验在线视频的情况)当前的播放帧截图。

调研准备

首先应用需要获取设备存储的读写权限,需要在AndroidManifest.xml中加上请求权限的配置代码:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

此外,Android原生的视频播放组件VideoView不支持修改视频的分辨率(视频分辨率与容器宽高不一致时,需要让视频拉伸填充容器),因此需要自己封装一个继承了VideoView的组件;在项目中新建一个MyVideoView.java,内容如下:

package XXX;

import android.content.Context;
import android.media.MediaPlayer;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.VideoView;
import com.lzy.okgo.utils.HttpUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class MyVideoView extends VideoView {
  private static final String TAG = "####MyVideoView ";
  
  // 记录当前播放视频的路径(用于截取播放帧)
  private String currentVideoUrl;
  
  public MyVideoView(Context paramContext) {
    super(paramContext);
  }
  
  public MyVideoView(Context paramContext, AttributeSet paramAttributeSet) {
    super(paramContext, paramAttributeSet);
  }
  
  public MyVideoView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
    super(paramContext, paramAttributeSet, paramInt);
  }
  
  // 判断是否为视频文件
  public static boolean isVideo(String filePath) {
    filePath = filePath.toLowerCase();
    String[] vFiles = {".mov", ".mkv", ".mp4", ".avi"};
    for (byte vIdx = 0; vIdx < vFiles.length; vIdx++) {
      if (filePath.endsWith(vFiles[vIdx])) return true; 
    } 
    return false;
  }
  
  // 循环播放视频
  public void LoopPlayBack(final String videoPath) {
    File file = new File(videoPath);
    if (!file.exists() || !isVideo(videoPath)) return;

    // 开始播放视频
    this.currentVideoUrl = videoPath;
    setVideoPath(videoPath);
    start();

    // 视频播放完成,重新开始播放
    setOnCompletionListener(new MediaPlayer.OnCompletionListener() {  
      @Override
      public void onCompletion(MediaPlayer mp) {
        MyVideoView.this.start();
      }
    });
    // 视频播放报错监听
    setOnErrorListener(new MediaPlayer.OnErrorListener() {
      @Override
      public boolean onError(MediaPlayer mp) {
        Log.d(TAG, "播放错误..");
        return false;
      }
    });
  }
  
  public String getCurrentVideoUrl() { // 获取视频文件路径(用于截取播放帧)
    return this.currentVideoUrl;
  }
  
  protected void onMeasure(int paramInt1, int paramInt2) { // 调整视频分辨率,使视频拉伸填充容器
    setMeasuredDimension(getDefaultSize(getWidth(), paramInt1), getDefaultSize(getHeight(), paramInt2));
  }
}

在相应的布局.xml中使用MyVideoView视频组件:
P.S.如果要实现圆角视频效果,可以在MyVideoView外再套一层CardView,可参考:CardView-卡片布局

<LinearLayout
  android:orientation="horizontal"
  android:layout_width="800.0px"
  android:layout_height="600.0px"
>
   <XXX.MyVideoView
     android:id="@id/video"
     android:visibility="visible"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:layout-align-parent-top="true"
   />
</LinearLayout>

参考代码

如果界面中有视频播放,使用getDrawingCache截取整个应用界面时,视频区域会显示为黑屏;因此要另外获取视频当前的播放帧,再通过Canvas绘制Bitmap将视频截图“粘贴”到界面截图相应区域,从而实现截取整个界面(包括视频)的效果:

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;

// 调用此函数进行完整截屏(参数:本地视频路径)
private void taskScreenshot(String videoPath) {
	try {
		Bitmap screenPic = takeScreenBitmap(); // 屏幕截图
		Bitmap videoPic = getCurrentVideoBitmap(this.myVideoView); // 视频截图
		if (screenPic != null) {
			Bitmap wholePic = screenPic; // 完整截图(默认取屏幕截图)
			if (videoPic != null) // 如果获取到了视频截图,完整截图由屏幕截图“粘贴”视频截图得到
				wholePic = mergeBitmap(screenPic, scaleBitmap(videoPic, 2), 0, 2); 

			// 获取截图保存路径
			String picPath = Environment.getExternalStorageDirectory().getPath() + "/screenshot/testPic.png";
			File picFile = new File(picPath);
			if (picFile.exists())
				picFile.delete(); 
			FileOutputStream fileOutputStream = new FileOutputStream();
			// 保存截图
			wholePic.compress(Bitmap.CompressFormat.PNG, 80, fileOutputStream);
			fileOutputStream.flush();
			fileOutputStream.close();
		} 
	} catch (Exception e) {
	} 
}

// 截屏(不包含视频)
private Bitmap takeScreenBitmap() {
	int width = getWindow().getDecorView().getRootView().getWidth();
	int height = getWindow().getDecorView().getRootView().getHeight();
	View view = getWindow().getDecorView().getRootView();
	view.setDrawingCacheEnabled(false);
	view.buildDrawingCache();
	Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, width, height);
	view.destroyDrawingCache();
	return bitmap;
}

// 截取视频关键帧
public static Bitmap getCurrentVideoBitmap(MyVideoView paramMyVideoView) {
	Bitmap bitmap = null;
	MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
	String videoPath = paramMyVideoView.getCurrentVideoUrl();
	try {
	  if (Build.VERSION.SDK_INT >= 24) {
			Uri uri = Uri.parse(videoPath);
			mediaMetadataRetriever.setDataSource(this, uri);
	  } else {
			FileInputStream fileInputStream = new FileInputStream();
			File file = new File();
			this(videoPath);
			this(file.getAbsolutePath());
			mediaMetadataRetriever.setDataSource(fileInputStream.getFD());
	  } 

	  bitmap = mediaMetadataRetriever.getFrameAtTime((paramMyVideoView.getCurrentPosition() * 1000), MediaMetadataRetriever.OPTION_CLOSEST);
	}
	catch (Exception e) { }
	finally {
		try {
			mediaMetadataRetriever.release();
	  } catch (RuntimeException runtimeException1) {
			stringBuilder = new StringBuilder();
			stringBuilder.append("getCurrentVideoBitmap-RuntimeException2:");
			stringBuilder.append(runtimeException1.getMessage());
			StoreData.appendLogFile("####FullscreenActivity ", stringBuilder.toString());
	  }
		return (bitmap == null) ? null : Bitmap.createBitmap(bitmap);
	}
}

// bitmap变换:截取的视频截图尺寸和页面容器可能不一致,需拉伸为容器尺寸
private Bitmap scaleBitmap(Bitmap paramBitmap, float cWidth, float cHeight) {
	if (paramBitmap == null)
		return null; 
	int vWidth = paramBitmap.getWidth();
	int vHeight = paramBitmap.getHeight();
	Matrix matrix = new Matrix();
	matrix.postScale(cWidth / vWidth, cHeight / vHeight);
	Bitmap bitmap = Bitmap.createBitmap(paramBitmap, 0, 0, vWidth, vHeight, matrix, false);
	if (!paramBitmap.isRecycled()) paramBitmap.recycle(); 
	return bitmap;
}

// bitmap变换:将视频截图“粘贴”到屏幕截图的对应区域
private Bitmap mergeBitmap(Bitmap paramBitmap1, Bitmap paramBitmap2, int[] size1, int[] size2) {
	int width1 = size1[0], height1 = size1[1];
	int width2 = size2[0], height2 = size2[1];
	// 创建与屏幕截图大小一样的画布,然后分别将屏幕截图、视频截图绘制到画布对应位置
	Bitmap bitmap = Bitmap.createBitmap(width1, height1, Bitmap.Config.RGB_565);
	Canvas canvas = new Canvas(bitmap);
	canvas.drawBitmap(paramBitmap1, new Rect(0, 0, width1, height1), new Rect(0, 0, width1, height1), null);
	canvas.drawBitmap(paramBitmap2, new Rect(0, 0, width2, height2), new Rect(0, 0, width2, height2), null);
	return bitmap;
}

参考文档
[1] Android播放网络视频截图
[2] setDataSource RuntimeException 0xFFFFFFEA
[3] Android Bitmap相关操作
[4] Android使用Canvas绘制Bitmap相关
[5] CardView-卡片布局

猜你喜欢

转载自blog.csdn.net/qq_36604536/article/details/124291140
今日推荐