[:ru]В этом уроке разберем код приложения, которое записывает видео с экрана устройства со звуком. До Андроид 5.0 приложение для записи видео с экрана мобильных устройств требовало рут-доступ и не гарантировало нормальную работу на устройствах разных производителей. Все изменилось в API 21 версии. Здесь появился класс MediaProjection, который предоставляет доступ для записи видео с экрана или звука с аудио системы.
Давайте рассмотрим код приложения для записи видео с экрана устройства.

Макет экрана содержит одну кнопку для старта и остановки записи.

Перейдем к коду. В главном пакете видим три класса- класс RecordApplication, класс MainActivity и класс RecordService.

Рассмотрим класс RecordApplication. Его вызов происходит в манифесте в секции application в строке android:name=.

Класс RecordApplication унаследован от класса Application. Согласно документации, класс Application или его наследник инициализируется перед любым другим классом, когда создается процесс приложения.

Здесь вызывается метод startService, который запускает сервис RecordService при старте приложения.

Класс RecordService унаследован от класса Service. О том, что такое сервисы, подробно можно узнать из видеоуроков на нашем канале, начиная с урока 92.
Если в двух словах, сервис – это некая задача, которая работает в фоне и не использует UI. Поскольку нам нужно записывать все, что происходит на экране, независимо от того, какое приложение запущено, поэтому мы и будем использовать сервис.

Здесь объявлены переменные классов:

MediaProjection — это токен, предоставляющий приложению возможность захватить содержимое экрана и/или записывать аудио системы.
MediaRecorder — класс, который используется для записи аудио и видео.
VirtualDisplay Представляет собой виртуальный экран, содержимое которого рендерится в Surface, который мы передаем методу createVirtualDisplay.

О том, что такое Surface, мы говорим в уроке 132.
В двух словах — это компонент, на который выводится изображение.
Объявляем еще несколько переменных: логическую running, которой будем присваивать true в процессе записи.

Далее параметры виртуального экрана, разрешение установим, а плотность пока не указываем.

Далее идет метод IBinder onBind, который позволяет приложению подключиться к сервису и взаимодействовать с ним через возвращаемый объект RecordBinder. Подробнее в уроке 97

Теперь методы жизненного цикла сервиса.

Метод onStartCommand срабатывает при старте сервиса методом startService, который вызывается в классе RecordApplication. Он возвращает флаг START_STICKY – это значит, сервис будет перезапущен, если будет убит системой.

В методе onCreate? который вызывается в начале работы сервиса, создаем отдельный поток serviceThread с использованием класса HandlerThread. Это вспомогательный класс для запуска нового потока, который имеет лупер, который может использоваться для создания обработчиков классов. На вход передаем произвольное имя потока и флаг THREAD_PRIORITY_BACKGROUND — Стандартный приоритет фоновых потоков.

Далее стартуем поток, сбрасываем значение переменной running и создаем mediaRecorder.

В методе onDestroy, который вызывается при остановке сервиса, просто вызываем метод суперкласса.

Метод setMediaProject будет вызываться в MainActivity и передавать объект mediaProjection.

Далее геттер для переменной running.

Метод setConfig устанавливает параметры виртуального экрана.

В методе startRecord проверяем, если объект mediaProjection не существует и переменная running имеет значение true, возвращаем false.
Вызываем здесь методы initRecorder и createVirtualDisplay, которые рассмотрим позже, стартуем запись вызовом mediaRecorder.start(); присваиваем переменной running = true; и возвращаем true.

Метод stopRecord выполняет обратные операции, останавливает запись и перезапускает mediaRecorder в состояние ожидания. Освобождаем virtualDisplay и останавливаем mediaProjection.

Теперь метод createVirtualDisplay, который вызывается выше в методе startRecord. Здесь выполняется создание виртуального экрана через метод mediaProjection.createVirtualDisplay, которому передается произвольное имя дисплея, его параметры, флаг VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, который позволяет отразить содержимое приватного дисплея, если его содержимое не отображается, и Surface.

Теперь в методе initRecorder, который вызывается выше в методе startRecord работаем с объектом mediaRecorder.
Метод setAudioSource устанавливает источник звука, используемый для записи.
setVideoSource задает источник видео, который будет использоваться для записи.
setOutputFormat устанавливает формат получаемого файла записи.
setOutputFile устанавливает целевое местоположение и имя файла записи.
setVideoSize устанавливает размер видео.
setVideoEncoder определяет кодировщик видео.
setAudioEncoder определяет кодировщик аудио.
setVideoEncodingBitRate устанавливает битрейт файла записи, здесь жестко прописано постоянное значение, равное 5 Мбит.
setVideoFrameRate задает частоту кадров, здесь 30 кадров в секунду.
Метод prepare() подготавливает mediaRecorder для записи и кодирования данных. Выполняем его в блоке try…catch с перехватом ошибки IOException.

Далее в методе getsaveDirectory задаем путь для сохранения файла записи и показываем тост об этом пользователю.

Ниже здесь создаем внутренний класс RecordBinder, это биндер для связи и взаимодействия с сервисом в приложении.

В классе MainActivity.java объявлены константы _REQUEST_CODE с произвольными значениями. Эти константы используются в интентах и запросах разрешений, чтобы отличать друг от друга пришедшие результаты. Подробнее об этом смотрите урок 30

Далее объявляем переменные классов MediaProjectionManager — Управляет получением определенных типов токенов MediaProjection.

MediaProjection вы уже знаете — это токен, предоставляющий приложению возможность захватить содержимое экрана и/или записывать аудио системы.

Также объявляем экземпляр нашего сервиса RecordService и обычную кнопку.

В методе onCreate получаем экземпляр MediaProjectionManager для управления сессиями отображения медиа-данных.

Создаем кнопку, по умолчанию неактивную, и слушатель для нее.
В методе onClick по нажатию кнопки будем вызывать метод recordService.stopRecord, в случае,если запись идет. Иначе создаем интент с projectionManager.createScreenCaptureIntent() и отправляем его методом startActivityForResult.

Далее идут запросы разрешений на запись в память устройства и на запись аудио.

Еще здесь мы создаем еще один интент для запуска сервиса и передаем его методу bindService, который выполняет привязку сервиса к приложению, а если сервис не работает, то стартует его предварительно.

В методе onDestroy отвязываем сервис.

Метод onActivityResult получает результат вызова метода startActivityForResult, где мы отправляем captureIntent и RECORD_REQUEST_CODE.
Если запрос прошел успешно, создадим объект MediaProjection, полученный от успешного запроса захвата экрана. Он будет иметь значение NULL, если результат от startActivityForResult() будет не RESULT_OK.

Вызываем метод recordService.startRecord(); и меняем текст кнопки на «Остановить запись»

Метод onRequestPermissionsResult — это обратный вызов для результата запроса разрешений. Этот метод вызывается для каждого вызова метода requestPermissions. Вполне возможно, что процесс запроса разрешений с пользователем был прерван. В этом случае вы получите пустые разрешения и массивы, которые должны рассматриваться как отмена.

Далее создается экземпляр интерфейса ServiceConnection с реализацией его методов onServiceConnected и onServiceDisconnected. Интерфейс служит для мониторинга состояния сервиса.
Второй метод оставляем пустым, а в первом методе onServiceConnected, который вызывается в случае подключения приложения к сервису через биндер, создаем объект класса DisplayMetrics, который служит для определения реальных параметров экрана устройства — разрешения и плотности.

Получаем экземпляр recordService и через его метод setConfig устанавливаем параметры виртуального экрана, передавая полученные значения.
Здесь же делаем кнопку активной и меняем текст на ней.

Запускаем приложение и наслаждаемся его работой.[:en]In this lesson you will look at the code of the application that records video from your device screen with sound. To Android 5.0 the app to record video from the screen of mobile devices required root access and does not guarantee the normal operation of devices from different manufacturers. That all changed in API 21 version. There is a class MediaProjection, which provides access to video recording from the screen or sound.

Let’s look at the application code to record video from the screen of the device.

The screen layout contains a single button to start and stop recording.

Let’s move on to the code. In the main package we see three classes — class RecordApplication, the class MainActivity and the class RecordService.

Consider a class RecordApplication. The call happens in the manifest in the application section in the line android:name=.

Class RecordApplication inherited from the Application class. According to the documentation, the Application class or its successor is initialized before any other class that creates the application process.

This call to the startService method which runs the service RecordService when you start the application.

Class RecordService inherited from the Service class. About what services, details can be found from the video tutorials on our channel, starting with lesson 92.

In a nutshell, the service is a kind of task that works in background and does not use the UI. Because we need to record everything that happens on the screen no matter what app is running, so we will use the service.

Here the declared variables of classes:

MediaProjection is a token that grants the app the ability to capture screen contents and/or record system audio.

MediaRecorder class, which is used to record audio and video.

VirtualDisplay Is a virtual screen, the contents of which is rendered in the Surface, which we pass to the method createVirtualDisplay.

On what Surface are we talking lesson 132.

In a nutshell, is a component that displays an image.

Declare some more variables: a Boolean running, which will assign true to the recording process.

Further parameters of the virtual screen, the resolution is set, and the density is not yet specified.

Next comes IBinder onBind method, which allows the app to connect to the service and interact with it using the returned object RecordBinder. Read more in lesson 97

Now the lifecycle methods of a service.

OnStartCommand method is triggered at the start of the service by startService method, which is invoked in the class RecordApplication. It returns START_STICKY – this means the service will be restarted if killed by the system.

In the onCreate method? which is called at the beginning of the service, create a separate thread serviceThread using the HandlerThread class. This is a helper class for starting a new thread that has a looper that can be used to create handler classes. Input pass an arbitrary stream name and flag THREAD_PRIORITY_BACKGROUND — Standard priority background threads.

Next, we start the flow, reset the value of the variable running and create a mediaRecorder.

In the onDestroy method that is called when you stop a service, simply call the superclass method.

SetMediaProject method will be called in the MainActivity and pass the object mediaProjection.

Further, the getter for the variable running.

Method setConfig sets the parameters of the virtual screen.

In the method startRecord check if the object mediaProjection does not exist and the variable running is set to true, return false.

Call here methods initRecorder and createVirtualDisplay, which we consider later, we start recording call mediaRecorder.start(); assign a variable running = true; and return true.

StopRecord method performs the reverse operation, stops the recording and restarts the mediaRecorder to the idle state. Exempt virtualDisplay and stop mediaProjection.

Now createVirtualDisplay method that is invoked above in the method startRecord. Here, you create a virtual screen through the method mediaProjection.createVirtualDisplay, which takes an arbitrary display name, its parameters, the flag VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, which reflects the contents of a private display if the content is not displayed, and Surface.

Now initRecorder in a method that is invoked above in the method startRecord work with the mediaRecorder object.

The method setAudioSource sets the audio source used for recording.

setVideoSource specifies the video source to be used for recording.

setOutputFormat sets the format of the received record file.

setOutputFile sets the destination location and file name of the recording.

setVideoSize sets the size of the video.

setVideoEncoder determines the video encoder.

setAudioEncoder specifies the audio encoder.

setVideoEncodingBitRate sets the recording rate of the recording file, here hardcoded a constant value equal to 5 Mbps.

setVideoFrameRate sets the frame rate, 30 frames per second.

The prepare() method prepares the mediaRecorder for recording and coding data. Execute it in a try…catch block with the IOException error trapping.

Further, in the method getsaveDirectory specify the path to save record file and show a toast about this user.

Below here create an inner class RecordBinder, this is the binder for communication and interaction with the service application.

In the class MainActivity.java _REQUEST_CODE declared constants with arbitrary values. These constants are used in intents and permissions requests to distinguish from each other come results. Read more about this, see lesson 30

Next, declare variables, classes MediaProjectionManager — Manages the retrieval of certain types of tokens MediaProjection.

MediaProjection you already know is a token that grants the app the ability to capture screen contents and/or record system audio.

Also declare an instance of our service RecordService and normal button.

In the onCreate method get the instance MediaProjectionManager session control display of media data.

Create a button, by default inactive, and the listener for her.

The onClick method for the button will call a method recordService.stopRecord, in the case that the recording is going. Otherwise, create an intent with projectionManager.createScreenCaptureIntent() and send it by the method startActivityForResult.

Next come requests permission to write to the device memory and on the recording itself.

Still here we create another intent to start the service and pass it to the method bindService, which binds the service to the application, and if the service is not running, starts it first.

In the method onDestroy untied service.

OnActivityResult method gets the result of calling the method startActivityForResult, where we send captureIntent and RECORD_REQUEST_CODE.

If the query is successful, create object MediaProjection derived from the successful query screen capture. It will be NULL if the result from the startActivityForResult() is not RESULT_OK.

The called method recordService.startRecord(); and change the button text to «Stop recording»

OnRequestPermissionsResult method is the callback for the result of the permission request. This method is invoked for each method call requestPermissions. It is possible that the process permission request from the user was interrupted. In this case you will get an empty permissions and arrays, which should be treated as a cancellation.

Next, it creates an instance of the ServiceConnection interface implementation methods onServiceConnected and onServiceDisconnected. Interface is used to monitor the status of the service.

The second method is leave blank, and in the first onServiceConnected method, which is called whenever a connection application to the service through a binder, create a class object DisplayMetrics, which is used to determine the actual parameters of the device screen resolution and density.

Get instance recordService and through his method setConfig set the parameters of the virtual screen, passing the obtained values.

Here make active button and change the text on it.

Run the application and enjoy his work.[:]

7 thoughts on “[:ru]Как создать приложение для записи экрана андроид-смартфона[:en]How to create a screen recording application for Android[:]

  1. Не могу понять:
    HandlerThread serviceThread = new HandlerThread("service_thread",
    android.os.Process.THREAD_PRIORITY_BACKGROUND);
    serviceThread.start();

    Зачем? Можете объяснить?

    • В ресурсах нужно строки с этими именами прописать

Добавить комментарий