在Android13 中录屏有个专门的类 ScreenMediaRecorder.java。我们先看ScreenMediaRecorder#start()方法:
// ScreenMediaRecorder.java
void start() throws IOException, RemoteException, RuntimeException {
Log.d(TAG, "start recording");
// 准备录屏
prepare();
// 开始录屏
mMediaRecorder.start();
// 开始内部音频录制
recordInternalAudio();
}
这个 ScreenMediaRecorder#start()
在 RecordingService.java
的 onStartCommand() 方法里被调用。
接着看 ScreenMediaRecorder#prepare() 准备录屏:
// ScreenMediaRecorder.java
private void prepare() throws IOException, RemoteException, RuntimeException {
//Setup media projection
// 拉起 MEDIA_PROJECTION_SERVICE
IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
IMediaProjectionManager mediaService =
IMediaProjectionManager.Stub.asInterface(b);
IMediaProjection proj = null;
proj = mediaService.createProjection(mUser, mContext.getPackageName(),
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
IBinder projection = proj.asBinder();
mMediaProjection = new MediaProjection(mContext,
IMediaProjection.Stub.asInterface(projection));
// 创建文件、设置格式
File cacheDir = mContext.getCacheDir();
cacheDir.mkdirs();
mTempVideoFile = File.createTempFile("temp", ".mp4", cacheDir);
// 设置媒体记录器
// MediaRecorde 主要提供了一些方法用来支持录屏录音
mMediaRecorder = new MediaRecorder();
// 设置音频源
if (mAudioSource == MIC) {
// 设置声音来源,一般传入 MediaRecorder. AudioSource.MIC参数指定录制来自麦克风的声音。(这里如果只录屏可以不设置)
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
}
// 设置用于录制的视频来源。如屏幕等
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
// 设置所录制的音视频文件的格式。
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
// 设置视频
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealMetrics(metrics);
int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate();
int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate);
int width = dimens[0];
int height = dimens[1];
refreshRate = dimens[2];
int vidBitRate = width * height * refreshRate / VIDEO_FRAME_RATE
* VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO;
// 设置视频的编码格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoEncodingProfileLevel(
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
MediaCodecInfo.CodecProfileLevel.AVCLevel3);
// 设置要拍摄的宽度和视频的高度,最高只能设置640x480
mMediaRecorder.setVideoSize(width, height);
// 设置录制视频的捕获帧速率
mMediaRecorder.setVideoFrameRate(refreshRate);
// 设置所录制视频的编码位率
mMediaRecorder.setVideoEncodingBitRate(vidBitRate);
// 设置录制会话的最长持续时间(以ms为单位)
mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
// 设置最大文件大小
mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
// 设置音频
if (mAudioSource == MIC) {
// 设置音频编码格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mMediaRecorder.setAudioChannels(TOTAL_NUM_TRACKS);
// 设置所录制视频的编码位率
mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
}
// 设置录制的音频文件的保存位置
mMediaRecorder.setOutputFile(mTempVideoFile);
// 准备录制
mMediaRecorder.prepare();
// Create surface
mInputSurface = mMediaRecorder.getSurface();
// VirtualDisplay类代表一个虚拟显示器,需要调用DisplayManager 类的 createVirtualDisplay()方法,
// 将虚拟显示器的内容渲染在一个Surface控件上,即 捕捉屏幕了。
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
"Recording Display", // 实际的流媒体显示实体名字,不能为null;
width, // 实际的流媒体显示实体的宽度,单位为像素,必须大于0
height, // 实际的流媒体显示实体的高度,单位为像素,必须大于0;
metrics.densityDpi, // 实际的流媒体显示实体的像素密度,单位为dp,必须大于0;
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, // 实际的流媒体显示实体标志的结合
mInputSurface, // 播放流媒体的surface实例,可为null,
null, // 实际的流媒体显示实体状态改变时的回调方法,可能为null;
null); // 调用第 7 个参数回调方法的handler
mMediaRecorder.setOnInfoListener(mListener);
if (mAudioSource == INTERNAL ||
mAudioSource == MIC_AND_INTERNAL) {
mTempAudioFile = File.createTempFile("temp", ".aac",
mContext.getCacheDir());
mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(),
mMediaProjection, mAudioSource == MIC_AND_INTERNAL);
}
}
上述 prepare() 方法,主要:进行了拉起后台服务,创建VirtualDisplay用于屏幕参数设定、设置录屏问文件路径等操作;
再接着看ScreenMediaRecorder#end()方法:
// ScreenMediaRecorder.java
void end() {
//jingtao.guo add try catch for TFBAAA-341 crash
try {
mMediaRecorder.stop();
mMediaRecorder.release();
mInputSurface.release();
mVirtualDisplay.release();
mMediaProjection.stop();
mMediaRecorder = null;
mMediaProjection = null;
stopInternalAudioRecording();
Log.d(TAG, "end recording");
} catch (RuntimeException e) {
Log.e(TAG, "end recording" + e.getMessage());
}
}
end() 方法结束录屏,调用了mMediaRecorder的stop与release方法,stopInternalAudioRecording()方法中通过 mAudio.end()方法来停止录音。
缩略图
在谷歌原生设计中,录屏的缩略图并非是视频的第一帧,而是前20帧中的最大有效帧作为预览图。这种设计的好处是避免了第一帧是黑色或者不清楚,从而使得用户看起来比较清晰,更好辨别出所录的视频内容;
网友评论