Tuesday, July 11, 2017

Test Automation Screen Recording

Test analyzing become more sophisticated and time consuming.Record a video of your test failure will make things awesome for you.I will explain step by step how to record a video of your test failure.


Initially you should have this dependency placed in your pom.xml 

<dependency>
    <groupId>com.pojosontheweb</groupId>
    <artifactId>monte-repack</artifactId>
    <version>1.0</version>
</dependency>



Create screen recorder class


import org.monte.media.*;
import org.monte.media.FormatKeys.MediaType;
import org.monte.media.avi.AVIWriter;
import org.monte.media.beans.AbstractStateModel;
import org.monte.media.color.Colors;
import org.monte.media.converter.CodecChain;
import org.monte.media.converter.ScaleImageCodec;
import org.monte.media.image.Images;
import org.monte.media.math.Rational;
import org.monte.media.quicktime.QuickTimeWriter;

import javax.sound.sampled.*;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.BooleanControl.Type;
import javax.sound.sampled.DataLine.Info;
import javax.swing.*;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class ScreenRecorder extends AbstractStateModel {
    public static final String ENCODING_BLACK_CURSOR = "black";
    public static final String ENCODING_WHITE_CURSOR = "white";
    public static final String ENCODING_YELLOW_CURSOR = "yellow";
    private final Object sync;
    protected Format mouseFormat;
    protected long recordingStartTime;
    protected volatile long recordingStopTime;
    protected ScheduledThreadPoolExecutor mouseCaptureTimer;
    protected int videoTrack;
    protected int audioTrack;
    protected ScreenRecorder.MouseGrabber mouseGrabber;
    protected ScheduledFuture mouseFuture;
    protected File movieFolder;
    long counter;
    File screenRecorderName;
    private ScreenRecorder.State state;
    private String stateMessage;
    private Format fileFormat;
    private Format screenFormat;
    private Format audioFormat;
    private Rectangle captureArea;
    private MovieWriter w;
    private long fileStartTime;
    private ArrayBlockingQueue<Buffer> mouseCaptures;
    private ScheduledThreadPoolExecutor screenCaptureTimer;
    private ScheduledThreadPoolExecutor audioCaptureTimer;
    private volatile Thread writerThread;
    private BufferedImage cursorImg;
    private BufferedImage cursorImgPressed;
    private Point cursorOffset;
    private ArrayBlockingQueue<Buffer> writerQueue;
    private Codec frameEncoder;
    private Rational outputTime;
    private Rational ffrDuration;
    private ArrayList<File> recordedFiles;
    private GraphicsDevice captureDevice;
    private ScreenRecorder.AudioGrabber audioGrabber;
    private ScreenRecorder.ScreenGrabber screenGrabber;
    private ScheduledFuture audioFuture;
    private ScheduledFuture screenFuture;
    private AWTEventListener awtEventListener;
    private long maxRecordingTime;
    private long maxFileSize;
    private Mixer mixer;

    public ScreenRecorder(GraphicsConfiguration cfg) throws IOException, AWTException {
        this(cfg, (Rectangle) null, new Format(new Object[]{FormatKeys.MediaTypeKey, MediaType.FILE, FormatKeys.MimeTypeKey, "video/quicktime"}), new Format(new Object[]{FormatKeys.MediaTypeKey, MediaType.VIDEO, FormatKeys.EncodingKey, "rle ", VideoFormatKeys.CompressorNameKey, "Animation", VideoFormatKeys.DepthKey, Integer.valueOf(24), FormatKeys.FrameRateKey, new Rational(15L, 1L)}), new Format(new Object[]{FormatKeys.MediaTypeKey, MediaType.VIDEO, FormatKeys.EncodingKey, "black", FormatKeys.FrameRateKey, new Rational(30L, 1L)}), new Format(new Object[]{FormatKeys.MediaTypeKey, MediaType.AUDIO, FormatKeys.EncodingKey, "twos", FormatKeys.FrameRateKey, new Rational(48000L, 1L), AudioFormatKeys.SampleSizeInBitsKey, Integer.valueOf(16), AudioFormatKeys.ChannelsKey, Integer.valueOf(2), AudioFormatKeys.SampleRateKey, new Rational(48000L, 1L), AudioFormatKeys.SignedKey, Boolean.valueOf(true), AudioFormatKeys.ByteOrderKey, ByteOrder.BIG_ENDIAN}));
    }

    public ScreenRecorder(GraphicsConfiguration cfg, Format fileFormat, Format screenFormat, Format mouseFormat, Format audioFormat) throws IOException, AWTException {
        this(cfg, (Rectangle) null, fileFormat, screenFormat, mouseFormat, audioFormat);
    }

    public ScreenRecorder(GraphicsConfiguration cfg, Rectangle captureArea, Format fileFormat, Format screenFormat, Format mouseFormat, Format audioFormat) throws IOException, AWTException {
        this(cfg, (Rectangle) null, fileFormat, screenFormat, mouseFormat, audioFormat, (File) null);
    }

    public ScreenRecorder(GraphicsConfiguration cfg, Rectangle captureArea, Format fileFormat, Format screenFormat, Format mouseFormat, Format audioFormat, File movieFolder) throws IOException, AWTException {
        this.state = ScreenRecorder.State.DONE;
        this.stateMessage = null;
        this.sync = new Object();
        this.videoTrack = 0;
        this.audioTrack = 1;
        this.maxRecordingTime = 3600000L;
        this.maxFileSize = 9223372036854775807L;
        this.counter = 0L;
        this.fileFormat = fileFormat;
        this.screenFormat = screenFormat;
        this.mouseFormat = mouseFormat;
        if (this.mouseFormat == null) {
            this.mouseFormat = new Format(new Object[]{FormatKeys.FrameRateKey, new Rational(0L, 0L), FormatKeys.EncodingKey, "black"});
        }

        this.audioFormat = audioFormat;
        this.recordedFiles = new ArrayList();
        this.captureDevice = cfg.getDevice();
        this.captureArea = captureArea == null ? cfg.getBounds() : captureArea;
        if (mouseFormat != null && ((Rational) mouseFormat.get(FormatKeys.FrameRateKey)).intValue() > 0) {
            this.mouseCaptures = new ArrayBlockingQueue(((Rational) mouseFormat.get(FormatKeys.FrameRateKey)).intValue() * 2);
            if (((String) this.mouseFormat.get(FormatKeys.EncodingKey)).equals("black")) {

//Please provide these images location it is the screen pointers
                String filelocation = System.getProperty("user.dir") + "/src/main/resources/images/Cursor.black.png";
                String filelocation2 = System.getProperty("user.dir") + "/src/main/resources/images/Cursor.black.pressed.png";

                this.cursorImg = Images.toBufferedImage(createImage(filelocation));
                this.cursorImgPressed = Images.toBufferedImage(createImage(filelocation2));
            } else if (((String) this.mouseFormat.get(FormatKeys.EncodingKey)).equals("yellow")) {
                this.cursorImg = Images.toBufferedImage(Images.createImage(ScreenRecorder.class, "images/Cursor.yellow.png"));
                this.cursorImgPressed = Images.toBufferedImage(Images.createImage(ScreenRecorder.class, "images/Cursor.yellow.pressed.png"));
            } else {
                this.cursorImg = Images.toBufferedImage(Images.createImage(ScreenRecorder.class, "images/Cursor.white.png"));
                this.cursorImgPressed = Images.toBufferedImage(Images.createImage(ScreenRecorder.class, "images/Cursor.white.pressed.png"));
            }

            this.cursorOffset = new Point(this.cursorImg.getWidth() / -2, this.cursorImg.getHeight() / -2);
        }

        this.movieFolder = movieFolder;
        if (this.movieFolder == null) {
            this.movieFolder = new File(System.getProperty("java.io.tmpdir") + File.separator + "screenrecorder");
        }

    }


    public static Image createImage(String resource) {
        Image image = Toolkit.getDefaultToolkit().createImage(resource);
        return image;
    }

    protected MovieWriter createMovieWriter(String testName) throws IOException {
        screenRecorderName = this.createMovieFile(this.fileFormat, testName);
        File f = screenRecorderName;
        this.recordedFiles.add(f);
        MovieWriter mw = this.w = Registry.getInstance().getWriter(this.fileFormat, f);
        Rational videoRate = Rational.max((Rational) this.screenFormat.get(FormatKeys.FrameRateKey), (Rational) this.mouseFormat.get(FormatKeys.FrameRateKey));
        this.ffrDuration = videoRate.inverse();
        Format videoInputFormat = this.screenFormat.prepend(new Object[]{FormatKeys.MediaTypeKey, MediaType.VIDEO, FormatKeys.EncodingKey, "image", VideoFormatKeys.WidthKey, Integer.valueOf(this.captureArea.width), VideoFormatKeys.HeightKey, Integer.valueOf(this.captureArea.height), FormatKeys.FrameRateKey, videoRate});
        Format videoOutputFormat = this.screenFormat.prepend(new Object[]{FormatKeys.FrameRateKey, videoRate, FormatKeys.MimeTypeKey, this.fileFormat.get(FormatKeys.MimeTypeKey)}).append(new Object[]{VideoFormatKeys.WidthKey, Integer.valueOf(this.captureArea.width), VideoFormatKeys.HeightKey, Integer.valueOf(this.captureArea.height)});
        this.videoTrack = this.w.addTrack(videoOutputFormat);
        if (this.audioFormat != null) {
            this.audioTrack = this.w.addTrack(this.audioFormat);
        }

        Codec encoder = Registry.getInstance().getEncoder(this.w.getFormat(this.videoTrack));
        if (encoder == null) {
            throw new IOException("No encoder for format " + this.w.getFormat(this.videoTrack));
        } else {
            this.frameEncoder = encoder;
            this.frameEncoder.setInputFormat(videoInputFormat);
            this.frameEncoder.setOutputFormat(videoOutputFormat);
            if (this.frameEncoder.getOutputFormat() == null) {
                throw new IOException("Unable to encode video frames in this output format:\n" + videoOutputFormat);
            } else {
                if (!videoInputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}).matches(videoOutputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}))) {
                    ScaleImageCodec qtw = new ScaleImageCodec();
                    qtw.setInputFormat(videoInputFormat);
                    qtw.setOutputFormat(videoOutputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}).append(videoInputFormat));
                    this.frameEncoder = new CodecChain(qtw, this.frameEncoder);
                }

                if (((Integer) this.screenFormat.get(VideoFormatKeys.DepthKey)).intValue() == 8) {
                    if (this.w instanceof AVIWriter) {
                        AVIWriter qtw1 = (AVIWriter) this.w;
                        qtw1.setPalette(this.videoTrack, Colors.createMacColors());
                    } else if (this.w instanceof QuickTimeWriter) {
                        QuickTimeWriter qtw2 = (QuickTimeWriter) this.w;
                        qtw2.setVideoColorTable(this.videoTrack, Colors.createMacColors());
                    }
                }

                this.fileStartTime = System.currentTimeMillis();
                return mw;
            }
        }
    }

    protected MovieWriter createMovieWriter() throws IOException {
        File f = this.createMovieFile(this.fileFormat);
        this.recordedFiles.add(f);
        MovieWriter mw = this.w = Registry.getInstance().getWriter(this.fileFormat, f);
        Rational videoRate = Rational.max((Rational) this.screenFormat.get(FormatKeys.FrameRateKey), (Rational) this.mouseFormat.get(FormatKeys.FrameRateKey));
        this.ffrDuration = videoRate.inverse();
        Format videoInputFormat = this.screenFormat.prepend(new Object[]{FormatKeys.MediaTypeKey, MediaType.VIDEO, FormatKeys.EncodingKey, "image", VideoFormatKeys.WidthKey, Integer.valueOf(this.captureArea.width), VideoFormatKeys.HeightKey, Integer.valueOf(this.captureArea.height), FormatKeys.FrameRateKey, videoRate});
        Format videoOutputFormat = this.screenFormat.prepend(new Object[]{FormatKeys.FrameRateKey, videoRate, FormatKeys.MimeTypeKey, this.fileFormat.get(FormatKeys.MimeTypeKey)}).append(new Object[]{VideoFormatKeys.WidthKey, Integer.valueOf(this.captureArea.width), VideoFormatKeys.HeightKey, Integer.valueOf(this.captureArea.height)});
        this.videoTrack = this.w.addTrack(videoOutputFormat);
        if (this.audioFormat != null) {
            this.audioTrack = this.w.addTrack(this.audioFormat);
        }

        Codec encoder = Registry.getInstance().getEncoder(this.w.getFormat(this.videoTrack));
        if (encoder == null) {
            throw new IOException("No encoder for format " + this.w.getFormat(this.videoTrack));
        } else {
            this.frameEncoder = encoder;
            this.frameEncoder.setInputFormat(videoInputFormat);
            this.frameEncoder.setOutputFormat(videoOutputFormat);
            if (this.frameEncoder.getOutputFormat() == null) {
                throw new IOException("Unable to encode video frames in this output format:\n" + videoOutputFormat);
            } else {
                if (!videoInputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}).matches(videoOutputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}))) {
                    ScaleImageCodec qtw = new ScaleImageCodec();
                    qtw.setInputFormat(videoInputFormat);
                    qtw.setOutputFormat(videoOutputFormat.intersectKeys(new FormatKey[]{VideoFormatKeys.WidthKey, VideoFormatKeys.HeightKey}).append(videoInputFormat));
                    this.frameEncoder = new CodecChain(qtw, this.frameEncoder);
                }

                if (((Integer) this.screenFormat.get(VideoFormatKeys.DepthKey)).intValue() == 8) {
                    if (this.w instanceof AVIWriter) {
                        AVIWriter qtw1 = (AVIWriter) this.w;
                        qtw1.setPalette(this.videoTrack, Colors.createMacColors());
                    } else if (this.w instanceof QuickTimeWriter) {
                        QuickTimeWriter qtw2 = (QuickTimeWriter) this.w;
                        qtw2.setVideoColorTable(this.videoTrack, Colors.createMacColors());
                    }
                }

                this.fileStartTime = System.currentTimeMillis();
                return mw;
            }
        }
    }

    public List<File> getCreatedMovieFiles() {
        return Collections.unmodifiableList(this.recordedFiles);
    }

    protected File createMovieFile(Format fileFormat) throws IOException {
        if (!this.movieFolder.exists()) {
            this.movieFolder.mkdirs();
        } else if (!this.movieFolder.isDirectory()) {
            throw new IOException("\"" + this.movieFolder + "\" is not a directory.");
        }

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd \'at\' HH.mm.ss");
        File f = new File(this.movieFolder, "ScreenRecording " + dateFormat.format(new Date()) + "." + Registry.getInstance().getExtension(fileFormat));
        return f;
    }

    protected File createMovieFile(Format fileFormat, String name) throws IOException {

        if (!this.movieFolder.exists()) {
            this.movieFolder.mkdirs();
        } else if (!this.movieFolder.isDirectory()) {
            throw new IOException("\"" + this.movieFolder + "\" is not a directory.");
        }

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd \'at\' HH.mm.ss");
        File f = new File(this.movieFolder, name + dateFormat.format(new Date()) + "." + Registry.getInstance().getExtension(fileFormat));
        return f;

    }

    public ScreenRecorder.State getState() {
        return this.state;
    }

    public String getStateMessage() {
        return this.stateMessage;
    }

    private void setState(ScreenRecorder.State newValue, String msg) {
        this.state = newValue;
        this.stateMessage = msg;
        this.fireStateChanged();
    }

    public long getStartTime() {
        return this.recordingStartTime;
    }

    public void start() throws IOException {
        this.stop();
        this.recordedFiles.clear();
        this.createMovieWriter();

        try {
            this.recordingStartTime = System.currentTimeMillis();
            this.recordingStopTime = 9223372036854775807L;
            this.outputTime = new Rational(0L, 0L);
            this.startWriter();

            IOException ioe;
            try {
                this.startScreenCapture();
            } catch (AWTException var4) {
                ioe = new IOException("Start screen capture failed");
                ioe.initCause(var4);
                this.stop();
                throw ioe;
            } catch (IOException var5) {
                this.stop();
                throw var5;
            }

            if (this.mouseFormat != null && ((Rational) this.mouseFormat.get(FormatKeys.FrameRateKey)).intValue() > 0) {
                this.startMouseCapture();
            }

            if (this.audioFormat != null) {
                try {
                    this.startAudioCapture();
                } catch (LineUnavailableException var3) {
                    ioe = new IOException("Start audio capture failed");
                    ioe.initCause(var3);
                    this.stop();
                    throw ioe;
                }
            }

            this.setState(ScreenRecorder.State.RECORDING, (String) null);
        } catch (IOException var6) {
            this.stop();
            throw var6;
        }
    }


    public void start(String testName) throws IOException {
        this.stop();
        this.recordedFiles.clear();
        this.createMovieWriter(testName);

        try {
            this.recordingStartTime = System.currentTimeMillis();
            this.recordingStopTime = 9223372036854775807L;
            this.outputTime = new Rational(0L, 0L);
            this.startWriter();

            IOException ioe;
            try {
                this.startScreenCapture();
            } catch (AWTException var4) {
                ioe = new IOException("Start screen capture failed");
                ioe.initCause(var4);
                this.stop();
                throw ioe;
            } catch (IOException var5) {
                this.stop();
                throw var5;
            }

            if (this.mouseFormat != null && ((Rational) this.mouseFormat.get(FormatKeys.FrameRateKey)).intValue() > 0) {
                this.startMouseCapture();
            }

            if (this.audioFormat != null) {
                try {
                    this.startAudioCapture();
                } catch (LineUnavailableException var3) {
                    ioe = new IOException("Start audio capture failed");
                    ioe.initCause(var3);
                    this.stop();
                    throw ioe;
                }
            }

            this.setState(ScreenRecorder.State.RECORDING, (String) null);
        } catch (IOException var6) {
            this.stop();
            throw var6;
        }
    }

    private void startScreenCapture() throws AWTException, IOException {
        this.screenCaptureTimer = new ScheduledThreadPoolExecutor(1);
        int delay = Math.max(1, (int) (1000.0D / ((Rational) this.screenFormat.get(FormatKeys.FrameRateKey)).doubleValue()));
        this.screenGrabber = new ScreenRecorder.ScreenGrabber(this, this.recordingStartTime);
        this.screenFuture = this.screenCaptureTimer.scheduleAtFixedRate(this.screenGrabber, (long) delay, (long) delay, TimeUnit.MILLISECONDS);
        this.screenGrabber.setFuture(this.screenFuture);
    }

    protected void startMouseCapture() throws IOException {
        this.mouseCaptureTimer = new ScheduledThreadPoolExecutor(1);
        int delay = Math.max(1, (int) (1000.0D / ((Rational) this.mouseFormat.get(FormatKeys.FrameRateKey)).doubleValue()));
        this.mouseGrabber = new ScreenRecorder.MouseGrabber(this, this.recordingStartTime, this.mouseCaptureTimer);
        this.mouseFuture = this.mouseCaptureTimer.scheduleAtFixedRate(this.mouseGrabber, (long) delay, (long) delay, TimeUnit.MILLISECONDS);
        final ScreenRecorder.MouseGrabber mouseGrabberF = this.mouseGrabber;
        this.awtEventListener = new AWTEventListener() {
            public void eventDispatched(AWTEvent event) {
                if (event.getID() == 501) {
                    mouseGrabberF.setMousePressed(true);
                } else if (event.getID() == 502) {
                    mouseGrabberF.setMousePressed(false);
                }

            }
        };
        Toolkit.getDefaultToolkit().addAWTEventListener(this.awtEventListener, 16L);
        this.mouseGrabber.setFuture(this.mouseFuture);
    }

    protected void stopMouseCapture() {
        if (this.mouseCaptureTimer != null) {
            this.mouseGrabber.setStopTime(this.recordingStopTime);
        }

        if (this.awtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(this.awtEventListener);
            this.awtEventListener = null;
        }

    }

    protected void waitUntilMouseCaptureStopped() throws InterruptedException {
        if (this.mouseCaptureTimer != null) {
            try {
                this.mouseFuture.get();
            } catch (InterruptedException var2) {
                ;
            } catch (CancellationException var3) {
                ;
            } catch (ExecutionException var4) {
                ;
            }

            this.mouseCaptureTimer.shutdown();
            this.mouseCaptureTimer.awaitTermination(5000L, TimeUnit.MILLISECONDS);
            this.mouseCaptureTimer = null;
            this.mouseGrabber.close();
            this.mouseGrabber = null;
        }

    }

    private void startAudioCapture() throws LineUnavailableException {
        this.audioCaptureTimer = new ScheduledThreadPoolExecutor(1);
        boolean delay = true;
        this.audioGrabber = new ScreenRecorder.AudioGrabber(this.mixer, this.audioFormat, this.audioTrack, this.recordingStartTime, this.writerQueue);
        this.audioFuture = this.audioCaptureTimer.scheduleWithFixedDelay(this.audioGrabber, 0L, 10L, TimeUnit.MILLISECONDS);
        this.audioGrabber.setFuture(this.audioFuture);
    }

    public float getAudioLevelLeft() {
        ScreenRecorder.AudioGrabber ag = this.audioGrabber;
        return ag != null ? ag.getAudioLevelLeft() : -1.0F;
    }

    public float getAudioLevelRight() {
        ScreenRecorder.AudioGrabber ag = this.audioGrabber;
        return ag != null ? ag.getAudioLevelRight() : -1.0F;
    }

    private void startWriter() {
        this.writerQueue = new ArrayBlockingQueue(Math.max(((Rational) this.screenFormat.get(FormatKeys.FrameRateKey)).intValue(), ((Rational) this.mouseFormat.get(FormatKeys.FrameRateKey)).intValue()) + 1);
        this.writerThread = new Thread() {
            public void run() {
                try {
                    while (ScreenRecorder.this.writerThread == this || !ScreenRecorder.this.writerQueue.isEmpty()) {
                        try {
                            Buffer e = (Buffer) ScreenRecorder.this.writerQueue.take();
                            ScreenRecorder.this.doWrite(e);
                        } catch (InterruptedException var2) {
                            break;
                        }
                    }
                } catch (Throwable var3) {
                    var3.printStackTrace();
                    ScreenRecorder.this.recordingFailed(var3.getMessage() == null ? var3.toString() : var3.getMessage());
                }

            }
        };
        this.writerThread.start();
    }

    private void recordingFailed(final String msg) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    ScreenRecorder.this.stop();
                    ScreenRecorder.this.setState(ScreenRecorder.State.FAILED, msg);
                } catch (IOException var2) {
                    var2.printStackTrace();
                }

            }
        });
    }

    public void stop() throws IOException {
        if (this.state == ScreenRecorder.State.RECORDING) {
            this.recordingStopTime = System.currentTimeMillis();
            this.stopMouseCapture();
            if (this.screenCaptureTimer != null) {
                this.screenGrabber.setStopTime(this.recordingStopTime);
            }

            if (this.audioCaptureTimer != null) {
                this.audioGrabber.setStopTime(this.recordingStopTime);
            }

            try {
                this.waitUntilMouseCaptureStopped();
                if (this.screenCaptureTimer != null) {
                    try {
                        this.screenFuture.get();
                    } catch (InterruptedException var5) {
                        ;
                    } catch (CancellationException var6) {
                        ;
                    } catch (ExecutionException var7) {
                        ;
                    }

                    this.screenCaptureTimer.shutdown();
                    this.screenCaptureTimer.awaitTermination(5000L, TimeUnit.MILLISECONDS);
                    this.screenCaptureTimer = null;
                    this.screenGrabber.close();
                    this.screenGrabber = null;
                }

                if (this.audioCaptureTimer != null) {
                    try {
                        this.audioFuture.get();
                    } catch (InterruptedException var2) {
                        ;
                    } catch (CancellationException var3) {
                        ;
                    } catch (ExecutionException var4) {
                        ;
                    }

                    this.audioCaptureTimer.shutdown();
                    this.audioCaptureTimer.awaitTermination(5000L, TimeUnit.MILLISECONDS);
                    this.audioCaptureTimer = null;
                    this.audioGrabber.close();
                    this.audioGrabber = null;
                }
            } catch (InterruptedException var8) {
                ;
            }

            this.stopWriter();
            this.setState(ScreenRecorder.State.DONE, (String) null);
        }

    }

    public void stop(boolean writeFile) throws IOException {
        if (this.state == ScreenRecorder.State.RECORDING) {
            this.recordingStopTime = System.currentTimeMillis();
            this.stopMouseCapture();
            if (this.screenCaptureTimer != null) {
                this.screenGrabber.setStopTime(this.recordingStopTime);
            }

            if (this.audioCaptureTimer != null) {
                this.audioGrabber.setStopTime(this.recordingStopTime);
            }

            try {
                this.waitUntilMouseCaptureStopped();
                if (this.screenCaptureTimer != null) {
                    try {
                        this.screenFuture.get();
                    } catch (InterruptedException var5) {
                        ;
                    } catch (CancellationException var6) {
                        ;
                    } catch (ExecutionException var7) {
                        ;
                    }

                    this.screenCaptureTimer.shutdown();
                    this.screenCaptureTimer.awaitTermination(5000L, TimeUnit.MILLISECONDS);
                    this.screenCaptureTimer = null;
                    this.screenGrabber.close();
                    this.screenGrabber = null;
                }

                if (this.audioCaptureTimer != null) {
                    try {
                        this.audioFuture.get();
                    } catch (InterruptedException var2) {
                        ;
                    } catch (CancellationException var3) {
                        ;
                    } catch (ExecutionException var4) {
                        ;
                    }

                    this.audioCaptureTimer.shutdown();
                    this.audioCaptureTimer.awaitTermination(5000L, TimeUnit.MILLISECONDS);
                    this.audioCaptureTimer = null;
                    this.audioGrabber.close();
                    this.audioGrabber = null;
                }
            } catch (InterruptedException var8) {
                ;
            }

            this.stopWriter(writeFile);
            this.setState(ScreenRecorder.State.DONE, (String) null);
        }

    }

    private void stopWriter() throws IOException {
        Thread pendingWriterThread = this.writerThread;
        this.writerThread = null;

        try {
            if (pendingWriterThread != null) {
                pendingWriterThread.interrupt();
                pendingWriterThread.join();
            }
        } catch (InterruptedException var3) {
            var3.printStackTrace();
        }

        if (this.w != null) {
            this.w.close();
            this.w = null;
        }

    }

    private void stopWriter(boolean writeToFile) throws IOException {
        Thread pendingWriterThread = this.writerThread;
        this.writerThread = null;

        try {
            if (pendingWriterThread != null) {
                pendingWriterThread.interrupt();
                pendingWriterThread.join();
            }
        } catch (InterruptedException var3) {
            var3.printStackTrace();
        }


        if (!writeToFile) {
            try {
                screenRecorderName.delete();
            } catch (Exception e) {
                System.out.print(e.getMessage());
            }
            w.close();
        }
        if (this.w != null) {
            this.w.close();
            this.w = null;
        }

    }

    protected void write(Buffer buf) throws IOException, InterruptedException {
        MovieWriter writer = this.w;
        if (writer != null) {
            Buffer wbuf;
            if (buf.track == this.videoTrack) {
                if (!((Boolean) writer.getFormat(this.videoTrack).get(VideoFormatKeys.FixedFrameRateKey, Boolean.valueOf(false))).booleanValue()) {
                    wbuf = new Buffer();
                    this.frameEncoder.process(buf, wbuf);
                    this.writerQueue.put(wbuf);
                } else {
                    Rational wbuf2 = buf.timeStamp.add(buf.sampleDuration);

                    for (boolean isFirst = true; this.outputTime.compareTo(wbuf2) < 0; this.outputTime = this.outputTime.add(this.ffrDuration)) {
                        buf.timeStamp = this.outputTime;
                        buf.sampleDuration = this.ffrDuration;
                        if (isFirst) {
                            isFirst = false;
                        } else {
                            buf.setFlag(BufferFlag.SAME_DATA);
                        }

                        Buffer wbuf1 = new Buffer();
                        if (this.frameEncoder.process(buf, wbuf1) != 0) {
                            throw new IOException("Codec failed or could not process frame in a single step.");
                        }

                        this.writerQueue.put(wbuf1);
                    }
                }
            } else {
                wbuf = new Buffer();
                wbuf.setMetaTo(buf);
                wbuf.data = ((byte[]) ((byte[]) buf.data)).clone();
                wbuf.length = buf.length;
                wbuf.offset = buf.offset;
                this.writerQueue.put(wbuf);
            }

        }
    }

    private void doWrite(Buffer buf) throws IOException {
        final MovieWriter mw = this.w;
        long now = System.currentTimeMillis();
        if (buf.track == this.videoTrack && buf.isFlag(BufferFlag.KEYFRAME) && (mw.isDataLimitReached() || now - this.fileStartTime > this.maxRecordingTime)) {
            (new Thread() {
                public void run() {
                    try {
                        mw.close();
                    } catch (IOException var2) {
                        var2.printStackTrace();
                    }

                }
            }).start();
            // mw = this.createMovieWriter();        }

        mw.write(buf.track, buf);
    }

    public long getMaxRecordingTime() {
        return this.maxRecordingTime;
    }

    public void setMaxRecordingTime(long maxRecordingTime) {
        this.maxRecordingTime = maxRecordingTime;
    }

    public long getMaxFileSize() {
        return this.maxFileSize;
    }

    public void setMaxFileSize(long maxFileSize) {
        this.maxFileSize = maxFileSize;
    }

    public Mixer getAudioMixer() {
        return this.mixer;
    }

    public void setAudioMixer(Mixer mixer) {
        this.mixer = mixer;
    }

    public static enum State {
        DONE,
        FAILED,
        RECORDING;

        private State() {
        }
    }

    private static class AudioGrabber implements Runnable {
        private final TargetDataLine line;
        private final BlockingQueue<Buffer> queue;
        private final Format audioFormat;
        private final int audioTrack;
        private final long startTime;
        private volatile long stopTime = 9223372036854775807L;
        private long totalSampleCount;
        private ScheduledFuture future;
        private long sequenceNumber;
        private float audioLevelLeft = -1.0F;
        private float audioLevelRight = -1.0F;
        private Mixer mixer;

        public AudioGrabber(Mixer mixer, Format audioFormat, int audioTrack, long startTime, BlockingQueue<Buffer> queue) throws LineUnavailableException {
            this.mixer = mixer;
            this.audioFormat = audioFormat;
            this.audioTrack = audioTrack;
            this.queue = queue;
            this.startTime = startTime;
            Info lineInfo = new Info(TargetDataLine.class, AudioFormatKeys.toAudioFormat(audioFormat));
            if (mixer != null) {
                this.line = (TargetDataLine) mixer.getLine(lineInfo);
            } else {
                this.line = (TargetDataLine) AudioSystem.getLine(lineInfo);
            }

            try {
                BooleanControl e = (BooleanControl) this.line.getControl(Type.MUTE);
                e.setValue(false);
            } catch (IllegalArgumentException var10) {
                ;
            }

            try {
                FloatControl e1 = (FloatControl) this.line.getControl(FloatControl.Type.VOLUME);
                e1.setValue(Math.max(e1.getValue(), 0.2F));
            } catch (IllegalArgumentException var9) {
                ;
            }

            this.line.open();
            this.line.start();
        }

        public void setFuture(ScheduledFuture future) {
            this.future = future;
        }

        public void close() {
            this.line.close();
        }

        public synchronized long getStopTime() {
            return this.stopTime;
        }

        public synchronized void setStopTime(long newValue) {
            this.stopTime = newValue;
        }

        public void run() {
            Buffer buf = new Buffer();
            AudioFormat lineFormat = this.line.getFormat();
            buf.format = AudioFormatKeys.fromAudioFormat(lineFormat).append(new Object[]{AudioFormatKeys.SilenceBugKey, Boolean.valueOf(true)});
            int bufferSize = lineFormat.getFrameSize() * (int) lineFormat.getSampleRate();
            if (((int) lineFormat.getSampleRate() & 1) == 0) {
                bufferSize /= 2;
            }

            byte[] bdat = new byte[bufferSize];
            buf.data = bdat;
            Rational sampleRate = Rational.valueOf((double) lineFormat.getSampleRate());
            Rational frameRate = Rational.valueOf((double) lineFormat.getFrameRate());
            int count = this.line.read(bdat, 0, bdat.length);
            if (count > 0) {
                this.computeAudioLevel(bdat, count, lineFormat);
                buf.sampleCount = count / (lineFormat.getSampleSizeInBits() / 8 * lineFormat.getChannels());
                buf.sampleDuration = sampleRate.inverse();
                buf.offset = 0;
                buf.sequenceNumber = (long) (this.sequenceNumber++);
                buf.length = count;
                buf.track = this.audioTrack;
                buf.timeStamp = (new Rational(this.totalSampleCount, 1L)).divide(frameRate);
                Rational stopTS = new Rational(this.getStopTime() - this.startTime, 1000L);
                if (buf.timeStamp.add(buf.sampleDuration.multiply((long) buf.sampleCount)).compareTo(stopTS) > 0) {
                    buf.sampleCount = Math.max(0, (int) Math.ceil((double) stopTS.subtract(buf.timeStamp).divide(buf.sampleDuration).floatValue()));
                    buf.length = buf.sampleCount * lineFormat.getSampleSizeInBits() / 8 * lineFormat.getChannels();
                    this.future.cancel(false);
                }

                if (buf.sampleCount > 0) {
                    try {
                        this.queue.put(buf);
                    } catch (InterruptedException var10) {
                        ;
                    }
                }

                this.totalSampleCount += (long) buf.sampleCount;
            }

        }

        private void computeAudioLevel(byte[] data, int length, AudioFormat format) {
            this.audioLevelLeft = this.audioLevelRight = -1.0F;
            if (format.getEncoding().equals(Encoding.PCM_SIGNED)) {
                switch (format.getSampleSizeInBits()) {
                    case 8:
                        switch (format.getChannels()) {
                            case 1:
                                this.audioLevelLeft = this.computeAudioLevelSigned8(data, 0, length, format.getFrameSize());
                                return;
                            case 2:
                                this.audioLevelLeft = this.computeAudioLevelSigned8(data, 0, length, format.getFrameSize());
                                this.audioLevelRight = this.computeAudioLevelSigned8(data, 1, length, format.getFrameSize());
                                return;
                            default:
                                return;
                        }
                    case 16:
                        if (format.isBigEndian()) {
                            switch (format.getChannels()) {
                                case 1:
                                    this.audioLevelLeft = this.computeAudioLevelSigned16BE(data, 0, length, format.getFrameSize());
                                    break;
                                case 2:
                                    this.audioLevelLeft = this.computeAudioLevelSigned16BE(data, 0, length, format.getFrameSize());
                                    this.audioLevelRight = this.computeAudioLevelSigned16BE(data, 2, length, format.getFrameSize());
                            }
                        } else {
                            switch (format.getChannels()) {
                                case 1:
                                case 2:
                            }
                        }
                }
            }

        }

        private float computeAudioLevelSigned16BE(byte[] data, int offset, int length, int stride) {
            double sum = 0.0D;

            for (int rms = offset; rms < length; rms += stride) {
                int value = data[rms] << 8 | data[rms + 1] & 255;
                sum += (double) (value * value);
            }

            double rms1 = Math.sqrt(sum / (double) ((length - offset) / stride));
            return (float) (rms1 / 32768.0D);
        }

        private float computeAudioLevelSigned8(byte[] data, int offset, int length, int stride) {
            double sum = 0.0D;

            for (int rms = offset; rms < length; rms += stride) {
                byte value = data[rms];
                if (value != -128) {
                    sum += (double) (value * value);
                }
            }

            double rms1 = Math.sqrt(sum / (double) (length / stride));
            return (float) (rms1 / 128.0D);
        }

        public float getAudioLevelLeft() {
            return this.audioLevelLeft;
        }

        public float getAudioLevelRight() {
            return this.audioLevelRight;
        }
    }

    protected static class MouseGrabber implements Runnable {
        private Point prevCapturedMouseLocation = new Point(2147483647, 2147483647);
        private ScheduledThreadPoolExecutor timer;
        private ScreenRecorder recorder;
        private GraphicsDevice captureDevice;
        private Rectangle captureArea;
        private BlockingQueue<Buffer> mouseCaptures;
        private volatile long stopTime = 9223372036854775807L;
        private long startTime;
        private Format format;
        private ScheduledFuture future;
        private volatile boolean mousePressed;
        private volatile boolean mouseWasPressed;
        private volatile boolean mousePressedRecorded;

        public MouseGrabber(ScreenRecorder recorder, long startTime, ScheduledThreadPoolExecutor timer) {
            this.timer = timer;
            this.format = recorder.mouseFormat;
            this.captureDevice = recorder.captureDevice;
            this.captureArea = recorder.captureArea;
            this.mouseCaptures = recorder.mouseCaptures;
            this.startTime = startTime;
        }

        public void setFuture(ScheduledFuture future) {
            this.future = future;
        }

        public void setMousePressed(boolean newValue) {
            if (newValue) {
                this.mouseWasPressed = true;
            }

            this.mousePressed = newValue;
        }

        public void run() {
            try {
                this.grabMouse();
            } catch (Throwable var2) {
                var2.printStackTrace();
                this.timer.shutdown();
                this.recorder.recordingFailed(var2.getMessage());
            }

        }

        public synchronized long getStopTime() {
            return this.stopTime;
        }

        public synchronized void setStopTime(long newValue) {
            this.stopTime = newValue;
        }

        private void grabMouse() throws InterruptedException {
            long now = System.currentTimeMillis();
            if (now > this.getStopTime()) {
                this.future.cancel(false);
            } else {
                PointerInfo info = MouseInfo.getPointerInfo();
                Point p = info.getLocation();
                if (!info.getDevice().equals(this.captureDevice) || !this.captureArea.contains(p)) {
                    p.setLocation(2147483647, 2147483647);
                }

                if (!p.equals(this.prevCapturedMouseLocation) || this.mouseWasPressed != this.mousePressedRecorded) {
                    Buffer buf = new Buffer();
                    buf.format = this.format;
                    buf.timeStamp = new Rational(now, 1000L);
                    buf.data = p;
                    buf.header = Boolean.valueOf(this.mouseWasPressed);
                    this.mousePressedRecorded = this.mouseWasPressed;
                    this.mouseCaptures.put(buf);
                    this.prevCapturedMouseLocation.setLocation(p);
                }

                if (!this.mousePressed) {
                    this.mouseWasPressed = false;
                }

            }
        }

        public void close() {
        }
    }

    private static class ScreenGrabber implements Runnable {
        private final Format mouseFormat;
        private final Object sync;
        private Point prevDrawnMouseLocation = new Point(2147483647, 2147483647);
        private boolean prevMousePressed = false;
        private BufferedImage screenCapture;
        private ScreenRecorder recorder;
        private ScheduledThreadPoolExecutor screenTimer;
        private Robot robot;
        private Rectangle captureArea;
        private BufferedImage videoImg;
        private Graphics2D videoGraphics;
        private ArrayBlockingQueue<Buffer> mouseCaptures;
        private Rational prevScreenCaptureTime;
        private BufferedImage cursorImg;
        private BufferedImage cursorImgPressed;
        private Point cursorOffset;
        private int videoTrack;
        private long startTime;
        private volatile long stopTime = 9223372036854775807L;
        private ScheduledFuture future;
        private long sequenceNumber;

        public ScreenGrabber(ScreenRecorder recorder, long startTime) throws AWTException, IOException {
            this.recorder = recorder;
            this.captureArea = recorder.captureArea;
            this.robot = new Robot(recorder.captureDevice);
            this.mouseFormat = recorder.mouseFormat;
            this.mouseCaptures = recorder.mouseCaptures;
            this.sync = recorder.sync;
            this.cursorImg = recorder.cursorImg;
            this.cursorImgPressed = recorder.cursorImgPressed;
            this.cursorOffset = recorder.cursorOffset;
            this.videoTrack = recorder.videoTrack;
            this.prevScreenCaptureTime = new Rational(startTime, 1000L);
            this.startTime = startTime;
            Format screenFormat = recorder.screenFormat;
            if (((Integer) screenFormat.get(VideoFormatKeys.DepthKey, Integer.valueOf(24))).intValue() == 24) {
                this.videoImg = new BufferedImage(this.captureArea.width, this.captureArea.height, 1);
            } else if (((Integer) screenFormat.get(VideoFormatKeys.DepthKey)).intValue() == 16) {
                this.videoImg = new BufferedImage(this.captureArea.width, this.captureArea.height, 9);
            } else {
                if (((Integer) screenFormat.get(VideoFormatKeys.DepthKey)).intValue() != 8) {
                    throw new IOException("Unsupported color depth " + screenFormat.get(VideoFormatKeys.DepthKey));
                }

                this.videoImg = new BufferedImage(this.captureArea.width, this.captureArea.height, 13, Colors.createMacColors());
            }

            this.videoGraphics = this.videoImg.createGraphics();
            this.videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
            this.videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
            this.videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        }

        public void setFuture(ScheduledFuture future) {
            this.future = future;
        }

        public synchronized long getStopTime() {
            return this.stopTime;
        }

        public synchronized void setStopTime(long newValue) {
            this.stopTime = newValue;
        }

        public void run() {
            try {
                this.grabScreen();
            } catch (Throwable var2) {
                var2.printStackTrace();
                this.screenTimer.shutdown();
                this.recorder.recordingFailed(var2.getMessage());
            }

        }

        private void grabScreen() throws IOException, InterruptedException {
            BufferedImage previousScreenCapture = this.screenCapture;
            long timeBeforeCapture = System.currentTimeMillis();

            try {
                this.screenCapture = this.robot.createScreenCapture(this.captureArea);
            } catch (IllegalMonitorStateException var13) {
                return;
            }

            long timeAfterCapture = System.currentTimeMillis();
            if (previousScreenCapture == null) {
                previousScreenCapture = this.screenCapture;
            }

            this.videoGraphics.drawImage(previousScreenCapture, 0, 0, (ImageObserver) null);
            Buffer buf = new Buffer();
            buf.format = new Format(new Object[]{FormatKeys.MediaTypeKey, MediaType.VIDEO, FormatKeys.EncodingKey, "image"});
            boolean hasMouseCapture = false;
            if (this.mouseFormat != null && ((Rational) this.mouseFormat.get(FormatKeys.FrameRateKey)).intValue() > 0) {
                while (!this.mouseCaptures.isEmpty() && ((Buffer) this.mouseCaptures.peek()).timeStamp.compareTo(new Rational(timeAfterCapture, 1000L)) < 0) {
                    Buffer p = (Buffer) this.mouseCaptures.poll();
                    if (p.timeStamp.compareTo(this.prevScreenCaptureTime) > 0) {
                        if (p.timeStamp.compareTo(new Rational(timeBeforeCapture, 1000L)) < 0) {
                            previousScreenCapture = this.screenCapture;
                            this.videoGraphics.drawImage(previousScreenCapture, 0, 0, (ImageObserver) null);
                        }

                        Point mcp = (Point) p.data;
                        this.prevMousePressed = ((Boolean) p.header).booleanValue();
                        this.prevDrawnMouseLocation.setLocation(mcp.x - this.captureArea.x, mcp.y - this.captureArea.y);
                        Point p1 = this.prevDrawnMouseLocation;
                        long localStopTime = this.getStopTime();
                        if (p.timeStamp.compareTo(new Rational(localStopTime, 1000L)) > 0) {
                            break;
                        }

                        hasMouseCapture = true;
                        if (this.prevMousePressed) {
                            this.videoGraphics.drawImage(this.cursorImgPressed, p1.x + this.cursorOffset.x, p1.y + this.cursorOffset.y, (ImageObserver) null);
                        } else {
                            this.videoGraphics.drawImage(this.cursorImg, p1.x + this.cursorOffset.x, p1.y + this.cursorOffset.y, (ImageObserver) null);
                        }

                        buf.clearFlags();
                        buf.data = this.videoImg;
                        buf.sampleDuration = p.timeStamp.subtract(this.prevScreenCaptureTime);
                        buf.timeStamp = this.prevScreenCaptureTime.subtract(new Rational(this.startTime, 1000L));
                        buf.track = this.videoTrack;
                        buf.sequenceNumber = (long) (this.sequenceNumber++);
                        buf.header = p1.x == 2147483647 ? null : p1;
                        this.recorder.write(buf);
                        this.prevScreenCaptureTime = p.timeStamp;
                        this.videoGraphics.drawImage(previousScreenCapture, p1.x + this.cursorOffset.x, p1.y + this.cursorOffset.y, p1.x + this.cursorOffset.x + this.cursorImg.getWidth() - 1, p1.y + this.cursorOffset.y + this.cursorImg.getHeight() - 1, p1.x + this.cursorOffset.x, p1.y + this.cursorOffset.y, p1.x + this.cursorOffset.x + this.cursorImg.getWidth() - 1, p1.y + this.cursorOffset.y + this.cursorImg.getHeight() - 1, (ImageObserver) null);
                    }
                }

                if (!hasMouseCapture && this.prevScreenCaptureTime.compareTo(new Rational(this.getStopTime(), 1000L)) < 0) {
                    Point p2 = this.prevDrawnMouseLocation;
                    if (p2 != null) {
                        if (this.prevMousePressed) {
                            this.videoGraphics.drawImage(this.cursorImgPressed, p2.x + this.cursorOffset.x, p2.y + this.cursorOffset.y, (ImageObserver) null);
                        } else {
                            this.videoGraphics.drawImage(this.cursorImg, p2.x + this.cursorOffset.x, p2.y + this.cursorOffset.y, (ImageObserver) null);
                        }
                    }

                    buf.data = this.videoImg;
                    buf.sampleDuration = (new Rational(timeAfterCapture, 1000L)).subtract(this.prevScreenCaptureTime);
                    buf.timeStamp = this.prevScreenCaptureTime.subtract(new Rational(this.startTime, 1000L));
                    buf.track = this.videoTrack;
                    buf.sequenceNumber = (long) (this.sequenceNumber++);
                    buf.header = p2.x == 2147483647 ? null : p2;
                    this.recorder.write(buf);
                    this.prevScreenCaptureTime = new Rational(timeAfterCapture, 1000L);
                    if (p2 != null) {
                        this.videoGraphics.drawImage(previousScreenCapture, p2.x + this.cursorOffset.x, p2.y + this.cursorOffset.y, p2.x + this.cursorOffset.x + this.cursorImg.getWidth() - 1, p2.y + this.cursorOffset.y + this.cursorImg.getHeight() - 1, p2.x + this.cursorOffset.x, p2.y + this.cursorOffset.y, p2.x + this.cursorOffset.x + this.cursorImg.getWidth() - 1, p2.y + this.cursorOffset.y + this.cursorImg.getHeight() - 1, (ImageObserver) null);
                    }
                }
            } else if (this.prevScreenCaptureTime.compareTo(new Rational(this.getStopTime(), 1000L)) < 0) {
                buf.data = this.videoImg;
                buf.sampleDuration = (new Rational(timeAfterCapture, 1000L)).subtract(this.prevScreenCaptureTime);
                buf.timeStamp = this.prevScreenCaptureTime.subtract(new Rational(this.startTime, 1000L));
                buf.track = this.videoTrack;
                buf.sequenceNumber = (long) (this.sequenceNumber++);
                buf.header = null;
                this.recorder.write(buf);
                this.prevScreenCaptureTime = new Rational(timeAfterCapture, 1000L);
            }

            if (timeBeforeCapture > this.getStopTime()) {
                this.future.cancel(false);
            }

        }

        public void close() {
            this.videoGraphics.dispose();
            this.videoImg.flush();
        }
    }
}