I am trying to convert a method from java to kotlin and replace AsyncTask
with coroutines, but I do not know how to return value from coroutines
This is my method
override fun getCompressedVideo(context:Context ,video: Uri) {
GlobalScope.launch(Dispatchers.Main) {
val inputFile = video.getRealPathFromVideoUri(context)
val loadJNI: LoadJNI = LoadJNI();
try {
val workFolder: String = context.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
);
val complexCommand = arrayOf (
"ffmpeg", "-y"
, "-i", inputFile
, "-strict", "experimental"
, "-s", "320x240"
, "-r", "25"
, "-aspect", "4:3"
, "-ab", "48000"
, "-ac", "2"
, "-vcodec", "mpeg4"
, "-movflags", "+faststart"
, "-ar", "22050"
, "-b", "2097k"
, outputFile);
loadJNI.run(complexCommand, workFolder, context);
return outputFile
} catch (th: Throwable) {
return@launch
}
}
}
the line of return outputFile makes compilation error, can anyone please help, it is my first time to use coroutines
EDIT
here is the method after using suspend, but now I do not know how I can return value if any problem happen
override suspend fun getCompressedVideo(context: Context, video: Uri) {
val outputFile = withContext(Dispatchers.IO) {
val inputFile = video.getRealPathFromVideoUri(context)
val loadJNI: LoadJNI = LoadJNI();
try {
val workFolder: String = context.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
);
val complexCommand = arrayOf(
"ffmpeg", "-y"
, "-i", inputFile
, "-strict", "experimental"
, "-s", "320x240"
, "-r", "25"
, "-aspect", "4:3"
, "-ab", "48000"
, "-ac", "2"
, "-vcodec", "mpeg4"
, "-movflags", "+faststart"
, "-ar", "22050"
, "-b", "2097k"
, outputFile
);
loadJNI.run(complexCommand, workFolder, context)
}catch (th: Throwable) {
}
}
}
EDIT 2
you mean like this
override suspend fun getCompressedVideo(context: Context, video: Uri) : String {
try {
val retValue = withContext(Dispatchers.IO) {
val inputFile = video.getRealPathFromVideoUri(context)
val loadJNI: LoadJNI = LoadJNI()
val workFolder: String = context.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
)
val complexCommand = arrayOf(
"ffmpeg", "-y"
, "-i", inputFile
, "-strict", "experimental"
, "-s", "320x240"
, "-r", "25"
, "-aspect", "4:3"
, "-ab", "48000"
, "-ac", "2"
, "-vcodec", "mpeg4"
, "-movflags", "+faststart"
, "-ar", "22050"
, "-b", "2097k"
, outputFile
)
loadJNI.run(complexCommand, workFolder, context)
}
return retValue.toString()
} catch (th: Throwable) {
return ""
}
}
and call it like
GlobalScope.launch {
val retValue = ffmpegFacade.getCompressedVideo(this@TestActivity, Uri.parse(""))
}
If you expect this function
override fun getCompressedVideo(context: Context, video: Uri)
to return when the compression is already done, this is not how it can work. Your code launches a concurrent task that will complete at some arbitrary time after your getCompressedVideo
has returned.
Instead, I think you should approach it as follows:
override suspend fun getCompressedVideo(
context: Context, video: Uri
): String? = withContext(Dispatchers.IO) {
try {
val inputFile = video.getRealPathFromVideoUri(context)
val loadJNI = LoadJNI()
val workFolder: String = context.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT,
System.currentTimeMillis())
)
val complexCommand = arrayOf("-i", inputFile, "other-params")
loadJNI.run(complexCommand, workFolder, context);
outputFile
} catch (t: Throwable) {
null
}
}
As you can see, this means changing the declaration of getCompressedVideo
to a suspend fun
. You can't call it directly from an Android callback. So, at the call site, write
this.launch {
val videoFile = ffmpegfacade.getCompressedVideo(context, Uri.parse("example.org/video"))
// continue processing on the UI thread using videoFile
}
Here take note that we call launch
with this
as the receiver. The receiver of launch
must be a CoroutineScope
and you should implement it in your MainActivity
or whatever is the context you're calling it from. See structured concurrency for an explanation.
One of the possible ways to solve it is to use GlobalScope.async
builder:
fun getCompressedVideo() = GlobalScope.async {
val outputFile: String = ""
// ... compress video
outputFile
}
// Calling getCompressedVideo() from outside
fun compressVideoAsync() {
GlobalScope.launch(Dispatchers.Main) {
val compression = getCompressedVideo()
val outputFile = compression.await() // wait for result of compression operation without blocking the main thread
// outputFile is ready to use
}
}
You can specify a Kotlin function return type like so:
override fun getCompressedVideo(context: Context, video: Uri): String {
However you still can't because it's asynchronous, a function is synchronous.
To return from the method it would have to wait until it is complete, which goes against the whole purpose of doing it asynchronously.
You can instead use a high-order function to specify what to do with the data once the async task has been completed.
override fun getCompressedVideo(context:Context ,video: Uri, action: (String?) -> Unit) {
GlobalScope.launch(Dispatchers.Main) {
val inputFile = video.getRealPathFromVideoUri(context)
val loadJNI: LoadJNI = LoadJNI();
try {
val workFolder: String = context.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
);
val complexCommand = arrayOf (
"ffmpeg", "-y"
, "-i", inputFile
, "-strict", "experimental"
, "-s", "320x240"
, "-r", "25"
, "-aspect", "4:3"
, "-ab", "48000"
, "-ac", "2"
, "-vcodec", "mpeg4"
, "-movflags", "+faststart"
, "-ar", "22050"
, "-b", "2097k"
, outputFile);
loadJNI.run(complexCommand, workFolder, context);
action(outputFile)
} catch (th: Throwable) {
action(null)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With