Skip to content

Commit 32703f2

Browse files
committed
add capture videos
1 parent a04da29 commit 32703f2

File tree

3 files changed

+80
-8
lines changed

3 files changed

+80
-8
lines changed

09-camerax-app/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CameraXApp
22

3-
A camera app, using CameraX to show a viewfinder and take photos.
3+
A camera app, using CameraX to show a viewfinder, take photos and capture videos.
44

55
<!-- <p align="center">
66
<img src="screenshot.png" style="width:528px;max-width: 100%;">

09-camerax-app/app/src/main/java/com/hfad/cameraxapp/MainActivity.kt

+78-6
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import android.util.Log
1111
import android.widget.Toast
1212
import androidx.camera.core.*
1313
import androidx.camera.lifecycle.ProcessCameraProvider
14+
import androidx.camera.video.*
1415
import androidx.camera.video.VideoCapture
15-
import androidx.camera.video.Recorder
16-
import androidx.camera.video.Recording
1716
import androidx.core.app.ActivityCompat
1817
import androidx.core.content.ContextCompat
18+
import androidx.core.content.PermissionChecker
1919
import com.hfad.cameraxapp.databinding.ActivityMainBinding
2020
import java.nio.ByteBuffer
2121
import java.text.SimpleDateFormat
@@ -114,7 +114,72 @@ class MainActivity : AppCompatActivity() {
114114
)
115115
}
116116

117-
private fun captureVideo() {}
117+
private fun captureVideo() {
118+
val videoCapture = this.videoCapture ?: return
119+
120+
viewBinding.videoCaptureButton.isEnabled = false
121+
122+
val curRecording = recording
123+
if (curRecording != null) {
124+
// Stop the current recording session
125+
curRecording.stop()
126+
recording = null
127+
return
128+
}
129+
130+
// Create and start a new recording session
131+
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
132+
.format(System.currentTimeMillis())
133+
val contentValues = ContentValues().apply {
134+
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
135+
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
136+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
137+
put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video")
138+
}
139+
}
140+
141+
val mediaStoreOutputOptions = MediaStoreOutputOptions
142+
.Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
143+
.setContentValues(contentValues)
144+
.build()
145+
recording = videoCapture.output
146+
.prepareRecording(this, mediaStoreOutputOptions)
147+
.apply {
148+
if (PermissionChecker.checkSelfPermission(this@MainActivity,
149+
Manifest.permission.RECORD_AUDIO) ==
150+
PermissionChecker.PERMISSION_GRANTED) {
151+
withAudioEnabled()
152+
}
153+
}
154+
.start(ContextCompat.getMainExecutor(this)) { recordEvent ->
155+
when(recordEvent) {
156+
is VideoRecordEvent.Start -> {
157+
viewBinding.videoCaptureButton.apply {
158+
text = getString(R.string.stop_capture)
159+
isEnabled = true
160+
}
161+
}
162+
is VideoRecordEvent.Finalize -> {
163+
if (!recordEvent.hasError()) {
164+
val msg = "Video capture succeeded: " +
165+
"${recordEvent.outputResults.outputUri}"
166+
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
167+
Log.d(TAG, msg)
168+
} else {
169+
recording?.close()
170+
recording = null
171+
Log.e(TAG, "Video capture ends with error: " + "${recordEvent.error}")
172+
}
173+
viewBinding.videoCaptureButton.apply {
174+
text = getString(R.string.start_capture)
175+
isEnabled = true
176+
}
177+
}
178+
}
179+
}
180+
181+
182+
}
118183

119184
private fun startCamera() {
120185
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
@@ -134,13 +199,19 @@ class MainActivity : AppCompatActivity() {
134199
imageCapture = ImageCapture.Builder().build()
135200

136201
// Analyze
137-
val imageAnalyzer = ImageAnalysis.Builder()
202+
/*val imageAnalyzer = ImageAnalysis.Builder()
138203
.build()
139204
.also {
140205
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
141206
Log.d(TAG, "Average luminosity: $luma")
142207
})
143-
}
208+
}*/
209+
210+
// Capture Video
211+
val recorder = Recorder.Builder()
212+
.setQualitySelector(QualitySelector.from(Quality.HIGHEST))
213+
.build()
214+
videoCapture = VideoCapture.withOutput(recorder)
144215

145216
// Select back camera as a default
146217
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
@@ -154,7 +225,8 @@ class MainActivity : AppCompatActivity() {
154225
cameraSelector,
155226
preview,
156227
imageCapture,
157-
imageAnalyzer
228+
// imageAnalyzer
229+
videoCapture
158230
)
159231

160232
} catch (exc: Exception) {

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
| 06 | [Guessing Game](https://github.com/solygambas/kotlin-projects/tree/main/06-guessing-game) | A guessing game to discover how to use view models with live data and data binding. |
1313
| 07 | [Tasks](https://github.com/solygambas/kotlin-projects/tree/main/07-tasks) | A to do list using the MVVM design pattern, a Room database and a recycler view. |
1414
| 08 | [Temperature Converter](https://github.com/solygambas/kotlin-projects/tree/main/08-temperature-converter) | A tool to convert temperatures from Celsius to Fahrenheit, using Compose. |
15-
| 09 | [CameraXApp](https://github.com/solygambas/kotlin-projects/tree/main/09-camerax-app) | A camera app, using CameraX to show a viewfinder and take photos. |
15+
| 09 | [CameraXApp](https://github.com/solygambas/kotlin-projects/tree/main/09-camerax-app) | A camera app, using CameraX to show a viewfinder, take photos and capture videos. |
1616

1717
Check the [playground](https://github.com/solygambas/kotlin-projects/tree/main/playground) if you want to learn Kotlin core concepts first.
1818

0 commit comments

Comments
 (0)