[Bug]: Android camera 'back' is used when camera prop is 'front'
Version
v2.0.0
Which operating systems have you used?
- [X] Android
- [X] iOS
Environment that reproduces the issue
Google Pixel 7
Is it reproducible in the example application?
Not tested
RTMP Server
NA, happens before streaming
Reproduction steps
- pass prop camera={'front'}
<ApiVideoLiveStreamView
key={'ApiVideoLiveStreamView'}
style={styles.liveStreamView}
ref={ref}
camera={'front'}
enablePinchedZoom={true}
video={{
fps: 30,
resolution: { width: 1080, height: 1920 },
bitrate: 2 * 1024 * 1024, // # 2 Mbps
gopDuration: 1 // 1 second
}}
audio={{
bitrate: 128000,
sampleRate: 44100,
isStereo: true
}}
isMuted={false}
onConnectionSuccess={onConnectionSuccess}
onConnectionFailed={onConnectionFailed}
onDisconnect={onDisconnect}
/>
Expected result
'front' camera is used
Actual result
'back' camera is used.
Additional context
The bug report form did not have that version v2.0.0. !!! Works correctly on iOS.
Workaround:
- Set camera prop to undefined initially.
- Within a setTimeout (500ms - 1sec) trigger state changes to set the camera prop to 'front'.
Relevant logs output
No response
Hi,
It is a random issue. I made changes in the Android dependency. It will be fixed in 2.0.1.
Hi,
Could you test on main branch?
@ThibaultBee , I tried to test main on Android, and it seems like the correct camera is initially used. I could not test it much as the library crashes for me with:
E FATAL EXCEPTION: pool-40-thread-1
Process: com.**********, PID: 2574
java.lang.IllegalStateException: LifecycleScope is not available
at io.github.thibaultbee.streampack.views.PreviewView.startPreviewIfReady(PreviewView.kt:272)
at io.github.thibaultbee.streampack.views.PreviewView.startPreview(PreviewView.kt:245)
at video.api.livestream.views.ApiVideoView.startPreview$livestream_release(ApiVideoView.kt:62)
at video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:373)
at video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:364)
at video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:75)
at video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:72)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:38)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:35)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager$requestPermissions$1.onAllGranted(PermissionsManager.kt:51)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:76)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:49)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.requestPermissions$lambda$1(SerialPermissionsManager.kt:35)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.$r8$lambda$dzig6asy22NzT_1QrKBawhgieas(Unknown Source:0)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$$ExternalSyntheticLambda1.run(Unknown Source:10)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
The scenario it crashes is either (re) Mount or unmount of ApiVideoLiveStreamView. In my flow i have multiple screens displaying ApiVideoLiveStreamView:
- Screen with camera selector + next button.
- Screen with stream configuration. + next button
- Actual live streaming Screen, where users can stream by pressing a "record" button.
Screens 1 & 2 just show the camera output. 3 does the streaming.
The crash happens when moving between screen 1 & 2 above. This worked ok with2.0.0+iOS/Android and main+iOS
@giantslogik Could you provide a sample where I can test directly?
I'll try and modify the example in the repo. May take me a while to get to it.
The exception comes from here: https://github.com/ThibaultBee/StreamPack/blob/079ff7da527d78ed7e60daeeb7ff8bc9015edd6e/core/src/main/java/io/github/thibaultbee/streampack/views/PreviewView.kt#L272 The question is : Why does it happen?
Instead of throwing an exception here, we can just log an error. But that might hide other issues.
This worked without an Exception with api.video-reactnative-live-stream 2.0.0 Android. I'm trying to see what has changed in streampack and api.video-reactnative-live-stream since then. Do you have tags i can diff by. Having some trouble determining what version of streampack is used by api.video-reactnative-live-stream 2.0.0
I think that this commit , introduced the issue:
https://github.com/ThibaultBee/StreamPack/commit/27d3a9e8c8563c17139bc14b2e9397dd8df777b8#diff-0e5bf720cab5ddf6afd6d26f19aa08330afc6d2615f80e6609c572eb0aa45cb1
I know but I want to be sure there isn't any side effect in the patch and if this would solve your issue. Have you made progress on a standalone sample where I can reproduce this issue?
I haven't had the time to write a sample. I will pull main again , set shouldFailSilently = true and see if that works. (i.e. try the "Instead of throwing an exception here, we can just log an error." )
@giantslogik another way of dealing with this is to catch the exception when calling startPreview. I could create a branch with this fix. Could you test it?
I also have this problem on my application. Only on android.
I'm currently on the main branch because I had to fix another error I had before, which can be found on this issue. #82
(java.lang.NullPointerException: Attempt to invoke virtual method 'int android.media.audiofx.AcousticEchoCanceler.setEnabled(boolean)' on a null object reference)
Now, I got the "LifecycleScope" when I start a livestream, here is my log error:
Your app just crashed. See the error below.
java.lang.IllegalStateException: LifecycleScope is not available
io.github.thibaultbee.streampack.views.PreviewView.startPreviewIfReady(PreviewView.kt:272)
io.github.thibaultbee.streampack.views.PreviewView.startPreview(PreviewView.kt:245)
video.api.livestream.views.ApiVideoView.startPreview$livestream_release(ApiVideoView.kt:62)
video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:373)
video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:364)
video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:75)
video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:72)
video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:38)
video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:35)
video.api.reactnative.livestream.utils.permissions.PermissionsManager$requestPermissions$1.onAllGranted(PermissionsManager.kt:51)
video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:76)
video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:49)
video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.requestPermissions$lambda$1(SerialPermissionsManager.kt:35)
video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.$r8$lambda$dzig6asy22NzT_1QrKBawhgieas(Unknown Source:0)
video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$$ExternalSyntheticLambda1.run(Unknown Source:10)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
java.lang.Thread.run(Thread.java:1012)
Is the branch with the fix available? I could test it. Thank you !
@giantslogik @safee-cases Could you test https://github.com/apivideo/api.video-reactnative-live-stream/pull/90? I am trying to find the source of the crash. If you find anything relevant so I can reproduce it, please tell us :)
@ThibaultBee I tested with
"@api.video/react-native-livestream": "https://github.com/apivideo/api.video-reactnative-live-stream.git#bugfix/missing_lifecycle_scope",
Still crashes.
2024-09-05 15:35:18.916 14990-15818 AndroidRuntime *************** E FATAL EXCEPTION: pool-35-thread-1
Process: *************, PID: 14990
java.lang.IllegalStateException: LifecycleScope is not available
at io.github.thibaultbee.streampack.views.PreviewView.startPreviewIfReady(PreviewView.kt:272)
at io.github.thibaultbee.streampack.views.PreviewView.startPreview(PreviewView.kt:245)
at video.api.livestream.views.ApiVideoView.startPreview$livestream_release(ApiVideoView.kt:62)
at video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:373)
at video.api.livestream.ApiVideoLiveStream$startPreview$1.invoke(ApiVideoLiveStream.kt:364)
at video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:75)
at video.api.reactnative.livestream.LiveStreamView$1$1.invoke(LiveStreamView.kt:72)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:38)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$requestPermissions$request$1$1.invoke(SerialPermissionsManager.kt:35)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager$requestPermissions$1.onAllGranted(PermissionsManager.kt:51)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:76)
at video.api.reactnative.livestream.utils.permissions.PermissionsManager.requestPermissions(PermissionsManager.kt:49)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.requestPermissions$lambda$1(SerialPermissionsManager.kt:35)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager.$r8$lambda$dzig6asy22NzT_1QrKBawhgieas(Unknown Source:0)
at video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager$$ExternalSyntheticLambda1.run(Unknown Source:10)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
This is a race condition between unmounting previous LiveStreamView and mounting another LiveStreamView. (Since i display it on two consecutive screens.) I was able to avoid the crash by using a wrapper component which implements the following logic. (Don't know if its works consistently, and it causes flicker and flashing of the screen.)
- 1st render: Wrapper renders null and triggers an immediate re-render.
- 2nd render: Wrapper renders ApiVideoLiveStreamView with undefined camera and triggers a re-render after 500 ms
- 3rd render: Wrapper renders ApiVideoLiveStreamView with preferred camera
Part of this logic is my workaround for original wrong camera being used bug, as well handling issues when using other camera libs (react-native-vision-camera) which MAY have camera ownership before i display ApiVideoLiveStreamView.
enum LiveVideoStreamInitLifecycle {
NOT_INITALIZED,
INITIALIZING,
INITIALIZED
}
export type LiveVideoStreamProps = {
camera: 'front' | 'back' | undefined;
/* Mitigate bugs by a delayed initialize.*/
delayInit?: boolean;
....
};
const LiveVideoStream = forwardRef<LiveVideoStreamMethods, LiveVideoStreamProps>((props, extRef) => {
let {
camera,
delayInit = true,
....
} = props;
const [initialized, setInitialized] = useState<LiveVideoStreamInitLifecycle>(
delayInit ? LiveVideoStreamInitLifecycle.NOT_INITALIZED : LiveVideoStreamInitLifecycle.INITIALIZING
);
useEffect(() => {
setTimeout(
() => {
setInitialized(
initialized === LiveVideoStreamInitLifecycle.NOT_INITALIZED
? LiveVideoStreamInitLifecycle.INITIALIZING
: LiveVideoStreamInitLifecycle.INITIALIZED
);
},
initialized === LiveVideoStreamInitLifecycle.NOT_INITALIZED ? 0 : 500
);
}, [initialized]);
camera = initialized === LiveVideoStreamInitLifecycle.INITIALIZED ? camera : undefined;
return (
<>
{initialized ? (
<ApiVideoLiveStreamView
key={'ApiVideoLiveStreamView'}
style={styles.streamstyle}
ref={ref}
camera={camera}
enablePinchedZoom={true}
video={{
fps: 30,
resolution: { width: 1080, height: 1920 },
bitrate: 2 * 1024 * 1024, // # 2 Mbps
gopDuration: 1 // 1 second
}}
audio={{
bitrate: 128000,
sampleRate: 44100,
isStereo: true
}}
isMuted={false}
.....
/>
) : null}
</>
);
});
I still don't understand why it happens... But anyway, as we don't have a way to reproduce it and their is a workaround, I am going to release the 2.0.1 without a fix and we will open an issue with this issue.
Could you test the original issue on 2.0.1?
Stalled issue. Closing.