웹페이지 비디오 레코딩

aosamesan·2022년 12월 12일
0

Playwright + OpenCV를 이용해서 비디오 레코딩을 해봅시다.

환경

  • macOS Ventura 13.0.1
  • kotlin 1.7.20
  • Playwright 1.28.1 (com.microsoft.playwright:playwright)
  • OpenCV 4.6.0

어떻게 하나?

Playwright는 스크린샷을 찍어서 바이트 어레이로 받을 수 있기 때문에 다음과 같이 하면 된다.

fun record(browser: Browser, width: Int, height: Int, url: String, fps: Int, length: Int) {
 val frames = fps * length
 val context = browser.newContext(
  Browser.NewContextOptions()
  	.setScreenSize(width, height)
    .setViewportSize(width, height)
 )
 
 val page = context.newPage()
 page.navigate(url, Page.NavigationOptions()
 	.setTimeout(300000.0)
    .setWaitUntil(WaitUtilState.NETWORKIDLE))
 val writer = VideoWriter("test.mov",
  VideoWriter.fourcc('a', 'v', 'c', '1'),
  fps.toDouble(),j Size(width.toDouble(), height.toDouble()
 )
  
 for (frame in 0 until frames) {
  val bytes = page.screenshot(Page.ScreenshotOptions()
   .setType(ScreenshotType.PNG)
   .setScale(ScreenshotType.DEVICE)
  )
  val mat = Imgcodecs.imdecode(MatOfByte(*bytes), CvType.CV_32S)
  writer.write(mat)
 }
 
 writer.release()
}

물론 스크린샷 찍는데 100ms정도 걸리므로 fps는 있으나마나다.


아무리 생각해도 위 방법은 별로다.
완성품이 별로다.

Playwright는 페이지가 실행되고 나서부터의 행적을 녹화할 수 있는데, 이걸 이용하는게 훨씬 좋을 것 같다.
물론 처음 페이지가 로딩되는 것까지 녹화가 되므로 이 부분은 잘라내야한다. 어떻게? 잘.

fun recordPage(filePath: String, url: String, width: Int, height: Int, length: Long) {
	playwright {
    	executablePath("path/to/chrome/executable")
        
        launch {	browser ->
        	val context = browser.newContext(
            	Browser.NewContextOptions()
                	.setScreenSize(width, height)
                    .setViewportSize(width, height)
                    .setRecordVideoDir(Path("temp/videos")
                    .setRecordVideoSize(width, height)
            )
            val start = System.currentTimeMillis()
            val page = context.newPage()
            page.navigate(
            	url,
                Page.NavigationOptions()
                	.setWaitUntil(WaitUntilState.NETWORKIDLE)
            )
            val end = System.currentTimeMillis()
            val loadingTime = (end - start) / 1000
            // 여유 있게 5초 정도 더 찍음
            TimeUnit.SECOND.sleep(loadingTime + length + 5)
            page.close()
            val video = page.video()
            val capture = VideoCapture(video!!.path().absolutePathString())
            val frameRate = capture.get(Videoio.CAP_PROP_FPS)
            val targetFramesLength = (frameRate * length).toInt()
            // 여유 있게 2초 정도 더 줌
            val loadingFrame = (frameRate * (loadingTime + 2)).toInt()
            
            val writer = VideoWriter(filePath, VideoWriter.fourcc('a', 'v', 'c', '1'), frameRate, Size(width.toDouble(), height.toDouble()))
            
            for (f in 0 until loadingFrame + targetFramesLength) {
            	val frame = Mat()
                capture.read(frame)
                if (f < loadingFrame)
                	continue
                if (frame.empty())
                	break
                writer.write(frame)
            }
            
            writer.release()
        
        }
    }
}

영상 커서 옮기는게 VideoCapture.set 으로 된다는데 잘 안되서 어거지로 처음부터 읽되 로딩중이었던 프레임은 무시하는 것으로 했다.

profile
재미로 개발 하는 사람

0개의 댓글