2

I am trying to record screen in Java, using newest MonteMedia ScreenRecorder:

<dependency>
  <groupId>ch.randelshofer</groupId>
  <artifactId>org.monte.media</artifactId>
  <version>17.1</version>
</dependency>

My code is similar to many examples I could find of usage of the library - however older version as I found no examples for the new one - so packages are different:

import org.monte.media.math.Rational;
import org.monte.media.screenrecorder.ScreenRecorder;
import org.monte.media.av.Format;
import org.monte.media.av.FormatKeys.MediaType;
import static org.monte.media.av.codec.video.VideoFormatKeys.*;

import java.awt.*;
import java.io.File;
import java.io.IOException;

public class Screencorder {

    private Screencorder() {}

    public static ScreenRecorder startRecording(String fileName) throws IOException, AWTException {
        GraphicsConfiguration gc = GraphicsEnvironment
                .getLocalGraphicsEnvironment()
                .getDefaultScreenDevice()
                .getDefaultConfiguration();

        Format fileFormat = new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI);
        Format screenFormat = new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 24, FrameRateKey,
                Rational.valueOf(15), QualityKey, 1.0f, KeyFrameIntervalKey, (15 * 60));
        Format mouseFormat = new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ScreenRecorder.ENCODING_BLACK_CURSOR, FrameRateKey, Rational.valueOf(30));

        ScreenRecorder screenRecorder = new ScreenRecorder(gc, null,
                fileFormat, screenFormat, mouseFormat,
                null, new File(fileName));

        screenRecorder.start();

        return screenRecorder;
    }

    public static void stopRecording(ScreenRecorder screenRecorder) throws IOException {
        screenRecorder.stop();
    }
}

Running a test:

import org.junit.jupiter.api.Test;
import org.monte.media.screenrecorder.ScreenRecorder;
import java.time.Duration;
import static org.awaitility.Awaitility.await;

class ScreenRecordingTest {

    @Test
    void recordingTest() throws Exception {
        ScreenRecorder recorder = Screencorder.startRecording("/tmp/");

        Thread.sleep(2000)

        Screencorder.stopRecording(recorder);
    }
}

gives me:

java.io.IOException: Error no writer found for file format: Format{mimeType:video/avi,mediaType:FILE}.

I tried various JDKs - 17, 20 - without success.
Tried changing directory to a file, same error.
Tried a different MIME type - same error regardless.

Can someone help how to run this, so it records an .avi? Or other movie-like format.

I hope I am doing some obvious mistake.

Anna
  • 31
  • 5
  • I'm not familiar with it, but is it possible that it needs an additional dependency to provide support for the file format? Just an thought. – David Conrad Jul 14 '23 at 21:43
  • Thanks! I think you were on to something, I have added 2 more dependencies: `ch.randelshofer:org.monte.media:17.1` and `ch.randelshofer:org.monte.media.swing:17.1` Swing was used in the old recorder so why not. And I found `AVIWriter` in `org.monte.media` dependency. Unfortunately it didn't solve the problem, still same error. It must be some simple mistake, this should work. – Anna Jul 15 '23 at 20:24
  • 1
    Ok so the biggest difference was made by adding `module-info.java` and thoroughly cleaning up IntelliJ. A working example of this is now shown [here](https://github.com/sikorka/screencorder/tree/master/com.github.sikorka.util.screen/src/main/java). – Anna Jul 18 '23 at 20:10
  • I'm glad you got it working! You can add an answer to your own question with the solution. Self-answering is totally okay on SO and it will probably help others in the future. – David Conrad Jul 18 '23 at 21:23
  • Thanks! Will do :) – Anna Jul 19 '23 at 20:16

1 Answers1

1

Adding module-info.java and thoroughly cleaning up IntelliJ as described here got it working. A simple working example of this is shown here.

Summarising:

  1. you need Java >= 17 on your machine

  2. maven module's pom.xml has 3 main MonteMedia dependencies:

<dependencies>
        <dependency>
            <groupId>ch.randelshofer</groupId>
            <artifactId>org.monte.media.screenrecorder</artifactId>
            <version>17.1</version>
        </dependency>
        <dependency>
            <groupId>ch.randelshofer</groupId>
            <artifactId>org.monte.media</artifactId>
            <version>17.1</version>
        </dependency>
        <dependency>
            <groupId>ch.randelshofer</groupId>
            <artifactId>org.monte.media.swing</artifactId>
            <version>17.1</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  1. src/main/java needs a module.info with those dependencies and some additional ones required by them + an export to use the module in a test:
module com.your.path.util.screen { //<= this is the maven module name too
    requires java.desktop;
    requires java.prefs;

    requires org.monte.media;
    requires org.monte.media.swing;
    requires org.monte.media.screenrecorder;

    exports com.your.path.util.screen;
}
  1. in package com.your.path.util.screen there is a class Screencorder that will record a movie with everything that's necessary to start a full screen recording in start and stop methods:
package com.your.path.util.screen;

import org.monte.media.av.Format;
import org.monte.media.math.Rational;
import org.monte.media.screenrecorder.ScreenRecorder;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.time.Duration;

import static org.monte.media.av.FormatKeys.*;
import static org.monte.media.av.codec.video.VideoFormatKeys.*;

public class Screencorder {
    private ScreenRecorder screenRecorder;
    public final String movieFolder;

    public void start() throws IOException, AWTException {
        GraphicsConfiguration gc = GraphicsEnvironment
                .getLocalGraphicsEnvironment()
                .getDefaultScreenDevice()
                .getDefaultConfiguration();

        Format fileFormat = new Format(MediaTypeKey, MediaType.FILE,
                MimeTypeKey, MIME_AVI);
        Format screenFormat = new Format(MediaTypeKey, MediaType.VIDEO,
                EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                DepthKey, 24,
                FrameRateKey, Rational.valueOf(15),
                QualityKey, 1.0f,
                KeyFrameIntervalKey, (15 * 60));
        Format mouseFormat = new Format(MediaTypeKey, MediaType.VIDEO,
                EncodingKey, ScreenRecorder.ENCODING_BLACK_CURSOR,
                FrameRateKey, Rational.valueOf(30));

        screenRecorder = new ScreenRecorder(gc, null,
                fileFormat, screenFormat, mouseFormat,
                null, new File(movieFolder));

        screenRecorder.start();
    }

    public void stop() throws IOException {
        screenRecorder.stop();
    }

    public static void main(String[] args) throws IOException, AWTException, InterruptedException {
        Screencorder recorder = new Screencorder();

        recorder.start();
        Thread.sleep(Duration.ofSeconds(7).toMillis());
        recorder.stop();
    }

    public Screencorder() {
        if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
            movieFolder = System.getProperty("user.home") + File.separator + "Videos";
        } else {
            movieFolder = System.getProperty("user.home") + File.separator + "Movies";
        }

        File movieDir = new File(movieFolder);
        if (!movieDir.exists()) {
            movieDir.mkdirs();
        }
    }

    public String getMovieFolder() {
        return movieFolder;
    }
}
  1. src/test/java needs a module.info as well - it will require the above module and test libraries + it will open itself for reflection to junit:
module com.your.path.util.screen.test {
    requires com.your.path.util.screen;
    requires org.junit.jupiter.api;
    requires hamcrest.all;

    opens com.your.path.util.screen.test to org.junit.platform.commons;
}
  1. the test package needs to be different than for main code, for example com.your.path.util.screen.test, the test class goes in here

  2. In case of problems with IntelliJ: after cleaning as described above, the module com.your.path.util.screen needs to have selected the MonteMedia dependencies in Module Settings > com.your.path.util.screen > Dependencies > chec 3 MonteMedia dependencies (same as in pom.xml)

Now you can run main() method in IntelliJ as well as the test.

Anna
  • 31
  • 5
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 23 '23 at 14:05
  • Done. Hope it looks good :) – Anna Jul 24 '23 at 09:46