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?