飞道的博客

安卓录像封装之自定义相机(RK主板上通用)

267人阅读  评论(0)

前言:

自从从手机应用转到RK安卓主板开发以后,发现有些手机上通用的东西在定制的主板上无法使用,比如:自定义相机录像功能 

权限:

<!-- 增加文件存储和访问摄像头的权限 -->  
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.CAMERA" />  
<uses-feature android:name="android.hardware.camera" />   

 第一步:在布局中:


   
  1. <SurfaceView
  2. android:id= "@+id/preview"
  3. android:layout_width= "match_parent"
  4. android:layout_height= "match_parent" />

自定义相机的主要方法,也可以封装到一起,下一节再讲!

下一步:使用


  
  1. import android.app.Activity
  2. import android.content.Intent
  3. import android.hardware.Camera
  4. import android.os.Bundle
  5. import android.util.Log
  6. import android.view.SurfaceHolder
  7. import android.view.SurfaceView
  8. import app.SpeechApp
  9. import com.airiche.httplibrary.http.Rk.MediaTecorderManger
  10. import com.sunchip.vendingmachine.R
  11. import kotlinx.android.synthetic.main.activity_open_shopping.*
  12. import java.io.IOException
  13. class TestActivity : Activity(), SurfaceHolder.Callback {
  14. private var mCamera //声明相机(不要倒错包)
  15. : Camera? = null
  16. private var mPrevice //声明视图
  17. : SurfaceView? = null
  18. private var mHolder
  19. : SurfaceHolder? = null
  20. override fun onCreate(savedInstanceState: Bundle?) {
  21. super.onCreate(savedInstanceState)
  22. setContentView(R.layout.activity_open_shopping)
  23. initView()
  24. }
  25. private fun initView() {
  26. mPrevice = findViewById(R.id.preview);
  27. mHolder = mPrevice!!.getHolder();
  28. mHolder!!.addCallback( this);
  29. }
  30. // 相机和Activity的生命周期进行绑定
  31. override fun onResume() {
  32. super.onResume()
  33. StartCamer()
  34. }
  35. //相机的启动
  36. private fun StartCamer() {
  37. if (mCamera == null) {
  38. mCamera = getCamera()
  39. if (mHolder != null) {
  40. setStartPrevicw(mCamera!!, mHolder!!)
  41. }
  42. }
  43. //直接开始录像
  44. var name = System.currentTimeMillis().toString()+ ".mp4"
  45. MediaTecorderManger.instance!!.prepareVideoRecorder(mCamera!!, "/sdcard/", name)
  46. }
  47. //相机的启动和Activity的生命周期进行绑定
  48. override fun onPause() {
  49. super.onPause()
  50. //同时停止录像
  51. MediaTecorderManger.instance!!.StopMediaRecorder( this)
  52. releaseCamera()
  53. }
  54. /**
  55. * 打开摄像头
  56. *
  57. * @return
  58. */
  59. fun getCamera(): Camera? {
  60. var camera: Camera? = null
  61. if (camera != null) {
  62. camera.stopPreview() //停掉原来摄像头的预览
  63. camera.release() //释放资源
  64. }
  65. // Camera.getNumberOfCameras()可访问的摄像机设备总数,如果没有摄像头,则为0。
  66. val cametacount = Camera.getNumberOfCameras()
  67. camera = Camera. open(cametacount - 1)
  68. return camera
  69. }
  70. /**
  71. * 开始预览相机内容
  72. */
  73. private fun setStartPrevicw(
  74. camera: Camera,
  75. holder: SurfaceHolder
  76. ) {
  77. try {
  78. val rotation = this.windowManager.defaultDisplay.rotation
  79. Log.e( "TAG", "rotation=============$rotation")
  80. camera.setPreviewDisplay(holder)
  81. //系统预览角度的调整
  82. camera.setDisplayOrientation( 0)
  83. //打开摄像头
  84. camera.startPreview()
  85. } catch (e: IOException) {
  86. Log.e( "TAG", "IOException==setStartPrevicw==e:" + e.message)
  87. //释放摄像头
  88. releaseCamera()
  89. }
  90. }
  91. /**
  92. * 释放相机资源
  93. */
  94. private fun releaseCamera() {
  95. if (mCamera != null) {
  96. mCamera!!.lock()
  97. mCamera!!.stopPreview()
  98. mCamera!!.setPreviewCallback( null)
  99. mCamera!!.setPreviewCallbackWithBuffer( null)
  100. mCamera!!.release()
  101. mCamera = null
  102. }
  103. }
  104. override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
  105. mCamera!!.stopPreview();
  106. setStartPrevicw(mCamera!!, mHolder!!);
  107. Log.e( "TAG", "surfaceChanged=============");
  108. }
  109. override fun surfaceDestroyed(holder: SurfaceHolder) {
  110. releaseCamera();
  111. }
  112. override fun surfaceCreated(holder: SurfaceHolder) {
  113. setStartPrevicw(mCamera!!, mHolder!!);
  114. }
  115. companion object {
  116. fun getTestActivity() {
  117. val intent = Intent(SpeechApp.mSpeechApp, TestActivity:: class.java)
  118. intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
  119. SpeechApp.mSpeechApp!!.startActivity(intent)
  120. }
  121. }
  122. }

 介绍:如果你们单独使用这个类是会有问题的:比如,

SpeechApp.mSpeechApp :这个是公共的Application
MediaTecorderManger.instance!!.prepareVideoRecorder(mCamera!!, AppConfig.path, name) 这个就是封装的录像代码
MediaTecorderManger.instance!!.StopMediaRecorder(this) 这个就是停止录像方法

  
  1. package com.airiche.httplibrary.http.Rk
  2. import android.content.Context
  3. import android.hardware.Camera
  4. import android.media.CamcorderProfile
  5. import android.media.MediaRecorder
  6. import android.util.Log
  7. import android.widget.Toast
  8. import com.airiche.httplibrary.http.manager.FileManger
  9. import com.airiche.httplibrary.http.utils.FileSizeUtil
  10. import com.airiche.httplibrary.http.utils.FileUtils
  11. import com.airiche.httplibrary.http.utils.HttpURLUtils
  12. import com.airiche.httplibrary.http.utils.SingletonUtils
  13. import java.io.IOException
  14. /**
  15. *
  16. * 录像 仅仅在Rk系统上使用 这是定制系统 google原生录像跑不起来
  17. * MediaRecorder的一些参数有些系统不支持
  18. */
  19. class MediaTecorderManger {
  20. private var mVideoFilename = ""
  21. //摄像机
  22. private var mMediaRecorder: MediaRecorder? = null
  23. var fileManger = FileManger()
  24. var mProfile: CamcorderProfile? = null
  25. //开始录像
  26. fun prepareVideoRecorder(mCamera: Camera?, Path: String, name: String): Boolean {
  27. Log.v(TAG, "开始录制视频========")
  28. FileSizeUtil.ConfirmFile(Path)
  29. mVideoFilename = Path + name
  30. fileManger.updateStorageSpaceAndHint(FileManger.OnStorageUpdateDoneListener {
  31. initializeRecorder(mCamera)
  32. if (mMediaRecorder == null) {
  33. Log.e(
  34. TAG,
  35. "Fail to initialize media recorder"
  36. )
  37. return @OnStorageUpdateDoneListener
  38. }
  39. try {
  40. mMediaRecorder!!.start() // Recording is now started
  41. } catch (e: RuntimeException) {
  42. Log.e(TAG, "Could not start media recorder. " + e)
  43. releaseMediaRecorder()
  44. mCamera!!.lock()
  45. return @OnStorageUpdateDoneListener
  46. }
  47. })
  48. return true
  49. }
  50. //初始化录像参数
  51. fun initializeRecorder(mCamera: Camera?) {
  52. mProfile = CamcorderProfile. get( 0, CamcorderProfile.QUALITY_HIGH)
  53. mMediaRecorder = MediaRecorder()
  54. mCamera!!.unlock()
  55. mMediaRecorder!!.setCamera(mCamera)
  56. mMediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
  57. mMediaRecorder!!.setVideoSource(MediaRecorder.VideoSource.CAMERA)
  58. mMediaRecorder!!.setProfile(mProfile)
  59. mMediaRecorder!!.setVideoSize(
  60. mProfile!!.videoFrameWidth,
  61. mProfile!!.videoFrameHeight
  62. )
  63. mMediaRecorder!!.setMaxDuration( 5 * 60 * 1000)
  64. mMediaRecorder!!.setOutputFile(mVideoFilename)
  65. var maxFileSize: Long = 1024 * 1024 * 1024.toLong() //最大一个G
  66. try {
  67. mMediaRecorder!!.setMaxFileSize(maxFileSize)
  68. } catch (exception: java.lang.RuntimeException) {
  69. // We are going to ignore failure of setMaxFileSize here, as
  70. // a) The composer selected may simply not support it, or
  71. // b) The underlying media framework may not handle 64-bit range
  72. // on the size restriction.
  73. }
  74. mMediaRecorder!!.setOrientationHint( 0)
  75. try {
  76. mMediaRecorder!!.prepare()
  77. } catch (e: IOException) {
  78. Log.e(TAG, "prepare failed for $mVideoFilename" + e)
  79. releaseMediaRecorder()
  80. throw RuntimeException(e)
  81. }
  82. mMediaRecorder!!.setOnErrorListener( object : MediaRecorder.OnErrorListener {
  83. override fun onError(mr: MediaRecorder?, what: Int, extra: Int) {
  84. mMediaRecorder!!.setOnErrorListener( null)
  85. mMediaRecorder!!.setOnInfoListener( null)
  86. mMediaRecorder!!.stop()
  87. }
  88. })
  89. mMediaRecorder!!.setOnInfoListener( object : MediaRecorder.OnInfoListener {
  90. override fun onInfo(mr: MediaRecorder?, what: Int, extra: Int) {
  91. }
  92. })
  93. }
  94. fun releaseMediaRecorder() {
  95. Log.i(TAG, "Releasing media recorder.")
  96. if (mMediaRecorder != null) {
  97. mMediaRecorder!!.reset()
  98. mMediaRecorder!!.release()
  99. mMediaRecorder = null
  100. }
  101. mVideoFilename = ""
  102. }
  103. fun StopMediaRecorder(mContext: Context) {
  104. mMediaRecorder!!.setOnErrorListener( null)
  105. mMediaRecorder!!.setOnInfoListener( null)
  106. mMediaRecorder!!.setPreviewDisplay( null)
  107. try {
  108. mMediaRecorder!!.stop()
  109. } catch (e: RuntimeException) {
  110. Toast.makeText(mContext, "录制时间太短!", Toast.LENGTH_SHORT).show()
  111. }
  112. //重置和释放 mediaRecorder
  113. mMediaRecorder!!.reset()
  114. mMediaRecorder!!.release()
  115. mMediaRecorder = null
  116. Log.v(TAG, "停止录制,文件已保存====" + mVideoFilename)
  117. }
  118. companion object {
  119. var TAG = "MediaTecorderManger"
  120. private val mMediaTecorderManger: SingletonUtils<MediaTecorderManger?, Void?> =
  121. object : SingletonUtils<MediaTecorderManger?, Void?>() {
  122. override fun create(var1: Void?): MediaTecorderManger? {
  123. return MediaTecorderManger()
  124. }
  125. }
  126. val instance: MediaTecorderManger?
  127. get() = mMediaTecorderManger[ null]
  128. }
  129. }

  
  1. package com.airiche.httplibrary.http.utils
  2. /**
  3. * Created by yuon 2020/11/28.
  4. */
  5. abstract class SingletonUtils<T, P> {
  6. @Volatile
  7. private var mInstance: T? = null
  8. protected abstract fun create(var1: P): T
  9. operator fun get(p: P): T {
  10. if (mInstance == null) {
  11. if (mInstance == null) {
  12. mInstance = create(p)
  13. }
  14. }
  15. return mInstance!!
  16. }
  17. }
FileManger:

  
  1. package com.airiche.httplibrary.http.manager;
  2. import android.os.AsyncTask;
  3. import android.os.Environment;
  4. import android.os.StatFs;
  5. import android.util.Log;
  6. import java.io.File;
  7. import static android.content.ContentValues.TAG;
  8. /**
  9. * Created by Administrator on 2020/12/05.
  10. */
  11. public class FileManger {
  12. public static final long LOW_STORAGE_THRESHOLD_BYTES = 50000000;
  13. private final Object mStorageSpaceLock = new Object();
  14. private long mStorageSpaceBytes = LOW_STORAGE_THRESHOLD_BYTES;
  15. public interface OnStorageUpdateDoneListener {
  16. public void onStorageUpdateDone(long bytes);
  17. }
  18. private boolean mPaused = false;
  19. public void updateStorageSpaceAndHint(final OnStorageUpdateDoneListener callback) {
  20. /*
  21. * We execute disk operations on a background thread in order to
  22. * free up the UI thread. Synchronizing on the lock below ensures
  23. * that when getStorageSpaceBytes is called, the main thread waits
  24. * until this method has completed.
  25. *
  26. * However, .execute() does not ensure this execution block will be
  27. * run right away (.execute() schedules this AsyncTask for sometime
  28. * in the future. executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
  29. * tries to execute the task in parellel with other AsyncTasks, but
  30. * there's still no guarantee).
  31. * e.g. don't call this then immediately call getStorageSpaceBytes().
  32. * Instead, pass in an OnStorageUpdateDoneListener.
  33. */
  34. ( new AsyncTask<Void, Void, Long>() {
  35. @Override
  36. protected Long doInBackground(Void... arg) {
  37. synchronized (mStorageSpaceLock) {
  38. mStorageSpaceBytes = getAvailableSpace();
  39. return mStorageSpaceBytes;
  40. }
  41. }
  42. @Override
  43. protected void onPostExecute(Long bytes) {
  44. // updateStorageHint(bytes);
  45. // This callback returns after I/O to check disk, so we could be
  46. // pausing and shutting down. If so, don't bother invoking.
  47. if (callback != null && !mPaused) {
  48. callback.onStorageUpdateDone(bytes);
  49. } else {
  50. Log.v(TAG, "ignoring storage callback after activity pause");
  51. }
  52. }
  53. }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  54. }
  55. public static final String DCIM =
  56. Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
  57. public static final String DIRECTORY = DCIM + "/Camera";
  58. public static final long PREPARING = - 2L;
  59. public static final long UNAVAILABLE = - 1L;
  60. public static final long UNKNOWN_SIZE = - 3L;
  61. public static long getAvailableSpace() {
  62. String state = Environment.getExternalStorageState();
  63. if (Environment.MEDIA_CHECKING.equals(state)) {
  64. return PREPARING;
  65. }
  66. if (!Environment.MEDIA_MOUNTED.equals(state)) {
  67. return UNAVAILABLE;
  68. }
  69. File dir = new File(DIRECTORY);
  70. dir.mkdirs();
  71. if (!dir.isDirectory() || !dir.canWrite()) {
  72. return UNAVAILABLE;
  73. }
  74. try {
  75. StatFs stat = new StatFs(DIRECTORY);
  76. return stat.getAvailableBlocks() * ( long) stat.getBlockSize();
  77. } catch (Exception e) {
  78. Log.i(TAG, "Fail to access external storage", e);
  79. }
  80. return UNKNOWN_SIZE;
  81. }
  82. }

  
  1. FileSizeUtil.ConfirmFile(Path)
  2. /**
  3. * 创建目录和文件
  4. */
  5. fun ConfirmFile(filePath: String?) {
  6. try {
  7. val f = File(filePath)
  8. val parent = f.parentFile
  9. if (!parent.exists()) parent.mkdirs()
  10. if (!f.exists()) f.createNewFile()
  11. } catch (ex: Exception) {
  12. ex.printStackTrace()
  13. }
  14. }

 

基本源码都在这了, 录像也跟原生有点像,毕竟还是安卓,只不过RK的需要多配置一些东西!有用的话请点个赞,要demo(RK主板)的可以找我!

 

 


转载:https://blog.csdn.net/qq_36333309/article/details/111505818
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场