2013-11-03 12 views
6

Tôi đang cố gắng sử dụng MediaCodec để truy xuất tất cả các khung hình từ video để xử lý hình ảnh, tôi đang cố gắng hiển thị video và chụp khung từ outBuffers nhưng tôi có thể 't bắt đầu một thể hiện bitmap từ các byte nhận được.MediaCodec nhận tất cả các khung hình từ video

Tôi đã cố gắng hiển thị nó lên bề mặt hoặc không có gì (null), bởi vì tôi đã nhận thấy rằng khi bạn kết xuất thành rỗng thì các outBuffer nhận được các byte của khung được hiển thị.

Đây là mã:

private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/test_videos/sample2.mp4"; 
private PlayerThread mPlayer = null; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    SurfaceView sv = new SurfaceView(this); 
    sv.getHolder().addCallback(this); 
    setContentView(sv); 
} 

protected void onDestroy() { 
    super.onDestroy(); 
} 

@Override 
public void surfaceCreated(SurfaceHolder holder) { 
} 

@Override 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
    if (mPlayer == null) { 
     mPlayer = new PlayerThread(holder.getSurface()); 
     mPlayer.start(); 
    } 
} 

@Override 
public void surfaceDestroyed(SurfaceHolder holder) { 
    if (mPlayer != null) { 
     mPlayer.interrupt(); 
    } 
} 

private void writeFrameToSDCard(byte[] bytes, int i, int sampleSize) { 
    try { 
     Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, sampleSize); 

     File file = new File(Environment.getExternalStorageDirectory() + "/test_videos/sample" + i + ".png"); 
     if (file.exists()) 
      file.delete(); 

     file.createNewFile(); 

     FileOutputStream out = new FileOutputStream(file.getAbsoluteFile()); 

     bmp.compress(Bitmap.CompressFormat.PNG, 90, out); 
     out.close(); 

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

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(SAMPLE); 

     int index = extractor.getTrackCount(); 
     Log.d("MediaCodecTag", "Track count: " + index); 

     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, 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; 
     long startMs = System.currentTimeMillis(); 

     int i = 0; 
     while (!Thread.interrupted()) { 
      if (!isEOS) { 
       int inIndex = decoder.dequeueInputBuffer(10000); 
       if (inIndex >= 0) { 
        ByteBuffer buffer = inputBuffers[inIndex]; 

        int sampleSize = extractor.readSampleData(buffer, 0); 

        if (sampleSize < 0) { 
         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(); 
        } 
       } 
      } 

      /* saves frame to sdcard */ 
      int outIndex = decoder.dequeueOutputBuffer(info, 10000); // outIndex most of the times null 

      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: 
       ByteBuffer buffer = outputBuffers[outIndex]; 
       Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer); 

       // 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); 
       try { 
        byte[] dst = new byte[outputBuffers[outIndex].capacity()]; 
        outputBuffers[outIndex].get(dst); 
        writeFrameToSDCard(dst, i, dst.length); 
        i++; 
       } catch (Exception e) { 
        Log.d("iDecodeActivity", "Error while creating bitmap with: " + e.getMessage()); 
       } 

       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(); 
    } 
} 

Bất kỳ trợ giúp sẽ được nhiều appriciated

+0

Hai Tôi đang cố gắng lấy khung hình từ video thực sự tôi đã thử với mediametedatareteriver nhưng hình ảnh liên tục giống nhau cho tất cả hình ảnh tôi đã thử rất nhiều nhưng không thể khắc phục nó sau khi sử dụng ffmpeg một số vấn đề sau đó tôi ' d đi với MediaExcator tôi không thể hiểu nó bằng cách lướt internet cho nó thấy mã của bạn trong stackoverflow vì vậy bạn có thể xin vui lòng giúp tôi bằng mã làm việc đầy đủ của bạn Sir bởi vì tôi mới đến Android. – Manoj

Trả lời

12

Bạn có thể giải mã một Surface hoặc một ByteBuffer, nhưng không phải cả hai. Vì bạn đang cấu hình một Surface, sẽ luôn có 0 byte dữ liệu trong bộ đệm đầu ra.

Nếu bạn định cấu hình để giải mã ByteBuffer, định dạng dữ liệu sẽ thay đổi, nhưng theo hiểu biết của tôi sẽ không bao giờ là định dạng ARGB mà Bitmap hiểu. Bạn có thể xem các ví dụ về hai định dạng YUV đang được kiểm tra trong các thử nghiệm đệm-bộ đệm trong CTS EncodeDecodeTest trong phương thức checkFrame(). Lưu ý, tuy nhiên, điều đầu tiên nó làm là kiểm tra định dạng và trả về ngay lập tức nếu nó không được nhận dạng.

Hiện tại (Android 4.4), cách đáng tin cậy nhất để thực hiện việc này là giải mã thành SurfaceTexture, hiển thị bằng GLES và trích xuất dữ liệu RGB với glReadPixels(). Mã mẫu có sẵn trên bigflake - xem ExtractMpegFramesTest (yêu cầu API 16+).

+0

Cảm ơn bạn đã phản hồi, như bạn đã nói tôi trả về byte buffer (đặt null thay vì bề mặt, và trong releaseOutputBuffer tôi putted false). Bây giờ, như tôi thấy, đối với API dưới 18 nếu tôi muốn nhận các khung như bitmap hơn tôi cần phải xác định định dạng và tự thay đổi nó thành RGB? âm thanh như một cách khó xử để có được khung hình ra khỏi video, có lẽ openCV là phù hợp hơn cho loại công việc – Nativ

+0

Chưa sử dụng OpenCV. Cách tiếp cận GLES là hiệu quả nhất, nhưng cũng phức tạp nhất để thực hiện. Lưu ý các công trình này bắt đầu từ API 16; Tôi đã đề cập API 18 vì không có gì tốt hơn ở đó. API 19 giới thiệu 'android.media.ImageReader', nhưng tôi cần kiểm tra những định dạng nào hỗ trợ. – fadden

+0

Tôi đã thêm mã mẫu vào hình lớn. Tôi cũng xác nhận rằng 'ImageReader' không xử lý đầu ra bộ giải mã' MediaCodec'. – fadden

Các vấn đề liên quan