Skip to content

Instantly share code, notes, and snippets.

@tyrcho
Last active August 29, 2015 14:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyrcho/6d8f1825cfff810d2c63 to your computer and use it in GitHub Desktop.
Save tyrcho/6d8f1825cfff810d2c63 to your computer and use it in GitHub Desktop.
mp4 video from screenshots in scala
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Snippets</groupId>
<artifactId>Snippets</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmArgs>
<jvmArg>-Xms64m</jvmArg>
<jvmArg>-Xmx400m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jcodec</groupId>
<artifactId>jcodec</artifactId>
<version>0.1.5</version>
</dependency>
<dependency>
<groupId>org.clapper</groupId>
<artifactId>grizzled-slf4j_2.10</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
</dependencies>
</project>
import java.awt.GraphicsEnvironment
import java.awt.Rectangle
import java.awt.Robot
import java.awt.image.BufferedImage
import java.io.File
import java.nio.ByteBuffer
import java.util.ArrayList
import java.util.Arrays
import scala.Array.canBuildFrom
import org.jcodec.codecs.h264.H264Encoder
import org.jcodec.codecs.h264.H264Utils
import org.jcodec.common.NIOUtils
import org.jcodec.common.model.ColorSpace
import org.jcodec.common.model.Picture
import org.jcodec.containers.mp4.Brand
import org.jcodec.containers.mp4.MP4Packet
import org.jcodec.containers.mp4.TrackType
import org.jcodec.containers.mp4.muxer.MP4Muxer
import org.jcodec.scale.AWTUtil
import org.jcodec.scale.RgbToYuv420
import grizzled.slf4j.Logging
import javax.imageio.ImageIO
object Screenshots extends App with Logging {
val folder = "c:/temp/screens"
val rollVideo = 30 * 25 // 30 sec
val delay = 200 // 2 sec between shots
def encoder(c: Int)(screen: Int) = new SequenceEncoder(new File(s"$folder/video-$screen-$c.mp4"))
new Thread {
override def run() = {
var i = 0
var videoId = 0
var encoders = 1 to 2 map encoder(0)
while (true) {
i += 1
val id = i % rollVideo
if (id == 0) {
videoId += 1
for (enc <- encoders) enc.finish()
encoders = 1 to 2 map encoder(videoId)
}
Thread.sleep(delay)
info(s"capturing image #$i")
for ((img, enc) <- screenshotImages zip encoders)
enc.encodeImage(img)
}
}
}.start()
def screenshotImages =
for (device <- GraphicsEnvironment.getLocalGraphicsEnvironment.getScreenDevices) yield {
val mode = device.getDisplayMode
val bounds = new Rectangle(0, 0, mode.getWidth, mode.getHeight)
new Robot(device).createScreenCapture(bounds)
}
def takeScreenshots(j: Int): Unit =
for ((img, i) <- screenshotImages.zipWithIndex) {
val filename = s"$folder/$i/saved$j.png"
new File(filename).mkdirs
val outputfile = new File(filename)
ImageIO.write(img, "png", outputfile)
println("saved to " + filename)
}
}
class SequenceEncoder(out: File) {
val ch = NIOUtils.writableFileChannel(out)
val transform = new RgbToYuv420(0, 0)
val encoder = new H264Encoder()
val spsList = new ArrayList[ByteBuffer]()
val ppsList = new ArrayList[ByteBuffer]()
val _out = ByteBuffer.allocate(1920 * 1080 * 6)
var frameNo = 0
val muxer = new MP4Muxer(ch, Brand.MP4)
val outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25)
def encodeImage(bi: BufferedImage) {
val toEncode = Picture.create(bi.getWidth, bi.getHeight, ColorSpace.YUV420)
for (i <- 0 until 3) Arrays.fill(toEncode.getData()(i), 0)
transform.transform(AWTUtil.fromBufferedImage(bi), toEncode)
_out.clear()
val result = encoder.encodeFrame(_out, toEncode)
spsList.clear()
ppsList.clear()
H264Utils.encodeMOVPacket(result, spsList, ppsList)
outTrack.addFrame(new MP4Packet(result, frameNo, 25, 1, frameNo, true, null, frameNo, 0))
frameNo += 1
}
def finish() {
outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList))
muxer.writeHeader()
NIOUtils.closeQuietly(ch)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment