2626 @if (videoOptions .Count > 0 )
2727 {
2828 <label for =" videoSource" >Video Source </label >
29- <select id =" videoSource" @bind =selectedVideoSource @bind:after =" LoadMediaStream " >
29+ <select id =" videoSource" @bind =selectedVideoSource @bind:after =" SelectDevice " >
3030 @foreach ( var option in videoOptions )
3131 {
3232 <option value =" @option.id" selected =" @(option.id == selectedVideoSource)" >@option.label </option >
3333 }
3434 </select >
3535 <br />
3636 <label for =" frameRate" >Frame Rate (Current Camera Capabilites are @minimalFrameRate - @maximumFrameRate )</label >
37- <input id =" frameRate" type =" number" step =" 1" @bind =" frameRate" @bind:after =" LoadMediaStream " />
37+ <input id =" frameRate" type =" number" step =" 1" @bind =" frameRate" @bind:after =" ChangeFrameRate " />
3838 }
3939 <video @ref =" videoElement" width =" 100%" height =" 400" autoplay controls ></video >
4040}
4141
4242@code {
43- private MediaDevices mediaDevices = default ! ;
43+ private MediaDevices ? mediaDevices = default ;
4444 private MediaStreamTrack ? videoStreamTrack ;
4545 private string ? error ;
4646 private ElementReference videoElement ;
5151 private double minimalFrameRate ;
5252 private double maximumFrameRate ;
5353
54- protected override async Task OnInitializedAsync ()
55- {
56- mediaDevices = await MediaDevicesService .GetMediaDevicesAsync ();
57- }
58-
5954 async Task OpenVideo ()
6055 {
6156 try
6257 {
63- await LoadMediaStream ();
58+ if (mediaDevices is null )
59+ mediaDevices = await MediaDevicesService .GetMediaDevicesAsync ();
6460
61+ await SelectDevice ();
6562 var deviceInfos = await mediaDevices .EnumerateDevicesAsync ();
6663 videoOptions .Clear ();
6764 foreach (var device in deviceInfos )
8077 }
8178 catch (Exception ex )
8279 {
83- error = $" { ex . GetType (). Name }: { ex . Message } " ;
84- await StopVideoTrack ();
80+ throw ;
81+ // await StopVideoTrack();
8582 }
8683 }
8784
88- async Task LoadMediaStream ()
85+ async Task ChangeFrameRate ()
8986 {
9087 try
9188 {
9289 var mediaTrackConstraints = new MediaTrackConstraints ()
9390 {
94- AspectRatio = 16 . 0 / 9 . 0 ,
95- FrameRate = frameRate is { } setFrameRate ? new ConstrainDoubleRange () { Exact = setFrameRate } : frameRate ,
96- DeviceId = selectedVideoSource is null ? null : new ConstrainDomString (selectedVideoSource )
91+ FrameRate = frameRate is { } setFrameRate ? new ConstrainDoubleRange () { Exact = setFrameRate } : frameRate
9792 };
98- if (videoStreamTrack is null || shownVideoSource != selectedVideoSource )
93+ if (selectedVideoSource is not null )
9994 {
100- var mediaStream = await mediaDevices .GetUserMediaAsync (new MediaStreamConstraints () { Video = mediaTrackConstraints });
101- var videoTracks = await mediaStream .GetVideoTracksAsync ();
102- videoStreamTrack = videoTracks .FirstOrDefault ();
103- if (videoStreamTrack is not null )
104- {
105- var capabilities = await videoStreamTrack .GetCapabilitiesAsync ();
106- minimalFrameRate = capabilities .FrameRate ? .Min ?? 0 ;
107- maximumFrameRate = capabilities .FrameRate ? .Max ?? 0 ;
108- frameRate = (maximumFrameRate + minimalFrameRate ) / 2 . 0 ;
109- selectedVideoSource = capabilities .DeviceId ;
110- shownVideoSource = selectedVideoSource ;
111- }
112-
113- StateHasChanged ();
114- // We don't have a wrapper for HtmlMediaElement's yet so we use simple JSInterop for this part.
115- var htmlMediaElement = await JSRuntime .InvokeAsync <IJSObjectReference >(" getReference" , videoElement );
116- await JSRuntime .InvokeVoidAsync (" setAttribute" , htmlMediaElement , " srcObject" , mediaStream .JSReference );
117- }
118- else
119- {
120- await videoStreamTrack .ApplyContraintsAsync (mediaTrackConstraints );
95+ mediaTrackConstraints .DeviceId = new ConstrainDOMStringParameters () { Exact = selectedVideoSource };
12196 }
97+ await videoStreamTrack .ApplyContraintsAsync (mediaTrackConstraints );
12298 }
12399 catch (OverconstrainedErrorException ex )
124100 {
@@ -130,6 +106,44 @@ else
130106 error = $" {ex .GetType ().Name }: {ex .Message }" ;
131107 await StopVideoTrack ();
132108 }
109+ catch (Exception )
110+ {
111+ throw ;
112+ }
113+ }
114+
115+ async Task SelectDevice ()
116+ {
117+ if (selectedVideoSource != null && shownVideoSource == selectedVideoSource )
118+ return ; // They picked the same device
119+
120+ var mediaTrackConstraints = new MediaTrackConstraints ();
121+ if (selectedVideoSource is not null )
122+ {
123+ mediaTrackConstraints .DeviceId = new ConstrainDOMStringParameters () { Exact = selectedVideoSource };
124+ }
125+
126+ await using var mediaStream = await mediaDevices ! .GetUserMediaAsync (new MediaStreamConstraints () { Video = mediaTrackConstraints });
127+ var videoTracks = await mediaStream .GetVideoTracksAsync ();
128+ videoStreamTrack = videoTracks .FirstOrDefault ();
129+ foreach (var unusedTrack in videoTracks .Skip (1 ))
130+ {
131+ await unusedTrack .DisposeAsync ();
132+ }
133+ if (videoStreamTrack is not null )
134+ {
135+ var capabilities = await videoStreamTrack .GetCapabilitiesAsync ();
136+ minimalFrameRate = capabilities .FrameRate ? .Min ?? 0 ;
137+ maximumFrameRate = capabilities .FrameRate ? .Max ?? 0 ;
138+ frameRate = (maximumFrameRate + minimalFrameRate ) / 2 . 0 ;
139+ selectedVideoSource = capabilities .DeviceId ;
140+ shownVideoSource = selectedVideoSource ;
141+ }
142+
143+ StateHasChanged ();
144+ // We don't have a wrapper for HtmlMediaElement's yet so we use simple JSInterop for this part.
145+ await using var htmlMediaElement = await JSRuntime .InvokeAsync <IJSObjectReference >(" getReference" , videoElement );
146+ await JSRuntime .InvokeVoidAsync (" setAttribute" , htmlMediaElement , " srcObject" , mediaStream .JSReference );
133147 }
134148
135149 async Task StopVideoTrack ()
142156 frameRate = null ;
143157 if (mediaDevices is not null )
144158 await mediaDevices .DisposeAsync ();
159+ mediaDevices = null ;
160+ shownVideoSource = null ;
161+ selectedVideoSource = null ;
145162 }
146163
147164 public async ValueTask DisposeAsync ()
0 commit comments