-
Notifications
You must be signed in to change notification settings - Fork 737
VIDEO-5734/blur background feature #550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
77e0d97
51eb265
3cbf1ef
c0855e7
ab13b81
32335c6
438cb04
91ba438
066fd43
ca56a40
eb67e4a
62e5e8c
1ab582f
976e954
3d4a940
94f218b
ad52f8c
8adde08
30a9f59
4faab69
ceae6d0
5c5a8b8
f0a2997
eb0a31a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ export interface IVideoContext { | |
| setIsBackgroundSelectionOpen: (value: boolean) => void; | ||
| backgroundSettings: BackgroundSettings; | ||
| setBackgroundSettings: (settings: BackgroundSettings) => void; | ||
| removeProcessor: () => void; | ||
| } | ||
|
|
||
| export const VideoContext = createContext<IVideoContext>(null!); | ||
|
|
@@ -79,9 +80,12 @@ export function VideoProvider({ options, children, onError = () => {} }: VideoPr | |
| ); | ||
| useHandleTrackPublicationFailed(room, onError); | ||
| useRestartAudioTrackOnDeviceChange(localTracks); | ||
| console.log('tracks: '); | ||
| console.log(localTracks); | ||
|
||
|
|
||
| const [isBackgroundSelectionOpen, setIsBackgroundSelectionOpen] = useState(false); | ||
| const [backgroundSettings, setBackgroundSettings] = useBackgroundSettings(); | ||
| const videoTrack = localTracks.find(track => track.name.includes('camera')) as LocalVideoTrack | undefined; | ||
| const [backgroundSettings, setBackgroundSettings, removeProcessor] = useBackgroundSettings(videoTrack); | ||
|
|
||
| return ( | ||
| <VideoContext.Provider | ||
|
|
@@ -102,6 +106,7 @@ export function VideoProvider({ options, children, onError = () => {} }: VideoPr | |
| setIsBackgroundSelectionOpen, | ||
| backgroundSettings, | ||
| setBackgroundSettings, | ||
| removeProcessor, | ||
| }} | ||
| > | ||
| <SelectedParticipantProvider room={room}>{children}</SelectedParticipantProvider> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { act, renderHook } from '@testing-library/react-hooks'; | ||
| import useBackgroundSettings, { BackgroundSettings } from './useBackgroundSettings'; | ||
| const mockLoadModel = jest.fn(); | ||
|
|
||
| jest.mock('@twilio/video-processors', () => { | ||
| return { | ||
| GaussianBlurBackgroundProcessor: jest.fn().mockImplementation(() => { | ||
| return { | ||
| loadModel: mockLoadModel, | ||
| }; | ||
| }), | ||
| }; | ||
| }); | ||
|
|
||
| const defaultSettings = { | ||
| type: 'none', | ||
| index: 0, | ||
| }; | ||
|
|
||
| const blurSettings = { | ||
| type: 'blur', | ||
| }; | ||
|
|
||
| describe('The useBackgroundSettings hook ', () => { | ||
|
||
| let mockVideoTrack: any; | ||
| beforeEach(() => { | ||
| mockVideoTrack = { | ||
| kind: 'video', | ||
| processor: '', | ||
| addProcessor: jest.fn(), | ||
| removeProcessor: jest.fn(), | ||
| }; | ||
| }); | ||
|
|
||
| it('should return the backgroundsettings and update function.', () => { | ||
| const { result } = renderHook(() => useBackgroundSettings(mockVideoTrack as any)); | ||
| expect(result.current).toEqual([defaultSettings, expect.any(Function), expect.any(Function)]); | ||
| }); | ||
|
|
||
| it('should set the background settings correctly and set the video processor when "blur" is selected', async () => { | ||
| const { result } = renderHook(() => useBackgroundSettings(mockVideoTrack as any)); | ||
| await act(async () => { | ||
| result.current[1](blurSettings as BackgroundSettings); | ||
|
||
| }); | ||
| expect(result.current[0].type).toEqual('blur'); | ||
| expect(mockVideoTrack.addProcessor).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should set the background settings correctly and remove the video processor when "none" is selected', async () => { | ||
| const { result } = renderHook(() => useBackgroundSettings(mockVideoTrack as any)); | ||
| // set video processor to non-null value | ||
| await act(async () => { | ||
| result.current[1](blurSettings as BackgroundSettings); | ||
| }); | ||
| // set video processor to none | ||
| await act(async () => { | ||
| result.current[1](defaultSettings as BackgroundSettings); | ||
| }); | ||
| expect(mockVideoTrack.addProcessor).toHaveBeenCalled(); | ||
| expect(result.current[0].type).toEqual('none'); | ||
| }); | ||
|
|
||
| it('should set the background settings correctly and set the video processor when "image" is selected', () => { | ||
| // TODO add test after implementing virtual background feature/logic | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,7 @@ | ||||||||||
| import { useState } from 'react'; | ||||||||||
| import { LocalVideoTrack } from 'twilio-video'; | ||||||||||
| import { useState, useEffect } from 'react'; | ||||||||||
| import { SELECTED_BACKGROUND_SETTINGS_KEY } from '../../../constants'; | ||||||||||
| import { GaussianBlurBackgroundProcessor } from '@twilio/video-processors'; | ||||||||||
| import AbstractThumb from '../../../images/thumb/Abstract.jpg'; | ||||||||||
| import BohoHomeThumb from '../../../images/thumb/BohoHome.jpg'; | ||||||||||
| import BookshelfThumb from '../../../images/thumb/Bookshelf.jpg'; | ||||||||||
|
|
@@ -65,13 +68,44 @@ export const backgroundConfig = { | |||||||||
| images, | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| // TODO : Add video processing logic after backgroundSettings change | ||||||||||
| // useEffect hooks, etc ... | ||||||||||
| const virtualBackgroundAssets = '/virtualbackground'; | ||||||||||
| let blurProcessor: GaussianBlurBackgroundProcessor; | ||||||||||
|
|
||||||||||
| export default function useBackgroundSettings() { | ||||||||||
| const [backgroundSettings, setBackgroundSettings] = useState({ | ||||||||||
| type: 'none', | ||||||||||
| index: 0, | ||||||||||
| } as BackgroundSettings); | ||||||||||
| return [backgroundSettings, setBackgroundSettings] as const; | ||||||||||
| export default function useBackgroundSettings(videoTrack: LocalVideoTrack | undefined) { | ||||||||||
| const [backgroundSettings, setBackgroundSettings] = useState<BackgroundSettings>(() => { | ||||||||||
| const localStorageSettings = window.localStorage.getItem(SELECTED_BACKGROUND_SETTINGS_KEY); | ||||||||||
| return localStorageSettings ? JSON.parse(localStorageSettings) : { type: 'none', index: 0 }; | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| // Specifically used for handling room disconnection | ||||||||||
|
||||||||||
| // Specifically used for handling room disconnection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that this needs to be in a useEffect hook. Would it work if we moved it outside of useBackgroundSettings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can reuse the method you created above
| if (videoTrack.processor) { | |
| videoTrack.removeProcessor(videoTrack.processor); | |
| } | |
| removeProcessor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Charlie, I made it a separate function and didn't use it inside the useEffect hook since I was getting warnings about missing removeProcessor as a dependency, but when I include it, I get another warning about how removeProcessor is updated on every render. What would be the best step/approach to this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @timmydoza
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a way to make this work, but I wonder if we need to worry about removing the processor when we disconnect from a room? When we disconnect from a room, track.stop() is automatically called because of this hook here (this is to turn off the camera light on laptops when the user disconnects from a room). Then when the user goes back to the Join Room screens, new tracks are acquired.
@charliesantos Since we are stopping the tracks on disconnect, do we need to remove the processor as well?
If we do, I think we can just handle that in the removeVideoTrack function in the useLocalTracks hook here. Something like this maybe (but only if needed):
const removeLocalVideoTrack = useCallback(() => {
if (videoTrack) {
if (videoTrack.processor) {
videoTrack.removeProcessor(videoTrack.processor)
}
videoTrack.stop();
setVideoTrack(undefined);
}
}, [videoTrack]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we need to remove the processor that way it won't show in the join screen. On @timmydoza 's suggestion, it doesn't seem to save us anything 😄 . I was hoping we don't have to duplicate the code. I think I'm leaning more towards keeping our current implementation. We will probably remove this later if we decide to launch the virtual background settings UI in the join screen page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized the background is also applied in the prejoin screen when I refresh. I think instead of calling the remove processor here, maybe we should call it in the prejoin screen.