videokit-ai / videokit

Low-code, cross-platform media SDK for Unity Engine. Register at https://videokit.ai
https://videokit.ai
Apache License 2.0
107 stars 14 forks source link

After calling MediaAsset.FromCameraRoll on Android, if you cancel it, a crash occurs. #146

Closed HyundongHwang closed 2 weeks ago

HyundongHwang commented 2 months ago

@olokobayusuf

Hello. I am a developer who is making a motion analysis app using CamDevice and MediaAsset of VideoKit. As a paid user of VideoKit, I have been using it for a few years and I love VideoKit. The crash case is simple. When I cancel after calling MediaAsset.FromCameraRoll, I expect null, but in reality, the app crashes on Android.

VideoKit : 0.0.22 Android : all Unity : 22.3.37f1 Host : macOS 14.6.1, Sillicon

my code


var mediaAsset = await MediaAsset.FromCameraRoll(MediaAsset.MediaType.Video);

if (mediaAsset == null) { LogSloth.d($"MEDIAASSET_IS_NULL_USER_CANCEL"); return null; }

LogSloth.d($"mediaAsset.path:{mediaAsset.path}"); LogSloth.d($"mediaAsset.type:{mediaAsset.type}"); LogSloth.d($"mediaAsset.width:{mediaAsset.width}"); LogSloth.d($"mediaAsset.height:{mediaAsset.height}"); LogSloth.d($"mediaAsset.frameRate:{mediaAsset.frameRate}"); LogSloth.d($"mediaAsset.channelCount:{mediaAsset.channelCount}"); LogSloth.d($"mediaAsset.sampleRate:{mediaAsset.sampleRate}");


> err log

2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime FATAL EXCEPTION: main 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime Process: app.streamstudio.stream, PID: 26394 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=3, result=0, data=null} to activity {app.streamstudio.stream/ai.videokit.videokit.Asset}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.net.Uri android.content.Intent.getData()' on a null object reference 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.ActivityThread.deliverResults(ActivityThread.java:6063) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.ActivityThread.handleSendResult(ActivityThread.java:6102) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:67) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2685) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.os.Handler.dispatchMessage(Handler.java:106) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.os.Looper.loopOnce(Looper.java:230) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.os.Looper.loop(Looper.java:319) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.ActivityThread.main(ActivityThread.java:8919) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at java.lang.reflect.Method.invoke(Native Method) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.net.Uri android.content.Intent.getData()' on a null object reference 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at ai.videokit.videokit.Asset.onActivityResult(Asset.java:174) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.Activity.dispatchActivityResult(Activity.java:9362) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime at android.app.ActivityThread.deliverResults(ActivityThread.java:6056) 2024/09/19 14:09:56.004 26394 26394 Error AndroidRuntime ... 13 more



> crash demo
https://github.com/user-attachments/assets/c32d7436-52f7-402b-b0a8-89ce195d430d
HyundongHwang commented 2 months ago

@olokobayusuf

Hello. Somehow, I solved this issue in my own way, and I'm sharing the solution. I hope it will be helpful for you as you improve. In fact, not only Android, but also iOS has the same issue of CameraRoll not returning when canceled. However, iOS does not crash even when canceled in CameraRoll, so there was no big problem in using it. However, I hope iOS MediaAsset.FromCameraRoll is improved to return null when canceled. The solution I implemented is only for Android. I think it will be almost the same if you create an aar library project with the same structure as VideoKit and use it as a communication between UnityPlayer Activity.

https://gist.github.com/HyundongHwang/dda397ecc3747c361cadb1cafdf3f845

After implementing it, I found that there are some parts that are slightly different from VideoKit's CameraRoll function.




Q1.

As shown below, the UX of CameraRoll(=Gallery) is different. In my solution, the system UI is provided by accessing MediaStore with the basic ACTION_PICK, Video condition, but VideoKit has a different appearance. Which one is better? It may be a matter of taste. I would appreciate it if you could explain the implementation differences of VideoKit.

my solution's UI

VideoKit's UI




Q2.

This seems to be a more important difference than 1. In my solution, I get the path of the DCIM folder by accessing CameraRoll and copy the file to the persistancePath subfolder. However, in VideoKit, the file of CameraRoll is moved to the file in the tmp folder. When I measured the file size, it is the same as the file in the DCIM path, so I think it is a simple copy. Is that right? In that case, since I already copied it from CameraRoll, I should move the file in the tmp folder and use it, right? In addition, it only appears as a simple file copy on Android, but is there a function in iOS that provides a more general codec through media transcoding, etc. in addition to the simple file copy function? I'm asking because I think it worked like this on iOS.

CameraRoll's origin video file path

/storage/emulated/0/DCIM/Camera/20240915_161940.mp4

VideoKit's video file path

/data/user/0/app.streamstudio.stream/cache/videokit-3273305464942776732.mp4
olokobayusuf commented 2 weeks ago

Hey @HyundongHwang this issue has been fixed in 0.0.22.

As shown below, the UX of CameraRoll(=Gallery) is different. In my solution, the system UI is provided by accessing MediaStore with the basic ACTION_PICK, Video condition, but VideoKit has a different appearance. Which one is better? It may be a matter of taste. I would appreciate it if you could explain the implementation differences of VideoKit.

I don't think there's much of a difference honestly.

When I measured the file size, it is the same as the file in the DCIM path, so I think it is a simple copy. Is that right?

Yup, we copy the file into the app's temporary files in case we need it for downstream operations. We need path we can always refer to.