DreamineVMS 1.0.0.0
Windows PC의 RTSP·USB 카메라 영상을 HLS로 변환해 원격 CCTV Viewer에 전달하는 데스크톱 에이전트입니다.
로딩중...
검색중...
일치하는것 없음
LivePageViewModel.cs
이 파일의 문서화 페이지로 가기
4using Microsoft.JSInterop;
5
7
24public sealed class LivePageViewModel : IAsyncDisposable
25{
52 private readonly IJSRuntime _jsRuntime;
61 private readonly object _gate = new();
70 private bool _isInitialized;
79 private bool _isDisposed;
80
122 IVmsCameraRepository repository,
123 ICameraRuntimeStateService runtimeState,
124 IJSRuntime jsRuntime)
125 {
126 _repository = repository ?? throw new ArgumentNullException(nameof(repository));
127 _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
128 _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
129 }
130
139 public event EventHandler? PlayersNeedSync;
140
149 public IReadOnlyList<CameraDevice> Cameras { get; private set; } = Array.Empty<CameraDevice>();
150
159 public void Initialize()
160 {
161 lock (_gate)
162 {
164 {
165 return;
166 }
167
168 Cameras = _repository.GetAll()
169 .Where(camera => camera.Enabled)
170 .OrderBy(camera => camera.DisplayOrder)
171 .Take(5)
172 .ToArray();
173
174 _runtimeState.StateChanged += OnRuntimeStateChanged;
175 _isInitialized = true;
176 }
177 }
178
203 public async Task OnAfterRenderAsync(bool firstRender)
204 {
205 if (!firstRender)
206 {
207 return;
208 }
209
211 }
212
229 public async Task SynchronizePlayersAsync()
230 {
231 if (_isDisposed)
232 {
233 return;
234 }
235
236 foreach (CameraDevice camera in Cameras)
237 {
238 CameraRuntimeState state = _runtimeState.GetState(camera.Id);
239
240 if (state.State is CameraConnectionState.Connected or CameraConnectionState.Connecting)
241 {
242 await EnsurePlayerAsync(camera);
243 continue;
244 }
245
246 await DestroyPlayerAsync(camera, state.LastMessage);
247 }
248 }
249
274 public static string GetPlayerId(CameraDevice camera)
275 {
276 ArgumentNullException.ThrowIfNull(camera);
277 return $"video-{camera.Id}";
278 }
279
296 public async ValueTask DisposeAsync()
297 {
298 lock (_gate)
299 {
300 if (_isDisposed)
301 {
302 return;
303 }
304
305 _isDisposed = true;
306
307 if (_isInitialized)
308 {
309 _runtimeState.StateChanged -= OnRuntimeStateChanged;
310 _isInitialized = false;
311 }
312 }
313
314 foreach (CameraDevice camera in Cameras)
315 {
316 await DestroyPlayerAsync(camera, "Live view disposed.");
317 }
318 }
319
320
353 private static string BuildVersionedHlsUrl(CameraDevice camera, CameraRuntimeState state)
354 {
355 ArgumentNullException.ThrowIfNull(camera);
356 ArgumentNullException.ThrowIfNull(state);
357
358 string separator = camera.HlsUrl.Contains('?', StringComparison.Ordinal) ? "&" : "?";
359 return $"{camera.HlsUrl}{separator}session={state.RestartCount}";
360 }
361
386 private async Task EnsurePlayerAsync(CameraDevice camera)
387 {
388 try
389 {
390 CameraRuntimeState state = _runtimeState.GetState(camera.Id);
391 string source = BuildVersionedHlsUrl(camera, state);
392
393 await _jsRuntime.InvokeVoidAsync(
394 "dreamineVmsHls.init",
395 GetPlayerId(camera),
396 source);
397 }
398 catch (JSDisconnectedException)
399 {
400 // Circuit이 끊긴 상태는 무시합니다.
401 }
402 catch (Exception ex)
403 {
404 System.Diagnostics.Debug.WriteLine($"[LivePageViewModel] player init failed: {camera.Id}: {ex}");
405 }
406 }
407
440 private async Task DestroyPlayerAsync(CameraDevice camera, string? reason)
441 {
442 try
443 {
444 await _jsRuntime.InvokeVoidAsync(
445 "dreamineVmsHls.destroy",
446 GetPlayerId(camera),
447 string.IsNullOrWhiteSpace(reason) ? "Stream stopped." : reason);
448 }
449 catch (JSDisconnectedException)
450 {
451 // Circuit이 끊긴 상태는 무시합니다.
452 }
453 catch
454 {
455 // 화면 종료 중 JS 런타임이 이미 끊긴 경우는 무시합니다.
456 }
457 }
458
483 private void OnRuntimeStateChanged(object? sender, CameraRuntimeState state)
484 {
485 if (Cameras.All(c => c.Id != state.CameraId))
486 {
487 return;
488 }
489
490 if (_isDisposed)
491 {
492 return;
493 }
494
495 PlayersNeedSync?.Invoke(this, EventArgs.Empty);
496 }
497}
readonly ICameraRuntimeStateService _runtimeState
async Task DestroyPlayerAsync(CameraDevice camera, string? reason)
static string GetPlayerId(CameraDevice camera)
async Task EnsurePlayerAsync(CameraDevice camera)
void OnRuntimeStateChanged(object? sender, CameraRuntimeState state)
static string BuildVersionedHlsUrl(CameraDevice camera, CameraRuntimeState state)
LivePageViewModel(IVmsCameraRepository repository, ICameraRuntimeStateService runtimeState, IJSRuntime jsRuntime)