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

2024-06-04 3077阅读

1. 前言

本文介绍如何从零开始,在Android中实现Camera1的接入,并在文末提供Camera1Manager工具类,可以用于快速接入Camera1。

Android Camera1 API虽然已经被Google废弃,但有些场景下不得不使用

并且Camera1返回的帧数据是NV21,不像Camera2、CameraX那样,需要自己再转一层,才能得到NV21。

Camera1的API调用也比Camera2简单不少,和CameraX的简单程度差不多,所以在一定的场景下,Camera1还是有其用途的。

2. 前置操作

2.1 添加权限

在AndroidManifest中添加如下权限



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


    

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!!.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 使用Camera1实现相机预览、拍照、录像 第1张

    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 使用Camera1实现相机预览、拍照、录像 第2张

    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 拍摄视频,需要谨慎管理 Camera 和 MediaRecorder,并且必须按特定顺序调用相应方法。您必须遵循以下顺序,才能使您的应用正常工作:

    • 打开相机。
    • 做好准备,并开始预览(如果您的应用会显示正在录制的视频,而通常情况下都是如此)。
    • 通过调用 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 = 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  {
                          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 - 实现预览、拍照、录制视频功能


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]