I am trying to save an mp4 file after trimming with the help of its url. I am using https://github.com/a914-gowtham/android-video-trimmer for trimming my video and the updated uri after trimming is fine as I trying playing it in exoplayer
and it works, the problem happens when I try to save it to external storage. I can see my file being saved to external storage as well but when I try to play it after saving via third party apps like vlc, mxplayer etc they throw error. So below is my code to save video after getting the uri
class MainActivity : AppCompatActivity() {
private lateinit var uri: Uri
private lateinit var playerView: PlayerView
private val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS =
mutableListOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE).toTypedArray()
private var player: ExoPlayer? = null
private var playWhenReady = true
private var currentItem = 0
private var playbackPosition = 0L
private fun setUpPlayer(url: String){
player = ExoPlayer.Builder(this)
.build()
.also { exoPlayer ->
playerView.player = exoPlayer
val mediaItem = MediaItem.fromUri(url)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.playWhenReady = playWhenReady
exoPlayer.seekTo(currentItem, playbackPosition)
exoPlayer.prepare()
}
}
private val startForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK &&
result.data != null
) {
uri = Uri.parse(TrimVideo.getTrimmedVideoPath(result.data))
Log.i("TAG", "Trimmed path:: $uri")
setUpPlayer(uri.toString())
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
lifecycleScope.launch {
val uri = saveVideoToExternalStorage()
Log.i("helloooo", "3487438734784387")
Toast.makeText(this@MainActivity, "Uri is $uri", Toast.LENGTH_LONG).show()
}
} else {
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
}
} else
Log.i("TAG", "Trimmed path11111:: Errorr")
}
private val singleVideoPickerLauncher =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { videoUri ->
TrimVideo.activity(videoUri.toString())
.setHideSeekBar(true)
.start(this, startForResult)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playerView = findViewById(R.id.playerview)
findViewById<Button>(R.id.btn).setOnClickListener {
singleVideoPickerLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
lifecycleScope.launch {
Log.i("helloooo", "000000")
val uri = saveVideoToExternalStorage()
Log.i("helloooo", "1111111")
Toast.makeText(this@MainActivity, "Uri is $uri", Toast.LENGTH_LONG).show()
}
} else {
Log.i("TAG", "Permission denied")
}
}
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
this, it
) == PackageManager.PERMISSION_GRANTED
}
private suspend fun saveVideoToExternalStorage(): Uri? {
return withContext(Dispatchers.Default) {
Log.i("helloooo", "1")
val videoCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
val file = uri.path?.let { File(it) }
Log.i("helloooo", "2")
val contentValues = ContentValues().apply {
Log.i("helloooo", "3")
put(MediaStore.Video.Media.DATA, uri.path)
put(MediaStore.MediaColumns.DISPLAY_NAME, "${System.currentTimeMillis()}${file?.name}.mp4")
put(MediaStore.Video.Media.TITLE, "${System.currentTimeMillis()}${file?.name}.mp4")
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
}
Log.i("helloooo", "4")
val uri =
contentResolver.insert(videoCollection, contentValues)
Log.i("helloooo", "5 ${uri}")
uri
}
}
}
You can use this method to save the video in device's having android 10 or above.
Just pass the file
and display name
.
@RequiresApi(api = Build.VERSION_CODES.Q)
@Throws(FileNotFoundException::class)
fun Context.addVideoToGalleryAPI29(
file: File,
displayName: String,
exportListener: VideoExportListener
) {
val savePath = (Environment.DIRECTORY_DCIM
+ File.separator + file.name)
val cv = ContentValues()
cv.put(
MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM
+ File.separator + File(savePath).name
)
cv.put(MediaStore.Video.Media.TITLE, "${displayName}.mp4")
cv.put(MediaStore.Video.Media.DISPLAY_NAME, "${displayName}.mp4")
cv.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
cv.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
cv.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis())
cv.put(MediaStore.Video.Media.IS_PENDING, 1)
val resolver = contentResolver
val collection =
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uriSavedVideo = resolver.insert(collection, cv)
val pfd = contentResolver.openFileDescriptor(uriSavedVideo!!, "w")
if (pfd != null) {
try {
val out =
FileOutputStream(pfd.fileDescriptor)
val inputStream = FileInputStream(file)
val buf = ByteArray(8192)
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
out.close()
inputStream.close()
pfd.close()
cv.clear()
cv.put(MediaStore.Video.Media.IS_PENDING, 0)
} catch (e: Exception) {
e.printStackTrace()
}
contentResolver.update(uriSavedVideo, cv, null, null)
}
exportListener.onVideoExported()
}