我目前正在开发一个基本的应用程序,该应用程序使用Android集成摄像头和Camera X来录制视频,尽管我正在努力,但我想同时在视频视图中查看完全不相关的视频同一个屏幕。
我认为问题可能出在 videoview 本身上,而且事实上它在同时使用时与录音机的播放效果不佳,所以我尝试了 webview
发生的情况是,视频视图或网络视图获得焦点并在没有预览的情况下运行视频,反之亦然 我尝试过使用链接到 youtube 的 Videoview 和 Webview,但仍然没有骰子 是否可以在使用相机/网络视图的同时观看视频?
Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Kamera">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="411dp"
android:layout_height="162dp"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout_editor_absoluteX="0dp" />
<Button
android:id="@+id/exit_to_main"
android:layout_width="107dp"
android:layout_height="57dp"
android:elevation="2dp"
android:text="@string/exit"
tools:layout_editor_absoluteX="149dp"
tools:layout_editor_absoluteY="624dp" />
<Button
android:id="@+id/image_capture_button"
android:layout_width="132dp"
android:layout_height="58dp"
android:elevation="2dp"
android:text="@string/take_photo"
tools:layout_editor_absoluteX="24dp"
tools:layout_editor_absoluteY="624dp" />
<Button
android:id="@+id/video_capture_button"
android:layout_width="132dp"
android:layout_height="58dp"
android:elevation="2dp"
android:text="@string/start_capture"
tools:layout_editor_absoluteX="256dp"
tools:layout_editor_absoluteY="624dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_centerline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/horizontal_centerline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.0" />
<WebView
android:id="@+id/Webview"
android:layout_width="411dp"
android:layout_height="478dp"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toTopOf="@+id/viewFinder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is my Kotlin code
```
import android.Manifest
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.MediaStoreOutputOptions
import androidx.camera.video.Recorder
import androidx.camera.video.Recording
import androidx.camera.video.VideoCapture
import androidx.camera.video.VideoRecordEvent
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import com.dabs.kitchenkaraoke.databinding.ActivityMainBinding
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class Kamera : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture<Recorder>? = null
private var recording: Recording? = null
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
// Request camera permissions
if (allPermissionsGranted()) {
startCamera()
} else {
requestPermissions()
}
// Set up the listeners for take photo and video capture buttons
viewBinding.imageCaptureButton.setOnClickListener { takePhoto() }
viewBinding.videoCaptureButton.setOnClickListener { captureVideo() }
viewBinding.exitToMain?.setOnClickListener { finish() }
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time stamped name and MediaStore entry.
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.UK)
.format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun
onImageSaved(output: ImageCapture.OutputFileResults) {
val msg = "Photo capture succeeded: ${output.savedUri}"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)
}
private fun captureVideo() {
val videoCapture = this.videoCapture ?: return
viewBinding.videoCaptureButton.isEnabled = true
val curRecording = recording
if (curRecording != null) {
// Stop the current recording session.
curRecording.stop()
recording = null
return
}
// create and start a new recording session
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.UK)
.format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video")
}
}
val mediaStoreOutputOptions = MediaStoreOutputOptions
.Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build()
recording = videoCapture.output
.prepareRecording(this, mediaStoreOutputOptions)
.apply {
if (PermissionChecker.checkSelfPermission(
this@Kamera,
Manifest.permission.RECORD_AUDIO
) ==
PermissionChecker.PERMISSION_GRANTED
) {
withAudioEnabled()
}
}
.start(ContextCompat.getMainExecutor(this)) { recordEvent ->
when (recordEvent) {
is VideoRecordEvent.Start -> {
viewBinding.videoCaptureButton.apply {
text = getString(R.string.stop_capture)
isEnabled = true
}
}
is VideoRecordEvent.Finalize -> {
if (!recordEvent.hasError()) {
val msg = "Video capture succeeded: " +
"${recordEvent.outputResults.outputUri}"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT)
.show()
Log.d(TAG, msg)
} else {
recording?.close()
recording = null
Log.e(
TAG, "Video capture ends with error: " +
"${recordEvent.error}"
)
}
viewBinding.videoCaptureButton.apply {
text = getString(R.string.start_capture)
isEnabled = true
}
}
}
}
}
@SuppressLint("RestrictedApi")
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
imageCapture = ImageCapture.Builder().build()
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, videoCapture)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun requestPermissions() {
activityResultLauncher.launch(REQUIRED_PERMISSIONS)
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it
) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
companion object {
private const val TAG = "CameraXApp"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
).apply {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
}
private val activityResultLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
)
{ permissions ->
// Handle Permission granted/rejected
var permissionGranted = true
permissions.entries.forEach {
if (it.key in REQUIRED_PERMISSIONS && it.value == false)
permissionGranted = false
}
if (!permissionGranted) {
Toast.makeText(
baseContext,
"Permission request denied",
Toast.LENGTH_SHORT
).show()
} else {
startCamera()
}
if (!permissionGranted) {
Toast.makeText(
baseContext,
"Permission request denied",
Toast.LENGTH_SHORT
).show()
}
else {
startCamera()
}
}
class Kamera : Activity() {
private var wv1: WebView? = null
val url = "https://www.youtube.com"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wv1 = findViewById<android.view.View>(R.id.Webview) as WebView
wv1!!.webViewClient = MyBrowser()
// val url = ed1!!.text.toString()
// Suppress scripting attacks
wv1!!.settings.loadsImagesAutomatically = true
wv1!!.settings.javaScriptEnabled = true
wv1!!.scrollBarStyle = android.view.View.SCROLLBARS_INSIDE_OVERLAY
wv1!!.settings.javaScriptEnabled = true
wv1!!.settings.saveFormData = true
wv1!!.loadUrl(url)
}
private inner class MyBrowser : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
view.loadUrl(url)
wv1!!.reload()
return true
}
}
}
}
```
问题似乎出在视图绑定上。经过一番深入研究,视频似乎没有正确初始化
private lateinit var viewBinding: ActivityMainBinding
private lateinit var frameL : FrameLayout
private lateinit var video : VideoView
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture<Recorder>? = null
private var recording: Recording? = null
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
viewBinding.videoView.setVideoURI(Uri.parse("https://law.duke.edu/cspd/contest/videos/Framed-Contest_Documentaries-and-You.mp4"))
viewBinding.videoView.start()