Pages

Sunday, November 5, 2017

Record selenium test video in mp4 format (how to record screen using java)

A little history about test case video recording
  1. ATU screen recorder
  2. Monte Screen Recorder
above mentioned libraries can help in recording video of selenium test. Both are equally good but these libraries record video in mov and avi format which are not HTML5 compatible and execution result video cannot be embedded on result page using video tag.

So is it possible to record video in HTML5 compatible MP4 format ?

Yes it is, using VLCJ library this problem can be solved. VLCJ is open source library which provides java binding for VLC media player(you can find more details here ). As VLC is portable, open source and  cross platform library which works on different type of operating systems, this solution can be used across multiple platforms to record screen.

STEP 1:
add all prerequisites:

Create maven project with below dependencies, or just add these jar files in your project manually

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<dependency>
    <groupId>uk.co.caprica</groupId>
    <artifactId>vlcj</artifactId>
    <version>3.10.1</version>
</dependency>
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.9.1</version>
</dependency>
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.8</version>
    <scope>test</scope>
</dependency>

STEP 2:
Add VLC native libraries into your projects lib folder. You can find these libraries in VLC installation(C:\Program Files\VideoLAN\VLC\)
  1. libvlc.dll
  2. libvlccore.dll
  3. plugins directory
Note: While copying these libraries, you must match the CPU architecture of the Java Virtual Machine and the native LibVLC libraries - if you use a 32-bit JVM you must use a 32-bit release of VLC; if you use a 64-bit JVM you must use a 64-bit release of VLC. You can not mix CPU architectures, it will not work.

STEP 3:
add log4j.xml file into /src/main/resources

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} %x - %m%n" />
        </layout>
    </appender>
    <appender name="file" class="org.apache.log4j.RollingFileAppender">
        <param name="append" value="false" />
        <param name="maxFileSize" value="5MB" />
        <param name="maxBackupIndex" value="5" />
        <param name="file" value="logs/ScreenRecorder.log" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
        </layout>
    </appender>
    <root>
        <priority value="info" />
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    </root>
</log4j:configuration>

STEP 4 :
Add code to record screen, (Below sample code uses 64-bit JVM and VLC libraries)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.exprimentalqa.screenrecorder;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.NativeLibrary;

import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.runtime.RuntimeUtil;

public class ScreenRecorder  {
    private final Logger logger =  LoggerFactory.getLogger(ScreenRecorder.class);
    private static final String[] OPTIONS = {
            "--quiet",
            "--quiet-synchro",
            "--intf",
            "dummy"
    };

    private static final String MRL     = "screen://";
    private static final String SOUT    = ":sout=#transcode{vcodec=h264,vb=%d,scale=%f}:duplicate{dst=file{dst=%s}}";
    private static final String FPS     = ":screen-fps=%d";
    private static final String CACHING = ":screen-caching=%d";

    private static final int    fps     = 20;
    private static final int    caching = 500;
    private static final int    bits    = 1024;
    private static final float  scale   = 0.5f;

    private final MediaPlayerFactory mediaPlayerFactory;
    private final MediaPlayer mediaPlayer;

    public ScreenRecorder() {
        NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), System.getProperty("user.dir")+"/lib");
        System.setProperty("VLC_PLUGIN_PATH",  System.getProperty("user.dir")+"/lib/plugins");
        mediaPlayerFactory = new MediaPlayerFactory(OPTIONS);
        mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer();
    }

    public void startRecording(String testName) {
        String mp4FileName = getFile(testName);
        logger.info("start recording, "+ mp4FileName);
        mediaPlayer.playMedia(MRL, getMediaOptions(mp4FileName));
    }

    public void stopRecording() {
        logger.info("stop recording ");
        mediaPlayer.stop();
    }
    
    public void releaseRecordingResources() {
        mediaPlayer.release();
        mediaPlayerFactory.release();
    }

    private String getFile(String testName) {
        File dir = new File(System.getProperty("user.dir"), "mp4Result");
        dir.mkdirs();
        DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
        return dir.getAbsolutePath() + "/" + testName + "-" + df.format(new Date()) + ".mp4";
    }
    
    private String[] getMediaOptions(String destination) {
        return new String[] {
                String.format(SOUT, bits, scale, destination),
                String.format(FPS, fps),
                String.format(CACHING, caching)
        };
    }
}


STEP 5:
Let's record tests, testNG ITestListner can be implemented to start and stop recording.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.exprimentalqa.listeners;

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import com.exprimentalqa.screenrecorder.ScreenRecorder;

public class TestListener implements ITestListener {
    ScreenRecorder recordTest;
    public void onFinish(ITestContext arg0) {
        recordTest.releaseRecordingResources();
    }

    public void onStart(ITestContext arg0) {
        recordTest = new ScreenRecorder();
    }

    public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) {
        recordTest.stopRecording();
    }

    public void onTestFailure(ITestResult arg0) {
        recordTest.stopRecording();
    }

    public void onTestSkipped(ITestResult arg0) {
        recordTest.stopRecording();
    }

    public void onTestStart(ITestResult arg0) {
        recordTest.startRecording(arg0.getName());
    }

    public void onTestSuccess(ITestResult arg0) {
        recordTest.stopRecording();
    }
}

STEP 6:
Now test if it is working by creating testNG test method,

1
2
3
4
5
6
@Test
public void test2() {
    logger.info("start test 2");
    // perform selenium test operations
    logger.info("end of test 2");
}

By changing trnacoding url you can record video in other HTML5 compatible format i.e. .ogg or .webm Try below mentioned transcode URLs and check if these are working,

1
2
private static final String RECORDINGFORMAT = ".webm";
private static final String SOUT = ":sout=#transcode{vcodec=VP80,vb=%d,scale=%f}:std{access=file{no-overwrite},mux=webm,dst='%s'}";

1
2
private static final String RECORDINGFORMAT = ".ogg";
private static final String SOUT = ":sout=#transcode{vcodec=theo,vb=%d,scale=%f}:std{access=file{no-overwrite},mux=ogg,dst='%s'}";

If you are interested more in video operations, check this VLCJ official wiki page and VLC command line examples
ScreenRecoder test project example can be downloaded from this github link

7 comments:

  1. Hi
    Can we record audio along with the video in it.Video is working fine.

    ReplyDelete
    Replies
    1. I have not tried audio recording. need to check.

      Delete
  2. Hi there, I have a question:

    Is it possible save the video if the script fails before the screenRecorder.stop(); method?

    Thanks in advance.

    ReplyDelete
    Replies
    1. yes, if test script fails TestListener identifies it. So on failure, skip result we are stopping recording which will create mp4 file. It can be further customized by adding extra parameters in config so that you can record video only for failed scripts.

      Delete
    2. Hi experimental qa, thank you so much for this amazing tutorial. Where can I find the extra script that only record video for failed scripts?

      Delete
  3. Does it work in windows iam getting Unable to load library 'libvlc': Native library (win32-x86-64/libvlc.dll) not found in resource path

    ReplyDelete
  4. when i execute this, i get video files in mp4result folder as expected. but all files are 158 bytes, not working video files. Any ideas?

    ReplyDelete