@@ -3,24 +3,37 @@ import Hls from 'hls.js'
33
44import { useHost } from '@opentrons/react-api-client'
55
6+ import { isTerminalRunStatus } from '/app/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/utils'
7+
68import type { RefObject } from 'react'
9+ import type { RunStatus } from '@opentrons/api-client'
710
811// TODO(jh, 09-05-25): /GET this from the /stream endpoint eventually.
912const STREAM_URL = ( robotIp : string ) : string =>
1013 `http://${ robotIp } :31950/hls/stream.m3u8`
1114
15+ const RETRY_DELAY_MS = 3000
16+
1217export interface UseHlsVideoResult {
1318 videoRef : RefObject < HTMLVideoElement >
1419 videoError : string | null
1520}
1621
17- // Sets up and manages an HLS video stream player that receives
18- // camera stream URLs from the main Electron process and displays them.
19- export function useHlsVideo ( ) : UseHlsVideoResult {
22+ export function useHlsVideo ( runStatus : RunStatus | null ) : UseHlsVideoResult {
2023 const host = useHost ( )
2124 const videoRef = useRef < HTMLVideoElement > ( null )
2225 const hlsRef = useRef < Hls | null > ( null )
2326 const [ error , setError ] = useState < string | null > ( null )
27+ const retryCountRef = useRef ( 0 )
28+ const retryTimeoutRef = useRef < NodeJS . Timeout | null > ( null )
29+
30+ const setupHls = useRef < ( ) => void > ( )
31+
32+ useEffect ( ( ) => {
33+ if ( error ) {
34+ console . error ( error )
35+ }
36+ } , [ error ] )
2437
2538 useEffect ( ( ) => {
2639 if ( videoRef . current == null || host == null ) {
@@ -29,7 +42,12 @@ export function useHlsVideo(): UseHlsVideoResult {
2942
3043 const video = videoRef . current
3144
32- if ( Hls . isSupported ( ) ) {
45+ setupHls . current = ( ) => {
46+ if ( ! Hls . isSupported ( ) ) {
47+ setError ( 'HLS streaming not supported in this browser.' )
48+ return
49+ }
50+
3351 if ( hlsRef . current ) {
3452 hlsRef . current . destroy ( )
3553 }
@@ -54,28 +72,25 @@ export function useHlsVideo(): UseHlsVideoResult {
5472 hls . attachMedia ( video )
5573
5674 hls . on ( Hls . Events . MANIFEST_PARSED , ( ) => {
75+ retryCountRef . current = 0
76+ setError ( null )
5777 video . play ( ) . catch ( e => {
5878 console . error ( 'Auto-play failed:' , e )
5979 setError ( 'Auto-play failed. Please close and reopen the stream.' )
6080 } )
6181 } )
6282
63- hls . on ( Hls . Events . ERROR , ( event , data ) => {
64- console . error ( 'HLS error:' , event , data )
65- if ( data . fatal ) {
66- switch ( data . type ) {
67- case Hls . ErrorTypes . NETWORK_ERROR :
68- setError (
69- `Network error: Cannot connect to ${ STREAM_URL ( host . hostname ) } `
70- )
71- break
72- case Hls . ErrorTypes . MEDIA_ERROR :
73- setError ( 'Media error in stream' )
74- break
75- default :
76- setError ( 'Fatal error loading stream' )
77- break
78- }
83+ hls . on ( Hls . Events . ERROR , ( _ , data ) => {
84+ // `fatal` prevents refetching over-aggressively, ex, when the buffer is temporarily depleted.
85+ if ( data . fatal && ! isTerminalRunStatus ( runStatus ) ) {
86+ retryCountRef . current ++
87+ setError (
88+ `Service unavailable. Retrying (${ retryCountRef . current } )...`
89+ )
90+
91+ retryTimeoutRef . current = setTimeout ( ( ) => {
92+ setupHls . current ?.( )
93+ } , RETRY_DELAY_MS )
7994 }
8095 } )
8196
@@ -84,18 +99,23 @@ export function useHlsVideo(): UseHlsVideoResult {
8499 }
85100
86101 video . addEventListener ( 'error' , handleVideoError )
102+ }
87103
88- return ( ) => {
89- video . removeEventListener ( 'error' , handleVideoError )
90- if ( hlsRef . current ) {
91- hlsRef . current . destroy ( )
92- hlsRef . current = null
93- }
104+ setupHls . current ( )
105+
106+ return ( ) => {
107+ if ( retryTimeoutRef . current ) {
108+ clearTimeout ( retryTimeoutRef . current )
109+ }
110+
111+ if ( hlsRef . current ) {
112+ hlsRef . current . destroy ( )
113+ hlsRef . current = null
94114 }
95- } else {
96- setError ( 'HLS streaming not supported in this browser.' )
115+
116+ retryCountRef . current = 0
97117 }
98- } , [ host ] )
118+ } , [ host , runStatus ] )
99119
100120 return { videoRef, videoError : error }
101121}
0 commit comments