title: Detect when video file has been really written?
body: <p>The cwac-camera library has event hooks that are called before a photo is saved:</p>
  
  <pre><code>@Override public void saveImage(PictureTransaction xact, byte[] image) {}
  </code></pre>
  
  <p>Have I just overlooked this or is there no sink which tells me when a video file has been saved?</p>
  
  <p>Some more background info: There are two ways to end a video. One is by calling</p>
  
  <pre><code>.stopRecording();
  </code></pre>
  
  <p>the other is by configuring the recorder for a max duration/max filesize (in which case recording stops without the explicit call to stopRecording():</p>
  
  <pre><code>recorder.setMaxDuration()
  </code></pre>
  
  <p>In both cases, as it looks to me, Android needs about 100 to 200 msec to finalize the file. As the video file is often corrupted when I just use</p>
  
  <pre><code>mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
          @Override public void onInfo(MediaRecorder mr, int what, int extra) {}
  }
  </code></pre>
  
  <p>to detect when it is finished or try reading it immediately after the call to stopRecording(). I tried using FileObserver to detect when media recorder actually closes the file, which works well.</p>
  
  <p>I'd like to discuss this issue, if anybody has had the same experience, and if there is a way how cwac-camera detects and signals a recorded file.</p>
  
  <p>--</p>
  
  <p>I just stepped over this in Android documentation here <a href="http://developer.android.com/reference/android/media/MediaRecorder.html#setMaxDuration(int)" rel="nofollow">http://developer.android.com/reference/android/media/MediaRecorder.html#setMaxDuration(int)</a></p>
  
  <p>"Stopping happens asynchronously, there is no guarantee that the recorder will have stopped by the time the listener is notified."</p>
  
  <p>This explains why the file sometimes isn't properly closed and it implicitly means that using FileObserver probably is the only safe and working way to access the file after it has been written.</p>
  
  <p>So we need something like this to detect the event.</p>
  
  <pre><code>@Override protected File getVideoPath() {
      File file = super.getVideoPath(); // assemble the video path
      fileObserverPath = file.getAbsolutePath();
  
      fileObserver = new FileObserver(fileObserverPath, FileObserver.CLOSE_WRITE) {
          @Override public void onEvent(int event, String videoPath) {
  
              System.out.println("**** CameraHost: write closed");
  
              if (fileObserver != null) { // defensive
                  fileObserver.stopWatching();
                  fileObserver = null;
              }
  
              // process the file here
          }
      };
      fileObserver.startWatching();
  
      return file;
  }
  </code></pre>
  
  <p>--</p>
  
  <p>I went ahead and added the following code to my CameraHost to get the timings:</p>
  
  <pre><code>@Override public void configureRecorderOutput(int cameraId, MediaRecorder recorder) {
  
      System.out.println("**** CameraHost: configureRecorderOutput");
  
      recorder.setMaxDuration(Constants.FRAMEPLAYER_MAX_RECORDING_LENGTH_PER_FRAME_MSEC);
      recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
          @Override public void onInfo(MediaRecorder mr, int what, int extra) {
              switch (what) {
                  case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: {
  
                      System.out.println("**** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED");
  
                      break;
                  }
              }
          }
      });
  
      super.configureRecorderOutput(cameraId, recorder);
  }
  </code></pre>
  
  <p>Timings:</p>
  
  <pre><code>11-20 07:33:28.974  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: configureRecorderOutput
  11-20 07:33:31.064  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  11-20 07:33:31.084  32602-32602/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  11-20 07:33:36.914  32602-32655/cc.closeup.android I/System.out﹕ **** CameraHost: write closed
  11-20 07:33:36.914  32602-32655/cc.closeup.android I/System.out﹕ **** CameraHost: write closed
  </code></pre>
  
  <p>Two things that are strange:</p>
  
  <ol>
  <li>MEDIA_RECORDER_INFO_MAX_DURATION_REACHED is raised, but the recording does not stop. Contrary to what is specified in the Android documentation, I must explicitly call stop.</li>
  <li>Even though configureRecorderOutput is only raised once, my listeners are called twice. I tried the same outside cwac camera and there listeners are only called once.</li>
  </ol>
  
  <p>Not sure if I made a mistake somewhere or something goes wrong in cwac-camera.</p>
  
  <p>Anybody there to comment?</p>
  
  <p>--</p>
  
  <p>Regarding 2., whatever that is, my log reveals that the media recorder which fires the event is the same, and short after what=800 (MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) an undocumented what=536871912 is raised. The second time the event is raised the undocumented what=268436456.</p>
  
  <p>[from Asus Memo Pad 7:]</p>
  
  <pre><code>11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 800, extra 0
  11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  11-20 09:41:25.748    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 536871912, extra 0
  
  11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 800, extra 0
  11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  11-20 09:41:25.778    3398-3398/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@41c9da88, what = 268436456, extra 0
  </code></pre>
  
  <p>I ran this on another device, where the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED event is also raised twice, but the undocumented what=? is not raised at all. Vendor specific implementation?</p>
  
  <p>[from Samsung Galaxy Note 2:]</p>
  
  <pre><code>11-20 09:46:42.711  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@42831c30, what = 800, extra 0
  11-20 09:46:42.711  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  
  11-20 09:46:42.776  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: onInfo: mr = android.media.MediaRecorder@42831c30, what = 800, extra 0
  11-20 09:46:42.776  25699-25699/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  </code></pre>
  
  <p>I keep posting...</p>
  
  <p>--</p>
  
  <p>Regarding 1., recording is actually stopped (the recorded file is one second long), but FileObserver() is obviously only called much later, namely after I explicitly call cameraView.stop().</p>
  
  <p>I tried to call stop() directly on the MediaRecorder passed in the event, which works in Android but not in cwac.</p>
  
  <p>--</p>
  
  <p>Regarding 1., I digged into this further and it looks like the video file is kept written until I explicitly call cameraView.stop() - only then is it truncated to one second and closed.</p>
  
  <pre><code>// startRecording() called here
  
  11-20 11:29:49.072    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.072    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 32, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:49.082    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.132    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  
  11-20 11:29:51.132    4987-4987/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  
  11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.142    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  (...)
  11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.182    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  
  11-20 11:29:51.192    4987-4987/cc.closeup.android I/System.out﹕ **** CameraHost: MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
  
  11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.192    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  (...)
  11-20 11:29:51.252    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:51.262    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  
  // stopRecording() called here
  
  11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.492    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.572    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  (...)
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 2, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: FileObserver: onEvent: event = 8, videoPath = media0.mp4
  11-20 11:29:52.582    4987-5167/cc.closeup.android I/System.out﹕ **** CameraHost: write closed: videoPath = media0.mp4
  </code></pre>