Nuke Olaf - Log Store

[Android] 안드로이드 - camera2 api 를 사용하여 동영상 촬영하는 앱 만들기 (feat. Kotlin) 본문

Android

[Android] 안드로이드 - camera2 api 를 사용하여 동영상 촬영하는 앱 만들기 (feat. Kotlin)

NukeOlaf 2019. 12. 14. 16:25

camera2 api를 사용하여 동영상을 촬영하는 것은 사진을 촬영하는 방법과 큰 차이는 없다.

사진을 촬영할때는 ImageReader 객체를 사용했지만, 동영상을 촬영할때는 MediaRecorder 객체를 사용한다는 차이가 있을 뿐이다. 사진을 촬영하는 부분과 다른 부분만 적도록 하겠다. 참고로, 여기서는 동영상 촬영중 앱을 나가는 상황에 대한 처리는 생략하도록 한다.

1. openCamera() 메서드 ->  카메라 미리보기를 위한 imageDimension 뿐만 아니라, 비디오 녹화에 필요한 VideoDimension 또한 설정해 주어야 한다.

1
2
3
4
// SurfaceTexture 에 사용할 Size 값을 map 에서 가져와 imageDimension 에 할당해준다
  imageDimension = map!!.getOutputSizes<SurfaceTexture>(SurfaceTexture::class.java)[0]
  // VideoRecording 에 사용할 Size 값을 map 에서 가져와 videoDimension 에 할당해준다
  videoDimension = map!!.getOutputSizes<MediaRecorder>(MediaRecorder::class.java)[0]

 

2. openCamera() 메서드 -> 동영상 촬영을 위해 사용할 MediaRecorder 객체를 생성해준다

1
2
// 동영상 촬영을 위해 MediaRecorder 객체를 생성해준다
mediaRecorder = MediaRecorder()

 

3. startRecodingVideo() 메서드 -> 동영상 촬영을 시작할 때 호출하는 메서드

closeCameraPreviewSession() : 동영상 촬영을 request 해서 받아오는 session 을 다시 만들기 위해, 기존의 session 을 닫아준다.

setUpMediaRecorder() : mediaRecorder 객체가 동영상을 어디에 저장할지, 어떤 파일 형식으로 어떻게 인코딩 할지 등을 설정해주는 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private fun startRecordingVideo() {
        
        try {
            closeCameraPreviewSession()
            setUpMediaRecorder()
 
            // 캡쳐세션을 만들기 전에 프리뷰를 위한 Surface 를 준비한다
            // 레이아웃에 선언된 textureView 로부터 surfaceTexture 를 얻을 수 있다
            texture = textureView.surfaceTexture
 
            // 미리보기를 위한 Surface 기본 버퍼의 크기는 카메라 미리보기크기로 구성
            texture.setDefaultBufferSize(imageDimension!!.width, imageDimension!!.height)
 
            // 미리보기 화면을 요청하는 RequestBuilder 를 만들어준다.
            // 이 요청은 위에서 만든 surface 를 타겟으로 한다
            captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
 
            var surfaces: ArrayList<Surface> = ArrayList()
 
            // Set up Surface for the camera preview
            var previewSurface: Surface = Surface(texture)
            surfaces!!.add(previewSurface)
            captureRequestBuilder.addTarget(previewSurface)
 
            // Set up Surface for the MediaRecorder
            var recorderSurface: Surface = mediaRecorder!!.surface
            surfaces!!.add(recorderSurface)
            captureRequestBuilder.addTarget(recorderSurface)
 
            cameraDevice!!.createCaptureSession(surfaces, object : CameraCaptureSession.StateCallback() {
                override fun onConfigureFailed(session: CameraCaptureSession) {
                    Toast.makeText(this@MainActivity, "Failed", Toast.LENGTH_SHORT).show()
                }
 
                override fun onConfigured(session: CameraCaptureSession) {
                    cameraCaptureSessions = session
 
                    cameraCaptureSessions!!.setRepeatingRequest(captureRequestBuilder.build(), nullnull)
 
                    btn_takePicture.setImageResource(R.drawable.ic_recoding_stop)
                    isVideoRecording = true
 
                    mediaRecorder!!.start()
                }
 
            }, null)
 
        }catch (e: CameraAccessException) {
            e.printStackTrace()
        }
    }
 

 

3-1. closeCameraPreviewSession() 메서드 -> 기존의 카메라 미리보기 세션을 닫아주는 메서드

1
2
3
4
5
6
 private fun closeCameraPreviewSession() {
        if (cameraCaptureSessions != null) {
            cameraCaptureSessions!!.close()
            cameraCaptureSessions = null
        }
    }
 

3-2. setUpMediaRecorder() 메서드 -> mediaRecorder 객체가 동영상을 어디에 저장할지, 어떤 파일 형식으로 어떻게 인코딩 할지 등을 설정해주는 메서드 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// mediaRecorder 객체의 값을 설정해주는 메서드
    @RequiresApi(Build.VERSION_CODES.O)
    private fun setUpMediaRecorder() {
        mediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
        mediaRecorder!!.setVideoSource(MediaRecorder.VideoSource.SURFACE)
        mediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
 
        var file = File(Environment.getExternalStorageDirectory().toString() + "/video${fileCount}.mp4")
        this.file = file
        mediaRecorder!!.setOutputFile(file)
        mediaRecorder!!.setVideoEncodingBitRate(10000000)
        mediaRecorder!!.setVideoFrameRate(30)
        mediaRecorder!!.setVideoSize(videoDimension!!.width, videoDimension!!.height)
        mediaRecorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
        mediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
 
        val rotation = windowManager.defaultDisplay.rotation
        mediaRecorder!!.setOrientationHint(ORIENTATIONS.get(rotation))
 
        mediaRecorder!!.prepare()
    }
 

 

4. stopRecodingVideo() 메서드 -> 동영상 촬영을 중지할 때 호출하는 메서드

1
2
3
4
5
6
7
8
9
10
11
private fun stopRecordingVideo() {
        isVideoRecording = false
        btn_takePicture.setImageResource(R.drawable.ic_recoding)
 
        mediaRecorder!!.stop()
        mediaRecorder!!.reset()
 
        Toast.makeText(this@MainActivity, "동영상이 촬영되었습니다", Toast.LENGTH_SHORT).show()
 
        createCameraPreviewSession()
    }
 
Comments