Android L新增了MediaProjection录屏的api,捣鼓了大半天,照着github上的demo撸了一遍代码,梳理梳理。

录屏实现依赖MediaProjectionManager

经过

mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE)

拿到是实例

而后建立intent,并startActivityForResultjava

Intent intent=mediaProjectionManager.createScreenCaptureIntent();

startActivityForResult(intent, 123);

在onActvitiyResult()中拿到MediaProjection实例git

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK && requestCode == 123) {

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);

if (mediaProjection != null ) {

mRecorder = new ScreenRecorder(mWidth, mHeight, mediaProjection, mDensty);

}

new Thread() {

@Override

public void run() {

mRecorder.startRecorder();

}

}.start();

btnRecorder.setText("中止录屏");

moveTaskToBack(true);

}

}

录屏很消耗资源,另起一个线程操做github

前面的这些步骤呢,主要目的就是一个,开启录屏。web

而后,经过MediaProjection实例建立一个虚拟屏幕,剩下的工做主要就是经过MediaCodec,MediaMuxer

将virtualDisplay的进行编码输出到MP4文件中。

思路仍是比较简短的,可是呢,我也是踩了很多坑,以前彻底不熟悉相关的API只能一遍一遍照着demo撸。

概括一下实现步骤

1.拿到MediaProjectionManager服务 getSystemService();

2.建立intent,并启动 mediaprojectionmanager.createCpatureIntent();

3.初始化编码器 MediaCodec.createEncoderByType(), 并建立一个suface

mEncoder.createInputSuface()

启动编码器,mEncoder.start();

4.使用suface建立虚拟屏幕 mediaprojection.createVirtualDisplay()

5.建立一个混合器,MediaMuxer;

6.编码并输出。api

具体代码ide

1.拿到MediaProjectionManager服务 getSystemService();svg

mediaProjectionManager=(MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);

2.建立intent,并启动 mediaprojectionmanager.createCpatureIntent();学习

Intent intent=mediaProjectionManager.createScreenCaptureIntent();

startActivityForResult(intent, 123);

拿到mediaprojection对象ui

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK && requestCode == 123) {

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);

if (mediaProjection != null ) {

mRecorder = new ScreenRecorder(mWidth, mHeight, mediaProjection, mDensty);

}

new Thread() {

@Override

public void run() {

mRecorder.startRecorder();

}

}.start();

btnRecorder.setText("中止录屏");

moveTaskToBack(true);

}

}

ScreenRecorder构造方法主要是进行了传值工做this

public ScreenRecorder(int mWidth, int mHeight, MediaProjection mediaProjection, int mDensty) {

this.mWidth = mWidth;

this.mHeight = mHeight;

this.mediaProjection = mediaProjection;

this.mDensty = mDensty;

File file = new File(savePath);

if (!file.exists()) {

file.mkdirs();

}

}

在onActivityResult中 另起一个线程调用了startRecorder()方法

看代码

public void startRecorder() {

prepareRecorder();

startRecording();

}

在prepareRecorder()中初始化编码器,并设置一些参数

设置了bit率,影响清晰度的4000000-6000000b比较合适

framerate 帧率 就是常说的fps

其余的设置仍是看google文档吧,我也不是特别清楚,还在学习中

private void prepareRecorder() {

mBufferInfo = new MediaCodec.BufferInfo(); //元数据,描述bytebuffer的数据,尺寸,偏移

//建立格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式

MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);

int frameRate = 45;

format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);

format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);

format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);

format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);

/*MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT,

MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000);

format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);*/

try {

mEncorder = MediaCodec.createEncoderByType(MIME_TYPE);

mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

mInputSurface = mEncorder.createInputSurface();

mEncorder.start();

} catch (IOException e) {

e.printStackTrace();

releaseEncorders();

}

}

将format传给mEncorder,而后调用start,编码器就开始工做了,会自动将数据写入到MediaCodec的缓冲区内,咱们拿出缓冲区内编码好的数据,使用MediaMuxer进行混合输出就ok了,看到有大神将,声音和视频的混合以后一块儿输出,就实现了录屏和录音同步。这个后面能够尝试下的

接着看

private void startRecording() {

File saveFile = new File(savePath, System.currentTimeMillis() + ".mp4");

try {

mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

Log.e("TAG", "mwidth " + mWidth + " heigh " + mHeight + " mdensty " + mDensty + " Flag "

+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC + " surface " + String.valueOf(mInputSurface == null));

mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,

mInputSurface, null, null);

drainEncoder();

} catch (Exception e) {

e.printStackTrace();

}finally {

releaseEncorders();

}

}

MediaMuxer须要一个文件来保存输出的视频,并传入一个输出格式

MediaMuxer输出格式目前只支持MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4

MUXER_OUTPUT_WEBM两种。

建立虚拟屏幕以后

就开始编码输出了,

private void drainEncoder() {

while (!mQuit.get()) {

Log.e("TAG", "drain.....");

int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);

if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());

if (!mMuxerStarted && mTrackIndex >= 0) {

mMuxer.start();

mMuxerStarted = true;

}

}

if (bufferIndex >= 0) {

Log.e("TAG", "drain...write..");

ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {

mBufferInfo.size = 0;

}

if (mBufferInfo.size != 0) {

if (mMuxerStarted) {

bufferData.position(mBufferInfo.offset);

bufferData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);

}

}

mEncorder.releaseOutputBuffer(bufferIndex, false);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {

break;

}

}

}

}

这段代码呢先拿到输出buffer的index

index的含义:

MediaCodec.INFO_TRY_AGAIN_LATER 超时

MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 格式改变

咱们在MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 这个的时候会开启muxer

if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());

if (!mMuxerStarted && mTrackIndex >= 0) {

mMuxer.start();

mMuxerStarted = true;

}

}

也比较容易理解

以前是muxer并无调用start(),因此这个时候是没有输出的,混合也是不能起任何做用的

MediaCodec.BUFFER_FLAG_END_OF_STREAM 输出结尾

MediaCodec.BUFFER_FLAG_CODEC_CONFIG 开始编码 咱们须要忽略

index大于0,写数据

if (mBufferInfo.size != 0) {

if (mMuxerStarted) {

bufferData.position(mBufferInfo.offset);

bufferData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);

}

}

不要忘了释放资源,这个是真消耗资源

我用的三星s6 9200,开始没有处理资源,手机卡死好几回,电量也耗得。。。。

代码就不贴了

好了,就到这里

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐