11
votes

Comment prendre une capture d'écran de bureau rapide avec Java sous Windows (FFMPEG, etc.)?

J'aimerais utiliser Java pour prendre une capture d'écran de ma machine à l'aide de FFMPEG ou d'une autre solution. Je sais que Linux fonctionne avec FFMPEG sans JNI, mais le fonctionnement dans Windows ne fonctionne pas et peut nécessiter (JNI?) Existe-t-il un échantillon de la simple classe Java (et autre chose nécessaire) pour capturer une capture d'écran exécutable dans un environnement Windows? Y a-t-il une alternative à FFMPEG? Je tiens à prendre une capture d'écran à une vitesse plus rapide que l'API de Java Robot, que j'ai constaté à travailler à prendre des captures d'écran, mais que je souhaite plus tard que je le souhaite.

Je connais sous Linux cela fonctionne très vite: < Pré> xxx

Cela ne fonctionne pas dans l'environnement Windows. Je ne suis pas sûr s'il y a une façon de pouvoir utiliser ce même code, mais utilisez JavACPP pour le faire fonctionner sans avoir à changer une grande partie du code ci-dessus.

objectif est de prendre des captures d'écran de l'écran, mais Ensuite, arrêtez-vous après avoir pris une capture d'écran «différente», aka. L'écran a changé en raison d'un événement comme une fenêtre fermée, etc.


2 commentaires

Problèmes avec cette question: (1) il s'agit de Java mais a été étiqueté c ++ (que j'ai supprimé), (2) "ne fonctionne pas" n'est pas une description de problème appropriée - vous devez donner des détails réels du problème, (3) n'ont montré aucun code, (4) demandes de recommandations de ressources tierces sont hors sujet


Vous avez mentionné une condition de temps, mais n'a pas précisé cela. Pour beaucoup de robot est rapide, quel est votre besoin? Avez-vous écrit du code pour le profiler? Partagez-le. Avez-vous vu ce fil . Ils couvrent également le code non robot et plusieurs moniteurs.


4 Réponses :


1
votes

Vous devrez utiliser JNI ou JNA pour appeler une combinaison de CreateCompatibleBitmap, XGetImage, DirectX ou OpenGL pour saisir une capture d'écran, puis copier des données bitmap brutes vers Java. Mon profilage a montré une vitesse d'environ 400% sur la classe de robots lors de l'accès aux données BitMap brutes sur X11. Je n'ai pas testé d'autres plates-formes pour le moment. Un code très précoce est disponible ici mais je n'ai pas eu beaucoup de temps pour y travailler récemment.


3 commentaires

Pourriez-vous fournir un échantillon qui fonctionne avec Windows? J'ai été capable d'utiliser FFMPEG sur X11, mais cela ne fonctionne pas sur Windows. J'ai vu Javacpp, mais personne ne semble l'avoir utilisé à aucun succès. Si seulement le robot était capable de pouvoir prendre des choses à un minimum de 30 fps ...


ici est un exemple de GDI simple. Il existe également un échantillon DirectX dans le même dossier.


J'ai tiré votre code source et je ne peux pas le faire exécuter à l'aide du code Visual Studio à l'aide de G ++ Compiler. Essayer d'exécuter cela sur Windows. Toujours obtenir l'erreur suivante Erreur fatale: ScreenCapture.h: Aucun fichier ou répertoire de ce type #include "ScreenCapture.h"



33
votes

Utilisation de la classe de robots intégrée forte> est une voie plus facile que d'autres bibliothèques Java et devrait probablement correspondre à vos besoins.

Si vous avez besoin d'une vidéo lisse avec> = 30FPS (plus de 30 captures d'écran par Deuxièmement), vous devriez d'abord essayer les robots approche plus les améliorations de performance en utilisant stockage asynchrone des captures d'écran strong>. p>

Si cela ne fonctionne pas pour vous, essayez d'utiliser JNA strong> et c'est (même s'il est plus complexe) presque garanti de travailler pour la capture d'écran lisse. P>

approche avec des robots h2>

La classe des robots est en effet capable de faire ce que vous Vous voulez, la plupart des approches de capture d'écran avec des robots ont le problème, est la épargne forte> des captures d'écran. Une approche pourrait ressembler à celle-là: boucle sur la méthode CapturesCreen (), saisissant l'écran dans un bufferedimage, convertissez-la en une matrice d'octet et enregistrez-la avec un écrivain de fichier asynchrone à un fichier cible après avoir ajouté la référence future de votre image à la ArrayList Pour pouvoir continuer tout en stockant les données d'image. P>

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.W32API;
import com.sun.jna.win32.W32APIOptions;

public class JNAScreenShot
{

    public static BufferedImage getScreenshot(Rectangle bounds)
    {
        W32API.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
        W32API.HBITMAP outputBitmap = GDI.CreateCompatibleBitmap(windowDC, bounds.width, bounds.height);
        try
        {
            W32API.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
            try
            {
                W32API.HANDLE oldBitmap = GDI.SelectObject(blitDC, outputBitmap);
                try
                {
                    GDI.BitBlt(blitDC, 0, 0, bounds.width, bounds.height, windowDC, bounds.x, bounds.y, GDI32.SRCCOPY);
                }
                finally
                {
                    GDI.SelectObject(blitDC, oldBitmap);
                }
                GDI32.BITMAPINFO bi = new GDI32.BITMAPINFO(40);
                bi.bmiHeader.biSize = 40;
                boolean ok = GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height, (byte[]) null, bi, GDI32.DIB_RGB_COLORS);
                if (ok)
                {
                    GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
                    bih.biHeight = -Math.abs(bih.biHeight);
                    bi.bmiHeader.biCompression = 0;
                    return bufferedImageFromBitmap(blitDC, outputBitmap, bi);
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                GDI.DeleteObject(blitDC);
            }
        }
        finally
        {
            GDI.DeleteObject(outputBitmap);
        }
    }

    private static BufferedImage bufferedImageFromBitmap(GDI32.HDC blitDC, GDI32.HBITMAP outputBitmap, GDI32.BITMAPINFO bi)
    {
        GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
        int height = Math.abs(bih.biHeight);
        final ColorModel cm;
        final DataBuffer buffer;
        final WritableRaster raster;
        int strideBits = (bih.biWidth * bih.biBitCount);
        int strideBytesAligned = (((strideBits - 1) | 0x1F) + 1) >> 3;
        final int strideElementsAligned;
        switch (bih.biBitCount)
        {
            case 16:
                strideElementsAligned = strideBytesAligned / 2;
                cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
                buffer = new DataBufferUShort(strideElementsAligned * height);
                raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null);
                break;
            case 32:
                strideElementsAligned = strideBytesAligned / 4;
                cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
                buffer = new DataBufferInt(strideElementsAligned * height);
                raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null);
                break;
            default:
                throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount);
        }
        final boolean ok;
        switch (buffer.getDataType())
        {
            case DataBuffer.TYPE_INT:
            {
                int[] pixels = ((DataBufferInt) buffer).getData();
                ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
            }
                break;
            case DataBuffer.TYPE_USHORT:
            {
                short[] pixels = ((DataBufferUShort) buffer).getData();
                ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
            }
                break;
            default:
                throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType());
        }
        if (ok)
        {
            return new BufferedImage(cm, raster, false, null);
        }
        else
        {
            return null;
        }
    }

    private static final User32 USER = User32.INSTANCE;

    private static final GDI32 GDI = GDI32.INSTANCE;

}

interface GDI32 extends com.sun.jna.platform.win32.GDI32
{
    GDI32 INSTANCE = (GDI32) Native.loadLibrary(GDI32.class);

    boolean BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, int dwRop);

    HDC GetDC(HWND hWnd);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, byte[] pixels, BITMAPINFO bi, int usage);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, short[] pixels, BITMAPINFO bi, int usage);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, int[] pixels, BITMAPINFO bi, int usage);

    int SRCCOPY = 0xCC0020;
}

interface User32 extends com.sun.jna.platform.win32.User32
{
    User32 INSTANCE = (User32) Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS);

    HWND GetDesktopWindow();
}


0 commentaires

0
votes

connaissez-vous Xuggler? Il utilise FFMPEG pour le codage et le décodage. J'ai appris à le savoir il y a quelques mois lorsque je devais extraire des cadres d'une vidéo et cela fonctionnait bien.

sur le site officiel, vous pouvez trouver quelques exemples, y compris celui appelé "capturesCreentofile.java". Pour plus d'informations, suivez ces liens:

http://www.xuggle.com/xuggler/

https://github.com/ ArtClarke / Xugue-Xuggler / Tree / Master / SRC / com / Xugier / Xuggler / Démos


0 commentaires