27

Używam próbki FaceTracker z interfejsu API wizji systemu Android. Mam jednak problemy z nagrywaniem filmów, gdy nakładane są na nich nakładki.Nagrywarka multimediów z interfejsem API Google Vision

Jednym ze sposobów jest zapisywanie bitmap jako obrazów i przetwarzanie ich przy użyciu FFmpeg lub Xuggler w celu scalenia ich jako wideo, ale zastanawiam się, czy istnieje lepsze rozwiązanie tego problemu, jeśli możemy nagrywać wideo w czasie wykonywania, ponieważ podgląd jest wyświetlany .

Aktualizacja 1: zaktualizowałem klasę following z nagrywającego, ale nagranie nadal nie działa. To rzuca się następujący błąd, gdy zgłoszę triggerRecording funkcji():

MediaRecorder: start zwany w nieprawidłowym stanie: 4

i mam zewnętrznego pozwolenie przechowywania w pliku manifestu.

Aktualizacja 2:

mam stałe powyższą kwestię w kodzie i przeniósł setupMediaRecorder() w onSurfaceCreated zwrotnego. Jednak po zatrzymaniu nagrywania zgłasza wyjątek środowiska wykonawczego. Według documentation, jeśli nie ma danych wideo/audio, zostanie zgłoszony wyjątek Runtime.

Więc czego tu brakuje?

public class CameraSourcePreview extends ViewGroup { 
    private static final String TAG = "CameraSourcePreview"; 

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 

    static { 
     ORIENTATIONS.append(Surface.ROTATION_0, 90); 
     ORIENTATIONS.append(Surface.ROTATION_90, 0); 
     ORIENTATIONS.append(Surface.ROTATION_180, 270); 
     ORIENTATIONS.append(Surface.ROTATION_270, 180); 
    } 

    private MediaRecorder mMediaRecorder; 
    /** 
    * Whether the app is recording video now 
    */ 
    private boolean mIsRecordingVideo; 

    private Context mContext; 
    private SurfaceView mSurfaceView; 
    private boolean mStartRequested; 
    private boolean mSurfaceAvailable; 
    private CameraSource mCameraSource; 

    private GraphicOverlay mOverlay; 

    public CameraSourcePreview(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     mContext = context; 
     mStartRequested = false; 
     mSurfaceAvailable = false; 

     mSurfaceView = new SurfaceView(context); 

     mSurfaceView.getHolder().addCallback(new SurfaceCallback()); 

     addView(mSurfaceView); 

     mMediaRecorder = new MediaRecorder(); 
    } 

    private void setUpMediaRecorder() throws IOException { 
     mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface()); 
     mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
     mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); 
     mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); 

     mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory() + File.separator + Environment.DIRECTORY_DCIM + File.separator + System.currentTimeMillis() + ".mp4"); 
     mMediaRecorder.setVideoEncodingBitRate(10000000); 
     mMediaRecorder.setVideoFrameRate(30); 
     mMediaRecorder.setVideoSize(480, 640); 
     mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); 
     mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); 
     //int rotation = mContext.getWindowManager().getDefaultDisplay().getRotation(); 
     //int orientation = ORIENTATIONS.get(rotation); 
     mMediaRecorder.setOrientationHint(ORIENTATIONS.get(0)); 
     mMediaRecorder.prepare(); 

     mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() { 
      @Override 
      public void onError(MediaRecorder mr, int what, int extra) { 
       Timber.d(mr.toString() + " : what[" + what + "]" + " Extras[" + extra + "]"); 
      } 
     }); 
    } 

    public void start(CameraSource cameraSource) throws IOException { 
     if (cameraSource == null) { 
      stop(); 
     } 

     mCameraSource = cameraSource; 

     if (mCameraSource != null) { 
      mStartRequested = true; 
      startIfReady(); 
     } 
    } 

    public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException { 
     mOverlay = overlay; 
     start(cameraSource); 
    } 

    public void stop() { 
     if (mCameraSource != null) { 
      mCameraSource.stop(); 
     } 
    } 

    public void release() { 
     if (mCameraSource != null) { 
      mCameraSource.release(); 
      mCameraSource = null; 
     } 
    } 

    private void startIfReady() throws IOException { 
     if (mStartRequested && mSurfaceAvailable) { 
      mCameraSource.start(mSurfaceView.getHolder()); 
      if (mOverlay != null) { 
       Size size = mCameraSource.getPreviewSize(); 
       int min = Math.min(size.getWidth(), size.getHeight()); 
       int max = Math.max(size.getWidth(), size.getHeight()); 
       if (isPortraitMode()) { 
        // Swap width and height sizes when in portrait, since it will be rotated by 
        // 90 degrees 
        mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing()); 
       } else { 
        mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing()); 
       } 
       mOverlay.clear(); 
      } 

      mStartRequested = false; 
     } 
    } 

    private class SurfaceCallback implements SurfaceHolder.Callback { 
     @Override 
     public void surfaceCreated(SurfaceHolder surface) { 
      mSurfaceAvailable = true; 
      surface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

      // setup the media recorder 
      try { 
       setUpMediaRecorder(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 

      try { 
       startIfReady(); 
      } catch (IOException e) { 
       Timber.e(TAG, "Could not start camera source.", e); 
      } 
     } 

     @Override 
     public void surfaceDestroyed(SurfaceHolder surface) { 
      mSurfaceAvailable = false; 
     } 

     @Override 
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
     } 
    } 

    @Override 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     int width = 320; 
     int height = 240; 
     if (mCameraSource != null) { 
      Size size = mCameraSource.getPreviewSize(); 
      if (size != null) { 
       width = size.getWidth(); 
       height = size.getHeight(); 
      } 
     } 

     // Swap width and height sizes when in portrait, since it will be rotated 90 degrees 
     if (isPortraitMode()) { 
      int tmp = width; 
      width = height; 
      height = tmp; 
     } 

     final int layoutWidth = right - left; 
     final int layoutHeight = bottom - top; 

     // Computes height and width for potentially doing fit width. 
     int childWidth = layoutWidth; 
     int childHeight = (int) (((float) layoutWidth/(float) width) * height); 

     // If height is too tall using fit width, does fit height instead. 
     if (childHeight > layoutHeight) { 
      childHeight = layoutHeight; 
      childWidth = (int) (((float) layoutHeight/(float) height) * width); 
     } 

     for (int i = 0; i < getChildCount(); ++i) { 
      getChildAt(i).layout(0, 0, childWidth, childHeight); 
     } 

     try { 
      startIfReady(); 
     } catch (IOException e) { 
      Timber.e(TAG, "Could not start camera source.", e); 
     } 
    } 

    private boolean isPortraitMode() { 
     int orientation = mContext.getResources().getConfiguration().orientation; 
     if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 
      return false; 
     } 
     if (orientation == Configuration.ORIENTATION_PORTRAIT) { 
      return true; 
     } 

     Timber.d(TAG, "isPortraitMode returning false by default"); 
     return false; 
    } 

    private void startRecordingVideo() { 
     try { 
      // Start recording 
      mMediaRecorder.start(); 
      mIsRecordingVideo = true; 
     } catch (IllegalStateException e) { 
      e.printStackTrace(); 
     } 
    } 

    private void stopRecordingVideo() { 
     // UI 
     mIsRecordingVideo = false; 
     // Stop recording 
     mMediaRecorder.stop(); 
     mMediaRecorder.reset(); 
    } 

    public void triggerRecording() { 
     if (mIsRecordingVideo) { 
      stopRecordingVideo(); 
      Timber.d("Recording stopped"); 
     } else { 
      startRecordingVideo(); 
      Timber.d("Recording starting"); 
     } 
    } 
} 
+1

Mam do czynienia z tym samym problemem –

+0

, więc znalazłeś jakieś rozwiązanie? – Reshma

+0

Z kilku powodów zmieniłem podejście i zamiast tego użyłem OpenCV z GLSurfaceView. Sprawdź moje inne pytanie: http://stackoverflow.com/q/33368655/1053097 – muneikh

Odpowiedz

0

Rozwiązanie 1: z Android Lollipop, A MediaProjection API została wprowadzona, które w połączeniu z MediaRecorder mogą być wykorzystywane do zapisywania SurfaceView do pliku wideo. This example shows how to output a SurfaceView to a video file.

Rozwiązanie 2: Alternatywnie, można użyć jednej z klas zgrabnych kodera przewidzianych w Grafika repository. Zauważ, że będzie to wymagało przeniesienia aplikacji FaceTracker, tak aby używała OpenGL do wykonywania wszystkich renderowań. Dzieje się tak dlatego, że próbki Grafika wykorzystują potoki OpenGL do szybkiego odczytu i zapisu danych tekstury.

Istnieje minimalny przykład, który pozwala uzyskać dokładnie to, czego chcesz, używając klasy CircularEncoder w klasie . Zapewnia to przykład Frame Blitting, wyświetlając jednocześnie dane bufora ramki na ekranie i wyświetlając wideo.

Główną zmianą byłoby użycie Grafiki WindowSurface zamiast aplikacji SurfaceView dla aplikacji FaceTracker, która ustawia Kontekst EGL, umożliwiając zapisanie danych bufora ramki do pliku za pomocą Encoder. Gdy możesz renderować wszystko do WindowSurface, ustawienie nagrywania w taki sam sposób, jak klasa ContinuousCaptureActivity jest trywialne.

Powiązane problemy