5

I've got a MediaRecorder recording video, and I'm very confused by the effect of setCaptureRate().

Specifically, I prepare my MediaRecorder as follows:

mMediaRecorder = new MediaRecorder();
mCamera.stopPreview();
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(CamcorderProfile.QUALITY_TIME_LAPSE_480P);
mMediaRecorder.setCaptureRate(30f);
mMediaRecorder.setOrientationHint(270);
mMediaRecorder.setOutputFile(...);
mMediaRecorder.setPreviewDisplay(...);
mMediaRecorder.prepare();

I record for five seconds (with a CountDownTimer, but that's irrelevant), and this is the file that gets generated:

$ ffmpeg -i ~/CaptureRate30fps.mp4 
...
Seems stream 0 codec frame rate differs from container frame rate: 180000.00 (180000/1) -> 30.00 (30/1)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/mspitz/CaptureRate30fps.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 0
    compatible_brands: isom3gp4
    creation_time   : 2013-06-04 00:52:00
  Duration: 00:00:02.59, start: 0.000000, bitrate: 5238 kb/s
    Stream #0.0(eng): Video: h264 (Baseline), yuv420p, 720x480, 5235 kb/s, PAR 65536:65536 DAR 3:2, 30 fps, 30 tbr, 90k tbn, 180k tbc
    Metadata:
      creation_time   : 2013-06-04 00:52:00

Note that the Duration is just about 3 seconds. The video also plays much faster, as if it were 5 seconds of video crammed into 3.

Now, if I record by preparing my mediaRecorder exactly as above, but subtracting the setCaptureRate(30f) line, I get a file like this:

$ ffmpeg -i ~/NoSetCaptureRate.mp4 
...
Seems stream 0 codec frame rate differs from container frame rate: 180000.00 (180000/1) -> 90000.00 (180000/2)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/mspitz/NoSetCaptureRate.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 0
    compatible_brands: isom3gp4
    creation_time   : 2013-06-04 00:50:41
  Duration: 00:00:04.87, start: 0.000000, bitrate: 2803 kb/s
    Stream #0.0(eng): Video: h264 (Baseline), yuv420p, 720x480, 2801 kb/s, PAR 65536:65536 DAR 3:2, 16.01 fps, 90k tbr, 90k tbn, 180k tbc
    Metadata:
      creation_time   : 2013-06-04 00:50:41

Note that the Duration is as expected, about 5 seconds. The video also plays at a normal speed.

I'm using setCaptureRate(30f) because 30 frames per second is the value of my CamcorderProfile's videoFrameRate. On my Galaxy Nexus S2 (4.2.1), omitting setCaptureRate() is fine, but when I tested on a Galaxy Nexus S3 (4.1.1), omitting setCaptureRate() results in the ever-helpful "start failed -22" error when I called mMediaRecorder.start().

So, what am I missing? I thought that the capture rate and the video frame rate were independent, but it's clear that they're not. Is there a way to determine programmatically what I need to set the capture rate at in order to determine that my video plays back at 1x speed?

spitzanator
  • 1,877
  • 4
  • 19
  • 29
  • Why `QUALITY_TIME_LAPSE_480P` and not `QUALITY_480P`? Time lapses are inherently not 1x playback speed. – Roman Nurik Jun 10 '13 at 15:44
  • Also have you looked into calling `MediaRecorder.setVideoFrameRate`? – Roman Nurik Jun 10 '13 at 15:47
  • @RomanNurik Thanks for your comment! I don't actually want to record sound, and the only way to do that appears to be by using a time lapse video. Re: setVideoFrameRate, it gets called when I call setProfile(): https://github.com/android/platform_frameworks_base/blob/master/media/java/android/media/MediaRecorder.java#L329 In fact, looking at the source is what tipped me off to needing to call setCaptureRate() in the first place. https://github.com/android/platform_frameworks_base/blob/master/media/java/android/media/MediaRecorder.java#L337 Otherwise, I got a -22. – spitzanator Jun 10 '13 at 16:11
  • Hmm, have you tried playing with `setAudioSource`? Alternatively, try modifying the profile returned from `CamcorderProfile.get` to remove audio. – Roman Nurik Jun 10 '13 at 17:13
  • If you setAudioSource() and apply a time-lapse profile, start() explodes because there's an audio source but you never set the audio encoder. In the source links I provided above, note that setProfile() only calls setAudioEncoder() (and other audio-related things) if the profile isn't time-lapse. – spitzanator Jun 10 '13 at 20:42
  • But that's what I'm confused by—why are you trying to apply a time-lapse profile if you're aiming for 1x playback? If you're using it purely to prevent audio recording, there should be a better way to do that. Otherwise, if you're really aiming to record a time lapse, you may want to explain in the question the exact desired effect, as it seems a bit confusing at the moment. – Roman Nurik Jun 11 '13 at 03:08
  • That's a good question. It seems that the time-lapse profile is the only way to disable audio recording without setting all of the video rates manually. I'm not aiming to do a time lapse. – spitzanator Jun 11 '13 at 14:10
  • 1
    Yeah in that case maybe just set all the video parameters manually from the `CamcorderProfile` instead of setting the profile on the `MediaRecorder` directly. It's more cumbersome, but should theoretically prevent audio from being recorded. – Roman Nurik Jun 11 '13 at 17:50

3 Answers3

2

(This is a summary of the resolution from the comments on the original question)

The problem may be that directly using the QUALITY_TIME_LAPSE_480P profile influences playback rates, since time lapses are implicitly not a 1x playback rate.

Furthermore, if the reason you're using that profile is to prevent audio from being recorded when using QUALITY_480P (since time lapses don't record audio), you may want to instead call CamcorderProfile.get(QUALITY_480P) and set video parameters on the MediaRecorder manually based on the profile, without calling MediaRecorder.setProfile directly. A MediaRecorder that doesn't have any audio parameters set shouldn't in theory record any audio.

Roman Nurik
  • 29,665
  • 7
  • 84
  • 82
0

According to documentation, setCaptureRate(double fps) is used to set time lapse recording, with audio ignored. It is most useful if you want less fps than the system can actually provide. You ask more fps than the system can provide, here is a quote from developer.android.com/reference:

Note that the recorder cannot guarantee that frames will be captured at the given rate due to camera/encoder limitations. However it tries to be as close as possible.

What your code does not call, is setVideoFrameRate(int rate). I believe that using this function, your recording will be fixed.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Unfortunately, that's not correct. Take a look at the source for MediaRecorder: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/MediaRecorder.java setProfile(CamcorderProfile profile) does call setVideoFrameRate(profile.videoFrameRate); The comment in there is what made me try setCaptureRate() in the first place to make it work on a Galaxy Nexus S3. – spitzanator Jun 04 '13 at 13:22
0

You are correct.I will give you correct answer.i have faced same situation.start failed-22 belongs to frame rate.hence capture rate which you are giving is not in the range of device. use getSupportedPreviewFpsRange() first to determine device fps range and then set accordingly.You can use PREVIEW_FPS_MAX_INDEX and PREVIEW_FPS_MIN_INDEX for maximum and minimum fFps.

use following code it will give result multiplied by 1000 i.e if frame rate is 5 ,it will return 5000.

Camera.Parameters camParameter = camera.getParameters();
    List<int[]> frame = camParameter.getSupportedPreviewFpsRange();
        Iterator<int[]> supportedPreviewFpsIterator = frame.iterator();
        while (supportedPreviewFpsIterator.hasNext()) {
            int[] tmpRate = supportedPreviewFpsIterator.next();
            StringBuffer sb = new StringBuffer();
            sb.append("supportedPreviewRate: ");
            for (int i = tmpRate.length, j = 0; j < i; j++) {
                sb.append(tmpRate[j] + ", ");
            }
            Log.d(VTAG, sb.toString());
        }
    }

for more check here :http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFpsRange()

EDIT: In android developer document. It is mentioned that setCaptureRate() Sets video frame capture rate. This can be used to set a different video frame capture rate than the recorded video's playback rate. This method also sets the recording mode to time lapse. Since it also sets recording mode to time lapse so check video after removing setprofile QUALITY_TIME_LAPSE_480P to any other supported normal profile such as QUALITY_480P or make pure custom mediaRecorder setting by setting video size and other.It may help you.

Amrendra
  • 2,019
  • 2
  • 18
  • 36
  • Thanks for your answer! You indeed need to check for supported framerates, but I verified that 30fps is well-within the acceptable boundaries on the Galaxy Nexus S3, so this isn't the issue. – spitzanator Jun 10 '13 at 16:13