android 5.0录屏的例子网上满天飞,我这里主要是总结一下,如何以VFR和CFR的方式来录屏。写这篇文章主要是因为我在做这个功能的过程中,在网上找了很久也没有找到一个固定帧率录屏的例子,后面还是在全球最大同性交友网站github找到了解决方案。好了,闲话不多说,下面来说下具体的解决方案。

首先了解一下VFR和CFR的概念:

  VFR(可变帧率)
     VFR 模式是一个非常好用的模式,使用这个模式,可以录制这个视频最低的 FPS 帧数,比如(您设置的FPS 是 60,但是您录制的这个视频,在某一个时间段这个画面都不会动,那么选择这个模式就可以记录60帧数以下的帧数,从而节省资源损耗,录制的体积也变小)

 CFR(恒定帧率)
      VFR 比 CFR 好用,但是一些视频编辑软件,却不支持 VFR,比如:Adobe Premiere 就不支持VFR,所以如果您选择的是用 Adobe Premiere 作为后期制作软件,那么您必须要选择 CFR 这个帧率模式。如果您选择的FPS 为60 ,帧率模式选择的是CFR,那么您录制的视频就是一个持续拥有FPS 为60的视频文件。

我从网上找了很多例子,都是可变帧率的,一个类搞定:

package com.jxd.jxdcamerapro.screen;import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.projection.MediaProjection;
import android.util.Log;
import android.view.Surface;import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;/*** 非固定帧率录制*/
public class ScreenRecorder extends Thread {private static final String TAG = "ScreenRecorder";private int mWidth;private int mHeight;private int mBitRate;private int mDpi;private String mDstPath;private MediaProjection mMediaProjection;// parameters for the encoderprivate static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Codingprivate static final int FRAME_RATE = 8; // 30 fpsprivate static final int IFRAME_INTERVAL = 10; // 10 seconds between I-framesprivate static final int TIMEOUT_US = 10000;private MediaCodec mEncoder;private Surface mSurface;private MediaMuxer mMuxer;private boolean mMuxerStarted = false;private int mVideoTrackIndex = -1;private AtomicBoolean mQuit = new AtomicBoolean(false);private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();private VirtualDisplay mVirtualDisplay;public ScreenRecorder(int width, int height, int bitrate, int dpi, MediaProjection mp, String dstPath) {super(TAG);mWidth = width;mHeight = height;mBitRate = bitrate;mDpi = dpi;mMediaProjection = mp;mDstPath = dstPath;}/*** stop task*/public final void quit() {mQuit.set(true);}@Overridepublic void run() {try {try {prepareEncoder();mMuxer = new MediaMuxer(mDstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);} catch (IOException e) {throw new RuntimeException(e);}mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG + "-display",mWidth, mHeight, mDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,mSurface, null, null);Log.d(TAG, "created virtual display: " + mVirtualDisplay);recordVirtualDisplay();} finally {release();}}private void recordVirtualDisplay() {while (!mQuit.get()) {int index = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);Log.i(TAG, "dequeue output buffer index=" + index);if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {resetOutputFormat();} else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {Log.d(TAG, "retrieving buffers time out!");try {// wait 10msThread.sleep(10);} catch (InterruptedException e) {}} else if (index >= 0) {if (!mMuxerStarted) {throw new IllegalStateException("MediaMuxer dose not call addTrack(format) ");}encodeToVideoTrack(index);mEncoder.releaseOutputBuffer(index, false);}}}private void encodeToVideoTrack(int index) {ByteBuffer encodedData = mEncoder.getOutputBuffer(index);if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// The codec config data was pulled out and fed to the muxer when we got// the INFO_OUTPUT_FORMAT_CHANGED status.// Ignore it.Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");mBufferInfo.size = 0;}if (mBufferInfo.size == 0) {Log.d(TAG, "info.size == 0, drop it.");encodedData = null;} else {Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size+ ", presentationTimeUs=" + mBufferInfo.presentationTimeUs+ ", offset=" + mBufferInfo.offset);}if (encodedData != null) {encodedData.position(mBufferInfo.offset);encodedData.limit(mBufferInfo.offset + mBufferInfo.size);mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);Log.i(TAG, "sent " + mBufferInfo.size + " bytes to muxer...");}}private void resetOutputFormat() {// should happen before receiving buffers, and should only happen onceif (mMuxerStarted) {throw new IllegalStateException("output format already changed!");}MediaFormat newFormat = mEncoder.getOutputFormat();Log.i(TAG, "output format changed.\n new format: " + newFormat.toString());mVideoTrackIndex = mMuxer.addTrack(newFormat);mMuxer.start();mMuxerStarted = true;Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);}private void prepareEncoder() throws IOException {//MediaFormat这个类是用来定义视频格式相关信息的//video/avc,这里的avc是高级视频编码Advanced Video Coding//mWidth和mHeight是视频的尺寸,这个尺寸不能超过视频采集时采集到的尺寸,否则会直接crashMediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);//COLOR_FormatSurface这里表明数据将是一个graphicbuffer元数据format.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);//设置码率,通常码率越高,视频越清晰,但是对应的视频也越大,这个值我默认设置成了2000000,也就是通常所说的2Mformat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);//设置帧率,通常这个值越高,视频会显得越流畅,一般默认我设置成30,你最低可以设置成24,不要低于这个值,低于24会明显卡顿format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);//IFRAME_INTERVAL是指的帧间隔,这是个很有意思的值,它指的是,关键帧的间隔时间。通常情况下,你设置成多少问题都不大。//比如你设置成10,那就是10秒一个关键帧。但是,如果你有需求要做视频的预览,那你最好设置成1//因为如果你设置成10,那你会发现,10秒内的预览都是一个截图format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);Log.d(TAG, "created video format: " + format);//创建一个MediaCodec的实例mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);//定义这个实例的格式,也就是上面我们定义的format,其他参数不用过于关注mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);//这一步非常关键,它设置的,是MediaCodec的编码源,也就是说,我要告诉mEncoder,你给我解码哪些流。//很出乎大家的意料,MediaCodec并没有要求我们传一个流文件进去,而是要求我们指定一个surface//而这个surface,其实就是我们在上一讲MediaProjection中用来展示屏幕采集数据的surfacemSurface = mEncoder.createInputSurface();Log.d(TAG, "created input surface: " + mSurface);mEncoder.start();}private void release() {if (mEncoder != null) {mEncoder.stop();mEncoder.release();mEncoder = null;}if (mVirtualDisplay != null) {mVirtualDisplay.release();}if (mMediaProjection != null) {mMediaProjection.stop();}if (mMuxer != null) {mMuxer.stop();mMuxer.release();mMuxer = null;}}
}

然后就是它的调用:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void startScreenRecord() {mediaProjectionManager = (MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE);if (mediaProjectionManager != null){Intent intent = mediaProjectionManager.createScreenCaptureIntent();PackageManager packageManager = getPackageManager();if (packageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY) != null){//存在录屏授权的ActivitystartActivityForResult(intent,START_RECORD_CODE);}else {toastShort("无法录制");}}}
  @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(bCFRMode){startCFRRecording(resultCode,data);return;}MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);if (mediaProjection == null) {Log.e("@@", "media projection is null");return;}if (requestCode == START_RECORD_CODE && resultCode == Activity.RESULT_OK){try {final int width = 360;final int height = 640;File file = new File(Environment.getExternalStorageDirectory() + "/"+ "1ScreenRecorder" + "/ScreenRecorder-" + width + "x" + height + "-"+ ".mp4");File dirs = new File(file.getParent());if (!dirs.exists())dirs.mkdirs();try {file.createNewFile();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
//        File file = new File(Environment.getExternalStorageDirectory(),
//                "record-" + width + "x" + height + "-" + System.currentTimeMillis() + ".mp4");final int bitrate = 1024*512;mRecorder = new ScreenRecorder(width, height, bitrate, 1, mediaProjection, file.getAbsolutePath());mRecorder.start();} catch (Exception e) {e.printStackTrace();}} else {toastShort("拒绝录屏");}}

然后就是固定帧率录制的方案:主要就是采用了opengl的方式来获取屏幕内容,由于这一块内容稍微多一点,不适合全部贴出来,所以麻烦各位移步那啥hub,https://github.com/jingxiongdi/JXDCameraPro,谢谢各位看官!

有兴趣的朋友还可以关注下公众号,听我给你们讲段子。

android 录屏方案 VFR和CFR相关推荐

  1. android 录屏自动运行,自动化录屏方案简介 for Android

    原标题:自动化录屏方案简介 for Android 前言 针对移动端项目的评测,为了记录并评估产品表现,时常需要对设备进行录屏以作后续分析. 那么,应该如何在Python脚本中可靠.可控地实现安卓设备 ...

  2. android 屏幕录制方案,Android录屏的三种解决方案

    本文总结三种用于安卓录屏的解决方案: adb shell命令screenrecord MediaRecorder, MediaProjection MediaProjection , MediaCod ...

  3. Android5.0录屏方案

    导语 本文主要是围绕android直播助手的功能做了一些研究,因为之前对Android多媒体相关的内容知之甚少,只有概念,于是查阅了相关资料并做以总结. 由于我对音视频相关知识零基础所以补充了一些相关 ...

  4. android录屏时不截入自定义悬浮框

    前提:使用MediaProjectionManager录屏方案. 问题:会截入自定义悬浮框 解决方案 1.本质上得修改framework层代码surfaceflinger去除悬浮框,在画布中就去除该悬 ...

  5. Android录屏并利用FFmpeg转换成gif(二)交叉编译FFmpeg源码

    Android录屏并利用FFmpeg转换成gif(二) 写博客时经常会希望用一段动画来演示app的行为,目前大多数的做法是在电脑上开模拟器,然后用gif录制软件录制模拟器屏幕,对于非开发人员来讲这种方 ...

  6. Android录屏分析(Android12源码)

    Android录屏分析 目录 Android录屏分析 prepare方法(准备录制) MediaProjection.MediaRecorder.VirtualDisplay的介绍 开始与结束 缩略图 ...

  7. android 录屏 github,GitHub - mabeijianxi/ScreenRecordPushStream: Android 录屏推流demo

    ScreenRecordPushStream 这是一个基于 rtmp 协议的 Android 录屏推流demo (Demo APK 下载) 使用方法 一. 服务器搭建 mkdir nms cd nms ...

  8. Android 录屏服务使用(源码)

    Android 录屏服务使用(源码) 从Android 5.0开始,可以对手机进行录屏,使用场景:如错误场景的视频上传,简单屏幕获取等,下面贴出使用用例和对使用的类一个简单的介绍 - MediaPro ...

  9. Android投屏方案(基于cling)

    一 .前言 最近做了一个浏览器&视频播放的项目,是在73.0.3683.90版本的chrome源码上修改而来,涉及到抓取网页里视频的播放地址.播放视频.视频投屏.视频下载.网页内广告屏蔽等方面 ...

最新文章

  1. python开源商城_Leaf - 一个开发友好、功能完备的开源微信商城框架
  2. oracle菜鸟学习之 分析函数-排序
  3. Hello XTCPC
  4. 2020牛客暑期多校训练营(第六场)
  5. 华为荣耀6 H60-L02/L12(联通版)救砖包【适用于无限重启】
  6. 使用BAT批处理执行sql
  7. 停在Z的危险:用苏斯博士的话,为什么孩子应该编码
  8. kaggle实战—泰坦尼克(五、模型搭建-模型评估)
  9. 开放式可编程保险市场Tidal Finance完成由KR1领投的195万美元种子轮融资
  10. 在CentOS上安装Git(转)
  11. VirtualBox虚拟机中安装XP系统
  12. 常用BUG管理工具系统
  13. 【转】SourceInsight4.0的使用--一遍很详细介绍source insight4使用的文章
  14. Linux 操作系统常用以下哪种编译器,Linux 操作系统期末复习资料(Alpha版)
  15. Kafka 数据丢失与优化
  16. 手机开机卡在android画面,手机一直停在开机画面怎么解决【图文】
  17. .Net C# 如何读取Excel数据内容写入数据库并通过DataGridView控件动态刷新显示
  18. 无线临ftp服务器1.3,Cerberus FTP Server Enterprise(FTP服务器管理工具)V11.3.1.1 最新版
  19. CDH kudu Unable to load consensus metadata for tablet
  20. Visual Studio 2022重命名解决方案和项目

热门文章

  1. 使用Docker如何搭建Web漏洞测试环境?
  2. 视频教程-深度学习30天系统实训-深度学习
  3. mysql毕业生信息管理系统,本科毕业设计信息管理系统的设计(Struts,MySQL)
  4. spool导出多列去空格
  5. 咖说 | 暗潮涌动,ERC20 BTC 正在搅动市场
  6. 云图说丨华为云区块链引擎服务:高安全的区块链技术服务平台,轻松部署,快速上链
  7. PS CC 2018 图层
  8. 利用python爬取知乎评论_一个简单的python爬虫,爬取知乎
  9. 出现java.sql.SQLException: Subquery returns more than 1 row错误的原因,解决方法
  10. SpringBoot 项目打成 .exe 程序,实战来了,超级详细!