2016-07-20 21 views
1

EDIT: Nevermind, löste ich mein Problem.Android MediaCodec, entschlüsseln MJPEG Frames

// -------------------------------------------- ----------------------------------

Ich habe ein paar Probleme mit MJPEG-Stream-Decodierung zu tun Verwenden der Android MediaCodec API. Was ich versuche ist, JPEG-Frames von meiner Webcam zu streamen und sie mit hardwarebeschleunigter Decodierung auf meinem Android-Gerät anzuzeigen (derzeit verwende ich Nvidia Shield Tablet, Android 6.0).

Im Moment bin ich an dem Punkt, wo ich einen JPEG-Rahmen und ich kann es auf dem Bildschirm angezeigt, indem Sie BitmapFactory.decodeStream() Funktion verwenden. Allerdings möchte ich das Bild auf einer dedizierten Hardware entschlüsseln, damit ich eine bessere Leistung und eine bessere Akkulaufzeit erzielen kann. Dazu möchte ich die MediaCodec API mit einem 'Video/mjpeg' Deocoder verwenden, von dem ich ziemlich sicher bin, dass er auf meinem Gerät verfügbar ist (ich habe die MediaCodecList geprüft und sie steht unter dem Namen 'OMX.Nvidia.mjpeg'. Decoder').

Ich verwende meinen MediaCodec mit einem SurfaceView, so wie es in 'Grafika' Repo gemacht wird. (https://github.com/google/grafika)

Dies ist der Decoder Initialisierungscode, genannt in surfaceCreated() Rückruf, schließt ohne Ausnahmen:

private Surface testSurface; 
private MediaCodec decoder; 

//.... 

private void initMediaApi() throws IOException { 
    decoder = MediaCodec.createDecoderByType("video/mjpeg"); 

    MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/mjpeg", 720, 480); 
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25); 

    decoder.configure(mediaFormat, testSurface, null, 0); 
    decoder.start(); 
} 

Danach habe ich eine Funktion, die jedes Mal, wenn ein neuer JPEG-Rahmen genannt wird Ist angekommen. Es hat zwei Parameter: einen Byte-Array mit JPEG-Bild und die Größe in Bytes dieses Bild:

private void testMediaApi(final byte[] frameBuffer, final int length) throws IOException { 

    final int TIMEOUT_USEC = 10000; 

    long firstInputTimeNsec = System.nanoTime(); 

    int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); 
    if (inputBufIndex < 0) { 
     return; 
    } 

    ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufIndex); 
    inputBuffer.clear(); 
    inputBuffer.put(frameBuffer, 0, length); 

    decoder.queueInputBuffer(inputBufIndex, 0, length, 0, 0); 

    boolean outputDone = false; 

    while (!outputDone) { 
     outputDone = true; 

     MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo(); 
     int decoderStatus = decoder.dequeueOutputBuffer(mBufferInfo, 1000000); 
     Log.d(LOG_TAG, "decoderStatus: " + decoderStatus); 

     if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
      // no output available yet 
      Log.d(TAG, "no output from decoder available"); 
     } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
      // not important for us, since we're using Surface 
      Log.d(TAG, "decoder output buffers changed"); 
     } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
      MediaFormat newFormat = decoder.getOutputFormat(); 
      Log.d(TAG, "decoder output format changed: " + newFormat); 
     } else if (decoderStatus < 0) { 
      throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus); 
     } else { // decoderStatus >= 0 
      if (firstInputTimeNsec != 0) { 
       // Log the delay from the first buffer of input to the first buffer 
       // of output. 
       long nowNsec = System.nanoTime(); 
       Log.d(TAG, "startup lag " + ((nowNsec - firstInputTimeNsec)/1000000.0) + " ms"); 
       firstInputTimeNsec = 0; 
      } 

      Log.d(TAG, "surface decoder given buffer " + decoderStatus + " (size=" + mBufferInfo.size + ")"); 
      if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
       Log.d(TAG, "output EOS"); 
       outputDone = true; 
      } 

      boolean doRender = (mBufferInfo.size != 0); 

      // As soon as we call releaseOutputBuffer, the buffer will be forwarded 
      // to SurfaceTexture to convert to a texture. We can't control when it 
      // appears on-screen, but we can manage the pace at which we release 
      // the buffers. 
      decoder.releaseOutputBuffer(decoderStatus, doRender); 
      Log.d(TAG, "Reached EOS, looping"); 
      decoder.flush(); // reset decoder state 
     } 
    } 
} 

Wie man sehen kann ich 1 Sekunde warte eine Frame-Decodierung abgeschlossen ist, die viel sein sollte. Leider erhalte ich nach dem ersten Anruf den Decoder-Statuscode -1, was bedeutet, dass die Ausgabedaten immer noch nicht verfügbar sind. Nachdem der zweite Rahmen der Warteschlange I erhalten Status -3 ‚Decoder Ausgangspuffer geändert‘ und dann stürzt das Ganze mir diese Ausnahme geben:

07-20 21:21:25.512 10096-10109/piotrek.androidfpvtest W/MediaCodec: ResourceManagerService died. 
07-20 21:21:25.513 10096-10127/piotrek.androidfpvtest E/ACodec: OMX/mediaserver died, signalling error! 
07-20 21:21:25.513 10096-10127/piotrek.androidfpvtest E/ACodec: signalError(omxError 0x8000100d, internalError -32) 
07-20 21:21:25.513 10096-10126/piotrek.androidfpvtest E/MediaCodec: Codec reported err 0xffffffe0, actionCode 0, while in state 6 
07-20 21:21:25.513 10096-10125/piotrek.androidfpvtest W/System.err: java.lang.IllegalStateException 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at android.media.MediaCodec.native_dequeueOutputBuffer(Native Method) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at android.media.MediaCodec.dequeueOutputBuffer(MediaCodec.java:2622) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at piotrek.androidfpvtest.MainActivity.testMediaApi(MainActivity.java:226) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at piotrek.androidfpvtest.MainActivity.doReceive(MainActivity.java:161) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at piotrek.androidfpvtest.MainActivity.access$000(MainActivity.java:29) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at piotrek.androidfpvtest.MainActivity$1.run(MainActivity.java:54) 
07-20 21:21:25.514 10096-10125/piotrek.androidfpvtest W/System.err:  at java.lang.Thread.run(Thread.java:818) 

Wenn statt kontinuierlich Warteschlangen Rahmen I für die erste warten decodiert werden das Programm wird in Endlosschleife mit Ausgabe wie folgt aussehen:

07-20 21:58:29.970 10577-10641/piotrek.androidfpvtest I/OMXClient: Using client-side OMX mux. 
07-20 21:58:30.110 10577-10640/piotrek.androidfpvtest I/MediaCodec: [OMX.Nvidia.mjpeg.decoder] setting surface generation to 10830849 
07-20 21:58:30.115 10577-10641/piotrek.androidfpvtest I/ACodec: Enable timestamp filtering for Video Decoder 
07-20 21:58:30.126 10577-10641/piotrek.androidfpvtest D/SurfaceUtils: set up nativeWindow 0xae47c708 for 720x480, color 0x106, rotation 0, usage 0x2b00 
07-20 21:58:56.719 10577-10641/piotrek.androidfpvtest D/SurfaceUtils: set up nativeWindow 0xae47c708 for 720x480, color 0x147, rotation 0, usage 0x2b00 
07-20 21:58:56.720 10577-10641/piotrek.androidfpvtest W/ACodec: [OMX.Nvidia.mjpeg.decoder] setting nBufferCountActual to 11 failed: -1010 
07-20 21:58:56.720 10577-10641/piotrek.androidfpvtest W/ACodec: [OMX.Nvidia.mjpeg.decoder] setting nBufferCountActual to 10 failed: -1010 
07-20 21:58:57.713 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:58:57.713 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:58:57.714 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -3 
07-20 21:58:57.714 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoder output buffers changed 
07-20 21:58:58.714 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:58:58.715 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:58:59.717 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:58:59.718 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:00.720 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:00.720 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:01.721 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:01.721 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:02.723 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:02.723 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:03.724 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:03.724 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:04.724 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:04.724 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:05.726 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:05.726 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 
07-20 21:59:06.727 10577-10639/piotrek.androidfpvtest D/MediaAPITest: decoderStatus: -1 
07-20 21:59:06.727 10577-10639/piotrek.androidfpvtest D/MediaAPITest: no output from decoder available 

Wenn jemand Erfahrung mit dieser API hat, würde ich für jede Hilfe sehr dankbar!

Antwort

1

Ich löste mein Problem selbst. Es stellt sich heraus, dass dieser spezielle Decoder keine Ausgabe an die Oberfläche unterstützt, also ist die Lösung, rohen ByteBuffer zu bekommen und dann GLES zu verwenden, um YUV-> RGB Umwandlung zu machen und es auf dem Bildschirm anzuzeigen.

+0

Wenn diese Frage nichts beiträgt, löschen Sie sie bitte. – FuzzyAmi