[JAVA] Erstellen Sie eine Software, die den Android-Bildschirm auf einen PC 1 spiegelt

Einführung

In diesem Artikel wird die Funktion zum Spiegeln des Android-Bildschirms auf einen PC erläutert. Fortsetzung von diesem Artikel Erstellen einer Software, die den Android-Bildschirm auf eine PC 2 Real-Time-Touch-Edition spiegelt es gibt.

Ich werde so etwas machen

capture3.gif

Erstellen Sie eine Software, die den Android-Bildschirm auf einen PC spiegelt. Ich weiß es nicht mit Gif, aber es funktioniert schleimig bei 50-60FPS. Es funktioniert auch in Windows-, Mac- und Linux-Umgebungen.

pic.jpg Die Bitrate von H.264 beträgt 5000 Kbit / s, die Auflösung 1/2 und die Verzögerung ca. 300 ms, also ziemlich in Echtzeit. Sie kann je nach Einstellung kürzer sein.

Motivation

Es gibt verschiedene Software, die den Android-Bildschirm spiegeln.

Unter diesen ist Vysor eine Software mit ausgezeichneter Bildqualität und Bildrate. Es bewegt sich normalerweise mit 60 FPS, daher war ich beeindruckt, als ich es zum ersten Mal verwendete. Die Bildqualität ist in der kostenlosen Version begrenzt, wird jedoch beim Aufladen storniert. (Es gibt ein Abonnementsystem und ein Kaufsystem)

Es ist jedoch schwierig für Studenten, Geld sanft zu bezahlen. Ich habe beschlossen, es selbst zu machen.

Dieses Mal erstellen wir jedoch nur eine Spiegelungsfunktion.

Spezifikation

fig1.png

Erstellen Sie einen Server auf der Android-Seite und stellen Sie eine Verbindung von einem PC aus her. Es ist über WLAN möglich, kommuniziert jedoch aus Stabilitätsgründen über USB.

Auf der Android-Seite sein

Erfassen Sie den Bildschirm mit Media Projection. (Daher sind kompatible Terminals 5.0 oder höher.) Codieren Sie die Aufnahme mit Media Codec und senden Sie sie an den PC.

Auf der PC-Seite sein

Stellen Sie eine Verbindung zum Server her, um den Stream zu dekodieren und anzuzeigen. Dieses Mal werde ich jedoch alles auf ffplay werfen und keine Programme auf der PC-Seite erstellen (lacht)

ffplay ist eine Videowiedergabesoftware, die im berühmten Videokonvertierungs-Tool FFmpeg enthalten ist. Sie können verschiedene Dinge spielen, indem Sie die Parameter angeben. Dieses Mal werden wir es verwenden, um den Stream zu dekodieren und in Echtzeit anzuzeigen. Ich habe diesmal zu Beginn über die Betriebssystemumgebung gesprochen, da dieses ffmpeg mit verschiedenen Betriebssystemen kompatibel ist.

Informationen zum zu verwendenden Codec

Es gibt eine Liste von Codecs, die auf der Android-Seite in Unterstützte Medienformate codiert werden können, aber am Ende scheint sie je nach Terminal geeignet zu sein. Ich habe es auf mehreren realen Maschinen und Emulatoren versucht, aber nur H.264 funktionierte auf allen.

Obwohl VP8 den Encoder selbst generieren kann, scheint etwas mit dem erfassten Puffer nicht zu stimmen und er schlägt mit einem Fehler fehl. VP9 wurde zu [Ungültige Daten bei der Verarbeitung der Eingabe gefunden] und ffplay erkannte sie nicht. H.265 kann mit jedem Terminal verwendet werden, das verwendet werden kann.

In diesem Beispiel kann der Codec angegeben werden. Probieren Sie ihn daher auf dem tatsächlichen Computer aus, um festzustellen, welcher funktioniert. Wenn Sie VP8 oder 9 verwenden können, wäre es einfacher, ohne sich um die Lizenz zu sorgen, aber es ist eine Schande.

In Bezug auf den Fehler werde ich ihn hinzufügen, sobald die Ursache bekannt ist. (Ich wäre Ihnen dankbar, wenn Sie mir mitteilen könnten, welche Informationen Sie haben.)

Einzelheiten zu den Codec-Typen finden Sie unter Unterschiede zu Video-Codec-Typen (H.264, VP9, MPEG, Xvid, DivX, WMV usw.) [Vergleich]. Eingeführt.

Erfassen Sie den Bildschirm von Android

Sie können den Bildschirm von der Anwendungsseite auf Android 5.0 oder höher abrufen. Verwenden Sie insbesondere die Medienprojektion. Machen Sie einen Screenshot aus der ANDROID 5.0-App Es wird hier ausführlich erklärt.

Ablauf der Medienprojektion

Es ist einfach, aber hier erfahren Sie, wie Sie Media Projection verwenden. Vielleicht möchten Sie es sich ansehen, während Sie sich auf den Code im obigen Artikel beziehen.

Klasse zu verwenden

・ ** MediaProjectionManager ** Zeigen Sie ein Dialogfeld an, in dem der Benutzer um Erlaubnis zum Erfassen des Bildschirms gebeten wird, und rufen Sie die Medienprojektion ab, falls dies zulässig ist.

・ ** Medienprojektion ** Bietet die Funktion zum Erfassen des Bildschirms. Um genau zu sein, wird ein Puffer erstellt, der als virtuelle Anzeige bezeichnet wird, und der Bildschirm dort gespiegelt. Es gibt verschiedene andere Modi als das Spiegeln.

・ ** VirtualDisplay ** Ein von MediaProjection erstellter und geschriebener Puffer. Es hat eine Oberfläche zum Schreiben, und es ist eigentlich ein Puffer. Sie können diese Oberfläche beim Erstellen angeben. Wenn Sie die Oberfläche von ImageReader angeben, können Sie das Bild daher über ImageReader abrufen. Wenn Sie die Oberfläche der Oberflächenansicht angeben, wird sie in Echtzeit in der Ansicht angezeigt.

· ** Oberfläche ** Ein Puffer, der sich im Gegensatz zu normalen Puffern auf das "Behandeln von Bildern" spezialisiert hat Zusätzlich zu VirtualDisplay wird es auch in SurfaceView- und Videowiedergabeplayern verwendet, die beim Erstellen von Spielen verwendet werden.

Bild bei Verwendung von ImageReader

Eigentlich hat ImageReader einen Mechanismus zum Speichern von Frames, aber es sieht so aus. fig2.png

Vorgehensweise (Code ist in der zweiten Hälfte)

  1. Holen Sie sich ** MeidaProjectionManager ** mit ** getSystemService **
  2. Erstellen Sie eine Absicht, die Sie um Erlaubnis bittet, den Bildschirm mit ** createScreenCaptureIntent ** von ** Manager ** zu erfassen. Wirf die in 3.2 erstellte Absicht und fange sie mit ** onActivityResult ** von ** Activity ** ab.
  3. Wenn der Benutzer die Berechtigung hat, rufen Sie ** MediaProjection ** mit ** getMediaProjection ** von ** Manager ** ab.
  4. Erstellen Sie eine virtuelle Anzeige im Spiegelungsmodus mit der erfassten ** Medienprojektion ** ** createVirtualDisplay ** Geben Sie zu diesem Zeitpunkt ** Oberfläche ** an, die Sie schreiben möchten Der Inhalt des Bildschirms wird in Echtzeit auf die in 6.5 angegebene ** Oberfläche ** geschrieben. Verwenden Sie diese.

Video auf Android verschlüsseln

Verwenden Sie MediaCodec.

Die folgenden Artikel waren hilfreich. Offizielles Dokument Übersicht über die japanische Übersetzung der MediaCodec-Klasse Komprimieren von Videos ohne FFmpeg mit MediaCodec unter Android (mit Bibliothek) Was wurde im obigen Artikel vorgestellt EncodeAndMuxTest (Obwohl die Methode alt ist, war die Prozedur hilfreich)

Ablauf der Verwendung von MediaCodec

Im Folgenden finden Sie einige allgemeine Schritte zur Verwendung von MediaCodec.

Klasse zu verwenden

・ ** MediaCodec ** Video-Encoder und -Decoder ・ ** MediaFormat ** Speichert Videoinformationen wie Codec, Bitrate und Bildrate. Wird zum Festlegen von MediaCodec verwendet.

Nutzungsbild

Sie können Buffer oder Surface für die Frame-Eingabe und -Ausgabe verwenden. Es ist auch möglich, Surface für die Eingabe und Buffer für die Ausgabe zu verwenden.

fig3.png

Vorgehensweise (Code ist in der zweiten Hälfte)

  1. Erstellen Sie einen Encoder und Decoder mit ** createEncoderByType / createDecoderByType **
  2. Erstellen Sie ** MediaFormat ** und stellen Sie das zu codierende und zu decodierende Video ein.
  3. Führen Sie ** configure ** von ** Media Codec ** aus. Geben Sie das in 2 erstellte Medienformat an.
  4. Legen Sie bei asynchroner Verarbeitung einen Rückruf fest
  5. Starten Sie die Konvertierung mit ** start **
  6. Senden Sie Daten vor dem Codieren / Decodieren an Input
  7. Extrahieren Sie die verarbeiteten Daten aus der Ausgabe

Vorsichtsmaßnahmen bei der Eingabe / Ausgabe von Daten

Wie oben erwähnt, können Oberfläche und Puffer zum Eingeben und Ausgeben von Mediencodec-Daten verwendet werden. Es gibt jedoch Unterschiede in der Versandart, je nachdem, was Sie verwenden.

Zur Eingabe

Wenn Sie Buffer verwenden, müssen Sie die Daten manuell an MediaCodec übergeben. Die Oberfläche wird automatisch übergeben, wenn der Inhalt aktualisiert wird.

Für die Ausgabe

Sie müssen die Daten manuell abrufen, wenn Sie Buffer verwenden. Der Inhalt von Surface wird automatisch aktualisiert.

Diese Software verwendet Surface für die Eingabe und Buffer für die Ausgabe.

Nun zur Implementierung

Layout

fig5.png ** Klicken Sie hier für Layout-XML (https://github.com/SIY1121/ScreenCastSample/blob/master/app/src/main/res/layout/activity_main.xml) **

Prozessablauf

Der Vorgang beginnt mit dem Klicken auf die Schaltfläche Start. fig4-2.png

Code

Ich habe alles in MainActivity.java zusammengestellt, daher habe ich es nicht in großem Umfang implementiert. Bitte beachten Sie auch, dass einige Teile nicht auf Fehler überprüft werden. ** Der gesamte Code ist hier **

Das Folgende ist ein Auszug aus dem Code. Bitte beziehen Sie sich auf den gesamten Code.

1. OnClick Zeigt ein Dialogfeld an, in dem Sie um Erlaubnis zum Erfassen gebeten werden

Code in den Zeilen 130-155

MainActivity.java


    button_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                switch (states) {
                    case Stop:
                        //Zeigen Sie einen Dialog zur Bestätigung der Erfassung an
                        manager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
                        startActivityForResult(manager.createScreenCaptureIntent(), REQUEST_CODE);
                        break;
                    case Waiting:
                        //Standby abbrechen
                        Disconnect();
                        break;
                    case Running:
                        //Trennen
                        Disconnect();
                        break;
                }

            }
        });

Da diese Schaltfläche auch zum Stoppen verwendet wird, wird der Prozess je nach Status verzweigt. Die Verarbeitung beginnt bei Stop. Ich habe ** MediaProjectionManager ** erworben und zeige einen Dialog zur Bestätigung der Erfassung für den Benutzer an.

2. Verarbeiten Sie das Ergebnis des Dialogfelds onActivityResult

Dies ist der Code in den Zeilen 162-206.

MainActivity.java


    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (resultCode != RESULT_OK) {
            Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
            return;
        }

        //Wenn der Benutzer die Bildschirmaufnahme genehmigt
        //Holen Sie sich Medienprojektion
        mediaProjection = manager.getMediaProjection(resultCode, intent);


        //Bestimmen Sie die Größe der virtuellen Anzeige
        double SCALE = seekBar_scale.getProgress() * 0.01;

        DisplayMetrics metrics = getResources().getDisplayMetrics();
        final int WIDTH = (int) (metrics.widthPixels * SCALE);
        final int HEIGHT = (int) (metrics.heightPixels * SCALE);
        final int DENSITY = metrics.densityDpi;


        try {

            PrepareEncoder(
                    WIDTH,
                    HEIGHT,
                    codecs[spinner_codec.getSelectedItemPosition()],
                    seekBar_bitrate.getProgress(),
                    seekBar_fps.getProgress(),
                    10//Ich Rahmen ist fest
            );

            SetupVirtualDisplay(WIDTH, HEIGHT, DENSITY);

            StartServer();



        } catch (Exception ex) {//Ein Fehler beim Erstellen eines Encoders
            ex.printStackTrace();
            Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
        }


    }

Wenn der Benutzer auf den in 1. angezeigten Dialog tippt, wird ** onActivityResult ** generiert. Wenn erlaubt, holen Sie sich ** MediaProjection ** mit ** getMediaProjection **. Holen Sie sich dann die Bildschirmgröße und bereiten Sie den Encoder und die virtuelle Anzeige vor.

3.PrecareEncoder Vorbereitung des Encoders

Dies ist der Code in den Zeilen 218-274.

MainActivity.java


//Vorbereitung des Encoders
    private void PrepareEncoder(int WIDTH, int HEIGHT, String MIME_TYPE, int BIT_RATE, int FPS, int IFRAME_INTERVAL) throws Exception {

        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
        //Legen Sie die Formateigenschaften fest
        //Wenn Sie die Mindesteigenschaften nicht festlegen, führt die Konfiguration zu einem Fehler.
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FPS);
        format.setInteger(MediaFormat.KEY_CAPTURE_RATE, FPS);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);


        //Holen Sie sich den Encoder
        codec = MediaCodec.createEncoderByType(MIME_TYPE);

        codec.setCallback(new MediaCodec.Callback() {
            @Override
            public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
                Log.d("MediaCodec", "onInputBufferAvailable : " + codec.getCodecInfo());

            }

            @Override
            public void onOutputBufferAvailable(@NonNull final MediaCodec codec, final int index, @NonNull MediaCodec.BufferInfo info) {
                Log.d("MediaCodec", "onOutputBufferAvailable : " + info.toString());
                ByteBuffer buffer = codec.getOutputBuffer(index);
                byte[] array = new byte[buffer.limit()];
                buffer.get(array);

                //Senden Sie verschlüsselte Daten
                Send(array);

                //Freier Puffer
                codec.releaseOutputBuffer(index, false);
            }

            @Override
            public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
                Log.d("MediaCodec", "onError : " + e.getMessage());
            }

            @Override
            public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
                Log.d("MediaCodec", "onOutputFormatChanged : " + format.getString(MediaFormat.KEY_MIME));
            }
        });

        //Encoder einstellen
        codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

        //Holen Sie sich die Oberfläche, mit der der Rahmen an den Encoder übergeben wird
        //Muss zwischen configure und start aufgerufen werden
        inputSurface = codec.createInputSurface();

    }

Erstellen Sie zunächst das ** Medienformat **. Stellen Sie dann die für die Codierung erforderlichen Parameter ein. Verwenden Sie dann ** createEncoderByType **, um ** MediaCodec ** zu erstellen. Führen Sie dann ** configure ** aus, um ** Media Format ** festzulegen.

Rufen Sie abschließend ** createInputSurface ** auf, um die Oberfläche für die Eingabe abzurufen. Wenn Sie ein Bild auf diese Oberfläche schreiben, wird dessen Inhalt automatisch codiert.

Außerdem setze ich hier einen Rückruf, aber ich verwende Nur ** onOutputBufferAvailable **, das aufgerufen wird, wenn die codierten Daten verfügbar werden. Erfasst die codierten Daten als Byte-Array und sendet sie an die PC-Seite.

4.SetupVirtualDisplay Erstellen einer virtuellen Anzeige

Dies ist der Code in den Zeilen 208-216.

MainActivity.java


//Einrichtung der virtuellen Anzeige
    private void SetupVirtualDisplay(int WIDTH, int HEIGHT, int DENSITY) {

        virtualDisplay = mediaProjection
                .createVirtualDisplay("Capturing Display",
                        WIDTH, HEIGHT, DENSITY,
                        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                        inputSurface, null, null);//Verwenden Sie für die Schreibfläche die vom Encoder erhaltene
    }

Ich erstelle eine virtuelle Anzeige. ** Das Wichtigste dabei ist, die vom Encoder erhaltene Eingangsfläche auf die Schreibfläche einzustellen. ** ** ** Auf diese Weise wird der gespiegelte Bildschirm direkt auf die Eingabefläche des Encoders geschrieben. Der Bildschirm wird ohne besondere Aktion codiert. Der Fluss ist unten gezeigt.

fig6.png

5. StartServer Starten Sie den Server-Thread

Dies ist der Code in den Zeilen 312 bis 322.

MainAcitvity


//Starten Sie den Standby-Modus und senden Sie Threads
    private void StartServer() {
        senderThread = new HandlerThread("senderThread");
        senderThread.start();
        senderHandler = new Handler(senderThread.getLooper());

        serverThread = new Thread(this);
        serverThread.start();

        setState(States.Waiting);
    }

Wir starten einen Thread zum Senden und einen Thread zum Abhören. Da der Standby-Thread Runnable in Activity implementiert, führt er dort die Verarbeitung in run () durch. Der sendende Thread verwendet HandlerThread, damit er in die Warteschlange gestellt werden kann.

6. Serververarbeitung Warten auf eine Verbindung von einem PC

Dies ist der Code in den Zeilen 324 bis 346.

MainActivity.java


    //Server-Thread
    //Akzeptiert die Verbindung nur einmal
    public void run() {
        try {
            listener = new ServerSocket();
            listener.setReuseAddress(true);
            listener.bind(new InetSocketAddress(8080));
            System.out.println("Server listening on port 8080...");

            clientSocket = listener.accept();//Warten Sie bis die Verbindung hergestellt ist

            inputStream = clientSocket.getInputStream();
            outputStream = clientSocket.getOutputStream();

            //Die Codierung muss gestartet werden, wenn der Client verbunden ist
            codec.start();

            setState(States.Running);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Starten Sie den Server-Socket und warten Sie. Dieses Mal ist es nicht erforderlich, auf mehrere PCs zu verteilen, daher akzeptieren wir Verbindungen nur einmal.

Nach dem Anschließen startet der Encoder. Andernfalls könnte es nicht auf der PC-Seite abgespielt werden. Der erste Frame nach dem Start der Codierung ist der I-Frame, der für die zukünftige Decodierung wesentlich ist. Wenn Sie den I-Frame nicht zuerst erhalten, können Sie ihn nicht auf der PC-Seite abspielen. Ich denke, das ist der Grund, warum es nicht gespielt werden kann. (Bitte darauf hinweisen, wenn es anders ist)

Ich rahme? ?? Diejenigen, die sagen Was ist ein Keyframe? Unterschied zwischen I-Frame, P-Frame und B-Frame [GOP] Bitte sehen Sie.

7. Übermittlung von Daten

Code in den Zeilen 348-366

MainActivity.java


    //Daten senden
    //Ändern Sie nicht die Reihenfolge
    //Zur Warteschlange hinzufügen
    private void Send(final byte[] array) {
        senderHandler.post(new Runnable() {
            @Override
            public void run() {

                try {
                    outputStream.write(array);
                } catch (IOException ex) {
                    //Wenn es nicht gesendet werden kann, wird es als getrennt betrachtet.
                    ex.printStackTrace();
                    Disconnect();
                }

            }
        });
    }

Es wird im Rückrufsatz im Encoder von 3 aufgerufen. Der Rückruf wird auf dem Hauptthread ausgeführt, ebenso wie diese aufgerufene Methode. Aufgrund der Einschränkung, dass die netzwerkbezogene Verarbeitung nicht im Hauptthread ausgeführt werden soll Ich versuche, den Übertragungsprozess im Übertragungsthread durchzuführen.

Abgesehen davon kann der auf der PC-Seite angezeigte Bildschirm gestört sein, wenn Sie Folgendes schreiben.

MainActivity.java


    private void Send(final byte[] array) {

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    outputStream.write(array);
                } catch (IOException ex) {
                    //Wenn es nicht gesendet werden kann, wird es als getrennt betrachtet.
                    ex.printStackTrace();
                    Disconnect();
                }

            }
        }).start();
    }

Erstens ist es zum Zeitpunkt des ständigen Erstellens von Threads kein guter Code. Dies garantiert nicht die Reihenfolge der zu sendenden Frames. Wie bereits im I-Frame-Kommentar erwähnt, Weil der komprimierte Frame nur den Unterschied zwischen dem vorherigen und dem nächsten Frame darstellt Wenn der Kontext gestört ist, wird er nicht korrekt dekodiert.

8. Schneiden und Nachbearbeiten

Dies ist der Code in den Zeilen 368-387.

MainActivity.java


//Schneidvorgang
    private void Disconnect() {

        try {
            codec.stop();
            codec.release();
            virtualDisplay.release();
            mediaProjection.stop();


            listener.close();
            if (clientSocket != null)
                clientSocket.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        }

        setState(States.Stop);
    }

Die bisher verwendeten Objekte werden gestoppt und freigegeben. Dadurch kehren Sie in den Stoppzustand zurück. Wenn Sie die Taste erneut drücken, beginnt der Vorgang von vorne und Sie können erneut eine Verbindung herstellen.

Kommunizieren Sie zwischen PC und Android-Gerät über USB

Insbesondere kann dies durch Verwendung eines ADB-Servers wie eines Proxyservers realisiert werden. Mit adb spielen Ich habe hier darauf hingewiesen.

Einfach mit einem einzigen Befehl

adb forward tcp:xxxx tcp:yyyy

Geben Sie in xxxx die auf der PC-Seite verwendete Portnummer und in yyyy die auf dem Terminal verwendete Portnummer an. Diesmal

adb forward tcp:8080 tcp:8080

Ich denke es ist okay. Wenn Sie nun eine Verbindung zum 8080-Port von localhost (127.0.0.1) auf der PC-Seite herstellen, wird die Verbindung zum 8080 auf der Terminalseite ** über USB hergestellt.

Abgesehen davon, warum wurde 127.0.0.1 der IP von localhost zugewiesen? Ich war neugierig darauf, und als ich es nachgeschlagen habe, scheint es einen historischen Hintergrund für IPv4 zu geben. Warum ist 127.0.0.1 ein lokaler Host?

Zeigen Sie den Bildschirm auf dem PC an

Vielen Dank, dass Sie diesen langen Artikel bisher gelesen haben. Zum Schluss möchte ich den Bildschirm auf dem PC anzeigen und fertig stellen. Da das am Anfang des Artikels eingeführte ffplay verwendet wird, laden Sie es bitte herunter, wenn Sie es nicht haben. Download FFmpeg Entpacken Sie nach dem Herunterladen die Datei und Sie finden den Ordner bin, der das Hauptgerät enthält. Wie bei FFmpeg wird ffplay durch Angabe von Parametern über die CUI gestartet.

Verfahren

    1. Schließen Sie Ihr Android-Gerät an Ihren PC an, damit es von adb erkannt wird.
  1. Führen Sie adb forward tcp: 8080 tcp: 8080 aus
    1. Starten Sie die App auf Ihrem Android-Gerät und drücken Sie Start
  2. Wechseln Sie nach dem Starten der Eingabeaufforderung oder von PowerShell in das Verzeichnis, in dem sich ffplay befindet, und führen Sie die folgenden Schritte aus
ffplay -framerate 60 -analyzeduration 100 -i tcp://127.0.0.1:8080

Der Bildschirm auf der Android-Seite wird jetzt auf Ihrem PC angezeigt. (Wenn es nicht angezeigt wird, senken Sie bitte die Statusleiste oder kehren Sie nach Hause zurück, um den Bildschirm zu aktualisieren.)

Sie können mit Esc beenden.

Bedeutung der Parameter

-framerate 60 gibt einfach die Bildrate an. Muss mit den Einstellungen der Cast-App übereinstimmen.

-analyzeduration 100 Begrenzt die Zeitspanne, in der ffplay empfangene Frames analysiert. (Diesmal 100 ms) ffplay analysiert und zeigt an, nachdem sich eine bestimmte Anzahl von Frames angesammelt hat. Wenn diese Option nicht angegeben ist, wird sie mit einer Verzögerung angezeigt.

-i tcp: //127.0.0.1:8080 Die Adresse, an der der Stream empfangen werden soll. Wenn Sie es über WLAN versuchen möchten, geben Sie bitte die IP des Terminals an. Wenn Sie hier den Dateipfad angeben, können Sie das Video auch normal abspielen.

ich bin in Schwierigkeiten

Ich habe ein persönliches Problem. Wenn Sie Informationen haben, lassen Sie es mich bitte wissen. ** Unter Android 8.0 erfolgt das Warten auf die Verbindung des Server-Sockets in einem separaten Thread

MainActivity.java


clientSocket = listener.accept();

Die Benutzeroberfläche ist blockiert mit. Die physischen Tasten funktionieren auch überhaupt nicht mehr. Wenn Sie keine Verbindung herstellen und die Blockierung aufheben, wird die Systembenutzeroberfläche nach einer Weile neu gestartet. Sie können es mit einem Emulator reproduzieren, versuchen Sie es also bitte.

Haben Sie in 8.0 irgendwelche Spezifikationen geändert ...? Es funktioniert gut vor 7.1.

Impressionen

Es reicht immer noch nicht aus, Vysor zu ersetzen, aber ich war überrascht, wie einfach es war, die Spiegelung zu implementieren. Es gibt immer noch nicht genügend Funktionen wie Touch-Verarbeitung in Echtzeit, aber ich würde es gerne in Zukunft machen.

Außerdem möchte ich eine Funktion erstellen, die das Terminal automatisch mit einem Skript bedienen kann. Integrieren Sie in diesem Zusammenhang die Skriptfunktion und den Editor in die C # -App, die unter Windows ausgeführt wird. Versuchen Sie, der C # -App eine Skriptfunktion hinzuzufügen Wir haben auch einen Artikel mit dem Titel veröffentlicht. Schauen Sie also bitte vorbei, wenn Sie interessiert sind.

Dann danke, dass du bis zum Ende zugesehen hast.

Nächster Erstellen einer Software, die den Android-Bildschirm auf eine PC 2 Real-Time-Touch-Edition spiegelt

Recommended Posts

Erstellen Sie eine Software, die den Android-Bildschirm auf einen PC 1 spiegelt
So erstellen Sie einen Begrüßungsbildschirm
[Android] So erstellen Sie ein Dialogfragment
[Android] Ich habe mit ListView + Bottom Sheet einen Materiallistenbildschirm erstellt
Wie erstelle ich Unity Native Plugin (Android-Version)
Wie man einen imposanten Android-Musikplayer macht
[Android Studio] [Java] So fixieren Sie den Bildschirm vertikal
Anfänger in der App-Entwicklung haben versucht, eine Android-Rechner-App zu erstellen
[Einführung in die Android App-Entwicklung] Machen wir einen Zähler