Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android MediaRecorder produces corrupt video with green lines #1

Open
rameshvoltella opened this issue May 9, 2017 · 15 comments
Open

Comments

@rameshvoltella
Copy link

hi i used the library and its good but when i test on jellybean device its have issue in recorded video,
The device am using is samsung star pro jellybean version, iproduces corrupt video with green lines when playing the recorded video, i research on stackoverflow http://stackoverflow.com/questions/5826712/android-mediarecorder-produces-corrupt-video-with-green-lines but it doesent help me to fix the issue

@Xlythe
Copy link
Owner

Xlythe commented May 9, 2017

I don't have access to that phone. Would you be able to pull down CameraView's source, and attempt a fix? Or I could make a few changes and give you an APK to test. I can help walk you through the code if needed (the bug is in LegacyCameraModule.java).

It seems like one of these (may) fix the bug.

  1. Add checks before calling mVideoRecorder.setProfile(...). Use CamcorderProfile.hasProfile(CamcorderProfile.HIGH) and fall down to a lower quality if the higher quality is unsupported.

  2. Call mCamera.stopPreview(); before calling mCamera.unlock(); Although this looks like it would freeze the CameraView while recording the video, which is ugly. Maybe we can use MediaRecorder.setPreviewDisplay().

@rameshvoltella
Copy link
Author

rameshvoltella commented May 14, 2017

i will go for a try but i want to mention i have had encounter issue with other camera lib where i try the similar fix as mention above. i hope it work i will pull and do the necessary changes

@rameshvoltella
Copy link
Author

mVideoRecorder.setPreviewDisplay(surfaceviewhere); i cant find surfaceview in leagacy, what should i do?

@rameshvoltella
Copy link
Author

rameshvoltella commented May 14, 2017

@Xlythe
Copy link
Owner

Xlythe commented May 14, 2017

What about trying "mVideoRecorder.setPreviewDisplay(new Surface(getSurfaceTexture()));"?

@rameshvoltella
Copy link
Author

rameshvoltella commented May 14, 2017

i will try the device is not right with me i will update once it tested

@rameshvoltella
Copy link
Author

rameshvoltella commented May 14, 2017

>  @Override
    public void startRecording(File file) {
        mVideoFile = file;
        mVideoRecorder = new MediaRecorder();

        mCamera.unlock();
        mVideoRecorder.setCamera(mCamera);

        mVideoRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mVideoRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        switch (getQuality()) {
            case HIGH:
                mVideoRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
                break;
            case MEDIUM:
                mVideoRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
                break;
            case LOW:
                mVideoRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
                break;
        }
        mVideoRecorder.setOutputFile(file.getAbsolutePath());
        mVideoRecorder.setMaxDuration((int) getMaxVideoDuration());
        mVideoRecorder.setMaxFileSize(getMaxVideoSize());
        mVideoRecorder.setMaxDuration((int) getMaxVideoDuration());
        mVideoRecorder.setMaxFileSize(getMaxVideoSize());
        mVideoRecorder.setOrientationHint(getRelativeCameraOrientation(false /* isPreview */));
        mVideoRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
            @Override
            public void onInfo(MediaRecorder mr, int what, int extra) {
                switch (what) {
                    case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
                        Log.w(TAG, "Max duration for recording reached");
                        break;
                    case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
                        Log.w(TAG, "Max filesize for recording reached");
                        break;
                }
            }
        });
        mVideoRecorder.setPreviewDisplay(new Surface(getSurfaceTexture()));
        try {
            mVideoRecorder.prepare();
            mVideoRecorder.start();
        } catch (IOException | RuntimeException e) {
            e.printStackTrace();
            mVideoRecorder = null;
        }
    }

this is the change i did on LegacyCameraModule.java am i correct?

@rameshvoltella
Copy link
Author

rameshvoltella commented May 15, 2017

screenshot_2017-05-15-01-19-08

when i install the source app without the above change on following device the preview look like this
Android version 4.0.3
eken tab

Like the above on jelly bean devices the issue occur after recording and the recorded video look like the screen shot

@rameshvoltella
Copy link
Author

`public class CameraLegacyActivity extends AppCompatActivity {

private Camera mCamera;
String LOG_TAG = CameraLegacyActivity.class.getName();
private CameraPreview mPreview;
private MediaRecorder mMediaRecorder;
Button videoBtn;
static boolean BACK_CAM = true;
static int currentCameraId;
static boolean mIsRecording=false;
String mNextVideoAbsolutePath;
FrameLayout preview;
private SurfaceHolder mHolder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(checkCameraHardware(getApplicationContext())) {
        // Create an instance of Camera
        //Open the back camera first
        if(mCamera == null) {
            mCamera = getCameraInstance(true);
        }
        if(mCamera!=null){
            Log.d(LOG_TAG,"Cameras == "+Camera.getNumberOfCameras());
        }
    }

    // Create our Preview view and set it as the content of our activity.
    mPreview = new CameraPreview(this, mCamera);
    preview = (FrameLayout) findViewById(R.id.camera_preview);
    preview.addView(mPreview);
    videoBtn = (Button)findViewById(R.id.button_capture);
    mMediaRecorder = new MediaRecorder();
    videoBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(!mIsRecording) {
                startRecording();
            }
            else{
                stopRecording();
            }
        }
    });

    Button switch_btn = (Button)findViewById(R.id.switch_btn);
    switch_btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mCamera.stopPreview();
            releaseCamera();
            if(BACK_CAM){
                mCamera = getCameraInstance(false);
                Log.d(LOG_TAG,"switched to front camera == "+mCamera);
            }
            else{
                mCamera = getCameraInstance(true);
                Log.d(LOG_TAG,"switched to back camera == "+mCamera);
            }
            if(mIsRecording){
                startRecording();
            }
            else{
                createPreview();
            }
        }
    });
}

private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

public static Camera getCameraInstance(boolean backCam){
    Camera c = null;
    try {
        if(backCam) {
            c = Camera.open(getBackCameraId()); // attempt to get a Camera instance
            BACK_CAM=true;
        }
        else{
            c = Camera.open(getFrontCameraId());
            BACK_CAM=false;
        }
    }
    catch (Exception e){
        e.printStackTrace();
        Log.e(CameraLegacyActivity.class.getName(),"Unable to open camera...");
    }
    return c; // returns null if camera is unavailable
}

public static int getBackCameraId()
{
    for(int i=0;i<Camera.getNumberOfCameras();i++)
    {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(i,cameraInfo);
        if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
            currentCameraId = i;
            return i;
        }
    }
    return 0;
}

public static int getFrontCameraId()
{
    for(int i=0;i<Camera.getNumberOfCameras();i++)
    {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(i,cameraInfo);
        if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
            currentCameraId = i;
            return i;
        }
    }
    return 0;
}

private void startRecording()
{
    mCamera.unlock();
    mMediaRecorder.setCamera(mCamera);
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) {
        mNextVideoAbsolutePath = getVideoFilePath(getApplicationContext());
    }
    mMediaRecorder.setProfile(CamcorderProfile.get(currentCameraId,CamcorderProfile.QUALITY_HIGH));
    mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
    mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

    if(!mIsRecording) {
        try {
            mMediaRecorder.prepare();
            mMediaRecorder.start();
            videoBtn.setText("STOP VIDEO");
            mIsRecording = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

private String getVideoFilePath(Context context) {
    return context.getExternalFilesDir(null).getAbsolutePath() + "/"
            + System.currentTimeMillis() + ".mp4";
}

private void stopRecording()
{
    videoBtn.setText("Take Video");
    mMediaRecorder.stop();
    mIsRecording=false;
    if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH){
        mCamera.lock();
    }
    Toast.makeText(getApplicationContext(), "Video saved: " + mNextVideoAbsolutePath,
            Toast.LENGTH_SHORT).show();
    Log.d(LOG_TAG, "Video saved: " + mNextVideoAbsolutePath);
}

private void releaseCamera(){
    if (mCamera != null){
        mCamera.release();        // release the camera for other applications
        mCamera = null;
        Log.d(LOG_TAG,"Camera released...");
    }
}

@Override
protected void onPause() {
    super.onPause();
    Log.d(LOG_TAG,"onPause");
    releaseMediaRecorder();       // if you are using MediaRecorder, release it first
    releaseCamera();              // release the camera immediately on pause event
}

@Override
protected void onResume(){
    super.onResume();
    Log.d(LOG_TAG,"onResume... "+mCamera+", BACK_CAM == "+BACK_CAM);
    if(mCamera == null) {
        if (BACK_CAM) {
            mCamera = getCameraInstance(true);
        } else {
            mCamera = getCameraInstance(false);
        }
    }
}

private void releaseMediaRecorder(){
    if (mMediaRecorder != null) {
        mMediaRecorder.reset();   // clear recorder configuration
        mMediaRecorder.release(); // release the recorder object
        mMediaRecorder = null;
        mCamera.lock();           // lock camera for later use
        Log.d(LOG_TAG,"media recorder released....");
    }
}

class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        if (mCamera != null) {
            createPreview();
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        createPreview();
    }
}

private void setCameraDisplayOrientation(Activity activity,
                                                int cameraId, android.hardware.Camera camera) {
    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = activity.getWindowManager().getDefaultDisplay()
            .getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0: degrees = 0; break;
        case Surface.ROTATION_90: degrees = 90; break;
        case Surface.ROTATION_180: degrees = 180; break;
        case Surface.ROTATION_270: degrees = 270; break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
        result = (info.orientation - degrees + 360) % 360;
    }
    camera.setDisplayOrientation(result);
}

private void createPreview()
{
    if (BACK_CAM) {
        setCameraDisplayOrientation(CameraLegacyActivity.this, getBackCameraId(), mCamera);
    } else {
        setCameraDisplayOrientation(CameraLegacyActivity.this, getFrontCameraId(), mCamera);
    }
    try {
        mCamera.setPreviewDisplay(mHolder);
    } catch (IOException e) {
        Log.e(LOG_TAG,"Unable to recreate preview");
        e.printStackTrace();
    }
    mCamera.startPreview();
}}

`

The above code will work on jelly bean and below device well but the problem it has some aspect ratio issue on camera preview . i just started working on camera and not having a clear idea how to fix this do u have any suggestions?

@Xlythe
Copy link
Owner

Xlythe commented May 15, 2017

Yea, looks like the only differences are...

  1. MediaRecorder.setPreviewDisplay
  2. Camera.lock after stopRecording

Let me know if making either of those changes helps

@rameshvoltella
Copy link
Author

rameshvoltella commented May 15, 2017

still have issue cam
we implement the above code is working fine only issue with above one is the aspect ratio any suggestion to fix that?

@Xlythe
Copy link
Owner

Xlythe commented May 15, 2017

You can look at LegacyCameraModule.transformPreview for the preview, and VideoView.transformPreview for the video

@rameshvoltella
Copy link
Author

what should i use for getTransform(matrix); and // setTransform(matrix);
in case surfaceview

@Xlythe
Copy link
Owner

Xlythe commented May 17, 2017

I haven't used SurfaceView, so not sure. But you may be able to override onDraw(Canvas) and scale/translate the canvas object before/after calling super.onDraw(Canvas).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants