Changeset View
Standalone View
web/media/encrypted-multimedia.react.js
// @flow | // @flow | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import 'react-circular-progressbar/dist/styles.css'; | import 'react-circular-progressbar/dist/styles.css'; | ||||
import { AlertCircle as AlertCircleIcon } from 'react-feather'; | import { AlertCircle as AlertCircleIcon } from 'react-feather'; | ||||
import type { Shape } from 'lib/types/core.js'; | |||||
import type { EncryptedMediaType } from 'lib/types/media-types.js'; | import type { EncryptedMediaType } from 'lib/types/media-types.js'; | ||||
import { decryptMedia } from './encryption-utils.js'; | import { decryptMedia } from './encryption-utils.js'; | ||||
import css from './media.css'; | import css from './media.css'; | ||||
import LoadingIndicator from '../loading-indicator.react.js'; | import LoadingIndicator from '../loading-indicator.react.js'; | ||||
type Props = { | type Props = { | ||||
+holder: string, | +holder: string, | ||||
+encryptionKey: string, | +encryptionKey: string, | ||||
+type: EncryptedMediaType, | +type: EncryptedMediaType, | ||||
+placeholderSrc?: ?string, | |||||
+elementStyle?: ?Shape<CSSStyleDeclaration>, | |||||
}; | }; | ||||
function EncryptedMultimedia(props: Props): React.Node { | function EncryptedMultimedia(props: Props): React.Node { | ||||
const { holder, encryptionKey } = props; | const { holder, encryptionKey, placeholderSrc, elementStyle } = props; | ||||
const [source, setSource] = React.useState(null); | const [source, setSource] = React.useState(null); | ||||
const videoRef = React.useRef(null); | const videoRef = React.useRef(null); | ||||
React.useEffect(() => { | React.useEffect(() => { | ||||
let isMounted = true, | let isMounted = true, | ||||
uriToDispose; | uriToDispose; | ||||
setSource(null); | setSource(null); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | function EncryptedMultimedia(props: Props): React.Node { | ||||
} | } | ||||
if (source?.error) { | if (source?.error) { | ||||
errorIndicator = <AlertCircleIcon className={css.uploadError} size={36} />; | errorIndicator = <AlertCircleIcon className={css.uploadError} size={36} />; | ||||
} | } | ||||
let mediaNode; | let mediaNode; | ||||
if (props.type === 'encrypted_photo') { | if (props.type === 'encrypted_photo') { | ||||
mediaNode = <img src={source?.uri} key={holder} />; | mediaNode = ( | ||||
<img | |||||
src={source?.uri ?? placeholderSrc} | |||||
key={holder} | |||||
style={elementStyle} | |||||
/> | |||||
); | |||||
} else { | } else { | ||||
mediaNode = <video controls ref={videoRef} key={holder} />; | // hide poster when video source is available | ||||
const poster = source?.uri ? undefined : placeholderSrc; | |||||
ashoat: For non-encrypted media, we set `poster` to `thumbnailURI` once it's available. Can you explain… | |||||
bartekAuthorUnsubmitted Done Inline ActionsEncrypted videos work totally differently. For encrypted videos, the source is unavailable (source?.uri == null) when the placeholder is displayed. But after decryption work, this is a local blob/data URI and browser can auto-generate poster immediately when replacing the src prop. This is not the case for non-encrypted videos, where src is available from the beginning, but the operation that takes time is downloading its content by the browser. bartek: Encrypted videos work totally differently.
First of all, we don't use `thumbnailURI` for them… | |||||
ashoatUnsubmitted Not Done Inline ActionsThanks for the clarification RE thumbnailURI vs. thumbnailHolder! I responded in more detail in D7902, but I'm still confused about why we're not showing the thumbnail at all here. I think we should try to decrypt the thumbnail, and the replace the thumbhash with the thumbnail as soon as possible. This will presumably happen before the whole video is decrypted, and will allow us to display a higher-res placeholder while the video continues to load. ashoat: Thanks for the clarification RE `thumbnailURI` vs. `thumbnailHolder`!
I [responded in more… | |||||
bartekAuthorUnsubmitted Done Inline Actionsw bartek: w | |||||
bartekAuthorUnsubmitted Done Inline Actions
Whoops, accidentaly added a comment bartek: > w
Whoops, accidentaly added a comment | |||||
mediaNode = ( | |||||
<video | |||||
controls | |||||
ref={videoRef} | |||||
key={holder} | |||||
poster={poster} | |||||
style={elementStyle} | |||||
/> | |||||
); | |||||
} | } | ||||
return ( | return ( | ||||
<> | <> | ||||
{mediaNode} | {mediaNode} | ||||
{loadingIndicator} | {loadingIndicator} | ||||
{errorIndicator} | {errorIndicator} | ||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
export default EncryptedMultimedia; | export default EncryptedMultimedia; |
For non-encrypted media, we set poster to thumbnailURI once it's available. Can you explain a little bit why we're doing it differently for encrypted media?
I'm guessing the idea is that the video is already loaded at that point, but it seems like in D7902 we wait for the video to be loaded before displaying the thumbnailURI anyways.
I guess it would also require some additional decryption work (decrypting the thumbnailURI), but that should be faster than decrypting the whole video.