1

I'm trying to get videos of failed tests on my CI server.

The goal is to start the screen capture on every test, and then when it fails save the video somewhere, and when it passes, remove the video. The CI server would pick up the files using adb.

I'm considering using screenrecord for that purpose. I figured it was easy because this binary is already present on the device and works really well when used through ADB.

Here's is how I use it programmatically: open up a thread that will start and follow the process, and start it before the test starts:

@Before
fun baseBefore() {
    startScreenRecording()
}

private fun startScreenRecording() {
    Timber.d("screenrecord: start record thread")
    screenRecordThread = Thread(screenRecordRunnable)
    screenRecordThread.start()
    Timber.d("screenrecord: wait a bit")
    Thread.sleep(5000)
    Timber.d("screenrecord: go")
}

I'm waiting for 5 seconds because screenrecord might need some time to fire up.

Here's the actual screenrecord invocation:

private class ScreenRecordRunnable : Runnable {
    private lateinit var process: Process

    override fun run() {
        try {
            Timber.d("screenrecord-record: start")

// tried also with the following: no luck
//          process = ProcessBuilder(
//                  "/system/bin/screenrecord",
//                  "--time-limit", "60",
//                  "--verbose",
//                  "--bit-rate", "4M",
//                  "--bugreport",
//                  "/sdcard/test.mp4",
//                  "&"
//          )
//                  .redirectErrorStream(true)
//                  .start()

           process = Runtime.getRuntime().exec("/system/bin/screenrecord --time-limit 60 --verbose --bit-rate 4M --bugreport /sdcard/test.mp4")
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            while (true) {
                val line = reader.readLine() ?: break
                Timber.d("screenrecord-record: log: $line")
            }

            Timber.d("screenrecord-record: wait for (isAlive: ${process.isAlive})")
            process.waitFor()

            Timber.d("screenrecord-record: wait stopped; exit value: ${process.exitValue()}")
            reader.close()
        } catch (e: IOException) {
            throw Exception(e)
        } catch (e: InterruptedException) {
            throw Exception(e)
        }
    }
}

In the @After method, I retrieve the PID of the screenrecord process (through pgrep, it works) and I send it kill -2 $pid, to simulate the Ctrl-C on that process, in order to stop screen recording.
Code for this is not included because it doesn't seem relevant.

Here's what's happening:

12-16 21:11:31.527  5487  5543 D InstrumentedTestBase: screenrecord: start record thread
12-16 21:11:31.528  5487  5543 D InstrumentedTestBase: screenrecord: wait a bit
12-16 21:11:31.528  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: start
12-16 21:11:31.633  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Main display is 1440x2960 @60.00fps (orientation=0)
12-16 21:11:31.633  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Configuring recorder for 1440x2960 video/avc at 4.00Mbps
12-16 21:11:32.362  5574  5574 W screenrecord: type=1400 audit(0.0:22906): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=19753 scontext=u:r:untrusted_app:s0:c242,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
12-16 21:11:32.492  5574  5574 W screenrecord: type=1400 audit(0.0:22907): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=19753 scontext=u:r:untrusted_app:s0:c242,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
12-16 21:11:32.543  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Bugreport overlay created
12-16 21:11:32.544  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Content area is 1440x2960 at offset x=0 y=0
12-16 21:11:39.780  5487  5543 D InstrumentedTestBase: screenrecord: go
12-16 21:11:43.546  5487  5543 D InstrumentedTestBase: screenrecord: wait for record thread to stop
12-16 21:11:43.546  5487  5543 D InstrumentedTestBase$ScreenRecordStopperRunnable: screenrecord-stopper: get pid
12-16 21:11:43.578  5487  5543 D InstrumentedTestBase$ScreenRecordStopperRunnable: screenrecord-stopper: getpid log: 5574
12-16 21:11:43.579  5487  5543 D InstrumentedTestBase$ScreenRecordStopperRunnable: screenrecord-stopper: kill -2 5574
12-16 21:11:43.679  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Encoder stopping; recorded 1 frames in 7 seconds
12-16 21:11:43.680  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Stopping encoder and muxer
12-16 21:11:43.784  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Executing: /system/bin/am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/test.mp4
12-16 21:11:43.860  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: log: Broadcasting: Intent { act=android.intent.action.MEDIA_SCANNER_SCAN_FILE dat=file:///sdcard/test.mp4 flg=0x400000 }
12-16 21:11:43.919  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: wait for (isAlive: true)
12-16 21:11:43.919  5487  5573 D InstrumentedTestBase$ScreenRecordRunnable: screenrecord-record: wait stopped; exit value: 0
12-16 21:11:44.013  5487  5543 D InstrumentedTestBase: screenrecord: rename video file to /sdcard/failed_test_emptyDeclarationState.mp4

As you can see everything looks fine, except that only one frame is saved:

Encoder stopping; recorded 1 frames in 7 seconds

that frame is the --bugreport result, with device information (make, model, etc.).

Is there anything I can do to capture a video of that test?

Benoit Duffez
  • 11,839
  • 12
  • 77
  • 125

0 Answers0