2014-04-27 4 views
4

J'essaye d'obtenir toutes les images du fichier vidéo en utilisant MediaCodec. Si j'essaie d'afficher de la vidéo sur SurfaceView, tout va bien. Mais si surface est nulle, et quand j'essaye d'obtenir Bitmap à partir du tableau byte, alwaus obtient une exception null ou runtime.Comment obtenir bitmap (images) à partir de la vidéo en utilisant MediaCodec

Ceci est mon code:

private class PlayerThread extends Thread { 
    private MediaExtractor extractor; 
    private MediaCodec decoder; 
    private Surface surface; 

    public PlayerThread(Surface surface) { 
     this.surface = surface; 
    } 

    @Override 
    public void run() { 
     extractor = new MediaExtractor(); 
     extractor.setDataSource(videoPath); 

     for (int i = 0; i < extractor.getTrackCount(); i++) { 
      MediaFormat format = extractor.getTrackFormat(i); 
      String mime = format.getString(MediaFormat.KEY_MIME); 
      if (mime.startsWith("video/")) { 
       extractor.selectTrack(i); 
       decoder = MediaCodec.createDecoderByType(mime); 
       decoder.configure(format, /*surface*/ null, null, 0); 
       break; 
      } 
     } 

     if (decoder == null) { 
      Log.e("DecodeActivity", "Can't find video info!"); 
      return; 
     } 

     decoder.start(); 

     ByteBuffer[] inputBuffers = decoder.getInputBuffers(); 
     ByteBuffer[] outputBuffers = decoder.getOutputBuffers(); 
     BufferInfo info = new BufferInfo(); 
     boolean isEOS = false; 

     while (!Thread.interrupted()) { 
      ++numFrames; 
      if (!isEOS) { 
       int inIndex = decoder.dequeueInputBuffer(10000); 
       if (inIndex >= 0) { 
        ByteBuffer buffer = inputBuffers[inIndex]; 
        int sampleSize = extractor.readSampleData(buffer, 0); 
        if (sampleSize < 0) { 
         // We shouldn't stop the playback at this point, 
         // just pass the EOS 
         // flag to decoder, we will get it again from the 
         // dequeueOutputBuffer 
         Log.d("DecodeActivity", 
           "InputBuffer BUFFER_FLAG_END_OF_STREAM"); 
         decoder.queueInputBuffer(inIndex, 0, 0, 0, 
           MediaCodec.BUFFER_FLAG_END_OF_STREAM); 
         isEOS = true; 
        } else { 
         decoder.queueInputBuffer(inIndex, 0, sampleSize, 
           extractor.getSampleTime(), 0); 
         extractor.advance(); 
        } 
       } 
      } 

      int outIndex = decoder.dequeueOutputBuffer(info, 10000); 
      switch (outIndex) { 
      case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: 
       Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED"); 
       outputBuffers = decoder.getOutputBuffers(); 
       break; 
      case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: 
       Log.d("DecodeActivity", 
         "New format " + decoder.getOutputFormat()); 
       break; 
      case MediaCodec.INFO_TRY_AGAIN_LATER: 
       Log.d("DecodeActivity", "dequeueOutputBuffer timed out!"); 
       break; 
      default: 
       // I tried get Bitmap on few ways 
       //1. 
       //ByteBuffer buffer = outputBuffers[outIndex]; 
       //byte[] ba = new byte[buffer.remaining()]; 
       //buffer.get(ba); 
       //final Bitmap bmp = BitmapFactory.decodeByteArray(ba, 0, ba.length);// this return null 
       //2. 
       //ByteBuffer buffer = outputBuffers[outIndex]; 
       //final Bitmap bmp = Bitmap.createBitmap(1920, 1080, Config.ARGB_8888);//using MediaFormat object I know width and height 
       //int a = bmp.getByteCount(); //8294400 
       //buffer.rewind(); 
       //int b = buffer.capacity();//3137536 
       //int c = buffer.remaining();//3137536 
       //bmp.copyPixelsFromBuffer(buffer); // java.lang.RuntimeException: Buffer not large enough for pixels 
       //I know what exception mean, but i don't know why xception occurs 

       //In this place I need bitmap 

       // We use a very simple clock to keep the video FPS, or the 
       // video 
       // playback will be too fast 
       while (info.presentationTimeUs/1000 > System 
         .currentTimeMillis() - startMs) { 
        try { 
         sleep(10); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
         break; 
        } 
       } 
       decoder.releaseOutputBuffer(outIndex, true); 
       break; 
      } 

      // All decoded frames have been rendered, we can stop playing 
      // now 
      if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
       Log.d("DecodeActivity", 
         "OutputBuffer BUFFER_FLAG_END_OF_STREAM"); 
       break; 
      } 
     } 

     decoder.stop(); 
     decoder.release(); 
     extractor.release(); 
    } 
} 

Je ne sais pas comment le résoudre.

Samos

Répondre

7

Il y a quelques problèmes avec votre code (ou, sans doute, avec MediaCodec).

Tout d'abord, la gestion de ByteBuffer dans MediaCodec est médiocre. Vous devez donc définir manuellement les paramètres du tampon à partir des valeurs de l'objet BufferInfo rempli par dequeueOutputBuffer(). Deuxièmement, les valeurs qui sortent du MediaCodec sont au format YUV, pas RVB. Depuis Android 4.4, le framework Android ne fournit pas de fonction permettant de convertir la sortie en bitmap. Vous devrez fournir vos propres convertisseurs YUV-à-RVB (pluriel - il existe plusieurs formats). Certains appareils utilisent des formats de couleur exclusifs et non documentés.

Vous pouvez voir un exemple d'extraction et d'examen du contenu du tampon de décodeur MediaCodec dans les méthodes tampon-tampon CTS EncodeDecodeTest (par exemple checkFrame()).

Une façon plus fiable de procéder est de revenir au décodage vers une surface, mais d'extraire les pixels avec OpenGL ES. Le bigflake ExtractMpegFramesTest montre comment faire cela.

1

La réponse courte est:

Dans la section par défaut de votre instruction switch, vous devez réinitialiser la position ByteBuffer, donc au lieu de:

ByteBuffer buffer = outputBuffers[outIndex]; 
byte[] ba = new byte[buffer.remaining()]; 
buffer.get(ba); 

vous devriez avoir quelque chose comme

ByteBuffer buffer = outputBuffers[outIndex]; 
buffer.position(info.offset); 
buffer.limit(info.offset + info.size); 
byte[] ba = new byte[buffer.remaining()]; 
buffer.get(ba); 

Dans votre code d'origine, vous trouverez que votre ByteBuffer a un reste() de 0.

Questions connexes