Как реализовать функцию замедленного движения с помощью API Android Camera API? [закрыто]Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Как реализовать функцию замедленного движения с помощью API Android Camera API? [закрыто]

Сообщение Anonymous »

Я пытаюсь реализовать функциональность медленного движения в моем приложении Android, но у меня нет достаточно знаний о том, как заставить его работать.
Что не так с моим кодом? />
[*] Профиль камеры: camcorderprofile.quality_high_speed_1080 .
Скорость захвата: setCaptureRate (профиль !! Скорость, вместо того, чтобы быть в замедленном движении. < /p>
Полный тестируемый пример кода: < /p>
class TempMainActivity : AppCompatActivity() {

companion object {
private const val TAG = "SlowMotionCamera"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}

private lateinit var textureView: TextureView
private lateinit var recordButton: Button
private lateinit var statusText: TextView

private lateinit var cameraManager: CameraManager
private lateinit var cameraId: String
private var cameraDevice: CameraDevice? = null
private var cameraCaptureSession: CameraCaptureSession? = null
private var mediaRecorder: MediaRecorder? = null
private var isRecording = false
private var videoFile: File? = null
private var profile: CamcorderProfile? = null

private lateinit var backgroundHandler: Handler
private lateinit var backgroundThread: HandlerThread

private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
openCamera()
}
override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {}
override fun onSurfaceTextureDestroyed(texture: SurfaceTexture) = true
override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {}
}

private val stateCallback = object : CameraDevice.StateCallback() {
override fun onOpened(camera: CameraDevice) {
cameraDevice = camera
createCameraPreviewSession()
}
override fun onDisconnected(camera: CameraDevice) {
camera.close()
cameraDevice = null
}
override fun onError(camera: CameraDevice, error: Int) {
camera.close()
cameraDevice = null
finish()
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_temp)

textureView = findViewById(R.id.textureView)
recordButton = findViewById(R.id.recordButton)
statusText = findViewById(R.id.statusText)

cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraId = cameraManager.cameraIdList[0] // Use back camera

// Set fixed size that matches high-speed resolution
textureView.layoutParams.width = 1920
textureView.layoutParams.height = 1080
textureView.requestLayout()

recordButton.setOnClickListener {
if (isRecording) {
stopRecording()
} else {
startRecording()
}
}
}

override fun onResume() {
super.onResume()
startBackgroundThread()
if (textureView.isAvailable) {
openCamera()
} else {
textureView.surfaceTextureListener = surfaceTextureListener
}
}

override fun onPause() {
stopRecording()
closeCamera()
stopBackgroundThread()
super.onPause()
}

private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}

private fun startBackgroundThread() {
backgroundThread = HandlerThread("CameraBackground").also { it.start() }
backgroundHandler = Handler(backgroundThread.looper)
}

private fun stopBackgroundThread() {
backgroundThread.quitSafely()
try {
backgroundThread.join()
} catch (e: InterruptedException) {
Log.e(TAG, "Error stopping background thread", e)
}
}

private fun openCamera() {
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return
}
cameraManager.openCamera(cameraId, stateCallback, backgroundHandler)
} catch (e: CameraAccessException) {
Log.e(TAG, "Cannot access the camera", e)
}
}

private fun closeCamera() {
cameraCaptureSession?.close()
cameraCaptureSession = null
cameraDevice?.close()
cameraDevice = null
}

private fun createCameraPreviewSession() {
try {
val texture = textureView.surfaceTexture
texture?.setDefaultBufferSize(1920, 1080)
val surface = Surface(texture)

val captureRequestBuilder = cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureRequestBuilder?.addTarget(surface)

cameraDevice?.createCaptureSession(
listOf(surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
if (cameraDevice == null) return

cameraCaptureSession = session
try {
captureRequestBuilder?.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
)
session.setRepeatingRequest(
captureRequestBuilder?.build()!!,
null,
backgroundHandler
)
} catch (e: CameraAccessException) {
Log.e(TAG, "Cannot access camera", e)
}
}

override fun onConfigureFailed(session: CameraCaptureSession) {
Toast.makeText(this@TempMainActivity,
"Failed to configure camera", Toast.LENGTH_SHORT).show()
}
},
backgroundHandler
)
} catch (e: CameraAccessException) {
Log.e(TAG, "Cannot access camera", e)
}
}

private fun startRecording() {
if (isRecording || cameraDevice == null || !textureView.isAvailable) {
Log.e(TAG, "Cannot start recording - invalid state")
return
}
try {
// Check device capabilities
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val highSpeedProfiles = characteristics.get(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
)?.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)
?: false

if (!highSpeedProfiles) {
Toast.makeText(this, "Slow motion not supported on this device",
Toast.LENGTH_LONG).show()
return
}

// Get the best available high speed profile
profile = when {
CamcorderProfile.hasProfile(cameraId.toInt(),
CamcorderProfile.QUALITY_HIGH_SPEED_1080P) -> {
CamcorderProfile.get(cameraId.toInt(),
CamcorderProfile.QUALITY_HIGH_SPEED_1080P)
}
CamcorderProfile.hasProfile(cameraId.toInt(),
CamcorderProfile.QUALITY_HIGH_SPEED_720P) -> {
CamcorderProfile.get(cameraId.toInt(),
CamcorderProfile.QUALITY_HIGH_SPEED_720P)
}
else -> {
Toast.makeText(this, "No high speed profile available",
Toast.LENGTH_LONG).show()
return
}
}

// Create video file
videoFile = createVideoFile()
if (videoFile == null) {
Toast.makeText(this, "Error creating video file",
Toast.LENGTH_SHORT).show()
return
}

// Set up media recorder
Log.d(TAG, "startRecording: >>>>>>>>>>>>>>>>>>>>>>>>>>>> profile!!.videoFrameRate ${profile!!.videoFrameRate}")
// Set up media recorder
mediaRecorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(profile!!.fileFormat)
setAudioEncoder(profile!!.audioCodec)
setVideoEncoder(profile!!.videoCodec)
setVideoSize(profile!!.videoFrameWidth, profile!!.videoFrameHeight)
setVideoFrameRate(profile!!.videoFrameRate)
setVideoEncodingBitRate(profile!!.videoBitRate * 2)
setOrientationHint(90)
setOutputFile(videoFile?.absolutePath)

// Critical slow motion settings
setCaptureRate(profile!!.videoFrameRate.toDouble())

// Alternative method for API 24+ to set slow motion
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
// Using reflection as a last resort since there's no public API
val setParametersMethod = MediaRecorder::class.java.getMethod(
"setParameters",
String::class.java
)
setParametersMethod.invoke(
this,
"slow-motion=${profile!!.videoFrameRate}"
)
} catch (e: Exception) {
Log.w(TAG, "Couldn't set slow-motion parameter", e)
}
}

try {
prepare()
} catch (e: Exception) {
Log.e(TAG, "MediaRecorder prepare failed", e)
release()
Toast.makeText(this@TempMainActivity,
"Prepare failed: ${e.message}", Toast.LENGTH_LONG).show()
return@apply
}
}

// Create recording session
val surfaces = listOf(
Surface(textureView.surfaceTexture),
mediaRecorder?.surface!!
)

// Configure for high speed FPS
val captureRequestBuilder = cameraDevice?.createCaptureRequest(
CameraDevice.TEMPLATE_RECORD).apply {
this?.addTarget(mediaRecorder?.surface!!)
this?.addTarget(Surface(textureView.surfaceTexture))
this?.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO)
this?.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
Range(profile!!.videoFrameRate, profile!!.videoFrameRate))
}

cameraDevice?.createConstrainedHighSpeedCaptureSession(
surfaces,
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cameraCaptureSession = session

try {
// Create high speed request list properly
val highSpeedRequestList = (session as? CameraConstrainedHighSpeedCaptureSession)?.let {
captureRequestBuilder?.build()?.let { request ->
it.createHighSpeedRequestList(request)
}
}

highSpeedRequestList?.let { requests ->
session.setRepeatingBurst(requests, null, backgroundHandler)
}

mediaRecorder?.start()
isRecording = true
runOnUiThread {
recordButton.text = "Stop Recording"
statusText.text = "Recording Slow Motion (${profile!!.videoFrameRate}FPS)"
}
} catch (e: CameraAccessException) {
Log.e(TAG, "Failed to start high speed recording", e)
runOnUiThread {
Toast.makeText(this@TempMainActivity,
"Failed to start recording", Toast.LENGTH_SHORT).show()
}
}
}

override fun onConfigureFailed(session: CameraCaptureSession) {
runOnUiThread {
Toast.makeText(this@TempMainActivity,
"Failed to configure high speed session", Toast.LENGTH_SHORT).show()
}
}
},
backgroundHandler
)

} catch (e: Exception) {
Log.e(TAG, "Error starting recording", e)
Toast.makeText(this, "Error starting recording: ${e.message}",
Toast.LENGTH_SHORT).show()
}
}

private fun stopRecording() {
if (!isRecording) return

try {
cameraCaptureSession?.stopRepeating()
cameraCaptureSession?.abortCaptures()
mediaRecorder?.apply {
stop()
reset()
}
saveVideoToGallery()
Toast.makeText(this, "Slow motion video saved to gallery",
Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
Log.e(TAG, "Error stopping recording", e)
Toast.makeText(this, "Error saving video", Toast.LENGTH_SHORT).show()
} finally {
mediaRecorder = null
isRecording = false
runOnUiThread {
recordButton.text = "Start Recording"
statusText.text = ""
}
createCameraPreviewSession() // Return to preview mode
}
}

private fun createVideoFile(): File? {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES)
return try {
File.createTempFile(
"SLOW_${timeStamp}_",
".mp4",
storageDir
).apply {
createNewFile()
}
} catch (e: Exception) {
Log.e(TAG, "Error creating video file", e)
null
}
}

private fun saveVideoToGallery() {
videoFile?.let { file ->
try {
val contentValues = ContentValues().apply {
put(MediaStore.Video.Media.TITLE, "Slow Motion Video")
put(MediaStore.Video.Media.DISPLAY_NAME, file.name)
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis())

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
profile?.let {
put(MediaStore.Video.Media.CAPTURE_FRAMERATE, it.videoFrameRate.toDouble())
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Video.Media.RELATIVE_PATH,
Environment.DIRECTORY_MOVIES + "/SlowMotion")
put(MediaStore.Video.Media.IS_PENDING, 1)
} else {
put(MediaStore.Video.Media.DATA, file.absolutePath)
}
}

val uri = contentResolver.insert(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
contentValues
) ?: throw IllegalStateException("Failed to create new MediaStore record")

contentResolver.openOutputStream(uri)?.use { outputStream ->
file.inputStream().use { inputStream ->
inputStream.copyTo(outputStream)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear()
contentValues.put(MediaStore.Video.Media.IS_PENDING, 0)
contentResolver.update(uri, contentValues, null, null)
}

// Notify gallery
sendBroadcast(
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)
)

// Delete temporary file
if (!file.delete()) {
Log.w(TAG, "Failed to delete temporary file: ${file.absolutePath}")
}

runOnUiThread {
Toast.makeText(this, "Video saved to gallery", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Log.e(TAG, "Error saving video to gallery", e)
runOnUiThread {
Toast.makeText(this, "Failed to save video: ${e.message}", Toast.LENGTH_LONG).show()
}
}
}
}
}


Подробнее здесь: https://stackoverflow.com/questions/795 ... camera-api
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»