How to solve artifact issue with front camera vide

2019-08-11 01:53发布

问题:

I am trying to programmatically record video from the front camera of an Android device. While most test devices I have, including the Nexus 4, Nexus 7, and the Samsung Galaxy S4, record the video correctly, some do not.

For example, here's a screenshot of a segment of video recorded by a Samsung Galaxy S3:

There are weird artifacts and colors, and the video does not seem to be centered (it is pointed at a person's face, which you can see is just barely visible in the shot).

Here's my code:

//
// starting code
//

CAMERA = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
Camera.Parameters cameraParameters = CAMERA.getParameters();
if (cameraParameters.isZoomSupported()) {
    cameraParameters.setZoom(0);
}
CAMERA.setParameters(cameraParameters);
CAMERA.setDisplayOrientation(90);

CAMERA.setPreviewDisplay(CAMERA_SURFACE_VIEW.getHolder());
CAMERA.startPreview();
CAMERA.unlock();

CAMERA_MEDIA_RECORDER = new MediaRecorder();
CAMERA_MEDIA_RECORDER.setCamera(CAMERA);
try {
    CAMERA_MEDIA_RECORDER.setOrientationHint(270);
} catch (Exception e) {
    // this is to fix "setParameter failed" b/c of unsupported orientation hint
    CAMERA_MEDIA_RECORDER.setOrientationHint(90);
}
CAMERA_MEDIA_RECORDER.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
CAMERA_MEDIA_RECORDER.setVideoSource(MediaRecorder.VideoSource.CAMERA);

ArrayList<Integer> profileIds = new ArrayList<Integer>();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    profileIds.add(CamcorderProfile.QUALITY_480P);
    profileIds.add(CamcorderProfile.QUALITY_720P);
}

profileIds.add(CamcorderProfile.QUALITY_HIGH);
profileIds.add(CamcorderProfile.QUALITY_LOW);

CamcorderProfile camcorderProfile = null;

for (int profileId : profileIds) {
    try {
        camcorderProfile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_FRONT, profileId);
        break; // found our most desired profile, done
    } catch (IllegalArgumentException e) {
        // profile not found on device... It's okay, the next one might exist.
        continue;
    } catch (RuntimeException e) {
        continue;
    }
}

if (camcorderProfile == null) {
    return false;
}

CAMERA_MEDIA_RECORDER.setProfile(camcorderProfile);
CAMERA_MEDIA_RECORDER.setAudioEncodingBitRate(96000);

int audioSamplingRate = 44100;

for (int rate : new int[] {44100, 32000, 22050, 16000, 11025, 8000}) {
    if (AudioRecord.getMinBufferSize(rate, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT) > 0) {
        audioSamplingRate = rate;
        break;
    }
}

CAMERA_MEDIA_RECORDER.setAudioSamplingRate(audioSamplingRate);
CAMERA_MEDIA_RECORDER.setVideoEncodingBitRate(700000);

String extension = ".3gpp";

if (camcorderProfile.fileFormat == MediaRecorder.OutputFormat.MPEG_4) {
    extension = ".mp4";
}

File file = new File(Internal.getStoragePath(Internal.CURRENT_ACTIVITY) + "/webcam" + extension);
file.createNewFile();

CAMERA_MEDIA_RECORDER.setOutputFile(file.toString());
CAMERA_MEDIA_RECORDER.setPreviewDisplay(CAMERA_SURFACE_VIEW.getHolder().getSurface());

CAMERA_MEDIA_RECORDER.prepare();
CAMERA_MEDIA_RECORDER.start();

//
// stopping code
//

try {
    CAMERA_MEDIA_RECORDER.stop();
} catch (IllegalStateException e) {
    // do nothing
} catch (RuntimeException e) {
    // do nothing
}

CAMERA_MEDIA_RECORDER.reset();
CAMERA_MEDIA_RECORDER.release();

CAMERA.lock();
CAMERA.stopPreview();
CAMERA.release();

CAMERA_MEDIA_RECORDER = null;
CAMERA = null;
CAMERA_SURFACE_VIEW = null;

Edit: I should point out that the surfaceView is 1 by 1 px and is outside of the screen's bounds. I do not wish to display a preview.

How do I make sure that the video recording is good on all devices with a front facing camera?

回答1:

The "accepted" answer is only a workaround and doesn't solve the problem because chances are the zoom or aspect will change when you release the preview and start recording since mediarecorder adjusts the preview surface to what it actually needs, so the framing of the shot will change when you hit record which is undesirable. The correct thing to do is to set the preview size to be consistent with the aspect ratio of the recording size so that mediarecorder gets a surface that it expects.

Check the Android camera app, it does not stop the preview and then start recording (you can check this by keeping the flash on, if you start recording the flash does not turn off and then on again which is what will happen if you release the surface before recording - you can test this in your own app).

Best thing is to check out how Google did it with the built-in Android Camera app. Look at the getOptimalPreviewSize() method inside their util.java class.

https://android.googlesource.com/platform/packages/apps/Camera.git/+/master/src/com/android/camera/Util.java



回答2:

I was finally able to get my hands on a Samsung Galaxy S3 device and run some tests. The issue was caused by the following lines:

CAMERA.setPreviewDisplay(CAMERA_SURFACE_VIEW.getHolder());
CAMERA.startPreview();

The following answer I found on StackOverflow led me to the solution: https://stackoverflow.com/a/14319270/379245

If the SurfaceView preview is shared between Camera and MediaRecorder, it can apparently cause issues such as the one I experienced. Removing the setting of the preview display from the Camera object ultimately fixed the issue.