如何在 KitKat 4.4 中以编程方式录制 Android 屏幕视频

Posted

技术标签:

【中文标题】如何在 KitKat 4.4 中以编程方式录制 Android 屏幕视频【英文标题】:How to Record Android Screen Video programmatically in KitKat 4.4 【发布时间】:2014-07-11 02:09:21 【问题描述】:

我知道这个问题已经被问过很多次,并且有很多问题、答案和讨论可用。但我不知道该做什么不该做什么。

我已经参考了下面的链接来获得解决方案,但没有成功。

https://***.com/questions/23438767/how-to-record-video-on-kitkat-4-4https://***.com/questions/23185125/i-cannot-screen-record-with-my-kitkat-4-4-moto-xandroid KitKat start screenrecord from Appscreen recorder with kitkatScreen Recording kitkat with button

通过大量搜索,我没有得到任何简单的示例来完成这项任务。两天以来,我一直在努力实现这一目标,但没有成功。

所以简单的问题是是否可以在android中录制我们自己屏幕的视频。我刚刚听说可以从 android 4.4 Kitkat 开始,我还检查了市场上的一些应用程序。

我知道要做到这一点,我们的设备应该是 root 和其他需要这样做的东西。

但我不知道如何以编程方式开发它。如果有人有任何想法,请指导我如何做到这一点。或任何示例或代码都会有很大帮助。

感谢您的任何帮助。

我尝试使用这段简单的代码进行开发,但没有得到任何东西

public void startRecording(View v) 
        File recordfolder = Environment.getExternalStorageDirectory();
        String record = "su      — bit rate 8000000 --time-limit 30 "
                + recordfolder + "Record.mp4";
        recordfolder.mkdir();
        try 
            Process screenrecording = Runtime.getRuntime().exec(record);
         catch (IOException e) 
            e.printStackTrace();
        
    

所以基本上我不知道我与这个Process screenrecording 有什么关系,我的意思是我怎样才能开始进步。

【问题讨论】:

我也有兴趣知道这一点,最好没有根要求。 嘿@user3660803,这个问题已经很久没有提出来了。从那以后你有什么新的见解吗? 【参考方案1】:

好问题但是答案取决于你想用什么类型的平台在android中录制屏幕。

这里有一些技巧.....

1) 使用这个类,您可以记录您需要 root 设备的屏幕堡垒,您也可以在 genymotion 4.4 中进行测试。

public static class MainFragment extends Fragment 
    private Context mContext;

    private EditText mWidthEditText;
    private EditText mHeightEditText;
    private EditText mBitrateEditText;
    private EditText mTimeEditText;
    private Button mRecordButton;

    public MainFragment() 
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) 
        View rootView = inflater.inflate(R.layout.fragment_main, container,
                false);

        mContext = getActivity();

        mRecordButton = (Button) rootView.findViewById(R.id.btn_record);
        mRecordButton.setOnClickListener(RecordOnClickListener);

        mWidthEditText = (EditText) rootView.findViewById(R.id.et_width);
        mHeightEditText = (EditText) rootView.findViewById(R.id.et_height);
        mBitrateEditText = (EditText) rootView
                .findViewById(R.id.et_bitrate);
        mBitrateEditText.addTextChangedListener(BitrateTextWatcher);
        mTimeEditText = (EditText) rootView.findViewById(R.id.et_time);
        mTimeEditText.addTextChangedListener(TimeTextWatcher);

        return rootView;
    

    private TextWatcher BitrateTextWatcher = new TextWatcher() 
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i,
                int i2, int i3) 
            // Not used.
        

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2,
                int i3) 
            if (TextUtils.isEmpty(charSequence)) 
                return;
            

            int value = Integer.valueOf(charSequence.toString());
            if (value > 50 || value == 0) 
                mBitrateEditText.setError(mContext
                        .getString(R.string.error_bitrate_edittext));
                return;
            

            mTimeEditText.setError(null);
        

        @Override
        public void afterTextChanged(Editable editable) 
            // Not used.
        
    ;

    private TextWatcher TimeTextWatcher = new TextWatcher() 
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i,
                int i2, int i3) 
            // Not used.
        

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2,
                int i3) 
            if (TextUtils.isEmpty(charSequence)) 
                return;
            

            int value = Integer.valueOf(charSequence.toString());
            if (value > 180 || value == 0) 
                mTimeEditText.setError(mContext
                        .getString(R.string.error_time_editext));
                return;
            
            mTimeEditText.setError(null);
        

        @Override
        public void afterTextChanged(Editable editable) 
            // Not used.
        
    ;

    private View.OnClickListener RecordOnClickListener = new View.OnClickListener() 
        @Override
        public void onClick(View view) 
            if (!TextUtils.isEmpty(mTimeEditText.getError())
                    || !TextUtils.isEmpty(mBitrateEditText.getError())) 
                Toast.makeText(mContext,
                        mContext.getString(R.string.toast_invalid_values),
                        Toast.LENGTH_LONG).show();
                return;
            

            boolean widthSet = !TextUtils.isEmpty(mWidthEditText.getText());
            boolean heightSet = !TextUtils.isEmpty(mHeightEditText
                    .getText());
            if ((!widthSet && heightSet) || (widthSet && !heightSet)) 
                Toast.makeText(mContext,
                        mContext.getString(R.string.error_invalid_wxh),
                        Toast.LENGTH_LONG).show();
                return;
            

            boolean bitrateSet = !TextUtils.isEmpty(mBitrateEditText
                    .getText());
            boolean timeSet = !TextUtils.isEmpty(mTimeEditText.getText());

            StringBuilder stringBuilder = new StringBuilder(
                    "/system/bin/screenrecord");
            if (widthSet) 
                stringBuilder.append(" --size ")
                        .append(mWidthEditText.getText()).append("x")
                        .append(mHeightEditText.getText());
            
            if (bitrateSet) 
                stringBuilder.append(" --bit-rate ").append(
                        mBitrateEditText.getText());
            
            if (timeSet) 
                stringBuilder.append(" --time-limit ").append(
                        mTimeEditText.getText());
            

            // TODO User definable location.
            stringBuilder
                    .append(" ")
                    .append(Environment.getExternalStorageDirectory()
                            .toString()).append("/recording.mp4");
            Log.d("TAG", "comamnd: " + stringBuilder.toString());

            try 
                new SuTask(stringBuilder.toString().getBytes("ASCII"))
                        .execute();

             catch (UnsupportedEncodingException e) 
                e.printStackTrace();
            
        
    ;

    private class SuTask extends AsyncTask<Boolean, Void, Boolean> 
        private final byte[] mCommand;

        public SuTask(byte[] command) 
            super();
            this.mCommand = command;
        

        @Override
        protected Boolean doInBackground(Boolean... booleans) 
            try 
                Process sh = Runtime.getRuntime().exec("su", null, null);
                OutputStream outputStream = sh.getOutputStream();
                outputStream.write(mCommand);
                outputStream.flush();
                outputStream.close();

                final NotificationManager notificationManager = (NotificationManager) mContext
                        .getSystemService(NOTIFICATION_SERVICE);
                notificationManager.notify(RUNNING_NOTIFICATION_ID,
                        createRunningNotification(mContext));

                sh.waitFor();
                return true;

             catch (InterruptedException e) 
                e.printStackTrace();
                Toast.makeText(mContext,
                        mContext.getString(R.string.error_start_recording),
                        Toast.LENGTH_LONG).show();

             catch (IOException e) 
                e.printStackTrace();
                Toast.makeText(mContext,
                        mContext.getString(R.string.error_start_recording),
                        Toast.LENGTH_LONG).show();
            

            return false;
        

        @Override
        protected void onPostExecute(Boolean bool) 
            super.onPostExecute(bool);
            if (bool) 
                final NotificationManager notificationManager = (NotificationManager) mContext
                        .getSystemService(NOTIFICATION_SERVICE);
                notificationManager.cancel(RUNNING_NOTIFICATION_ID);

                File file = new File(Environment
                        .getExternalStorageDirectory().toString()
                        + "/recording.mp4");
                notificationManager.notify(FINISHED_NOTIFICATION_ID,
                        createFinishedNotification(mContext, file));
            
        

        private Notification createRunningNotification(Context context) 
            Notification.Builder mBuilder = new Notification.Builder(
                    context)
                    .setSmallIcon(android.R.drawable.stat_notify_sdcard)
                    .setContentTitle(
                            context.getResources().getString(
                                    R.string.app_name))
                    .setContentText("Recording Running")
                    .setTicker("Recording Running")
                    .setPriority(Integer.MAX_VALUE).setOngoing(true);

            return mBuilder.build();
        

        private Notification createFinishedNotification(Context context,
                File file) 
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file), "video/mp4");

            PendingIntent pendingIntent = PendingIntent.getActivity(
                    context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

            Notification.Builder mBuilder = new Notification.Builder(
                    context)
                    .setSmallIcon(android.R.drawable.stat_notify_sdcard)
                    .setContentTitle(
                            context.getResources().getString(
                                    R.string.app_name))
                    .setContentText("Recording Finished")
                    .setTicker("Recording Finished")
                    .setContentIntent(pendingIntent).setOngoing(false)
                    .setAutoCancel(true);

            return mBuilder.build();
        
    

2)您可以捕获屏幕截图并从中制作视频,它适用于 3.0+ 设备。要将图像转换为视频,您可以使用 FFMPEG 或 JavaCV。

-对于 Rooted 设备(因为您也可以捕获键盘屏幕)

if (Environment.MEDIA_MOUNTED.equals(Environment
            .getExternalStorageState())) 

        // we check if external storage is\ available, otherwise
        // display an error message to the user using Toast Message
        File sdCard = Environment.getExternalStorageDirectory();
        File directory = new File(sdCard.getAbsolutePath() + "/ScreenShots");
        directory.mkdirs();

        String filename = "screenshot_jpeg_" + i + ".png";
        File yourFile = new File(directory, filename);



        try 
            Process sh = Runtime.getRuntime().exec("su", null, null);
            OutputStream os = sh.getOutputStream();
            os.write(("/system/bin/screencap -p " + "/sdcard/ScreenShots/" + filename).getBytes("ASCII"));


            os.flush();
            os.close();
            sh.waitFor();
            i++;
         catch (Exception e) 
            e.printStackTrace();
        
    

-对于没有 Rooted 的设备(因为您无法捕获键盘屏幕)

if (Environment.MEDIA_MOUNTED.equals(Environment
            .getExternalStorageState())) 

        // we check if external storage is\ available, otherwise
        // display an error message to the user using Toast Message
        File sdCard = Environment.getExternalStorageDirectory();
        File directory = new File(sdCard.getAbsolutePath() + "/ScreenShots");
        directory.mkdirs();

        String filename = "screenshot_jpeg_" + i + ".png";
        File yourFile = new File(directory, filename);



        try 
            Process sh = Runtime.getRuntime().exec("su", null, null);
            OutputStream os = sh.getOutputStream();
            os.write(("/system/bin/screencap -p " + "/sdcard/ScreenShots/" + filename).getBytes("ASCII"));


            os.flush();
            os.close();
            sh.waitFor();
            i++;
         catch (Exception e) 
            e.printStackTrace();
        
    

【讨论】:

您的带和不带 root 的代码是相同的。你的意思是在上一个示例中调用su 吗? @emkman 是的,您必须为 root 设备授予 su 权限。 是的,但是无根设备(无键盘)呢?你的两个例子是相同的。 第一组代码适用于我使用 genymotion 4.4 请注意在使用 genymotion 测试时将外部存储路径更改为 /mnt/shell/emulated/0 而不是使用 Environment.getExternalStorage() 4.4 或有根的 4.4 设备模拟器检查此答案以获取有关目录路径更改的更多详细信息***.com/a/20734816/2644384 @SmitPatel:我在没有 root 的设备上发现异常:行中的 null 环境:“Process sh = Runtime.getRuntime().exec("su", null, null);"

以上是关于如何在 KitKat 4.4 中以编程方式录制 Android 屏幕视频的主要内容,如果未能解决你的问题,请参考以下文章

如何检查我是否在Android 4.4及以上版本中以编程方式打开gps? [重复]

索尼google android 4.4 (kitkat)有啥功能

隐藏滚动条 Android KitKat 4.4

Android 4.4 Kitkat Phone工作流程浅析__InCallActivity显示更新流程

android 4.4(KitKat)上,如何开发SMS功能的APP

如何在android中以编程方式上传到服务器之前减小视频的大小[关闭]