Advertisement
thief_g

IsolatedVideoCaptureForFront&BackCam

May 22nd, 2023
1,017
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 43.05 KB | None | 0 0
  1. package com.example.camerax
  2.  
  3. import android.Manifest
  4. import android.annotation.SuppressLint
  5. import android.app.ProgressDialog
  6. import android.content.ContentValues
  7. import android.content.pm.PackageManager
  8. import android.os.Build
  9. import android.os.Bundle
  10. import android.os.Environment
  11. import android.os.Handler
  12. import android.os.HandlerThread
  13. import android.os.SystemClock
  14. import android.provider.MediaStore
  15. import android.util.Log
  16. import android.util.Size
  17. import android.view.Surface
  18. import android.view.Surface.ROTATION_0
  19. import android.view.Surface.ROTATION_180
  20. import android.view.View
  21. import android.widget.Chronometer
  22. import android.widget.Toast
  23. import androidx.appcompat.app.AppCompatActivity
  24. import androidx.camera.core.Camera
  25. import androidx.camera.core.CameraSelector
  26. import androidx.camera.core.ImageCapture
  27. import androidx.camera.core.ImageCaptureException
  28. import androidx.camera.core.Preview
  29. import androidx.camera.lifecycle.ProcessCameraProvider
  30. import androidx.camera.video.MediaStoreOutputOptions
  31. import androidx.camera.video.Quality
  32. import androidx.camera.video.QualitySelector
  33. import androidx.camera.video.Recorder
  34. import androidx.camera.video.Recording
  35. import androidx.camera.video.VideoCapture
  36. import androidx.camera.video.VideoRecordEvent
  37. import androidx.core.app.ActivityCompat
  38. import androidx.core.content.ContextCompat
  39. import androidx.core.content.PermissionChecker
  40. import com.example.camerax.databinding.ActivityMainBinding
  41. import com.otaliastudios.transcoder.Transcoder
  42. import com.otaliastudios.transcoder.TranscoderListener
  43. import com.otaliastudios.transcoder.TranscoderOptions
  44. import com.otaliastudios.transcoder.common.TrackStatus
  45. import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy
  46. import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy
  47. import com.otaliastudios.transcoder.strategy.TrackStrategy
  48. import com.otaliastudios.transcoder.validator.DefaultValidator
  49. import org.mp4parser.muxer.Movie
  50. import org.mp4parser.muxer.Track
  51. import org.mp4parser.muxer.builder.DefaultMp4Builder
  52. import org.mp4parser.muxer.container.mp4.MovieCreator
  53. import org.mp4parser.muxer.tracks.AppendTrack
  54. import java.io.File
  55. import java.io.FileOutputStream
  56. import java.text.SimpleDateFormat
  57. import java.util.Locale
  58. import java.util.concurrent.ExecutorService
  59. import java.util.concurrent.Executors
  60. import java.util.concurrent.Future
  61.  
  62.  
  63. class MainActivity : AppCompatActivity() {
  64.     private var isFront: Boolean = false
  65.     private lateinit var mainBinding: ActivityMainBinding
  66.     private var mIsVideoRecording: Boolean = false
  67.     private var mImageCapture: ImageCapture? = null
  68.     private lateinit var mImageCaptureExecutor: ExecutorService
  69.     private lateinit var mCameraSelector:CameraSelector
  70.     private lateinit var mCameraSelectorFrontCam:CameraSelector
  71.     private lateinit var mCameraSelectorBackCam:CameraSelector
  72.  
  73.     private var mVideoCapture: VideoCapture<Recorder>? = null
  74.     private var mVideoCaptureFrontCam: VideoCapture<Recorder>? = null
  75.     private var mVideoCaptureBackCam: VideoCapture<Recorder>? = null
  76.     private var mRecording: Recording? = null
  77.     private var mRecordingFrontCam: Recording? = null
  78.     private var mRecordingBackCam: Recording? = null
  79.     private var quality = Quality.HD
  80.     private val qualitySelector = QualitySelector.from(quality)
  81.     private var recorderBuilder = Recorder.Builder()
  82.     private var recorder = Recorder.Builder().build()
  83.     private var recorderFrontCam = Recorder.Builder().build()
  84.     private var recorderBackCam = Recorder.Builder().build()
  85.     private var mIsVideoPaused: Boolean = false
  86.     private  lateinit var mChronometer: Chronometer
  87.     private var isFlashOn: Boolean = false
  88.     private lateinit var camera: Camera
  89.     private var mIsCameraSwitched: Boolean = false
  90.     private lateinit var mVideoFileList: MutableList<File>
  91.     private lateinit var progressDialogue: ProgressDialog
  92.     private var timeWhenPaused: Long  = 0
  93.     private var cameraSwitchCount:Int = 0
  94.     private var mBackgroundThreadHandler: HandlerThread? = null
  95.     private var mBackgroundHandler: Handler? = null
  96.     private var mTranscodeFuture: Future<Void>? = null
  97.     private val mTranscodeVideoStrategy: TrackStrategy? = null
  98.     private val mTranscodeAudioStrategy: TrackStrategy? = null
  99.     private var outputFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "merged_video.mp4")
  100.     private var rotatedFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "Merged_VIDEO.mp4")
  101.     private lateinit var mScreenSize: Size
  102.  
  103.     override fun onCreate(savedInstanceState: Bundle?) {
  104.         super.onCreate(savedInstanceState)
  105.         mainBinding = ActivityMainBinding.inflate(layoutInflater)
  106.         setContentView(mainBinding.root)
  107.  
  108.         mImageCaptureExecutor = Executors.newSingleThreadExecutor()
  109.  
  110.         progressDialogue = ProgressDialog(this)
  111.  
  112.         mVideoFileList = mutableListOf()
  113.         mChronometer = mainBinding.chronometer
  114.         mChronometer.visibility = View.GONE
  115.         mainBinding.ivPauseResume.visibility = View.GONE
  116.  
  117.         recorderBuilder.setQualitySelector(qualitySelector)
  118.         mCameraSelectorFrontCam = CameraSelector.DEFAULT_FRONT_CAMERA
  119.         mCameraSelectorBackCam = CameraSelector.DEFAULT_BACK_CAMERA
  120.         checkPermissions()
  121.  
  122.         mainBinding.ivStartStop.setOnClickListener {
  123.             if (mIsVideoRecording) {
  124.                 mainBinding.ivTakePicture.visibility = View.VISIBLE
  125.                 mIsVideoRecording = false
  126.                 cameraSwitchCount = 0
  127.  
  128.                 stopRecordingFrontCam()
  129.  
  130.  
  131.             } else {
  132.                 mainBinding.ivTakePicture.visibility = View.GONE
  133.                 mIsVideoRecording = true
  134.  
  135.                 startRecordingVideoBackCam()
  136.  
  137.  
  138.             }
  139.         }
  140.  
  141.          timeWhenPaused = 0
  142.         mainBinding.ivPauseResume.setOnClickListener {
  143.             if (mIsVideoPaused) {
  144.                 mIsVideoPaused = false
  145.                 mRecording!!.resume()
  146.  
  147.             } else {
  148.                 mIsVideoPaused = true
  149.                 mRecording!!.pause()
  150.             }
  151.         }
  152.  
  153.         mainBinding.ivSwitchCamera.setOnClickListener {
  154.             mIsCameraSwitched = true
  155.             cameraSwitchCount++
  156.             if (mIsVideoRecording) {
  157.  
  158.                     stopRecordingBackCam()
  159.                     switchCamera()
  160.                     startRecordingVideoFrontCam()
  161.  
  162.  
  163.  
  164.             } else {
  165.                 switchCamera()
  166.             }
  167.         }
  168.         mainBinding.ivTakePicture.setOnClickListener {
  169.             takePhoto() // it will also save the photo
  170.         }
  171.  
  172.         mainBinding.ivFlash.setOnClickListener {
  173.             onFlashButtonClicked()
  174.         }
  175.  
  176.     }
  177.  
  178.     private fun stopRecordingFrontCam() {
  179.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_start_video_icon)
  180.         mRecordingFrontCam!!.stop()
  181.         mChronometer.stop()
  182.         mChronometer.visibility = View.GONE
  183.         if(!mIsVideoRecording) {
  184.             mainBinding.ivPauseResume.visibility = View.GONE
  185.             startBackCamera()
  186.         }
  187.     }
  188.  
  189.     private fun stopRecordingBackCam() {
  190.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_start_video_icon)
  191.         mRecordingBackCam!!.stop()
  192.         mChronometer.stop()
  193.         mChronometer.visibility = View.GONE
  194.         if(!mIsVideoRecording) {
  195.             mainBinding.ivPauseResume.visibility = View.GONE
  196.             startBackCamera()
  197.         }
  198.     }
  199.  
  200.     /**
  201.      * this method is responsible for the switch of the camera
  202.      */
  203.     private fun switchCamera() {
  204.         if ( mCameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
  205.             camera.cameraControl.enableTorch(false)
  206.             mCameraSelector =  CameraSelector.DEFAULT_FRONT_CAMERA
  207.             isFront = true
  208.             mainBinding.ivFlash.setBackgroundResource(R.drawable.ic_flash_off_icon)
  209.             startFrontCamera()
  210.         } else {
  211.             mCameraSelector =  CameraSelector.DEFAULT_BACK_CAMERA
  212.             startBackCamera()
  213.         }
  214.         //startCamera()
  215.     }
  216.  
  217.     /**
  218.      * this method will be invoked on click of flash button and turnOn/turnOff flash light accordingly
  219.      */
  220.     private fun onFlashButtonClicked() {
  221.         if(camera.cameraInfo.hasFlashUnit()) {
  222.             if (isFlashOn) {
  223.                 isFlashOn = false
  224.                 mainBinding.ivFlash.setBackgroundResource(R.drawable.ic_flash_off_icon)
  225.                 camera.cameraControl.enableTorch(isFlashOn)
  226.             } else {
  227.                 isFlashOn = true
  228.                 mainBinding.ivFlash.setBackgroundResource(R.drawable.ic_flash_icon)
  229.                 camera.cameraControl.enableTorch(isFlashOn)
  230.             }
  231.         } else {
  232.             isFlashOn = false
  233.             mainBinding.ivFlash.setBackgroundResource(R.drawable.ic_flash_off_icon)
  234.             Toast.makeText(this, "Flash is not available", Toast.LENGTH_SHORT).show()
  235.         }
  236.     }
  237.  
  238.     /**
  239.      * this method will start the camera preview
  240.      */
  241.     @SuppressLint("RestrictedApi")
  242.     private fun startCamera() {
  243.         mVideoCapture = VideoCapture.withOutput(recorder)
  244.         val preview = Preview.Builder()
  245.             .setTargetRotation(ROTATION_0)
  246.             .build()
  247.             .also {
  248.                 it.setSurfaceProvider(mainBinding.cameraPreview.surfaceProvider)
  249.             }
  250.         val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  251.         recorder = Recorder.Builder().build()
  252.  
  253.         if (ActivityCompat.checkSelfPermission(
  254.                 this,
  255.                 Manifest.permission.CAMERA
  256.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  257.                 this,
  258.                 Manifest.permission.WRITE_EXTERNAL_STORAGE
  259.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  260.                 this,
  261.                 Manifest.permission.READ_EXTERNAL_STORAGE
  262.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  263.                 this,
  264.                 Manifest.permission.RECORD_AUDIO
  265.             ) != PackageManager.PERMISSION_GRANTED
  266.         ) {
  267.             ActivityCompat.requestPermissions(
  268.                 this,
  269.                 arrayOf(
  270.                     Manifest.permission.CAMERA,
  271.                     Manifest.permission.WRITE_EXTERNAL_STORAGE,
  272.                     Manifest.permission.READ_EXTERNAL_STORAGE,
  273.                     Manifest.permission.RECORD_AUDIO
  274.                 ),
  275.                 REQUEST_CAMERA_PERMISSION
  276.             )
  277.             return
  278.         }
  279.         mVideoCapture!!.targetRotation = preview.targetRotation
  280.  
  281.         cameraProviderFuture.addListener({
  282.             val cameraProvider = cameraProviderFuture.get()
  283.             mImageCapture = ImageCapture.Builder()
  284.                 .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
  285.                 .build()
  286.  
  287.             try {
  288.                 cameraProvider.unbindAll()
  289.                 camera = cameraProvider.bindToLifecycle(this, mCameraSelector,mImageCapture,mVideoCapture,preview)
  290.                 mScreenSize = QualitySelector.getResolution(camera.cameraInfo, Quality.HD)!!
  291.             } catch (e: Exception) {
  292.                 Log.d("MainActivity", "Use case binding failed")
  293.             }
  294.  
  295.         }, ContextCompat.getMainExecutor(this))
  296.  
  297.     }
  298.  
  299.     fun startFrontCamera() {
  300.         mVideoCaptureFrontCam = VideoCapture.withOutput(recorderFrontCam)
  301.         val preview = Preview.Builder()
  302.             .build()
  303.  
  304.             .also {
  305.                 it.setSurfaceProvider(mainBinding.cameraPreview.surfaceProvider)
  306.             }
  307.         val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  308.         if (mIsCameraSwitched) {
  309.             recorderFrontCam = Recorder.Builder().build()
  310.  
  311.         }
  312.  
  313.         if (ActivityCompat.checkSelfPermission(
  314.                 this,
  315.                 Manifest.permission.CAMERA
  316.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  317.                 this,
  318.                 Manifest.permission.WRITE_EXTERNAL_STORAGE
  319.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  320.                 this,
  321.                 Manifest.permission.READ_EXTERNAL_STORAGE
  322.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  323.                 this,
  324.                 Manifest.permission.RECORD_AUDIO
  325.             ) != PackageManager.PERMISSION_GRANTED
  326.         ) {
  327.             ActivityCompat.requestPermissions(
  328.                 this,
  329.                 arrayOf(
  330.                     Manifest.permission.CAMERA,
  331.                     Manifest.permission.WRITE_EXTERNAL_STORAGE,
  332.                     Manifest.permission.READ_EXTERNAL_STORAGE,
  333.                     Manifest.permission.RECORD_AUDIO
  334.                 ),
  335.                 REQUEST_CAMERA_PERMISSION
  336.             )
  337.             return
  338.         }
  339.         //mVideoCaptureFrontCam!!.targetRotation = ROTATION_180
  340.  
  341.         cameraProviderFuture.addListener({
  342.             val cameraProvider = cameraProviderFuture.get()
  343. //            mImageCapture = ImageCapture.Builder()
  344. //                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
  345. //                .build()
  346.  
  347.             try {
  348.                 cameraProvider.unbindAll()
  349.                 camera = cameraProvider.bindToLifecycle(this, mCameraSelectorFrontCam,mVideoCaptureFrontCam,preview)
  350.                 mScreenSize = QualitySelector.getResolution(camera.cameraInfo, Quality.HD)!!
  351.             } catch (e: Exception) {
  352.                 Log.d("MainActivity", "Use case binding failed")
  353.             }
  354.  
  355.         }, ContextCompat.getMainExecutor(this))
  356.  
  357.     }
  358.     fun startBackCamera() {
  359.         mVideoCaptureBackCam = VideoCapture.withOutput(recorderBackCam)
  360.         val preview = Preview.Builder()
  361.             .build()
  362.             .also {
  363.                 it.setSurfaceProvider(mainBinding.cameraPreview.surfaceProvider)
  364.             }
  365.         val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  366.         if (mIsCameraSwitched){
  367.             recorderBackCam = Recorder.Builder().build()
  368.         }
  369.  
  370.  
  371.         if (ActivityCompat.checkSelfPermission(
  372.                 this,
  373.                 Manifest.permission.CAMERA
  374.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  375.                 this,
  376.                 Manifest.permission.WRITE_EXTERNAL_STORAGE
  377.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  378.                 this,
  379.                 Manifest.permission.READ_EXTERNAL_STORAGE
  380.             ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  381.                 this,
  382.                 Manifest.permission.RECORD_AUDIO
  383.             ) != PackageManager.PERMISSION_GRANTED
  384.         ) {
  385.             ActivityCompat.requestPermissions(
  386.                 this,
  387.                 arrayOf(
  388.                     Manifest.permission.CAMERA,
  389.                     Manifest.permission.WRITE_EXTERNAL_STORAGE,
  390.                     Manifest.permission.READ_EXTERNAL_STORAGE,
  391.                     Manifest.permission.RECORD_AUDIO
  392.                 ),
  393.                 REQUEST_CAMERA_PERMISSION
  394.             )
  395.             return
  396.         }
  397.       //  mVideoCaptureBackCam!!.targetRotation = ROTATION_180
  398.  
  399.         cameraProviderFuture.addListener({
  400.             val cameraProvider = cameraProviderFuture.get()
  401.  
  402.  
  403.             try {
  404.                 cameraProvider.unbindAll()
  405.                 camera = cameraProvider.bindToLifecycle(this, mCameraSelectorBackCam,mVideoCaptureBackCam,preview)
  406.                 mScreenSize = QualitySelector.getResolution(camera.cameraInfo, Quality.HD)!!
  407.             } catch (e: Exception) {
  408.                 Log.d("MainActivity", "Use case binding failed")
  409.             }
  410.  
  411.         }, ContextCompat.getMainExecutor(this))
  412.  
  413.     }
  414.  
  415.     /**
  416.      * this method will take the photo and save it to Gallery
  417.      */
  418.     private fun takePhoto() {
  419.         mImageCapture?.let{
  420.  
  421.             val imageFileName = SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis())
  422.             val contentValues = ContentValues().apply {
  423.                 put(MediaStore.MediaColumns.DISPLAY_NAME, imageFileName)
  424.                 put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
  425.                 if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
  426.                     put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
  427.                 }
  428.             }
  429.             val outputFileOptions = ImageCapture.OutputFileOptions
  430.                 .Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues
  431.                 ).build()
  432.             it.takePicture(
  433.                 outputFileOptions,
  434.                 mImageCaptureExecutor,
  435.                 object : ImageCapture.OnImageSavedCallback {
  436.                     override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults){
  437.                         startCamera()
  438.                     }
  439.  
  440.                     override fun onError(exception: ImageCaptureException) {
  441.                         Toast.makeText(
  442.                             mainBinding.root.context,
  443.                             "Error occurred in taking photo",
  444.                             Toast.LENGTH_LONG
  445.                         ).show()
  446.                         Log.d("MainActivity", "Error taking photo:$exception")
  447.                     }
  448.  
  449.                 })
  450.  
  451.             Toast.makeText( this@MainActivity , "The image has been saved to Gallery", Toast.LENGTH_SHORT).show()
  452.  
  453.         }
  454.     }
  455.  
  456.     /**
  457.      * this method will start the recording of video
  458.      */
  459.     private fun startRecordingVideo() {
  460.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_stop_video_icon)
  461.         mVideoCapture!!.let {
  462.             try {
  463.                 if (ActivityCompat.checkSelfPermission(
  464.                         this,
  465.                         Manifest.permission.CAMERA
  466.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  467.                         this,
  468.                         Manifest.permission.WRITE_EXTERNAL_STORAGE
  469.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  470.                         this,
  471.                         Manifest.permission.READ_EXTERNAL_STORAGE
  472.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  473.                         this,
  474.                         Manifest.permission.RECORD_AUDIO
  475.                     ) != PackageManager.PERMISSION_GRANTED
  476.                 ) {
  477.                     ActivityCompat.requestPermissions(
  478.                         this,
  479.                         arrayOf(
  480.                             Manifest.permission.CAMERA,
  481.                             Manifest.permission.WRITE_EXTERNAL_STORAGE,
  482.                             Manifest.permission.READ_EXTERNAL_STORAGE,
  483.                             Manifest.permission.RECORD_AUDIO
  484.                         ),
  485.                         REQUEST_CAMERA_PERMISSION
  486.                     )
  487.                     return
  488.                 }
  489.                 val contentValues = ContentValues().apply {
  490.                     put(MediaStore.MediaColumns.DISPLAY_NAME, "VID_${System.currentTimeMillis()}")
  491.                     put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
  492.                 }
  493.                 val mediaStoreOutputOptions = MediaStoreOutputOptions
  494.                     .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
  495.                     .setContentValues(contentValues)
  496.                     .build()
  497.                 mRecording = mVideoCapture!!.output
  498.                     .prepareRecording(this, mediaStoreOutputOptions)
  499.                     .apply {
  500.                         // Enable Audio for recording
  501.                         if (PermissionChecker.checkSelfPermission(this@MainActivity, Manifest.permission.RECORD_AUDIO) ==
  502.                             PermissionChecker.PERMISSION_GRANTED ) {
  503.                             withAudioEnabled()
  504.                         }
  505.                     }
  506.                     .start(ContextCompat.getMainExecutor(this)) { recordEvent ->
  507.                         when(recordEvent) {
  508.                             is VideoRecordEvent.Start -> {
  509.                                 mainBinding.ivPauseResume.visibility = View.VISIBLE
  510.                                 mChronometer.visibility = View.VISIBLE
  511.                                 mChronometer.base = SystemClock.elapsedRealtime()
  512.                                 mChronometer.start()
  513.                             }
  514.                             is VideoRecordEvent.Pause -> {
  515.                                 mIsVideoPaused = true
  516.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_resume_icon)
  517.                                 timeWhenPaused = mChronometer.base - SystemClock.elapsedRealtime()
  518.                                 mChronometer.stop()
  519.                             }
  520.                             is VideoRecordEvent.Finalize -> {
  521.                                 if (!recordEvent.hasError()) {
  522.  
  523.                                     val savedUri = recordEvent.outputResults.outputUri
  524.  
  525.                                     if (savedUri != null) {
  526.                                         val projection = arrayOf(MediaStore.Video.Media.DATA)
  527.                                         val cursor = contentResolver.query(savedUri, projection, null, null, null)
  528.                                         cursor?.use {
  529.                                             if (it.moveToFirst()) {
  530.                                                 val filePath = it.getString(it.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))
  531.                                                 val videoFile = File(filePath)
  532.  
  533.  
  534.                                                 if(videoFile.exists()) {
  535.                                                     mVideoFileList.add(videoFile)
  536.                                                     Toast.makeText(this,  "File Saved", Toast.LENGTH_SHORT).show()
  537.                                                 }
  538.                                             }
  539.                                         }
  540.                                     }
  541.                                     if(!mIsVideoRecording){
  542.                                        // mergeVideosUsingTranscoder(mVideoFileList)
  543.                                         mergeVideos(mVideoFileList)
  544.                                     }
  545.                                 } else {
  546.                                     mRecording?.close()
  547.                                     mRecording = null
  548.                                     Log.e("MainActivity", "Video capture ends with error: ${recordEvent.error}")
  549.                                 }
  550.  
  551.                             }
  552.                             is VideoRecordEvent.Resume -> {
  553.                                 mIsVideoPaused = false
  554.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_pause_icon)
  555.                                 mChronometer.base = SystemClock.elapsedRealtime() + timeWhenPaused
  556.                                 mChronometer.start()
  557.                             }
  558.                         }
  559.                     }
  560.  
  561.             } catch (e: Exception) {
  562.                 e.printStackTrace()
  563.             }
  564.         }
  565.  
  566.     }
  567.     private fun startRecordingVideoFrontCam() {
  568.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_stop_video_icon)
  569.         mVideoCaptureFrontCam!!.let {
  570.             try {
  571.                 if (ActivityCompat.checkSelfPermission(
  572.                         this,
  573.                         Manifest.permission.CAMERA
  574.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  575.                         this,
  576.                         Manifest.permission.WRITE_EXTERNAL_STORAGE
  577.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  578.                         this,
  579.                         Manifest.permission.READ_EXTERNAL_STORAGE
  580.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  581.                         this,
  582.                         Manifest.permission.RECORD_AUDIO
  583.                     ) != PackageManager.PERMISSION_GRANTED
  584.                 ) {
  585.                     ActivityCompat.requestPermissions(
  586.                         this,
  587.                         arrayOf(
  588.                             Manifest.permission.CAMERA,
  589.                             Manifest.permission.WRITE_EXTERNAL_STORAGE,
  590.                             Manifest.permission.READ_EXTERNAL_STORAGE,
  591.                             Manifest.permission.RECORD_AUDIO
  592.                         ),
  593.                         REQUEST_CAMERA_PERMISSION
  594.                     )
  595.                     return
  596.                 }
  597.                 val contentValues = ContentValues().apply {
  598.                     put(MediaStore.MediaColumns.DISPLAY_NAME, "VID_${System.currentTimeMillis()}")
  599.                     put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
  600.                 }
  601.                 val mediaStoreOutputOptions = MediaStoreOutputOptions
  602.                     .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
  603.                     .setContentValues(contentValues)
  604.                     .build()
  605.                 mVideoCaptureFrontCam!!.targetRotation = ROTATION_0
  606.                 mRecordingFrontCam = mVideoCaptureFrontCam!!.output
  607.                     .prepareRecording(this, mediaStoreOutputOptions)
  608.                     .apply {
  609.                         // Enable Audio for recording
  610.                         if (PermissionChecker.checkSelfPermission(this@MainActivity, Manifest.permission.RECORD_AUDIO) ==
  611.                             PermissionChecker.PERMISSION_GRANTED ) {
  612.                             withAudioEnabled()
  613.                         }
  614.                     }
  615.                     .start(ContextCompat.getMainExecutor(this)) { recordEvent ->
  616.                         when(recordEvent) {
  617.                             is VideoRecordEvent.Start -> {
  618.                                 mainBinding.ivPauseResume.visibility = View.VISIBLE
  619.                                 mChronometer.visibility = View.VISIBLE
  620.                                 mChronometer.base = SystemClock.elapsedRealtime()
  621.                                 mChronometer.start()
  622.                             }
  623.                             is VideoRecordEvent.Pause -> {
  624.                                 mIsVideoPaused = true
  625.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_resume_icon)
  626.                                 timeWhenPaused = mChronometer.base - SystemClock.elapsedRealtime()
  627.                                 mChronometer.stop()
  628.                             }
  629.                             is VideoRecordEvent.Finalize -> {
  630.                                 if (!recordEvent.hasError()) {
  631.  
  632.                                     val savedUri = recordEvent.outputResults.outputUri
  633.  
  634.                                     if (savedUri != null) {
  635.                                         val projection = arrayOf(MediaStore.Video.Media.DATA)
  636.                                         val cursor = contentResolver.query(savedUri, projection, null, null, null)
  637.                                         cursor?.use {
  638.                                             if (it.moveToFirst()) {
  639.                                                 val filePath = it.getString(it.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))
  640.                                                 val videoFile = File(filePath)
  641.  
  642.                                                 if(videoFile.exists()) {
  643.                                                     mVideoFileList.add(videoFile)
  644.                                                     Toast.makeText(this,  "File Saved", Toast.LENGTH_SHORT).show()
  645.                                                 }
  646.                                             }
  647.                                         }
  648.                                     }
  649.                                     if(!mIsVideoRecording){
  650.                                         // mergeVideosUsingTranscoder(mVideoFileList)
  651.                                         mergeVideos(mVideoFileList)
  652.                                     }
  653.                                 } else {
  654.                                     mRecordingFrontCam?.close()
  655.                                     mRecording = null
  656.                                     Log.e("MainActivity", "Video capture ends with error: ${recordEvent.error}")
  657.                                 }
  658.  
  659.                             }
  660.                             is VideoRecordEvent.Resume -> {
  661.                                 mIsVideoPaused = false
  662.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_pause_icon)
  663.                                 mChronometer.base = SystemClock.elapsedRealtime() + timeWhenPaused
  664.                                 mChronometer.start()
  665.                             }
  666.                         }
  667.                     }
  668.  
  669.             } catch (e: Exception) {
  670.                 e.printStackTrace()
  671.             }
  672.         }
  673.  
  674.     }
  675.  
  676.     private fun startRecordingVideoBackCam() {
  677.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_stop_video_icon)
  678.         mVideoCaptureBackCam!!.let {
  679.             try {
  680.                 if (ActivityCompat.checkSelfPermission(
  681.                         this,
  682.                         Manifest.permission.CAMERA
  683.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  684.                         this,
  685.                         Manifest.permission.WRITE_EXTERNAL_STORAGE
  686.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  687.                         this,
  688.                         Manifest.permission.READ_EXTERNAL_STORAGE
  689.                     ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
  690.                         this,
  691.                         Manifest.permission.RECORD_AUDIO
  692.                     ) != PackageManager.PERMISSION_GRANTED
  693.                 ) {
  694.                     ActivityCompat.requestPermissions(
  695.                         this,
  696.                         arrayOf(
  697.                             Manifest.permission.CAMERA,
  698.                             Manifest.permission.WRITE_EXTERNAL_STORAGE,
  699.                             Manifest.permission.READ_EXTERNAL_STORAGE,
  700.                             Manifest.permission.RECORD_AUDIO
  701.                         ),
  702.                         REQUEST_CAMERA_PERMISSION
  703.                     )
  704.                     return
  705.                 }
  706.                 val contentValues = ContentValues().apply {
  707.                     put(MediaStore.MediaColumns.DISPLAY_NAME, "VID_${System.currentTimeMillis()}")
  708.                     put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
  709.                 }
  710.                 val mediaStoreOutputOptions = MediaStoreOutputOptions
  711.                     .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
  712.                     .setContentValues(contentValues)
  713.                     .build()
  714.                 mRecordingBackCam = mVideoCaptureBackCam!!.output
  715.                     .prepareRecording(this, mediaStoreOutputOptions)
  716.                     .apply {
  717.                         // Enable Audio for recording
  718.                         if (PermissionChecker.checkSelfPermission(this@MainActivity, Manifest.permission.RECORD_AUDIO) ==
  719.                             PermissionChecker.PERMISSION_GRANTED ) {
  720.                             withAudioEnabled()
  721.                         }
  722.                     }
  723.                     .start(ContextCompat.getMainExecutor(this)) { recordEvent ->
  724.                         when(recordEvent) {
  725.                             is VideoRecordEvent.Start -> {
  726.                                 mainBinding.ivPauseResume.visibility = View.VISIBLE
  727.                                 mChronometer.visibility = View.VISIBLE
  728.                                 mChronometer.base = SystemClock.elapsedRealtime()
  729.                                 mChronometer.start()
  730.                             }
  731.                             is VideoRecordEvent.Pause -> {
  732.                                 mIsVideoPaused = true
  733.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_resume_icon)
  734.                                 timeWhenPaused = mChronometer.base - SystemClock.elapsedRealtime()
  735.                                 mChronometer.stop()
  736.                             }
  737.                             is VideoRecordEvent.Finalize -> {
  738.                                 if (!recordEvent.hasError()) {
  739.  
  740.                                     val savedUri = recordEvent.outputResults.outputUri
  741.  
  742.                                     if (savedUri != null) {
  743.                                         val projection = arrayOf(MediaStore.Video.Media.DATA)
  744.                                         val cursor = contentResolver.query(savedUri, projection, null, null, null)
  745.                                         cursor?.use {
  746.                                             if (it.moveToFirst()) {
  747.                                                 val filePath = it.getString(it.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))
  748.                                                 val videoFile = File(filePath)
  749.  
  750.  
  751.                                                 if(videoFile.exists()) {
  752.                                                     mVideoFileList.add(videoFile)
  753.                                                     Toast.makeText(this,  "File Saved", Toast.LENGTH_SHORT).show()
  754.                                                 }
  755.                                             }
  756.                                         }
  757.                                     }
  758.                                     if(!mIsVideoRecording){
  759.                                         // mergeVideosUsingTranscoder(mVideoFileList)
  760.                                         mergeVideos(mVideoFileList)
  761.                                     }
  762.                                 } else {
  763.                                     mRecordingBackCam?.close()
  764.                                     mRecordingBackCam = null
  765.                                     Log.e("MainActivity", "Video capture ends with error: ${recordEvent.error}")
  766.                                 }
  767.  
  768.                             }
  769.                             is VideoRecordEvent.Resume -> {
  770.                                 mIsVideoPaused = false
  771.                                 mainBinding.ivPauseResume.setBackgroundResource(R.drawable.ic_pause_icon)
  772.                                 mChronometer.base = SystemClock.elapsedRealtime() + timeWhenPaused
  773.                                 mChronometer.start()
  774.                             }
  775.                         }
  776.                     }
  777.  
  778.             } catch (e: Exception) {
  779.                 e.printStackTrace()
  780.             }
  781.         }
  782.  
  783.     }
  784.  
  785.     private fun stopRecording() {
  786.         mainBinding.ivStartStop.setBackgroundResource(R.drawable.ic_start_video_icon)
  787.         mRecording!!.stop()
  788.         mChronometer.stop()
  789.         mChronometer.visibility = View.GONE
  790.         if(!mIsVideoRecording) {
  791.             mainBinding.ivPauseResume.visibility = View.GONE
  792.             startBackCamera()
  793.         }
  794.     }
  795.  
  796.  
  797.     private fun mergeVideos(videoFiles: List<File>) {
  798.         try {
  799.             progressDialogue.setMessage("Merging Videos..")
  800.             progressDialogue.show()
  801.             val movieList = mutableListOf<Movie>()
  802.             for (videoFile  in videoFiles) {
  803.                 val movie = MovieCreator.build(videoFile.absolutePath)
  804.                 movieList.add(movie)
  805.             }
  806.             val videoTracks = mutableListOf<Track>()
  807.             val audioTracks = mutableListOf<Track>()
  808.  
  809.             for (movie in movieList) {
  810.                 for (track in movie.tracks) {
  811.                     if (track.handler == "vide") {
  812.                         videoTracks.add(track)
  813.                     }
  814.                     if (track.handler == "soun") {
  815.                         audioTracks.add(track)
  816.                     }
  817.                 }
  818.             }
  819.  
  820.             val mergedMovie = Movie()
  821.             if (videoTracks.size > 0) {
  822.                 mergedMovie.addTrack(AppendTrack(*videoTracks.toTypedArray()))
  823.             }
  824.  
  825.             if (audioTracks.size > 0) {
  826.                 mergedMovie.addTrack(AppendTrack(*audioTracks.toTypedArray()))
  827.             }
  828.  
  829.             val container = DefaultMp4Builder().build(mergedMovie)
  830.             val fileChannel = FileOutputStream(outputFile).channel
  831.             container.writeContainer(fileChannel)
  832.             fileChannel.close()
  833.             progressDialogue.cancel()
  834.  
  835.             Toast.makeText(this, "Videos merged successfully", Toast.LENGTH_SHORT).show()
  836.  
  837.         } catch (e : Exception) {
  838.             Log.e(TAG, e.message.toString())
  839.         }
  840.  
  841.     }
  842.  
  843.     /**
  844.      * this method used for merging multiple videos using Transcoder library
  845.      * @param videoFilesList
  846.      */
  847.     private fun mergeVideosUsingTranscoder(videoFiles: List<File>) {
  848.         progressDialogue.setMessage("Merging Videos..")
  849.         progressDialogue.show()
  850.         val builder: TranscoderOptions.Builder =
  851.             Transcoder.into(rotatedFile.absolutePath)
  852.         for(videoFile in videoFiles) {
  853.             builder.addDataSource(videoFile.absolutePath)
  854.         }
  855.  
  856.         // use DefaultVideoStrategy.exact(2560, 1440).build()  to restore 78% size of the video
  857.         //  use DefaultVideoStrategy.exact(mScreenSize.height, mScreenSize.width).build()  to restore 50% size of the video
  858.         val strategy: DefaultVideoStrategy = DefaultVideoStrategy.exact(mScreenSize.height, mScreenSize.width).build()
  859.         mTranscodeFuture = builder
  860.             .setAudioTrackStrategy(DefaultAudioStrategy.builder().build())
  861.             .setVideoTrackStrategy(strategy)
  862.             .setVideoRotation(0)
  863.             .setListener(object : TranscoderListener{
  864.  
  865.                 override fun onTranscodeProgress(progress: Double) {}
  866.  
  867.                 override fun onTranscodeCompleted(successCode: Int) {
  868.                     Toast.makeText(this@MainActivity, "Video Merged Successfully", Toast.LENGTH_SHORT).show()
  869.                 }
  870.  
  871.                 override fun onTranscodeCanceled() {
  872.                     Toast.makeText(this@MainActivity, "Video rotation cancelled", Toast.LENGTH_SHORT).show()
  873.                 }
  874.  
  875.                 override fun onTranscodeFailed(exception: Throwable) {
  876.                     Toast.makeText(this@MainActivity, exception.message, Toast.LENGTH_SHORT).show()
  877.                 }
  878.  
  879.             })
  880.             .setValidator(object : DefaultValidator() {
  881.                 override fun validate(videoStatus: TrackStatus, audioStatus: TrackStatus): Boolean {
  882.                     //  mIsAudioOnly = !videoStatus.isTranscoding
  883.                     return super.validate(videoStatus, audioStatus)
  884.                 }
  885.  
  886.             }).transcode()
  887.         progressDialogue.cancel()
  888.     }
  889.  
  890.     /**
  891.      * this method will check camera and other required permission to run the app
  892.      */
  893.     private  fun checkPermissions() {
  894.         if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
  895.             && ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
  896.             && ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
  897.             && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
  898.         ) {
  899.          mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
  900.          //startCamera()
  901.             startBackCamera()
  902.  
  903.         } else {
  904.             if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE )) {
  905.                 Toast.makeText(this, "app needs permission to be able to save videos", Toast.LENGTH_SHORT)
  906.                     .show()
  907.             }
  908.             requestPermissions(
  909.                 arrayOf(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO,Manifest.permission.READ_EXTERNAL_STORAGE),
  910.                 REQUEST_CAMERA_PERMISSION
  911.             )
  912.         }
  913.  
  914.     }
  915.  
  916.     /**
  917.      * this method receives the status of the permissions granted
  918.      * @param1 requestCode
  919.      * @param2: permissions
  920.      * @param3: grantResults
  921.      */
  922.     override fun onRequestPermissionsResult(
  923.         requestCode: Int,
  924.         permissions: Array<String?>,
  925.         grantResults: IntArray,
  926.     ) {
  927.         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
  928.         if (requestCode == REQUEST_CAMERA_PERMISSION) {
  929.             for(results in grantResults) {
  930.                 if (results == PackageManager.PERMISSION_DENIED) {
  931.                     // close the app
  932.                     Toast.makeText(
  933.                         this@MainActivity,
  934.                         "Sorry!!!, you can't use this app without granting permission",
  935.                         Toast.LENGTH_LONG
  936.                     ).show()
  937.                     finish()
  938.                 }
  939.             }
  940.         }
  941.  
  942.     }
  943.  
  944.     override fun onPause() {
  945.         super.onPause()
  946.         Log.e(TAG, "onPause()")
  947.         stopBackgroundThread()
  948.  
  949.     }
  950.  
  951.     /**
  952.      * this will start the background thread to run the processes
  953.      */
  954.     private fun startBackgroundThread() {
  955.         mBackgroundThreadHandler = HandlerThread("Camera Background")
  956.         mBackgroundThreadHandler!!.start()
  957.         mBackgroundHandler = Handler(mBackgroundThreadHandler!!.looper)
  958.     }
  959.  
  960.     /**
  961.      * this will stop the background thread if all the background processes are executed
  962.      */
  963.     private fun stopBackgroundThread() {
  964.         mBackgroundThreadHandler!!.quitSafely()
  965.         try {
  966.             mBackgroundThreadHandler!!.join()
  967.             mBackgroundThreadHandler = null
  968.             mBackgroundHandler = null
  969.         } catch (e: InterruptedException) {
  970.             e.printStackTrace()
  971.         }
  972.     }
  973.  
  974.     override fun onStop() {
  975.         super.onStop()
  976.         Log.e(TAG, "onStop()")
  977.  
  978.     }
  979.  
  980.     override fun onRestart() {
  981.         super.onRestart()
  982.         Log.e(TAG, "onRestart()")
  983.  
  984.     }
  985.  
  986.     override fun onResume() {
  987.         super.onResume()
  988.         Log.e(TAG, "onResume()")
  989.         startBackgroundThread()
  990.  
  991.         if(mIsVideoRecording) {
  992.             mRecording!!.resume()
  993.             mChronometer.start()
  994.         }
  995.     }
  996.     override fun onDestroy() {
  997.         super.onDestroy()
  998.         if(mIsVideoRecording) {
  999.             mRecording!!.stop()
  1000.             mChronometer.stop()
  1001.         }
  1002.     }
  1003.     companion object {
  1004.         const val TAG = "MainActivity"
  1005.         const val REQUEST_CAMERA_PERMISSION: Int = 0
  1006.         const val STORAGE_PERMISSION_CODE: Int = 1
  1007.         const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
  1008.     }
  1009. }
  1010.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement