Android 使用Camera1实现相机预览、拍照、录像

这篇具有很好参考价值的文章主要介绍了Android 使用Camera1实现相机预览、拍照、录像。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

本文介绍如何从零开始,在Android中实现Camera1的接入,并在文末提供Camera1Manager工具类,可以用于快速接入Camera1
Android Camera1 API虽然已经被Google废弃,但有些场景下不得不使用。
并且Camera1返回的帧数据是NV21,不像Camera2CameraX那样,需要自己再转一层,才能得到NV21
Camera1的API调用也比Camera2简单不少,和CameraX的简单程度差不多,所以在一定的场景下,Camera1还是有其用途的。

2. 前置操作

2.1 添加权限

AndroidManifest中添加如下权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

2.2 申请权限

别忘了申请权限

ActivityCompat.requestPermissions(
    this@WelComeActivity,
    arrayOf(
        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
        android.Manifest.permission.RECORD_AUDIO,
        android.Manifest.permission.CAMERA
    ),
    123
)

2.3 声明XML布局

新建一个Activity,在其XML中声明SurfaceView

<?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"
    android:background="@color/black"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="9:16"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3. 实现预览功能

3.1 添加SurfaceView的回调

binding.surfaceView.holder.addCallback(surfaceCallback)

private var surfaceCallback: SurfaceHolder.Callback = object : SurfaceHolder.Callback {
	// Surface创建时
    override fun surfaceCreated(holder: SurfaceHolder) {
    }

	// Surface改变时
    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
    }

	// Surface销毁时
    override fun surfaceDestroyed(holder: SurfaceHolder) {
    }
}

3.2 打开相机

Surface创建时,也就是在surfaceCreated的时候,打开相机

private var camera: Camera? = null
private fun openCamera(holder: SurfaceHolder) {
    try {
        camera = Camera.open(cameraId)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

3.3 开始预览

当我们打开相机后,就可以开始预览了
这里首先将设置camera1预览的尺寸,一般来说,通过camera!!.parameters.supportedPreviewSizes获取到的列表中,第一项就是最推荐的尺寸了。

private fun setPreviewSize() {
    //获取摄像头支持的宽、高
    val supportedPreviewSizes: List<Camera.Size> = camera!!.parameters.supportedPreviewSizes
    supportedPreviewSizes.forEach {
        Log.i("ZZZZ", "${it.width}*${it.height}")
    }
    val parameters = camera?.parameters
    val size = supportedPreviewSizes[0]
    parameters?.setPreviewSize(size.width, size.height)
    camera?.setParameters(parameters)
}

接着,将SurfaceHolder设置到camera中。setPreviewDisplay接受一个SurfaceHolder对象作为参数,该对象表示预览显示的表面。通过调用setPreviewDisplay方法,可以将相机的预览数据输出到指定的表面对象上,从而在预览界面中显示出相机的拍摄画面。

camera?.setPreviewDisplay(holder)

接着调用setDisplayOrientation方法来设置相机的预览方向。该方法接受一个参数,即预览方向的度数。例如,如果要在竖直模式下使用相机,而默认的预览方向是水平的,那么就可以通过调用setDisplayOrientation方法将预览方向顺时针旋转90度。

camera?.setDisplayOrientation(90)

最后,调用startPreview()就可以启动相机的预览了

camera?.startPreview()

来看一下完整代码

private fun startPreview(holder: SurfaceHolder) {
    try {
        setPreviewSize()
        camera?.setPreviewDisplay(holder)
        camera?.setDisplayOrientation(90)
        camera?.startPreview()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

3.4 效果如下

4. 实现拍照功能

4.1 调用拍照接口

要进行拍照,调用camera.takePicture即可,它共有3个回调参数

  • ShutterCallback shutter(捕获图片瞬间的回调):快门回调是在拍照时快门按下的瞬间调用的回调。它允许您在拍照时执行一些自定义操作,例如触发闪光灯或显示自定义的拍照界面。
  • PictureCallback raw(原始图像数据回调):原始图像数据回调是在拍照后,获取到原始未压缩的数据时调用的回调。您可以在这个回调中对图像数据进行处理或保存。
  • PictureCallback jpeg(JPEG图像数据回调):JPEG图像数据回调是在拍照后,获取到图像的JPEG格式数据时调用的回调。您可以在这个回调中对JPEG图像数据进行处理或保存。

这里我们只需要用到jpeg回调

private val threadPool = Executors.newCachedThreadPool()

binding.btnTakePicture.setOnClickListener {
    camera?.takePicture(
    	null,null,{ data, camera ->
            //jpeg回调
        })
}

4.2 在jpeg回调中保存图片

//MediaFileUtils类详见本文附录
val pictureFile: File = MediaFileUtils.getOutputMediaFile(MEDIA_TYPE_IMAGE)!!
try {
    val fos = FileOutputStream(pictureFile)
    fos.write(data)
    fos.close()
} catch (e: FileNotFoundException) {
    Log.d(TAG, "File not found: ${e.message}")
    errorCallBack.invoke(e)
} catch (e: IOException) {
    Log.d(TAG, "Error accessing file: ${e.message}")
    errorCallBack.invoke(e)
}

来查看下效果,可以看到图片已经被保存了,但是图片的方向目前是有问题的。
android相机demo,Android日常经验,android,相机,Camera1,拍照,录制视频,CameraHelper,CameraUtils

4.3 解决图片保存的方向问题

所以,我们需要先将图片转成bitmap,旋转角度后,再保存
修改代码为如下代码

//路径示例 : /storage/emulated/0/Pictures/MyCameraApp/IMG_20230726_135652.jpg
val pictureFile: File = MediaFileUtils.getOutputMediaFile(MediaFileUtils.MEDIA_TYPE_IMAGE)!!
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
val matrix = Matrix()
matrix.postRotate(270F)
val rotatedBitmap: Bitmap = Bitmap.createBitmap(
    bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
//ImageUtils需要依赖 implementation 'com.blankj:utilcodex:1.31.1'
ImageUtils.save(rotatedBitmap, pictureFile, Bitmap.CompressFormat.JPEG)

来看一下效果,可以看到现在图片方向是对了,但是图片左右的内容是相反的
android相机demo,Android日常经验,android,相机,Camera1,拍照,录制视频,CameraHelper,CameraUtils

4.4 解决图片保存镜像问题

要解决图片的镜像问题,就调用一下matrix.postScale左右水平变换就好了

matrix.postScale(-1F, 1F, bitmap.width / 2F, bitmap.height / 2F)

完整代码如下

val pictureFile: File =
MediaFileUtils.getOutputMediaFile(MediaFileUtils.MEDIA_TYPE_IMAGE)!!
//路径示例 : /storage/emulated/0/Pictures/MyCameraApp/IMG_20230726_135652.jpg
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
val matrix = Matrix()
matrix.postRotate(270F)
matrix.postScale(-1F, 1F, bitmap.width / 2F, bitmap.height / 2F)
val rotatedBitmap: Bitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
//ImageUtils需要依赖 implementation 'com.blankj:utilcodex:1.31.1'
ImageUtils.save(rotatedBitmap, pictureFile, Bitmap.CompressFormat.JPEG)

5. 实现录像功能

要录制视频,需要使用MediaRecorder,若要使用 Camera1 拍摄视频,需要谨慎管理 CameraMediaRecorder,并且必须按特定顺序调用相应方法。您必须遵循以下顺序,才能使您的应用正常工作:

  • 打开相机。
  • 做好准备,并开始预览(如果您的应用会显示正在录制的视频,而通常情况下都是如此)。
  • 通过调用 Camera.unlock() 解锁相机,以供 MediaRecorder 使用。
  • 通过在 MediaRecorder 上调用以下方法来配置录制:
    • 通过 setCamera(camera) 关联您的 Camera 实例。
    • 调用 setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
    • 调用 setVideoSource(MediaRecorder.VideoSource.CAMERA)
    • 调用 setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)) 以设置质量。
    • 调用 setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())
    • 如果您的应用提供视频预览,请调用 setPreviewDisplay(preview?.holder?.surface)
    • 调用 setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
    • 调用 setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
    • 调用 setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
    • 调用 prepare() 以完成 MediaRecorder 配置。
  • 如需开始录制,请调用 MediaRecorder.start()
  • 如需停止录制,请按以下顺序调用以下方法:
    • 调用 MediaRecorder.stop()
    • (可选)通过调用 MediaRecorder.reset() 移除当前的 MediaRecorder 配置。
    • 调用 MediaRecorder.release()
    • 通过调用 Camera.lock() 锁定相机,以便将来的 MediaRecorder 会话可以使用它。
  • 如需停止预览,请调用 Camera.stopPreview()
  • 最后,如需释放 Camera 以供其他进程使用,请调用 Camera.release()

具体可以见 Camera1 录制视频

下面直接附上代码,直接如下代码就好了

5.1 开始录制

fun startVideo(holder: SurfaceHolder) {
	mediaRecorder = MediaRecorder()
	//解锁相机,以供 MediaRecorder 使用
	camera?.unlock()
	//设置要用于视频捕获的相机
	mediaRecorder.setCamera(camera)
	//设置音频源
	mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
	//设置视频源
	mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA)
	//设置视频的输出格式和编码
	mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P))
	//设置输出视频播放的方向
	mediaRecorder.setOrientationHint(270)
	//设置输出文件
	mediaRecorder.setOutputFile(getVideoFilePath(this))
	//指定 SurfaceView 预览布局元素
	mediaRecorder.setPreviewDisplay(holder.surface)
	
	try {
	    mediaRecorder.prepare()
	} catch (e: IOException) {
	    e.printStackTrace()
	    releaseMediaRecorder()
	}
	
	Handler().postDelayed({
	    try {
	        mediaRecorder.start()
	    } catch (e: IOException) {
	        e.printStackTrace()
	        releaseMediaRecorder()
	    }
	}, 10)
}

fun getVideoFilePath(context: Context?): String {
    val filename = "VIDEO_${System.currentTimeMillis()}.mp4"
    val dir = context?.getExternalFilesDir("video")

    return "${dir!!.path}/$filename"
}

5.2 停止播放

fun stopVideo() {
    mediaRecorder.stop()
    mediaRecorder.release()
    camera?.lock()
}

5.3 释放资源

fun releaseMediaRecorder() {
    if (mediaRecorder != null) {
        mediaRecorder.reset() // 清除配置
        mediaRecorder.release()
        //mediaRecorder = null
        camera?.lock()
    }
}

6. CameraHelper工具类

可以直接使用这个工具类,来快速接入Camera1

class CameraHelper(
    private val activity: AppCompatActivity,
    private var cameraId: Int,
    private var width: Int = 720,
    private var height: Int = 1280,
) : Camera.PreviewCallback {
    private var surfaceHolder: SurfaceHolder? = null
    private var surfaceTexture: SurfaceTexture? = null
    private var mCamera: Camera? = null
    private var buffer: ByteArray? = null
    private var bytes: ByteArray? = null

    /**
     * 打开相机
     *
     * @param cameraId 后摄 Camera.CameraInfo.CAMERA_FACING_BACK
     *                 前摄 Camera.CameraInfo.CAMERA_FACING_FRONT
     */
    private fun open(cameraId: Int) {
        //获得camera对象
        mCamera = Camera.open(cameraId)

        mCamera?.let { camera ->
            //配置camera的属性
            val parameters = camera.parameters
            //设置预览数据格式为nv21
            parameters.previewFormat = ImageFormat.NV21
            //这是摄像头宽、高
            setPreviewSize(parameters!!)
            // 设置摄像头 图像传感器的角度、方向
            setPreviewOrientation(cameraId)
            camera.parameters = parameters
        }
    }

    /**
     * 切换摄像头
     */
    fun switchCamera() {
        val cameraId = if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
            Camera.CameraInfo.CAMERA_FACING_FRONT
        } else {
            Camera.CameraInfo.CAMERA_FACING_BACK
        }
        switchCamera(cameraId)
    }

    /**
     * 切换摄像头
     * @param cameraId 指定摄像头ID
     */
    fun switchCamera(cameraId: Int) {
        this.cameraId = cameraId
        previewAlign()
    }

    private fun previewAlign() {
        stopPreview()
        if (surfaceHolder != null) {
            startPreview(surfaceHolder!!)
        } else {
            startPreview(surfaceTexture!!)
        }
    }

    /**
     * 停止预览
     */
    fun stopPreview() {
        if (mCamera != null) {
            mCamera?.setPreviewCallback(null)
            mCamera?.stopPreview()
            mCamera?.release()
            mCamera = null
        }
    }

    /**
     * 开始预览
     */
    fun startPreview(surfaceHolder: SurfaceHolder) {
        open(cameraId)
        this.surfaceHolder = surfaceHolder
        buffer = ByteArray(width * height * 3 / 2)
        bytes = ByteArray(buffer!!.size)
        //数据缓存区
        mCamera?.addCallbackBuffer(buffer)
        mCamera?.setPreviewCallbackWithBuffer(this)
        //设置预览画面
        mCamera?.setPreviewDisplay(surfaceHolder)
        mCamera?.startPreview()
    }

    fun startPreview(surfaceTexture: SurfaceTexture) {
        open(cameraId)
        this.surfaceTexture = surfaceTexture
        buffer = ByteArray(width * height * 3 / 2)
        bytes = ByteArray(buffer!!.size)
        //数据缓存区
        mCamera?.addCallbackBuffer(buffer)
        mCamera?.setPreviewCallbackWithBuffer(this)
        //设置预览画面
        mCamera?.setPreviewTexture(surfaceTexture)
        mCamera?.startPreview()
    }

    private val threadPool = Executors.newCachedThreadPool()

    /**
     * 拍摄照片
     */
    fun takePicture(completedCallBack: () -> Unit, errorCallBack: (Exception) -> Unit) {
        mCamera?.takePicture(null, null, object : Camera.PictureCallback {
            override fun onPictureTaken(data: ByteArray?, camera: Camera?) {
                previewAlign()

                threadPool.execute {
                    val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE)!!

                    val bitmap : Bitmap
                    try {
                        //路径示例 : /storage/emulated/0/Pictures/MyCameraApp/IMG_20230726_135652.jpg
                        bitmap = BitmapFactory.decodeByteArray(data, 0, data!!.size)
                    }catch (e:Exception){
                        errorCallBack.invoke(e)
                        return@execute
                    }

                    val matrix = Matrix()
                    //修正图片方向,这里只是示例,需要根据实际手机方位来决定图片角度
                    matrix.postRotate(if (cameraId == 1) 270F else 90F)
                    if (cameraId == 1) {
                        //postScale在矩阵变换之后进行缩放
                        matrix.postScale(-1F, 1F, bitmap.width / 2F, bitmap.height / 2F)
                    }
                    val rotatedBitmap: Bitmap = Bitmap.createBitmap(
                        bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
                    )
                    //需要依赖"com.blankj:utilcodex:1.31.1"
                    ImageUtils.save(rotatedBitmap, pictureFile, Bitmap.CompressFormat.JPEG)
                    completedCallBack.invoke()
                }
            }
        })
    }

    override fun onPreviewFrame(data: ByteArray, camera: Camera?) {
        onPreviewListener?.onPreviewFrame(data, camera)
        camera!!.addCallbackBuffer(data)
    }

    private fun setPreviewSize(parameters: Camera.Parameters) {
        //获取摄像头支持的宽、高
        val supportedPreviewSizes = parameters.supportedPreviewSizes
        var size = supportedPreviewSizes[0]
        Log.d(TAG, "Camera支持: " + size.width + "x" + size.height)
        //选择一个与设置的差距最小的支持分辨率
        var m: Int = Math.abs(size.height * size.width - width * height)
        supportedPreviewSizes.removeAt(0)
        val iterator: Iterator<Camera.Size> = supportedPreviewSizes.iterator()
        //遍历
        while (iterator.hasNext()) {
            val next = iterator.next()
            Log.d(TAG, "支持 " + next.width + "x" + next.height)
            val n: Int = Math.abs(next.height * next.width - width * height)
            if (n < m) {
                m = n
                size = next
            }
        }
        width = size.width
        height = size.height
        parameters.setPreviewSize(width, height)
        Log.d(TAG, "预览分辨率 width:" + size.width + " height:" + size.height)
    }

    private val mOnChangedSizeListener: OnChangedSizeListener? = null

    private fun setPreviewOrientation(cameraId: Int) {
        val info = Camera.CameraInfo()
        Camera.getCameraInfo(cameraId, info)
        val rotation = activity.windowManager.defaultDisplay.rotation
        var degrees = 0
        when (rotation) {
            Surface.ROTATION_0 -> {
                degrees = 0
                mOnChangedSizeListener?.onChanged(height, width)
            }

            Surface.ROTATION_90 -> {
                degrees = 90
                mOnChangedSizeListener?.onChanged(width, height)
            }

            Surface.ROTATION_180 -> {
                degrees = 180
                mOnChangedSizeListener?.onChanged(height, width)
            }

            Surface.ROTATION_270 -> {
                degrees = 270
                mOnChangedSizeListener?.onChanged(width, height)
            }
        }
        var result: Int
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360
            result = (360 - result) % 360 // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360
        }
        //设置角度, 参考源码注释
        mCamera!!.setDisplayOrientation(result)
    }

    private lateinit var mediaRecorder: MediaRecorder
    private val handle = Handler(Looper.getMainLooper())

    /**
     * 开始录像
     */
    fun startVideo(path: String) {
        mediaRecorder = MediaRecorder()
        //解锁相机,以供 MediaRecorder 使用
        mCamera?.unlock()
        //设置要用于视频捕获的相机
        mediaRecorder.setCamera(mCamera)
        //设置音频源
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
        //设置视频源
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA)
        //设置视频的输出格式和编码
        mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH))
        //设置输出视频播放的方向,这里只是示例,需要根据实际手机方位来决定角度
        mediaRecorder.setOrientationHint(if (cameraId == 1) 270 else 90)
        //设置输出文件
        mediaRecorder.setOutputFile(path)
        //指定 SurfaceView 预览布局元素
        mediaRecorder.setPreviewDisplay(surfaceHolder!!.surface)
        try {
            mediaRecorder.prepare()
        } catch (e: IOException) {
            e.printStackTrace()
            releaseMediaRecorder()
        }

        handle.postDelayed({
            try {
                mediaRecorder.start()
            } catch (e: IOException) {
                e.printStackTrace()
                releaseMediaRecorder()
            }
        }, 10)
    }

    /**
     * 释放资源
     */
    fun releaseMediaRecorder() {
        if (mediaRecorder != null) {
            mediaRecorder.reset() // 清除配置
            mediaRecorder.release()
            //mediaRecorder = null
            mCamera?.lock()
        }
    }

    /**
     * 停止录像
     */
    fun stopVideo() {
        mediaRecorder.stop()
        mediaRecorder.release()
        mCamera?.lock()
    }

    interface OnChangedSizeListener {
        fun onChanged(width: Int, height: Int)
    }

    interface OnPreviewListener {
        fun onPreviewFrame(data: ByteArray, camera: Camera?)
    }

    private var onPreviewListener: OnPreviewListener? = null

    /**
     * 设置预览监听
     */
    fun setOnPreviewListener(listener: OnPreviewListener) {
        this.onPreviewListener = listener
    }

    companion object {
        private const val TAG = "CAMERA_HELPER"
    }
}

进行使用

//这里的Activity是横屏的
class MainActivity : AppCompatActivity(), SurfaceHolder.Callback {
    private lateinit var binding: ActivityMainBinding
    private lateinit var cameraHelper: CameraHelper
    private val cameraId = 1
    private val nativeLib = NativeLib()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ),
            1
        )

        nativeLib.load(assets, 0, 0)

        cameraHelper = CameraHelper(this, cameraId,1920,1080)
        cameraHelper.setOnPreviewListener(object : CameraHelper.OnPreviewListener {
            override fun onPreviewFrame(data: ByteArray, camera: Camera?) {
                //预览回调
            }
        })
        binding.surfaceView.holder.addCallback(this)

        binding.btnTakePicture.setOnClickListener {
            cameraHelper.takePicture({
                 //拍照成功
            },{
                //拍照失败
            })
        }

        binding.btnVideoCapture.setOnClickListener {
            //开始录制
            val path = MediaFileUtils.getOutputMediaFile(MediaFileUtils.MEDIA_TYPE_VIDEO)!!.path
            cameraHelper.startVideo(path)

            //cameraHelper.stopVideo() //结束录制
        }
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
    	//开始预览
        cameraHelper.startPreview(holder)
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {

    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
    	//停止预览
        cameraHelper.stopPreview()
    }
}

7. 附录

7.1 MediaFileUtils

获取媒体文件路径的工具类

object MediaFileUtils {
    val MEDIA_TYPE_IMAGE = 1
    val MEDIA_TYPE_VIDEO = 2

    /** Create a file Uri for saving an image or video */
    fun getOutputMediaFileUri(type: Int): Uri {
        return Uri.fromFile(getOutputMediaFile(type))
    }

    /** Create a File for saving an image or video */
    fun getOutputMediaFile(type: Int): File? {
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.

        val mediaStorageDir = File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            "MyCameraApp"
        )
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        mediaStorageDir.apply {
            if (!exists()) {
                if (!mkdirs()) {
                    Log.d("MyCameraApp", "failed to create directory")
                    return null
                }
            }
        }

        // Create a media file name
        val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        return when (type) {
            MEDIA_TYPE_IMAGE -> {
                File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg")
            }
            MEDIA_TYPE_VIDEO -> {
                File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4")
            }
            else -> null
        }
    }
}

7.2. 本文源码下载

Android Camera1 Demo - 实现预览、拍照、录制视频功能文章来源地址https://www.toymoban.com/news/detail-713261.html

到了这里,关于Android 使用Camera1实现相机预览、拍照、录像的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 十分钟实现 Android Camera2 相机拍照

    因为工作中要使用 Android Camera2 API ,但因为 Camera2 比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在 CSDN 上记录了下,希望能帮助到更多的小伙伴。 上篇文章 我们使用 Camera2 实现了相机预览的功能,这篇文章我们接着上文,来实现 Cam

    2024年02月11日
    浏览(42)
  • 十分钟实现 Android Camera2 相机预览

    因为工作中要使用 Android Camera2 API ,但因为 Camera2 比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在 CSDN 上记录了下,希望能帮助到更多的小伙伴。 Camera2 API 的包名是 android.hardware.camera2 ,是 Android 5.0 后推出的一套调用摄像头设备的接口

    2024年02月13日
    浏览(52)
  • Android Camera2(1)-Camera2在textureView中的预览和拍照

    解释上诉示意图,假如想要同时拍摄两张不同尺寸的图片,并且在拍摄过程中闪光灯必须亮起来。整个拍摄流程如下: 创建一个用于从 Pipeline 获取图片的 CaptureRequest。 修改 CaptureRequest 的闪光灯配置,让闪光灯在拍照过程中亮起来。 创建两个不同尺寸的 Surface 用于接收图片

    2024年02月05日
    浏览(47)
  • Android之camera1和2的简单使用

    前言 Android Framework提供Camera API来实现拍照与录制视频的功能,目前Android有三类API, Camera 此类是用于控制设备相机的旧版 API,现已弃用,在Android5.0以下使用 Camera2 此软件包是用于控制设备相机的主要 API,Android5.0以上使用 CameraX 基于Camera 2 API封装,简化了开发流程,并增加

    2024年02月09日
    浏览(33)
  • android camera系列(Camera1、Camera2、CameraX)的使用以及输出的图像格式

    1.1.1、布局 1.1.2、实现预览 Camera.open() 打开摄像头 setPreviewDisplay 设置预览展示的控件 startPreview 开始预览 发现预览是横着的,需要使用 setDisplayOrientation 调整预览图像的方向 1.1.3、获取摄像头的原始数据 setPreviewCallback 设置预览数据的回调 2560*1440 默认返回图像的分辨率 Image

    2024年02月21日
    浏览(36)
  • flutter开发实战-Camera自定义相机拍照功能实现

    flutter开发实战-Camera自定义相机拍照功能实现 在项目中使用image_picker插件时候,在android设备上使用无法默认设置前置摄像头(暂时不清楚什么原因),由于项目默认需要使用前置摄像头,所以最终采用自定义相机实现拍照功能。 在工程的iOS的info.plist文件中添加相机、麦克风

    2024年02月21日
    浏览(44)
  • 微信小程序 图片 相机 二维码 动画相关API(实现选择相册、拍照、录像、动画)

    本文探讨微信小程序 图片 相机 二维码 动画相关API(实现选择相册、拍照、录像、动画)功能,带有示例代码

    2024年02月04日
    浏览(38)
  • 【Android】一步步实现手机拍照、录像及存储至相册(CameraX)

    参考资料 https://developer.android.google.cn/codelabs/camerax-getting-started?hl=zh-cn#0 https://developer.android.google.cn/training/camerax/video-capture?hl=zh-cn //https://blog.csdn.net/teolih/article/details/120423971 对应视频及项目代码 【项目代码】https://gitee.com/hellosunshine/camerax_android_java.git 步骤(java版本) 基本功能

    2024年02月07日
    浏览(33)
  • Android 之 使用 Camera 拍照

    本节给大家带来的是Android中Camera的使用,简单点说就是拍照咯,无非两种: 1.调用系统自带相机拍照,然后获取拍照后的图片 2.要么自己写个拍照页面 本节我们来写两个简单的例子体验下上面的这两种情况~ 我们只需下面一席话语,即可调用系统相机,相机拍照后会返回一个

    2024年02月09日
    浏览(28)
  • 关于 Camera 预览和录像画质不一样的问题分析

    1、问题背景 基于之前安卓平台的一个项目,客户有反馈过一个 Camera app 预览的效果,和录像效果不一致的问题。 这里的预览是指打开 Camera app 后直接出图的效果;录像的效果则是指打开 Camera app 开启录像功能,录制一段视频,再用设备自带的播放器去看录制的视频的效果。

    2024年02月10日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包