Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android MediaRecorder stop() not being called

I have this really weird bug where some of my code is not being called, but doesn't look there is any reason why it wouldn't be called.

onFinishedRecording gets called and the tag "Finished recording." gets logged out, but the code after that does not get called at all. All the code stops being called when mMediaRecorder.stop(); is called. It also does not go into the catch block either. Why is this happening?

I thought it might have to do with different threads, but I checked all the thread names and they are all running on the same main thread.

Also, could there be something wrong with my camera preview setup? When I try to play the video back, it is corrupted and not able to playback.

In addition to the above problems, my back button doesn't do anything in the application. Not quite show why or how it is related to the code that I have implemented.

MyLibrary class (library module class)

public class MyLibrary implements PreciseCountdownTimer.PreciseCountdownTimerCallback {

    private static final String TAG = AngryOtter.class.getSimpleName();
    private static final long MAX_RECORD_TIME_MILLIS = 3000;
    private static final long INTERVAL_MILLIS = 1000;

    private static MyLibrary mInstance;

    private Activity mActivity;
    private CameraInitListener mCallback;

    private int mCameraId = -1;
    private Camera mCamera;
    private SurfaceView mCameraPreview;
    private MediaRecorder mMediaRecorder;
    private PreciseCountdownTimer mTimer;
    private File mTempVideoFile;

    public static MyLibrary getInstance() {
        if (mInstance == null) {
            mInstance = new MyLibrary();
        }
        return mInstance;
    }

    // Call this in onResume of the activity
    public void initialize(Activity activity) {
        mActivity = activity;

        try {
            mCallback = (CameraInitListener) mActivity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.getClass().getSimpleName()
                    + " must implement CameraInitListener");
        }

        if (ViewUtil.checkValidRootView(mActivity)) {
            PermissionUtil.requestPermissions(mActivity);
            prepareCamera();
            if (mCamera == null) {
                return;
            }
            addCameraPreview();
        }
    }

    // Call this in onPause of the activity
    public void release() {
        releaseMediaRecorder();
        releaseCamera();
        removeCameraPreview();
        releaseTimer();
    }

    public void startRecording() {
        if (checkPermissions()) {
            try {
                mMediaRecorder.start();
                mTimer.start();
                Log.d(TAG, "Recording started.");
            } catch (IllegalStateException e) {
                releaseMediaRecorder();
                releaseTimer();
            }
        } else {
            releaseMediaRecorder();
        }
    }

    public void stopRecording() {
        onFinishedRecording();
    }

    @Override
    public void onPreciseTimerTick(long remainingTime) {
        Log.d(TAG, "TICK: " + String.valueOf(remainingTime));
    }

    @Override
    public void onPreciseTimerFinished() {
        Log.d(TAG, "Timer Finished.");
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onFinishedRecording();
            }
        });
    }

    private boolean checkPermissions() {
        if (PermissionUtil.checkCameraPermission(mActivity)
                && PermissionUtil.checkRecordAudioPermission(mActivity)
                && PermissionUtil.checkWriteExternalStoragePermission(mActivity)) {
            return true;
        } else {
            return false;
        }
    }

    private void prepareCamera() {
        mCameraId = CameraUtil.getFrontCameraId();
        if (mCameraId != -1) {
            try {
                Log.d(TAG, "Initializing front camera.");
                mCamera = Camera.open(mCameraId);
            } catch (Exception e) {
                Log.e(TAG, "Error initializing front camera: " + e.getMessage());
                mCamera = null;
            }
        } else {
            mCamera = null;
        }
    }

    private void releaseCamera() {
        if (mCamera != null){
            Log.d(TAG, "Releasing camera.");
            mCamera.release();
            mCamera = null;
        }
    }

    private void addCameraPreview() {
        mCameraPreview = new SurfaceView(mActivity);
        mCameraPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Log.d(TAG, "Preview surface created.");
                try {
                    Log.d(TAG, "Setting preview display.");
                    mCamera.setPreviewDisplay(holder);
                    mCamera.startPreview();
                    onPreviewDisplaySet();
                } catch (IOException e) {
                    Log.e(TAG, "Error setting camera preview: " + e.getMessage());
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                Log.d(TAG, "Preview surface changed.");
                // 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 (mCameraPreview.getHolder().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
                }

                // Adjust orientation
                final int rotation = CameraUtil.getAdjustedDisplayOrientation(mActivity, mCameraId);
                mCamera.setDisplayOrientation(rotation);

                // set preview size and make any resize, rotate or
                // reformatting changes here

                // start preview with new settings
                try {
                    mCamera.setPreviewDisplay(mCameraPreview.getHolder());
                    mCamera.startPreview();
                } catch (Exception e){
                    Log.d(TAG, "Error starting camera preview: " + e.getMessage());
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                Log.d(TAG, "Preview surface destroyed.");
            }
        });
        mCameraPreview.setLayoutParams(new FrameLayout.LayoutParams(100, 100, Gravity.TOP|Gravity.RIGHT));
        mCameraPreview.setBackgroundColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark));

        final WindowManager windowManager =
                (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(1, 1,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, PixelFormat.UNKNOWN);
        windowManager.addView(mCameraPreview, layoutParams);
    }

    private void removeCameraPreview() {
        if (mCameraPreview != null) {
            final ViewGroup rootView = ViewUtil.getRootView(mActivity);
            rootView.removeView(mCameraPreview);
        }
    }

    private void onPreviewDisplaySet() {
        createTempVideoFile();

        prepareMediaRecorder();
        if (mMediaRecorder == null) {
            return;
        }

        prepareTimer();
        mCallback.onCameraInitialized();
    }

    private void createTempVideoFile() {
        mTempVideoFile = FileUtil.getTempVideoFile(mActivity);
    }

    private void prepareMediaRecorder() {
        if (mCamera != null) {
            mCamera.unlock();
        }

        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setProfile(CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH));
        mMediaRecorder.setOutputFile(mTempVideoFile.getAbsolutePath());
        mMediaRecorder.setPreviewDisplay(mCameraPreview.getHolder().getSurface());
//        mMediaRecorder.setOrientationHint(90);

        try {
            Log.d(TAG, "Preparing media recorder.");
            mMediaRecorder.prepare();
        } catch (IllegalStateException|IOException e) {
            Log.e(TAG, "Error preparing MediaRecorder: " + e.getMessage());
            releaseMediaRecorder();
        }
    }

    private void releaseMediaRecorder() {
        if (mMediaRecorder != null) {
            Log.d(TAG, "Releasing media recorder.");
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }

        if (mCamera != null) {
            mCamera.lock();
        }
    }

    private void prepareTimer() {
        mTimer = new PreciseCountdownTimer(MAX_RECORD_TIME_MILLIS, INTERVAL_MILLIS, this);
    }

    private void releaseTimer() {
        if (mTimer != null) {
            Log.d(TAG, "Stopping timer.");
            mTimer.stop();
        }
    }

    private void onFinishedRecording() {
        Log.d(TAG, "Finished recording.");
        try {
            mMediaRecorder.stop();
            Log.d(TAG, "Media recorder stopped.");
        } catch (Exception e) {
            e.printStackTrace();
        }

        releaseMediaRecorder();
        releaseTimer();
        getSignedUrl();
    }

    private void getSignedUrl() {
        new GcpSigningRequest(new NetworkCallback<String>() {
            @Override
            public void onSuccess(String response) {
                uploadVideo(response);
            }

            @Override
            public void onError() {
                Log.e(TAG, "Error getting signing request.");
            }
        }).addToQueue();
    }

    private void uploadVideo(String signedUrl) {
        new UploadToGoogleRequest(signedUrl, mTempVideoFile.getName(),
                Uri.parse(mTempVideoFile.getAbsolutePath()), new NetworkCallback<Boolean>() {
            @Override
            public void onSuccess(Boolean response) {

            }

            @Override
            public void onError() {

            }
        }).addToQueue();
    }
}

RecordActivity

public class RecordActivity extends AppCompatActivity implements
        CameraInitListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        MyLibrary.getInstance().initialize(this);
    }

    @Override
    protected void onPause() {
        MyLibrary.getInstance().release();
        super.onPause();
    }

    @Override
    public void onCameraInitialized() {
        MyLibrary.getInstance().startRecording();
    }
}

So everything works fine until this method is called:

    private void onFinishedRecording() {
        Log.d(TAG, "Finished recording.");
        try {
            mMediaRecorder.stop();
            Log.d(TAG, "Media recorder stopped.");
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }

        releaseMediaRecorder();
        releaseTimer();
        getSignedUrl();
    }

Full Phone Stacktrace

08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo I/dalvikvm: Could not find method android.content.res.Resources.getDrawable, referenced from method android.support.v7.widget.ResourcesWrapper.getDrawable
08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo W/dalvikvm: VFY: unable to resolve virtual method 690: Landroid/content/res/Resources;.getDrawable (ILandroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo D/dalvikvm: VFY: replacing opcode 0x6e at 0x0002
08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo I/dalvikvm: Could not find method android.content.res.Resources.getDrawableForDensity, referenced from method android.support.v7.widget.ResourcesWrapper.getDrawableForDensity
08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo W/dalvikvm: VFY: unable to resolve virtual method 692: Landroid/content/res/Resources;.getDrawableForDensity (IILandroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
08-15 21:17:46.735 24660-24660/com.walintukai.heatmapdemo D/dalvikvm: VFY: replacing opcode 0x6e at 0x0002
08-15 21:17:46.785 933-11266/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:46.785 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:46.785 731-2931/? E/EnterpriseContainerManager: ContainerPolicy Service is not yet ready!!!
08-15 21:17:46.785 731-2931/? D/EnterpriseDeviceManager: ContainerId: 0
08-15 21:17:46.785 731-2931/? W/LicenseLogService: log() failed
08-15 21:17:46.805 170-170/? E/SMD: DCD ON
08-15 21:17:46.865 933-11213/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:46.865 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:46.935 380-24718/? E/mm-camera: color_correct_apply_gain: cc_gain_adj 1.000, digital_gain_brightness 1.000 dig_gain = 1.000
08-15 21:17:47.055 731-24783/? W/ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.sendBroadcast:1509 com.android.server.InputMethodManagerService$4.run:2683 java.lang.Thread.run:841 <bottom of call stack> <bottom of call stack> 
08-15 21:17:47.145 174-234/? E/qdmemalloc: heap_msk=3000000 flags=1

08-15 21:17:47.165 174-234/? E/qdmemalloc: heap_msk=40000000 flags=1

08-15 21:17:47.185 933-10839/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.185 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.225 933-947/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.245 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.265 933-10842/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.275 174-940/? E/qdmemalloc: heap_msk=3000000 flags=1

08-15 21:17:47.275 174-940/? E/qdmemalloc: heap_msk=40000000 flags=1

08-15 21:17:47.285 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Tick: 2000
08-15 21:17:47.295 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.305 179-24742/? W/CameraSource: Timed out waiting for incoming camera video frames: 0 us
08-15 21:17:47.375 933-11213/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.375 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.506 933-953/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.506 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.666 731-838/? D/PackageManager: [MSG] CHECK_PENDING_VERIFICATION
08-15 21:17:47.766 179-460/? D/AudioStreamOutALSA: standby
08-15 21:17:47.766 179-460/? D/ALSAModule: s_standby: handle 0xb7c24650 h 0x0
08-15 21:17:47.766 179-460/? E/ALSAModule: s_standby handle h 0xb7ceb678
08-15 21:17:47.836 933-6432/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.846 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.846 174-234/? I/SurfaceFlinger: id=833 Removed ieatmapdemo (12/17)
08-15 21:17:47.846 174-940/? I/SurfaceFlinger: id=833 Removed ieatmapdemo (-2/17)
08-15 21:17:47.896 933-11464/? D/KeyguardUpdateMonitor: sendKeyguardVisibilityChanged(true)
08-15 21:17:47.906 933-933/? D/KeyguardUpdateMonitor: handleKeyguardVisibilityChanged(1)
08-15 21:17:47.956 179-460/? D/ALSAModule: checkRunningHandle return false
08-15 21:17:47.956 179-460/? D/alsa_ucm: snd_use_case_set(): uc_mgr 0xb7bf62a0 identifier _verb value Inactive
08-15 21:17:47.956 179-460/? D/alsa_ucm: Set mixer controls for HiFi enable 0
08-15 21:17:47.956 179-460/? D/alsa_ucm: Setting mixer control: PRI_RX Audio Mixer MultiMedia1, value: 0
08-15 21:17:47.956 179-460/? E/ALSAModule: Number of modifiers 1
08-15 21:17:47.956 179-460/? E/ALSAModule: index 0 modifier Capture Music
08-15 21:17:47.956 179-460/? E/ALSAModule: use case is Capture Music
08-15 21:17:47.956 179-460/? E/ALSAModule: usecase_type is 2
08-15 21:17:47.956 179-460/? D/alsa_ucm: snd_use_case_set(): uc_mgr 0xb7bf62a0 identifier _disdev value Speaker
08-15 21:17:47.956 179-460/? D/alsa_ucm: Set mixer controls for Speaker enable 0
08-15 21:17:47.956 179-460/? D/alsa_ucm: Setting mixer control: RX5 MIX1 INP1, value: ZERO
08-15 21:17:47.966 179-460/? D/alsa_ucm: Setting mixer control: RX5 MIX1 INP2, value: ZERO
08-15 21:17:47.966 179-460/? D/alsa_ucm: Setting mixer control: LINEOUT2 Volume, value: 0
08-15 21:17:47.966 179-460/? D/alsa_ucm: Setting mixer control: LINEOUT4 Volume, value: 0
08-15 21:17:47.966 179-460/? D/alsa_ucm: Setting mixer control: RX5 Digital Volume, value: 0
08-15 21:17:48.286 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Tick: 1000
08-15 21:17:48.296 731-824/? D/SensorService:   0.2 -0.0 11.0
08-15 21:17:48.647 731-824/? E/Sensors: accelHandler 0.162861 -0.044308 11.044633
08-15 21:17:48.727 179-24722/? E/mm-camera: poll type 1 returns 0
08-15 21:17:48.727 179-24723/? E/mm-camera: poll type 1 returns 0
08-15 21:17:48.727 179-24721/? E/mm-camera: poll type 1 returns 0
08-15 21:17:49.297 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Tick: 0
08-15 21:17:49.297 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Timer Finished
08-15 21:17:49.297 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Finished recording
08-15 21:17:49.297 179-20689/? D/MPEG4Writer: Stopping Video track
08-15 21:17:49.808 170-170/? E/SMD: DCD ON
08-15 21:17:50.309 179-24742/? W/CameraSource: Timed out waiting for incoming camera video frames: 0 us
08-15 21:17:51.440 179-24720/? E/mm-camera: poll type 1 returns 0
08-15 21:17:51.570 179-24724/? E/mm-camera: poll type 0 returns 0
08-15 21:17:51.800 731-824/? D/SensorService:   0.2 -0.0 11.1
08-15 21:17:52.111 731-856/? V/AlarmManager: waitForAlarm result :4
08-15 21:17:52.121 731-856/? V/AlarmManager: trigger ELAPSED_REALTIME_WAKEUP or RTC_WAKEUP
08-15 21:17:52.151 731-824/? E/Sensors: accelHandler 0.129331 -0.021555 11.078163
08-15 21:17:52.151 19505-24874/? I/Finsky: [3562] com.google.android.finsky.receivers.FlushLogsReceiver$FlushLogsService.onHandleIntent(163): Flushing event logs for [wwRg65ZPhINg_7-olzSHzcWExtM]
08-15 21:17:52.161 19505-19520/? I/PlayCommon: [3510] com.google.android.play.a.al.e(730): Preparing logs for uploading
08-15 21:17:52.161 19505-20595/? I/PlayCommon: [3552] com.google.android.play.a.w.a(27553): Starting to flush logs
08-15 21:17:52.161 19505-20595/? I/PlayCommon: [3552] com.google.android.play.a.w.a(27564): Log flushed by 0 successful uploads
08-15 21:17:52.201 1184-1184/? I/Auth: [AuthDelegateWrapper] Service intent: Intent { cmp=com.google.android.gms/.auth.account.authenticator.DefaultAuthDelegateService }.
08-15 21:17:52.201 1184-1184/? I/Auth: [AuthDelegateWrapper] Service intent: Intent { cmp=com.google.android.gms/.auth.account.authenticator.DefaultAuthDelegateService }.
08-15 21:17:52.261 19505-19520/? I/PlayCommon: [3510] com.google.android.play.a.al.a(870): Connecting to server: https://play.googleapis.com/play/log?format=raw&proto_v2=true
08-15 21:17:52.441 731-826/? W/ProcessCpuTracker: Skipping unknown process pid 24877
08-15 21:17:52.451 731-826/? W/ProcessCpuTracker: Skipping unknown process pid 24879
08-15 21:17:52.451 731-826/? W/ProcessCpuTracker: Skipping unknown process pid 24880
like image 732
The Nomad Avatar asked Aug 01 '16 04:08

The Nomad


1 Answers

Given how far the code execution is tracked and where it (seemingly) gets lost, I suggest the following modifications to your code which should give you a better clue as to what is going on. It will be interesting to see the entire stack trace after the "Going into the black hole."

private void onFinishedRecording() {
    Log.d(TAG, "Going into the black hole.");
    try {
        Log.d(TAG, "So far so good, but if you don't see this just pull your hair ...");
        mMediaRecorder.stop();
        Log.d(TAG, "Media recorder stopped.");
    } catch (Error e) {
        Log.d(TAG, "So there was an error ...");
        e.printStackTrace();
        Log.d(TAG, "Did you get what that error was?");
    } catch (Exception e) {
        Log.d(TAG, "So there was some kind of an exception.");
        e.printStackTrace();
        Log.d(TAG, "Did you get what the exception was?");
    } finally {
        Log.d(TAG, "Leaving the black hole.");
    }
    Log.d(TAG, "So I did get out of the black hole after all ...");
    releaseMediaRecorder();
    releaseTimer();
    getSignedUrl();
}

Good luck!

Update:

Please include the raw logcat entries (don't limit them to your app). Some errors in native code can cause errors that might not appear to be related to your app, but in fact they might be. Also, keep in mind that the preview size must be the same as the selected video size. See this post for more details.

Update 2:

Sorry, I have been away and haven't had a chance to dive in. Just a quick glance, however, I spotted a reference to a camera timeout:

08-15 21:17:49.297 24660-24660/com.walintukai.heatmapdemo D/RecordingService: Finished recording
08-15 21:17:49.297 179-20689/? D/MPEG4Writer: Stopping Video track
08-15 21:17:49.808 170-170/? E/SMD: DCD ON
08-15 21:17:50.309 179-24742/? W/CameraSource: Timed out waiting for incoming camera video frames: 0 us

From what I can tell, right after issuing the stop(), there is an attempt to stop the video, but it remain at Stopping and never gets to Stopped and then a couple of line later CameraSource times out. You might want to add a few more log entries to see what is happening after you prepare and after you start recording. There might be some clues there. It appears that somehow the stop() call is indefinitely waiting for something, like the camera (deadlock). This would be consistent with no signs of any error, yet nothing else happening after it. Put a breakpoint right there and check the state of various resources (camera specifically) and see if it is locked or at such a state that causing stop() to wait. Lastly, I would verify that the preview is using a supported size/resolution and make sure it is compatible with your recording size and resolution. Potential issues/inconsistencies might internally be the cause of the deadlock (or whatever that is preventing stop from getting executed.)

like image 199
Kaamel Avatar answered Oct 13 '22 03:10

Kaamel