2014-11-30 3 views
6

ich auf einer Anwendung arbeite, die den Bildschirm in eine Bitmap erfassen muss zu übertragen. Ich versuche, die neue Android 5.0 android.media.projection APIs zu verwenden, um die Screen-Capture zu tun.Systemfehler die Ausgabe einer MediaProjection virtuellen Anzeige zu einem Imagereader erfassen

Der Workflow für diese API gipfelt in einem Aufruf von

mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, DPI, 
    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null); 

In meinem ersten Versuch in dieser capture ich die Oberfläche Objekt aus einem Surface bezogen. Dies funktioniert korrekt; Das Endergebnis ist ein winziges Duplikat der Anzeige, die auf dem Bildschirm gezeichnet wird (was zu einem

0) Ich dachte, das Feature fast abgeschlossen, aber I then discovered, dass SurfaceViews (vom Code Standpunkt) nicht lesbar sind; Sie können keine Bitmap von ihnen erhalten.

In nach anderen Lösungen suchen stieß ich auf this question, die ein sehr ähnliches Ziel zu mir hat, und in diesem Thread ist es suggested einen Imagereader anstelle einem Surface zu verwenden, um die Oberfläche, die Sie den createVirtualDisplay API-Aufruf übergeben zu beziehen.

Wenn ich jedoch meinen Code ändere, um einen ImageReader anstelle von SurfaceView zu verwenden, erhalte ich Laufzeit-Logcat-Fehler (keine Ausnahmen) und die Callback-Funktion für den ImageReader wird nie aufgerufen. Der createVirtualDisplay Aufruf gibt auch ein scheinbar gültiges VirtualDisplay Objekt. Hier

ist die logcat:

9230-9270/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: createGraphicBuffer failed 
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count 
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count 
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count 
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count 

Die zweite Zeile wiederholt ~ 100-mal, bevor es auftritt, stoppt.

Stepping auf dem Debugger durch Ich sehe, dass der erste Fehler während des createVirtualDisplay Anruf auftritt, und alle anderen passieren irgendwann nach der Ausführung kehrt zum Systemcode.

Die only meaningful result für diesen Fehler bezieht sich auf ein Problem in Kitkat, wo die API, die ich versuche zu konsumieren, nicht existiert. Trotzdem habe ich versucht, die fix suggested here (android:hardwareAccelerated="false" im Manifest setzen). Dies hat das Verhalten der Anwendung nicht geändert.

Wie kann ich „die Pufferzählung“ oder auf andere Weise dieses Fehler umgehen und den Bildschirm als Bitmap erhalten?

P.S. Meine Entwicklungsplattform ist das Nexus 6.

Der vollständige Code-Block, wie gewünscht:

MediaProjection mediaProjection = mgr.getMediaProjection(resultCode, data); 
ImageReader ir = ImageReader.newInstance(WIDTH, HEIGHT, ImageFormat.JPEG, 5); 
VirtualDisplay v = mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, getApplicationContext().getResources().getDisplayMetrics().densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, ir.getSurface(), null, null); 

Edit: In Bezug auf das Artefakt Problem, hier ist der Code Ich bin mit dem Bitmap aus dem Bild zu erhalten und zeigt es:

public void onImageAvailable(ImageReader reader) { 
     Image image = null; 
     ByteArrayOutputStream bos = null; 

     try { 
      image = reader.acquireLatestImage(); 
      if (null == image){ 
       return; 
      } 
      bos = new ByteArrayOutputStream(); 
      final Image.Plane[] planes = image.getPlanes(); 
      final ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer().rewind(); 
      final Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888); 
      bitmap.copyPixelsFromBuffer(buffer); 
      //bitmap.compress(Bitmap.CompressFormat.WEBP, 50, bos); 

      runOnUiThread(new Runnable() { 
       public void run() { 
        iv.setImageBitmap(bitmap); 
       } 
      }); 
+0

Ich bezweifle, dass ich persönlich dazu beitragen kann, aber es wäre nützlich, wenn Sie den Code zeigen, den Sie * verwenden (und Ihnen den Fehler gibt), anstatt den Code, den Sie verwendet haben benutzen. Darüber hinaus könntest du stattdessen [faddens Antwort darauf] (http://stackoverflow.com/a/26551760/115145) versuchen, wenn man bedenkt, dass er etwa 9 Jahre lang an Android gearbeitet hat :-). – CommonsWare

+0

Hallo Ich habe das gleiche Problem, erhalten Sie irgendeine Lösung? – Charlesjean

+0

@Charlesjean No. Fadden scheint einen alternativen Ansatz vorzuschlagen, der dieses Problem lösen könnte, aber seine Komplexität würde mehr Zeit in Anspruch nehmen als geplant für dieses Projekt. – Techrocket9

Antwort

6

ich glaube, ich jetzt diese Frage beantworten, kann ich das gleiche Problem begegnet, und nachdem ich ändern ImageFormat.JPEG-PixelFormat.RGBA_8888 alles gut geht. Es scheint, dass ImageFormat.JPEG nicht unterstützt wird.

Sie müssen den folgenden Code verwenden, um die richtige Bitmap zu erhalten:

    int width = img.getWidth(); 
        int height = img.getHeight(); 
        int pixelStride = planes[0].getPixelStride(); 
        int rowStride = planes[0].getRowStride(); 
        int rowPadding = rowStride - pixelStride * width; 
        byte[] newData = new byte[width * height * 4]; 

        int offset = 0; 
        bitmap = Bitmap.createBitmap(metrics,width, height, Bitmap.Config.ARGB_8888); 
        ByteBuffer buffer = planes[0].getBuffer(); 
        for (int i = 0; i < height; ++i) { 
         for (int j = 0; j < width; ++j) { 
          int pixel = 0; 
          pixel |= (buffer.get(offset) & 0xff) << 16;  // R 
          pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G 
          pixel |= (buffer.get(offset + 2) & 0xff);  // B 
          pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A 
          bitmap.setPixel(j, i, pixel); 
          offset += pixelStride; 
         } 
         offset += rowPadding; 
        } 

Aus diese Weise kann der Inhalt der Bitmap ist, was Sie wollen.

PS: Ich möchte wirklich sagen, der Doc von Android ist ziemlich schlecht. Wir müssen zu viele Details untersuchen, um sdk api korrekt verwenden zu können.

+0

Nun, das ist zumindest Fortschritt. Keine seltsamen Systemfehler mehr, aber ich bekomme ein korruptes Bild heraus: http://i.imgur.com/Mu3R61X.png Zumindest ist es erkennbar. – Techrocket9

+0

@ Techrocket9 Ich habe das gleiche Problem. http://i.imgur.com/rJEjrYZ.jpg (Charlesjean) Hast du das selbe Problem oder ist dein Screenshot ohne Probleme? – binW

+0

@binW Ich habe nicht viel Zeit daran arbeiten, und ich habe nicht versucht, das Bild zu speichern. – Charlesjean

0

Eine bessere Möglichkeit, das Image von ImageReader zu erhalten, besteht darin, ein Bitmap in der richtigen Größe zu erstellen und die Methode copyPixelsFromBuffer() zu verwenden. Erstellen Sie Imagereader wie folgt:

mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.RGB_565, 2); 

Dann Sie das Bild von mImageReader bekommen Sie den Code unten.

final Image.Plane[] planes = image.getPlanes(); 
final ByteBuffer buffer = planes[0].getBuffer(); 
int offset = 0; 
int pixelStride = planes[0].getPixelStride(); 
int rowStride = planes[0].getRowStride(); 
int rowPadding = rowStride - pixelStride * mWidth; 
// create bitmap 
bitmap = Bitmap.createBitmap(mWidth+rowPadding/pixelStride, mHeight, Bitmap.Config.RGB_565); 
bitmap.copyPixelsFromBuffer(buffer); 
image.close(); 

Ich habe den Prozess der Erfassung Bildschirm mit MediaProjection API beschrieben zusammen mit den Fehlern der meisten Menschen gemacht, wenn sie von Imagereader in einem blog post bekommen Bild, das Sie bei Interesse lesen.

+0

Ich bekomme 'pixelStride == 0', verursacht ArithmeticException: dividiere durch Null. –