-
Notifications
You must be signed in to change notification settings - Fork 44
[Player-59] camera widget #159
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
base: epic/player-90-91-92-50
Are you sure you want to change the base?
Conversation
18a0722 to
908fec7
Compare
908fec7 to
68558f8
Compare
59e77c6 to
f8e069f
Compare
59db0d4 to
2bd6787
Compare
a76ab4c to
c2ebcf0
Compare
| transformIndexHtml(html) { | ||
| if (mode === 'development') { | ||
| let newHtml = html.replace( | ||
| /<script src="\.\.\/dist\/js\/device-renderer\.min\.js" data-player-url><\/script>/, |
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.
Would it be possible to move this code into another file, to keep the vite.config.js as lean and as declarative as possible?
| } else { | ||
| const acceptedTypes = accept.split(',').map((t) => t.trim().toLowerCase()); | ||
| isValid = acceptedTypes.some((type) => { | ||
| if (type.endsWith('/*')) { |
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.
Could we have one example of a type value, in a comment, in each of these if branches?
| accept = null, | ||
| classes = '', | ||
| i18n = {}, | ||
| mode = 'upload', |
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 don't see mode in the jsdoc. Did I miss something?
I see that in Camera.js, we specify mode: "select". Would be nice to document the two possible modes in the jsdoc.
| return { | ||
| // plugins: [viteSingleFile()], | ||
| plugins: [ | ||
| basicSsl(), |
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.
Is this basicSsl related to the hot reload?
I see that an ssl dependency is added, but not in this commit which introduces the plugin.
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.
No dependency to the hot reload, but browser grant access to camera only on https scheme
| if (this.instance.options.microphone && !isImage) { | ||
| try { | ||
| if (!this.audioContext) { | ||
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
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.
Is this AudioContext ever closed?
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 had push a small commit with a small refacto of destroy flow.
Here is a summary of the changes:
- Lifecycle Management: DeviceRenderer.destroy() now dynamically iterates over all registered widgets and calls their destroy() method.
- AudioContext: The Camera plugin now implements a destroy() method that explicitly closes the AudioContext and removes event listeners.
- Store Cleanup: Added a destroy() method to the Store to centrally clear all subscriptions, preventing memory leaks from lingering listeners.
- General Cleanup: Also addressed minor leaks like unrevoked Blob URLs and dead code in the Factory.
So to answer the question: Yes, the AudioContext is now properly closed when the player is destroyed :)
| } | ||
|
|
||
| this.audioNodes = this.audioNodes || []; | ||
| this.audioNodes.push({source, dest, type}); |
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.
Copilot mentions that this audioNodes array might have a potential memory leak. Not completely sure to understand: maybe because we put source nodes and destination nodes in the array, and when we're done with the camera, we don't clean the array? We'll have dangling references to these source/destination nodes?
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.
see comment above
7386fee to
c40ae3f
Compare
c2ebcf0 to
4eae368
Compare
b4ea2dc to
353b8f9
Compare
353b8f9 to
68c7a82
Compare
caarmen
left a comment
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 know it's in draft, but I had a couple of hours to look at this.
If any of my comments are irrelevant because you've been working on changes, just ignore them :)
I basically just looked at two files today:
GmDropdown.jsCamera.js
I'll take a look at other files when I get a chance.
src/components/GmDropdown.js
Outdated
| return item; | ||
| } | ||
| return item.valueToDisplay ?? item.element?.textContent ?? item.value; | ||
| return item.label ?? item.valueToDisplay ?? item.element?.textContent ?? item.value; |
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.
Why do we need to support a new attribute label?
Why can't Camera.js supply valueToDisplay? It seems to have the same results.
Could this component have some documentation in set items(newItems) explaining what are the accepted types of objects that can be passed for newItems?
src/plugins/Camera.js
Outdated
| if (d.label) { | ||
| videoOptions.push({ | ||
| value: d.deviceId, | ||
| label: d.label, |
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.
Why not supply valueToDisplay: d.label instead? (Same a few lines above).
| } | ||
| } | ||
|
|
||
| onFileSelected(file, type) { |
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.
Not sure where to put this:
If I select the same file multiple times, it only seems to work the first time.
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.
media-injection-bug.mp4
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.
works on firefox, fixed on chrome
src/plugins/Camera.js
Outdated
| } catch (error) { | ||
| this.permissionRequestView.classList.add('hidden'); | ||
|
|
||
| if (error.name === 'NotFoundError' || error.message.includes('The object can not be found')) { |
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.
Will these value NotFoundError and The object can not be found be consistent across browsers?
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.
NotFoundError is standard, we will keep only this check this modern browser send this code
| this.audioNodes = this.audioNodes.filter((node) => { | ||
| if (node.type === type) { | ||
| try { | ||
| node.source.disconnect(); |
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.
Should we be having side effects inside a filter? I think this isn't idiomatic for functional programming (methods like map, filter, etc typically should just return data, not have side effects).
Doing a filter first to get the relevant audioNodes, and then doing a for/forEach to disconnect() them would indeed end up processing the list twice.
But we're talking about just a few elements at most, right? Not sure this is a critical performance path. Clarity might be a better focus here in the clarity/perf tradeoff, no?
| if (typeof mediaElement.pause === 'function') { | ||
| mediaElement.pause(); | ||
| } | ||
| if (typeof mediaElement.load === 'function') { |
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.
Why not:
mediaElement.src = '';
if (typeof mediaElement.load === 'function') {
mediaElement.load();
}| } | ||
|
|
||
| const mediaElement = type === 'front' ? this.frontMediaElement : this.backMediaElement; | ||
| if (mediaElement) { |
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.
Could we have a comment at the top of this block?
Basically, in functions which are kind of large (this one is around 70 lines), with several sections, it might help to browse the code if each section has a quick comment.
Another option would be to split the function into multiple smaller functions, each one doing only one very small thing. Then, comments might not be needed. Another tradeoff :)
src/plugins/Camera.js
Outdated
| this.backFileInfo.classList.add('hidden'); | ||
| this.backUploader.classList.remove('hidden'); | ||
|
|
||
| this.instance.mediaManager.stopVideoStreaming('back'); |
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.
Quite a bit of copy/paste for the actions on the front/back streams. Not sure it would be simple to reduce this though. It might require more refactoring. Like extracting this into a function that takes a stream, video, fileinfo, and uploader arguments.
Then we could do something like:
if (type == "front") {
myFunctionWithAGoodName(
this.frontStream,
this.frontVideo,
this.frontFileInfo,
this.frontUploader,
"front"
};
} else {
myFunctionWithAGoodName(
this.backStream,
this.backVideo,
this.backFileInfo,
this.backUploader,
"back"
};
}
myFunctionWithAGoodName(
stream,
video,
fileInfo,
uploader,
someBetterName
){
if (stream) {
stream.getTracks().forEach((t) => {
t.stop();
});
stream = null;
}
video.classList.add('hidden');
video.srcObject = null;
fileInfo.classList.add('hidden');
uploader.classList.remove('hidden');
this.instance.mediaManager.stopVideoStreaming(someBetterName);
}A way to avoid passing in so many arguments would be to define an object containing properties for all these objects.
| this.toolbarBtn.setIndicator(''); | ||
| } | ||
| return; | ||
| } |
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.
Looks like the exact same logic for deviceId === 'none' and deviceId === 'file'. Did I miss something?
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.
the first branch has placeholder, the second one has uploader
src/plugins/MediaManager.js
Outdated
| } | ||
| // if the if the flag for bundled audio&video stream is set, we'll remove the audio track too | ||
| /* | ||
| * if the if the flag for bundled audio&video stream is set, we'll remove the audio track too |
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.
if the if the flag => If the flag
tests/unit/camera.test.js
Outdated
| test('widget is rendered with correct elements', () => { | ||
| // Check for dropdowns | ||
| expect(document.getElementsByClassName('gm-camera-dropdown-front')).toHaveLength(1); | ||
| /* |
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.
Could you check this comment?
| DRAG_DROP_TEXT: 'TEST DRAG DROP TEXT', | ||
| BROWSE_BUTTON_TEXT: 'TEST BROWSE', | ||
| FILE_TYPE_NOT_APK: 'TEST FILE TYPE NOT APK', | ||
| FILE_TYPE_INVALID: 'TEST FILE TYPE NOT APK', |
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.
Should the value be changed too?
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.
Since it's for test i18 it could, but in fact this is the right message
package.json
Outdated
| "types": "dist/index.d.ts", | ||
| "engines": { | ||
| "node": ">=16" | ||
| "node": ">=20" |
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.
This could be a breaking change for some applications using the player.
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 had changed the logic to avoid force the engine for the users of the player and check node version for the developper
9ee7468 to
b3fea28
Compare
9208f3f to
fdfa7a9
Compare
b3fea28 to
b7d73c5
Compare
fdfa7a9 to
48e2ec2
Compare
b7d73c5 to
7371ded
Compare
…leaks - Lifecycle Management: DeviceRenderer.destroy() now dynamically iterates over all registered widgets and calls their destroy() method. - AudioContext: The Camera plugin now implements a destroy() method that explicitly closes the AudioContext and removes event listeners. - Store Cleanup: Added a destroy() method to the Store to centrally clear all subscriptions, preventing memory leaks from lingering listeners. - General Cleanup: Also addressed minor leaks like unrevoked Blob URLs and dead code in the Factory.
…der to increase connexion stability
48e2ec2 to
c3304a5
Compare
d88f07b to
6982567
Compare
Description
This branch introduces camera injection management and improves the local development workflow.
Front and back camera are implemented but back camera is hidden, waiting for system handle more than one stream
Camera Functionality (feat(Camera))
Configuration & Dev (chore(vite))