当前活动的屏幕视频录制(Android)

30

在同一活动中录制当前运行活动的屏幕视频是否可行?
我知道如何截取当前活动的屏幕截图,但不知道如何录制屏幕视频记录。
我该如何开始?我不知道从哪里开始。


是的,可以捕捉屏幕并添加到视频中。 - Janmejoy
我如何在不使用任何外部工具的情况下完成它?你能给我一些指导吗? - captaindroid
你能更准确地解释一下你想要实现什么吗?有许多不同的方法可以记录视频。 - kritzikratzi
我想在我的Android项目中以编程方式即Java捕获设备屏幕视频。 - captaindroid
Android 4.4增加了一个命令行实用程序来录制屏幕。请参见http://developer.android.com/about/versions/kitkat.html#44-screen-recording - fadden
显示剩余5条评论
6个回答

33
自从Lollipop开始,我们可以使用Media Projection API!(API 21+) 以下是我用于录制的代码,请注意我们首先需要获取用户权限;)
private static final int CAST_PERMISSION_CODE = 22;
private DisplayMetrics mDisplayMetrics;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaRecorder mMediaRecorder;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mMediaRecorder = new MediaRecorder();

    mProjectionManager = (MediaProjectionManager) getSystemService
            (Context.MEDIA_PROJECTION_SERVICE);

    getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);

    prepareRecording();
}

private void startRecording() {
    // If mMediaProjection is null that means we didn't get a context, lets ask the user
    if (mMediaProjection == null) {
        // This asks for user permissions to capture the screen
        startActivityForResult(mProjectionManager.createScreenCaptureIntent(), CAST_PERMISSION_CODE);
        return;
    }
    mVirtualDisplay = createVirtualDisplay();
    mMediaRecorder.start();
}

private void stopRecording() {
    if (mMediaRecorder != null) {
        mMediaRecorder.stop();
        mMediaRecorder.reset();
    }
    if (mVirtualDisplay != null) {
        mVirtualDisplay.release();
    }
    if (mMediaProjection != null) {
        mMediaProjection.stop();
    }
    prepareRecording();
}

public String getCurSysDate() {
    return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date());
}

private void prepareRecording() {
    try {
        mMediaRecorder.prepare();
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }

    final String directory = Environment.getExternalStorageDirectory() + File.separator + "Recordings";
    if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
        Toast.makeText(this, "Failed to get External Storage", Toast.LENGTH_SHORT).show();
        return;
    }
    final File folder = new File(directory);
    boolean success = true;
    if (!folder.exists()) {
        success = folder.mkdir();
    }
    String filePath;
    if (success) {
        String videoName = ("capture_" + getCurSysDate() + ".mp4");
        filePath = directory + File.separator + videoName;
    } else {
        Toast.makeText(this, "Failed to create Recordings directory", Toast.LENGTH_SHORT).show();
        return;
    }

    int width = mDisplayMetrics.widthPixels;
    int height = mDisplayMetrics.heightPixels;

    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
    mMediaRecorder.setVideoFrameRate(30);
    mMediaRecorder.setVideoSize(width, height);
    mMediaRecorder.setOutputFile(filePath);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode != CAST_PERMISSION_CODE) {
        // Where did we get this request from ? -_-
        Log.w(TAG, "Unknown request code: " + requestCode);
        return;
    }
    if (resultCode != RESULT_OK) {
        Toast.makeText(this, "Screen Cast Permission Denied :(", Toast.LENGTH_SHORT).show();
        return;
    }
    mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
    // TODO Register a callback that will listen onStop and release & prepare the recorder for next recording
    // mMediaProjection.registerCallback(callback, null);
    mVirtualDisplay = getVirtualDisplay();
    mMediaRecorder.start();
}

private VirtualDisplay getVirtualDisplay() {
    screenDensity = mDisplayMetrics.densityDpi;
    int width = mDisplayMetrics.widthPixels;
    int height = mDisplayMetrics.heightPixels;

    return mMediaProjection.createVirtualDisplay(this.getClass().getSimpleName(),
            width, height, screenDensity,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            mMediaRecorder.getSurface(), null /*Callbacks*/, null /*Handler*/);
}

这不是最终代码,但是一个很好的起点 :)


这很好。在SD卡中获取MP4视频文件,但其中的视频包含黑色帧。有没有全屏视频和处理权限弹出窗口的解决方案? - Uday Nayak
太好了!谢谢你! - DmitryKanunnikoff
在Marshmallow上,Mediarecorder.getsurface()无法工作,抛出了IllegalStateException异常。 - Sunil Chaudhary
每次应用程序启动时都会询问权限吗?还有其他方法吗? - Sagar
1
我创建了一个库,可以为您处理所有这些内容,包括显示通知,请查看我的答案-https://dev59.com/AGYq5IYBdhLWcg3woB8W#57533525 - HB.

18

编辑: 这个回答已经被下面的Danpe所取代。

从您的应用程序中编程录制视频将需要root访问权限。您会注意到,在Play商店中可用于此类操作的应用程序在其应用程序描述中 prominently 列出了“需要ROOT”。您还会注意到,这种方法的实现可能还需要一些特定的硬件要求(如来自Screencast Video Recorder应用程序的描述:“在Galaxy Nexus或Tegra 2/3上无法工作”)。

我自己从未编写过这段代码,但我已经大致了解了所需的基本方法。从这篇帖子中可以看出,您必须通过“/dev/graphics/fb0”访问帧缓冲区数据。帧缓冲区的访问模式为660,这意味着您需要root访问权限才能使用它。一旦您获得了root访问权限,就可以使用帧缓冲区数据创建屏幕截图(这个项目可能适用于此任务),然后从这些屏幕截图中创建视频(请参见这个SO问题,了解如何从图像序列创建视频)。

我已经使用过Screencast应用程序,在Samsung Note上运行良好。我认为这就是他们采取的基本方法。


1
虽然我没有得到具体的答案,但您已经提供了有关屏幕截图帧缓冲区和根访问权限的一些想法。我将进一步研究它,并将其标记为正确的答案。感谢您的指导。 - captaindroid
2
这个选择的解决方案一定已经过时了,因为 Play Store 中肯定有一些应用程序不需要 root 权限就可以录制设备屏幕。因此可以放心地说,如果这些应用程序可以记录任何应用程序的整个屏幕,那么您同样可以记录一个活动。 - Johann

11

普通的 Android 应用程序缺少捕获帧缓冲区的权限(特别是,它们不是 AID_GRAPHICS 组的成员)。这是出于安全考虑——否则它们可以从软键盘中窥探密码等信息。因此,通常情况下,在没有某种方式绕过特权问题的情况下,您无法从 Android 应用程序中捕获屏幕。

您可以通过遍历到视图层次结构顶部的 View 并使用 View.draw(Canvas) 将其绘制到 Bitmap 中来更或多少地捕获当前应用程序所占据的屏幕区域的快照,但这不会记录状态栏、软键盘、OpenGL 表面等。

如果您想做得更好,您需要使用外部工具。这些工具需要 root 或使用 ADB 接口,因为通过 ADB 接口启动的进程具有 AID_GRAPHICS 特权。使用后一种方法,非特权应用程序可以连接到特权服务器以进行录制。

大致上,工具可以分为以下几类:

  • 仅适用于 root 的帧缓冲记录器应用程序(例如 Screencast)。它们直接从 /dev/graphics/fb0 设备记录,但仅适用于可读取帧缓冲区的设备(例如,不适用于 Tegra 2 Nexus 7)。

  • 仅适用于 root 的屏幕捕获记录器应用程序(例如 SCR、Rec 等)。它们通过 SurfaceFlinger 捕获屏幕,并适用于更广泛的设备范围。

  • 非 root 屏幕捕获记录器应用程序(例如 Recordable、Ascrecorder)。这些需要用户启用 USB 调试并在连接到主机 PC 时启动守护进程。之后,在设备重新启动之前,主机 PC 不再需要。还可以录制音频。

  • ADB 工具(例如 Android 4.4 上内置的 screenrecorder)。需要通过 USB 连接,并且无法捕获音频。

为了完整起见,还有一些 USB 工具(例如 Mobizen),可以通过 USB 传输屏幕(受低 USB 带宽限制,无法录制音频),某些设备也可以通过 wifi 传输屏幕,然后可以在另一台设备上捕获。


虽然这篇帖子已经有7年的历史了,但是点击.mobi链接会跳转到一个看起来像病毒的网站。 - PapaWhiskey
是的,该域名已被抢注。链接已移除。 - Christopher Fraser

6
我创建了一个,可以为你处理这个问题。包括显示可自定义并且可以禁用的通知。
它需要API 21>。
这里是一个关于如何使用它的简单演示(更多信息可在库的自述文件中找到):
首先,在你的Activity中声明并初始化它:
public class MainActivity extends AppCompatActivity implements HBRecorderListener {
    //Declare HBRecorder
    HBRecorder hbRecorder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);     

        //Init HBRecorder
        hbRecorder = new HBRecorder(this, this);        

}

当您想要开始录制时,可以调用以下命令:

private void startRecordingScreen() {
    MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    Intent permissionIntent = mediaProjectionManager != null ? mediaProjectionManager.createScreenCaptureIntent() : null;
    startActivityForResult(permissionIntent, SCREEN_RECORD_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SCREEN_RECORD_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            //It is important to call this before starting the recording
            hbRecorder.onActivityResult(resultCode, data, this);
            //Start screen recording
            hbRecorder.startScreenRecording(data);
        }
    }
}

您可以通过调用以下方法停止录制:
hbRecorder.stopScreenRecording();

onCompleteListener 会告诉您文件何时被创建:

@Override
public void HBRecorderOnComplete() {
    //This is called once the file was created
}

我还添加了一堆可以设置的参数,比如更改音频比特率音频采样率等。

1
另外,方法 hbRecorder.onActivityResult 不存在。 - Konstantin Konopko
@KonstantinKonopko 在该库上开了一个问题。 - HB.

4

这是一篇相对较旧的文章,但我希望它能帮助那些仍在寻找记录Android设备屏幕方法的人:

自Android 5.0以来,有一个新的API可用于屏幕录制: MediaProjection MediaProjection授予捕获屏幕内容的能力,但不包括系统音频。此外,它也无法捕获安全屏幕内容。

Matt Snider的页面上有一个很好的示例,演示如何使用该API将屏幕实际录制到SD卡上的文件中:LINK


2
您可以通过使用DDMS捕获屏幕,因为adb运行并具有对帧缓冲的权限:
请查看以下链接以获取更多详细信息:

http://thetechjournal.com/electronics/android/how-to-capture-screenshots-and-record-video-on-android-device.xhtml

同时,检查这些链接,也许可以得到一些关于你需要的想法:

http://answers.oreilly.com/topic/951-how-to-capture-video-of-the-screen-on-android/

http://www.mightypocket.com/2010/09/installing-android-screenshots-screen-capture-screen-cast-for-windows/

"并检查这个项目:"

http://sourceforge.net/projects/ashot/

希望这有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接