2015-12-03 1 views
2

J'essaie de mettre en œuvre un robinet pour enregistrer la fonctionnalité comme dans la vigne. Un exemple pour gérer l'enregistrement (pas de toucher à enregistrer) fourni dans javacv est https://github.com/bytedeco/javacv/blob/master/samples/RecordActivity.java. J'essaie de le modifier afin que les cadres de méthode onPreviewFrame soient ajoutés au tampon uniquement lorsque l'utilisateur a placé son doigt sur l'écran. Ces images sont ensuite essayées pour être combinées en vidéo finale dans la méthode stopRecording.Appuyez pour enregistrer comme dans la vigne en utilisant javacv

Le problème est que si je mets l'horodatage comme indiqué dans ci-dessous l'extrait de code (dans la méthode de stoprecording)

if (t > recorder.getTimestamp()) 
{ 
    recorder.setTimestamp(t); 
} 

le comportement est comme ci-dessous

Cas 1

Si je appuyez sur l'écran pour enregistrer pendant 2 secondes et retirez le doigt de l'écran pendant 3 secondes, puis placez à nouveau le doigt sur l'écran pour enregistrer pendant 4 secondes supplémentaires la vidéo résultante est comme,

Pour les 1ères secondes, la vidéo a un contenu enregistré. Pour les 3 prochaines secondes (temps où le doigt est rangé de l'écran). La vidéo montre simplement la dernière image enregistrée lorsque le doigt a été placé à l'écran en dernier. Ensuite, la vidéo a enregistré du contenu vidéo pour les 4 prochaines secondes. Il semble donc y avoir un problème dans la gestion de l'enregistrement vidéo lorsque le doigt est retiré de l'écran.

Cas n ° 2

Ensuite, je retiré l'horodatage de réglage du code à l'enregistreur (l'extrait de code ci-dessus) dans la méthode de stoprecording.

La vidéo résultante (pour les mêmes étapes essayées dans le cas 1) ne contient pas les 3 secondes du milieu (ce qui est requis) lorsque le doigt a été retiré de l'écran. Mais la vidéo joue à un rythme plus rapide. Il semble donc que nous devons définir l'horodatage afin que la vidéo joue à un rythme normal.

Le code complet de mon activité est donné ci-dessous. (S'il vous plaît noter que l'enregistrement vidéo est principalement traité des méthodes onPreviewFrame et stoprecording)

public class TouchToRecordActivity extends Activity implements OnClickListener, View.OnTouchListener { 

private final static String CLASS_LABEL = "TouchToRecordActivity"; 
private final static String LOG_TAG = CLASS_LABEL; 

private String ffmpeg_link = "/mnt/sdcard/stream.mp4"; 

long startTime = 0; 
boolean recording = false; 
boolean rec = false; 

private FFmpegFrameRecorder recorder; 

private boolean isPreviewOn = false; 

private int sampleAudioRateInHz = 44100; 
private int imageWidth = 640; 
private int imageHeight = 480; 
private int destWidth = 480; 
private int frameRate = 30; 

/* audio data getting thread */ 
private AudioRecord audioRecord; 
private AudioRecordRunnable audioRecordRunnable; 
private Thread audioThread; 
volatile boolean runAudioThread = true; 

/* video data getting thread */ 
private Camera cameraDevice; 
private CameraView cameraView; 

private Frame yuvImage = null; 

/* layout setting */ 
private final int bg_screen_bx = 232; 
private final int bg_screen_by = 128; 
private final int bg_screen_width = 700; 
private final int bg_screen_height = 500; 
private final int bg_width = 1123; 
private final int bg_height = 715; 
private final int live_width = 640; 
private final int live_height = 480; 
private int screenWidth, screenHeight; 
private Button btnRecorderControl; 

/* The number of seconds in the continuous record loop (or 0 to disable loop). */ 
final int RECORD_LENGTH = 20; 
Frame[] images; 
long[] timestamps; 
ShortBuffer[] samples; 
int imagesIndex, samplesIndex; 

long firstTime = 0; 
long startPauseTime = 0; 
long totalPauseTime = 0; 
long pausedTime = 0; 
long stopPauseTime = 0; 
long totalTime = 0; 

long totalRecordedTS = 0; 

private TextView txtTimer; 
private Handler mHandler = new Handler(); 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 

    setContentView(R.layout.touch_main); 

    initLayout(); 
} 

@Override 
protected void onDestroy() { 
    super.onDestroy(); 

    recording = false; 

    if (cameraView != null) { 
     cameraView.stopPreview(); 
    } 

    if (cameraDevice != null) { 
     cameraDevice.stopPreview(); 
     cameraDevice.release(); 
     cameraDevice = null; 
    } 
} 


private void initLayout() { 

    /* get size of screen */ 
    Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 
    screenWidth = display.getWidth(); 
    screenHeight = display.getHeight(); 
    RelativeLayout.LayoutParams layoutParam = null; 
    LayoutInflater myInflate = null; 
    myInflate = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    RelativeLayout topLayout = new RelativeLayout(this); 
    setContentView(topLayout); 
    LinearLayout preViewLayout = (LinearLayout) myInflate.inflate(R.layout.touch_main, null); 
    layoutParam = new RelativeLayout.LayoutParams(screenWidth, screenHeight); 
    topLayout.addView(preViewLayout, layoutParam); 

    txtTimer = (TextView) preViewLayout.findViewById(R.id.txtTimer); 

    /* add control button: start and stop */ 
    btnRecorderControl = (Button) findViewById(R.id.recorder_control); 
    btnRecorderControl.setText("Start"); 
    btnRecorderControl.setOnClickListener(this); 

    /* add camera view */ 
    int display_width_d = (int) (1.0 * bg_screen_width * screenWidth/bg_width); 
    int display_height_d = (int) (1.0 * bg_screen_height * screenHeight/bg_height); 
    int prev_rw, prev_rh; 
    if (1.0 * display_width_d/display_height_d > 1.0 * live_width/live_height) { 
     prev_rh = display_height_d; 
     prev_rw = (int) (1.0 * display_height_d * live_width/live_height); 
    } else { 
     prev_rw = display_width_d; 
     prev_rh = (int) (1.0 * display_width_d * live_height/live_width); 
    } 
    layoutParam = new RelativeLayout.LayoutParams(prev_rw, prev_rh); 
    layoutParam.topMargin = (int) (1.0 * bg_screen_by * screenHeight/bg_height); 
    layoutParam.leftMargin = (int) (1.0 * bg_screen_bx * screenWidth/bg_width); 

    cameraDevice = Camera.open(); 
    Log.i(LOG_TAG, "cameara open"); 
    cameraView = new CameraView(this, cameraDevice); 
    topLayout.addView(cameraView, layoutParam); 
    topLayout.setOnTouchListener(this); 
    Log.i(LOG_TAG, "cameara preview start: OK"); 
} 

//--------------------------------------- 
// initialize ffmpeg_recorder 
//--------------------------------------- 
private void initRecorder() { 

    Log.w(LOG_TAG, "init recorder"); 

    if (RECORD_LENGTH > 0) { 
     imagesIndex = 0; 
     images = new Frame[RECORD_LENGTH * frameRate]; 
     timestamps = new long[images.length]; 
     for (int i = 0; i < images.length; i++) { 
      images[i] = new Frame(destWidth, imageHeight, Frame.DEPTH_UBYTE, 2); 
      timestamps[i] = -1; 
     } 
    } else if (yuvImage == null) { 
     yuvImage = new Frame(destWidth, imageHeight, Frame.DEPTH_UBYTE, 2); 
     Log.i(LOG_TAG, "create yuvImage"); 
    } 
    Log.i(LOG_TAG, "ffmpeg_url: " + ffmpeg_link); 
    recorder = new FFmpegFrameRecorder(ffmpeg_link, destWidth, imageHeight, 1); 
    recorder.setFormat("mp4"); 
    recorder.setVideoCodecName("libx264"); 
    recorder.setSampleRate(sampleAudioRateInHz); 
    // Set in the surface changed method 
    recorder.setFrameRate(frameRate); 

    Log.i(LOG_TAG, "recorder initialize success"); 

    audioRecordRunnable = new AudioRecordRunnable(); 
    audioThread = new Thread(audioRecordRunnable); 
    runAudioThread = true; 
} 

public void startRecording() { 

    initRecorder(); 

    mHandler.removeCallbacks(mUpdateTimeTask); 
    mHandler.postDelayed(mUpdateTimeTask, 100); 

    try { 
     recorder.start(); 
     startTime = System.currentTimeMillis(); 
     recording = true; 
     audioThread.start(); 

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

public void stopRecording() { 

    runAudioThread = false; 
    try { 
     audioThread.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    audioRecordRunnable = null; 
    audioThread = null; 

    if (recorder != null && recording) { 
     if (RECORD_LENGTH > 0) { 
      Log.v(LOG_TAG, "Writing frames"); 
      try { 
       int firstIndex = imagesIndex % samples.length; 
       int lastIndex = (imagesIndex - 1) % images.length; 
       if (imagesIndex <= images.length) { 
        firstIndex = 0; 
        lastIndex = imagesIndex - 1; 
       } 
       if ((startTime = timestamps[lastIndex] - RECORD_LENGTH * 1000000L) < 0) { 
        startTime = 0; 
       } 
       if (lastIndex < firstIndex) { 
        lastIndex += images.length; 
       } 
       int videoCounter = 0; 
       for (int i = firstIndex; i <= lastIndex; i++) { 
        if (timestamps[i] == -1) { 
         Log.v(LOG_TAG, "frame not recorded"); 
        } 
        if (timestamps[i] != -1) { 
         long t = timestamps[i % timestamps.length] - startTime; 
         if (t >= 0) { 

          videoCounter++; 

          /*if (((i % images.length) != 0) && images[i % images.length] != images[(i % images.length) - 1]) { 
           if (t > recorder.getTimestamp()) { 
            recorder.setTimestamp(t); 
           }*/ 
           Log.v(LOG_TAG, "imageIndex=" + (i % images.length)); 
           recorder.record(images[i % images.length]); 
         /* }*/ 
          Log.v(LOG_TAG, "videoCounter=" + videoCounter); 
         } 
        } 
       } 

       firstIndex = samplesIndex % samples.length; 
       lastIndex = (samplesIndex - 1) % samples.length; 
       if (samplesIndex <= samples.length) { 
        firstIndex = 0; 
        lastIndex = samplesIndex - 1; 
       } 
       if (lastIndex < firstIndex) { 
        lastIndex += samples.length; 
       } 
       for (int i = firstIndex; i <= lastIndex; i++) { 
        if (timestamps[i] != -1) { 
         recorder.recordSamples(samples[i % samples.length]); 
        } 
       } 
      } catch (FFmpegFrameRecorder.Exception e) { 
       Log.v(LOG_TAG, e.getMessage()); 
       e.printStackTrace(); 
      } 
     } 

     recording = false; 
     Log.v(LOG_TAG, "Finishing recording, calling stop and release on recorder"); 
     try { 
      recorder.stop(); 
      recorder.release(); 
     } catch (FFmpegFrameRecorder.Exception e) { 
      e.printStackTrace(); 
     } 
     recorder = null; 

    } 
} 

@Override 
public boolean onKeyDown(int keyCode, KeyEvent event) { 

    if (keyCode == KeyEvent.KEYCODE_BACK) { 
     if (recording) { 
      stopRecording(); 
     } 

     finish(); 

     return true; 
    } 

    return super.onKeyDown(keyCode, event); 
} 

@Override 
public boolean onTouch(View view, MotionEvent motionEvent) { 
    switch (motionEvent.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      Log.v(LOG_TAG, "ACTION_DOWN" + recording); 

      if (!recording) { 
       startRecording(); 
      } else { 
       stopPauseTime = System.currentTimeMillis(); 
       totalPauseTime = stopPauseTime - startPauseTime - ((long) (1.0/(double) frameRate) * 1000); 
       pausedTime += totalPauseTime; 
      } 
      rec = true; 
      setTotalVideoTime(); 
      btnRecorderControl.setText(getResources().getString(R.string.stop)); 
      break; 
     case MotionEvent.ACTION_MOVE: 
      rec = true; 
      setTotalVideoTime(); 
      break; 
     case MotionEvent.ACTION_UP: 
      Log.v(LOG_TAG, "ACTION_UP"); 
      rec = false; 
      startPauseTime = System.currentTimeMillis(); 
      break; 
    } 
    return true; 
} 

private Runnable mUpdateTimeTask = new Runnable() { 
    public void run() { 
     if (recording) { 
      setTotalVideoTime(); 
     } 
     mHandler.postDelayed(this, 500); 
    } 
}; 

private synchronized void setTotalVideoTime() { 
    totalTime = System.currentTimeMillis() - firstTime - pausedTime - ((long) (1.0/(double) frameRate) * 1000); 
    if (totalTime > 0) 
     txtTimer.setText(getRecordingTimeFromMillis(totalTime)); 
} 

private String getRecordingTimeFromMillis(long millis) { 
    String strRecordingTime = null; 
    int seconds = (int) (millis/1000); 
    int minutes = seconds/60; 
    int hours = minutes/60; 

    if (hours >= 0 && hours < 10) 
     strRecordingTime = "0" + hours + ":"; 
    else 
     strRecordingTime = hours + ":"; 

    if (hours > 0) 
     minutes = minutes % 60; 

    if (minutes >= 0 && minutes < 10) 
     strRecordingTime += "0" + minutes + ":"; 
    else 
     strRecordingTime += minutes + ":"; 

    seconds = seconds % 60; 

    if (seconds >= 0 && seconds < 10) 
     strRecordingTime += "0" + seconds; 
    else 
     strRecordingTime += seconds; 

    return strRecordingTime; 

} 


//--------------------------------------------- 
// audio thread, gets and encodes audio data 
//--------------------------------------------- 
class AudioRecordRunnable implements Runnable { 

    @Override 
    public void run() { 
     android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); 

     // Audio 
     int bufferSize; 
     ShortBuffer audioData; 
     int bufferReadResult; 

     bufferSize = AudioRecord.getMinBufferSize(sampleAudioRateInHz, 
       AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); 
     audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioRateInHz, 
       AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); 

     if (RECORD_LENGTH > 0) { 
      samplesIndex = 0; 
      samples = new ShortBuffer[RECORD_LENGTH * sampleAudioRateInHz * 2/bufferSize + 1]; 
      for (int i = 0; i < samples.length; i++) { 
       samples[i] = ShortBuffer.allocate(bufferSize); 
      } 
     } else { 
      audioData = ShortBuffer.allocate(bufferSize); 
     } 

     Log.d(LOG_TAG, "audioRecord.startRecording()"); 
     audioRecord.startRecording(); 

     /* ffmpeg_audio encoding loop */ 
     while (runAudioThread) { 
      if (RECORD_LENGTH > 0) { 
       audioData = samples[samplesIndex++ % samples.length]; 
       audioData.position(0).limit(0); 
      } 
      //Log.v(LOG_TAG,"recording? " + recording); 
      bufferReadResult = audioRecord.read(audioData.array(), 0, audioData.capacity()); 
      audioData.limit(bufferReadResult); 
      if (bufferReadResult > 0) { 
       Log.v(LOG_TAG, "bufferReadResult: " + bufferReadResult); 
       // If "recording" isn't true when start this thread, it never get's set according to this if statement...!!! 
       // Why? Good question... 
       if (recording && rec) { 
        Log.v(LOG_TAG, "Recording audio"); 
        if (RECORD_LENGTH <= 0) try { 
         recorder.recordSamples(audioData); 
         //Log.v(LOG_TAG,"recording " + 1024*i + " to " + 1024*i+1024); 
        } catch (FFmpegFrameRecorder.Exception e) { 
         Log.v(LOG_TAG, e.getMessage()); 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
     Log.v(LOG_TAG, "AudioThread Finished, release audioRecord"); 

     /* encoding finish, release recorder */ 
     if (audioRecord != null) { 
      audioRecord.stop(); 
      audioRecord.release(); 
      audioRecord = null; 
      Log.v(LOG_TAG, "audioRecord released"); 
     } 
    } 
} 

//--------------------------------------------- 
// camera thread, gets and encodes video data 
//--------------------------------------------- 
class CameraView extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback { 

    private SurfaceHolder mHolder; 
    private Camera mCamera; 

    public CameraView(Context context, Camera camera) { 
     super(context); 
     Log.w("camera", "camera view"); 
     mCamera = camera; 
     mHolder = getHolder(); 
     mHolder.addCallback(CameraView.this); 
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
     mCamera.setPreviewCallback(CameraView.this); 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     try { 
      stopPreview(); 
      mCamera.setPreviewDisplay(holder); 
     } catch (IOException exception) { 
      mCamera.release(); 
      mCamera = null; 
     } 
    } 

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
     stopPreview(); 

     Camera.Parameters camParams = mCamera.getParameters(); 
     List<Camera.Size> sizes = camParams.getSupportedPreviewSizes(); 
     // Sort the list in ascending order 
     Collections.sort(sizes, new Comparator<Camera.Size>() { 

      public int compare(final Camera.Size a, final Camera.Size b) { 
       return a.width * a.height - b.width * b.height; 
      } 
     }); 

     camParams.setPreviewSize(imageWidth, imageHeight); 

     Log.v(LOG_TAG, "Setting imageWidth: " + imageWidth + " imageHeight: " + imageHeight + " frameRate: " + frameRate); 

     camParams.setPreviewFrameRate(frameRate); 
     Log.v(LOG_TAG, "Preview Framerate: " + camParams.getPreviewFrameRate()); 

     mCamera.setParameters(camParams); 

     List<Camera.Size> videoSizes = mCamera.getParameters().getSupportedVideoSizes(); 

     // Set the holder (which might have changed) again 
     try { 
      mCamera.setPreviewDisplay(holder); 
      mCamera.setPreviewCallback(CameraView.this); 
      startPreview(); 
     } catch (Exception e) { 
      Log.e(LOG_TAG, "Could not set preview display in surfaceChanged"); 
     } 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     try { 
      mHolder.addCallback(null); 
      mCamera.setPreviewCallback(null); 
     } catch (RuntimeException e) { 
      // The camera has probably just been released, ignore. 
     } 
    } 

    public void startPreview() { 
     if (!isPreviewOn && mCamera != null) { 
      isPreviewOn = true; 
      mCamera.startPreview(); 
     } 
    } 

    public void stopPreview() { 
     if (isPreviewOn && mCamera != null) { 
      isPreviewOn = false; 
      mCamera.stopPreview(); 
     } 
    } 

    @Override 
    public void onPreviewFrame(byte[] data, Camera camera) { 
     if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { 
      startTime = System.currentTimeMillis(); 
      return; 
     } 
     if (RECORD_LENGTH > 0) { 
      int i = imagesIndex++ % images.length; 
      Log.v(LOG_TAG, "recording:" + recording + "rec:" + rec); 
      if (recording && rec) { 
       yuvImage = images[i]; 
       timestamps[i] = 1000 * (System.currentTimeMillis() - startTime); 
       totalRecordedTS++; 
      } else { 
       Log.v(LOG_TAG, "recording is paused"); 
       yuvImage = null; 
       timestamps[i] = -1; 
      } 
     } 

     /* get video data */ 
     if (yuvImage != null && recording && rec) { 
      if (data.length != imageWidth * imageHeight) { 
       Camera.Size sz = camera.getParameters().getPreviewSize(); 
       imageWidth = sz.width; 
       imageHeight = sz.height; 
       destWidth = imageHeight; 
       Log.v(LOG_TAG, "data length:" + data.length); 
      } 

      ByteBuffer bb = (ByteBuffer) yuvImage.image[0].position(0); // resets the buffer 
      int start = 2 * ((imageWidth - destWidth)/4); // this must be even 
      for (int row = 0; row < imageHeight * 3/2; row++) { 
       bb.put(data, start, destWidth); 
       start += imageWidth; 
      } 

     } 
    } 
} 

@Override 
public void onClick(View v) { 
    if (!recording) { 
     startRecording(); 
     Log.w(LOG_TAG, "Start Button Pushed"); 
     btnRecorderControl.setText("Stop"); 
    } else { 
     // This will trigger the audio recording loop to stop and then set isRecorderStart = false; 
     stopRecording(); 
     Log.w(LOG_TAG, "Stop Button Pushed"); 
     btnRecorderControl.setText("Start"); 
    } 
}} 

Les modifications apportées selon les suggestions de Alex Cohn

Suggestion 1 - Estimer frame rate moyen

public void stopRecording() { 

    .............................. 

          if (((i % images.length) != 0) && images[i % images.length] != images[(i % images.length) - 1]) { 
           if (t > recorder.getTimestamp()) { 
            t += 1000000/frameRate; 
            recorder.setTimestamp(t); 
           } 

           recorder.record(images[i % images.length]); 
          } 
      .......................................... 


} 

La modification apportait l'ajout de t + = 1000000/frameRate; Mais cela a provoqué le gel de la vidéo (comme dans le cas 1 décrit ci-dessus) par portions lorsque le doigt a été placé loin de l'écran.

Suggestion 2 - Modification dans onPreviewFrame()

long[] timestampsForRecorder; 
private void initRecorder() { 

    Log.w(LOG_TAG, "init recorder"); 

    if (RECORD_LENGTH > 0) { 
     ....................................................... 
     timestampsForRecorder = new long[images.length]; 
     for (int i = 0; i < images.length; i++) { 
      images[i] = new Frame(destWidth, imageHeight, Frame.DEPTH_UBYTE, 2); 
      timestamps[i] = -1; 
      timestampsForRecorder[i] = -1; 
     } 
    } else if (yuvImage == null) { 
     yuvImage = new Frame(destWidth, imageHeight, Frame.DEPTH_UBYTE, 2); 
     Log.i(LOG_TAG, "create yuvImage"); 
    } 
    ................................................... 
} 

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
     if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { 
      startTime = SystemClock.elapsedRealtime(); 
      return; 
     } 
     if (RECORD_LENGTH > 0) { 
      int i = imagesIndex++ % images.length; 
      Log.v(LOG_TAG, "recording:" + recording + "rec:" + rec); 
      if (recording && rec) { 
       yuvImage = images[i]; 
       long thisFrameTime = SystemClock.elapsedRealtime(); 
       timestamps[i] = thisFrameTime; 
       long lastFrameTime = timestamps[(int) (imagesIndex == 0 ? startTime : ((imagesIndex-1) % images.length))]; 
       Log.v(LOG_TAG, "lastFrameTime:" + lastFrameTime+",stopPauseTime:" + stopPauseTime); 
       if (lastFrameTime > stopPauseTime) { 
        timestampsForRecorder[i] = 1000 * (thisFrameTime - Math.max(stopPauseTime, lastFrameTime)); 
       } 
      } 
     } 

     ..................................................... 
    } 

public void stopRecording() { 

    ....................................................... 

    if (recorder != null && recording) { 
     if (RECORD_LENGTH > 0) { 
      Log.v(LOG_TAG, "Writing frames"); 
      try { 
       int firstIndex = imagesIndex % samples.length; 
       int lastIndex = (imagesIndex - 1) % images.length; 
       if (imagesIndex <= images.length) { 
        firstIndex = 0; 
        lastIndex = imagesIndex - 1; 
       } 
       if ((startTime = timestampsForRecorder[lastIndex] - RECORD_LENGTH * 1000000L) < 0) { 
        startTime = 0; 
       } 
       if (lastIndex < firstIndex) { 
        lastIndex += images.length; 
       } 
       for (int i = firstIndex; i <= lastIndex; i++) { 

        if (timestampsForRecorder[i] != -1) { 
         long t = timestampsForRecorder[i % timestampsForRecorder.length] - startTime; 
         if (t >= 0) { 

          if (((i % images.length) != 0) && images[i % images.length] != images[(i % images.length) - 1]) { 
           if (t > recorder.getTimestamp()) { 
            recorder.setTimestamp(t); 
           } 
           Log.v(LOG_TAG, "imageIndex=" + (i % images.length)); 
           recorder.record(images[i % images.length]); 
          } 
         } 
        } 
       } 
       ............................................. 
      } catch (FFmpegFrameRecorder.Exception e) { 
       ................................. 
      } 
     } 

     ........................................... 

    } 
} 

la vidéo enregistrée en utilisant ces était d'avoir le problème dans le cas 2 ci-dessus. c'est-à-dire, il jouait à un rythme plus rapide

Répondre

2

La solution facile (mais imprécise) serait d'estimer le taux de trame moyen, et d'utiliser t += 1000000/average_fps; recorder.setTimestamp(t); au lieu de regarder les horodatages réels.

Pour être plus précis, vous pouvez modifier onPreviewFrame() comme suit:

long thisFrameTime = SystemClock.elapsedRealtime(); 
timestamps[i] = thisFrameTime; 
long lastFrameTime = timestamps[imagesIndex < 2 ? startTime : (imagesIndex-2) % images.length)]; 
if (lastFrameTime > stopPauseTime) { 
    timestampsForRecorder[i] = 1000 * (thisFrameTime - Math.max(stopPauseTime, lastFrameTime)); 
} 

Vous pouvez alimenter le deuxième tableau, timestampsForRecorder, directement à l'enregistreur.

Notez qu'il est plus sûr d'utiliser SystemClock.elapsedRealtime() partout:

Cette horloge est garanti d'être monotones, et continue à cocher même lorsque le CPU est en mode d'économie d'énergie, est donc la base de recommander pour l'intervalle d'usage général timing.

+0

Merci pour vos suggestions. J'ai édité la question avec le code et les résultats après avoir appliqué les suggestions. (Veuillez cocher «Changements faits selon les suggestions d'Alex Cohn».) Je suis toujours confronté à un problème de lecture vidéo plus rapide. – human123

+0

** 1. ** Pourquoi 'if (t> recorder.getTimestamp())'? setTimestamp doit être inconditionnel, ne dépend que du numéro d'image. –

+0

** 2. ** ** stopPauseTime ** utilise-t-il 'SystemClock.elapsedRealtime()'? –