DreamineVMS 1.0.0.0
Windows PC의 RTSP·USB 카메라 영상을 HLS로 변환해 원격 CCTV Viewer에 전달하는 데스크톱 에이전트입니다.
로딩중...
검색중...
일치하는것 없음
DreamineVMS.Services.Agent.HlsSegmentPusherService 클래스 참조sealed

더 자세히 ...

DreamineVMS.Services.Agent.HlsSegmentPusherService에 대한 상속 다이어그램 :
DreamineVMS.Services.Agent.HlsSegmentPusherService에 대한 협력 다이어그램:

Public 멤버 함수

 HlsSegmentPusherService (AgentApiClient api, IVmsCameraRepository repository, IConfiguration config, ILogger< HlsSegmentPusherService > logger)

Protected 멤버 함수

override async Task ExecuteAsync (CancellationToken stoppingToken)

Private 멤버 함수

async Task RunCameraLoopAsync (string cameraId, string outputRoot, CancellationToken ct)
async Task LoginWithRetryAsync (CancellationToken ct)
async Task EnsureLoggedInAsync (CancellationToken ct)
async Task SyncCamerasAsync ()
async Task< bool > TryPushFileAsync (string cameraId, string filePath, string filename)
string GetOutputRoot ()

정적 Private 멤버 함수

static List< string > ParseSegmentsFromM3u8 (byte[] bytes)

Private 속성

readonly AgentApiClient _api
readonly IVmsCameraRepository _repository
readonly IConfiguration _config
readonly ILogger< HlsSegmentPusherService_logger
readonly SemaphoreSlim _loginLock = new(1, 1)

상세한 설명

에이전트 모드: 자동 로그인 → 로컬 카메라를 서버에 등록 → HLS 세그먼트 업로드. 카메라별로 독립 루프를 병렬 실행하여 한 카메라 업로드가 다른 카메라를 블로킹하지 않음.

HlsSegmentPusherService.cs 파일의 17 번째 라인에서 정의되었습니다.

생성자 & 소멸자 문서화

◆ HlsSegmentPusherService()

DreamineVMS.Services.Agent.HlsSegmentPusherService.HlsSegmentPusherService ( AgentApiClient api,
IVmsCameraRepository repository,
IConfiguration config,
ILogger< HlsSegmentPusherService > logger )
inline

지정한 설정으로 HlsSegmentPusherService 클래스의 새 인스턴스를 초기화합니다.

매개변수
apiapi에 사용할 AgentApiClient 값입니다.
repositoryrepository에 사용할 IVmsCameraRepository 값입니다.
configconfig에 사용할 IConfiguration 값입니다.
loggerlogger에 사용할 ILogger<HlsSegmentPusherService> 값입니다.

HlsSegmentPusherService.cs 파일의 105 번째 라인에서 정의되었습니다.

110 {
111 _api = api;
112 _repository = repository;
113 _config = config;
114 _logger = logger;
115 }

다음을 참조함 : _api, _config, _logger, _repository.

멤버 함수 문서화

◆ EnsureLoggedInAsync()

async Task DreamineVMS.Services.Agent.HlsSegmentPusherService.EnsureLoggedInAsync ( CancellationToken ct)
inlineprivate

Ensure Logged In Async 작업을 수행합니다.

매개변수
ct취소 요청을 감시하는 토큰입니다.
반환값
Ensure Logged In Async 작업에서 생성한 Task 결과입니다.

HlsSegmentPusherService.cs 파일의 409 번째 라인에서 정의되었습니다.

410 {
411 if (_api.Token is not null)
412 {
413 return;
414 }
415
416 await _loginLock.WaitAsync(ct);
417 try
418 {
419 if (_api.Token is not null)
420 {
421 return;
422 }
423
424 await LoginWithRetryAsync(ct);
425 }
426 finally
427 {
428 _loginLock.Release();
429 }
430 }

다음을 참조함 : _api, _loginLock, LoginWithRetryAsync().

다음에 의해서 참조됨 : ExecuteAsync(), RunCameraLoopAsync().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ ExecuteAsync()

override async Task DreamineVMS.Services.Agent.HlsSegmentPusherService.ExecuteAsync ( CancellationToken stoppingToken)
inlineprotected

Execute Async 작업을 수행합니다.

매개변수
stoppingTokenstopping Token에 사용할 CancellationToken 값입니다.
반환값
Execute Async 작업에서 생성한 Task 결과입니다.

HlsSegmentPusherService.cs 파일의 141 번째 라인에서 정의되었습니다.

142 {
143 // 자격증명이 설정될 때까지 대기 (앱 실행 후 Agent Settings에서 입력해도 동작)
144 while (!stoppingToken.IsCancellationRequested)
145 {
146 if (!string.IsNullOrWhiteSpace(_config["Agent:Email"]) &&
147 !string.IsNullOrWhiteSpace(_config["Agent:Password"]))
148 break;
149
150 _logger.LogInformation("[Agent] Agent:Email/Password 미설정 — 30초 후 재확인.");
151 await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
152 }
153
154 if (stoppingToken.IsCancellationRequested) return;
155
156 var outputRoot = GetOutputRoot();
157 _logger.LogInformation("[Agent] 세그먼트 업로드 시작. 루트: {Root}", outputRoot);
158
159 // 카메라 목록을 동적으로 관리하는 메인 루프
160 var runningCameras = new Dictionary<string, CancellationTokenSource>();
161
162 while (!stoppingToken.IsCancellationRequested)
163 {
164 await EnsureLoggedInAsync(stoppingToken);
165 if (_api.Token is null) { await Task.Delay(5000, stoppingToken); continue; }
166
167 await SyncCamerasAsync();
168
169 // 활성 카메라 목록과 비교해서 추가/제거
170 var currentIds = _repository.GetAll()
171 .Where(c => c.Enabled && !c.IsDirectHls)
172 .Select(c => c.Id)
173 .ToHashSet();
174
175 // 제거된 카메라 루프 취소
176 foreach (var id in runningCameras.Keys.Except(currentIds).ToList())
177 {
178 runningCameras[id].Cancel();
179 runningCameras.Remove(id);
180 _logger.LogInformation("[Agent] 카메라 루프 중지: {Id}", id);
181 }
182
183 // 새 카메라 루프 시작
184 foreach (var id in currentIds.Except(runningCameras.Keys))
185 {
186 var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
187 runningCameras[id] = cts;
188 _ = Task.Run(() => RunCameraLoopAsync(id, outputRoot, cts.Token), cts.Token);
189 _logger.LogInformation("[Agent] 카메라 루프 시작: {Id}", id);
190 }
191
192 await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
193 }
194
195 // 종료 시 모든 루프 취소
196 foreach (var cts in runningCameras.Values) cts.Cancel();
197 }

다음을 참조함 : _api, _config, _logger, _repository, EnsureLoggedInAsync(), GetOutputRoot(), RunCameraLoopAsync(), SyncCamerasAsync().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

◆ GetOutputRoot()

string DreamineVMS.Services.Agent.HlsSegmentPusherService.GetOutputRoot ( )
inlineprivate

Output Root 값을 가져옵니다.

반환값
Get Output Root 작업에서 생성한 string 결과입니다.

HlsSegmentPusherService.cs 파일의 571 번째 라인에서 정의되었습니다.

572 {
573 var root = _config["Ffmpeg:OutputRoot"] ?? "hls";
574 return Path.IsPathRooted(root) ? root : Path.Combine(AppContext.BaseDirectory, root);
575 }

다음을 참조함 : _config.

다음에 의해서 참조됨 : ExecuteAsync().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ LoginWithRetryAsync()

async Task DreamineVMS.Services.Agent.HlsSegmentPusherService.LoginWithRetryAsync ( CancellationToken ct)
inlineprivate

Login With Retry Async 작업을 수행합니다.

매개변수
ct취소 요청을 감시하는 토큰입니다.
반환값
Login With Retry Async 작업에서 생성한 Task 결과입니다.

HlsSegmentPusherService.cs 파일의 374 번째 라인에서 정의되었습니다.

375 {
376 while (!ct.IsCancellationRequested && _api.Token is null)
377 {
378 var (ok, err) = await _api.LoginWithStoredCredentialsAsync();
379 if (ok) { _logger.LogInformation("[Agent] 로그인 성공."); return; }
380 _logger.LogWarning("[Agent] 로그인 실패: {Err}. 10초 후 재시도.", err);
381 await Task.Delay(TimeSpan.FromSeconds(10), ct);
382 }
383 }

다음을 참조함 : _api, _logger.

다음에 의해서 참조됨 : EnsureLoggedInAsync().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ ParseSegmentsFromM3u8()

List< string > DreamineVMS.Services.Agent.HlsSegmentPusherService.ParseSegmentsFromM3u8 ( byte[] bytes)
inlinestaticprivate

Parse Segments From M3u8 작업을 수행합니다.

매개변수
bytesbytes에 사용할 byte[] 값입니다.
반환값
Parse Segments From M3u8 작업에서 생성한 List<string> 결과입니다.

HlsSegmentPusherService.cs 파일의 541 번째 라인에서 정의되었습니다.

542 {
543 try
544 {
545 return System.Text.Encoding.UTF8.GetString(bytes)
546 .Split('\n')
547 .Where(l => l.TrimEnd().EndsWith(".ts", StringComparison.OrdinalIgnoreCase))
548 .Select(l => Path.GetFileName(l.Trim()))
549 .Where(f => !string.IsNullOrEmpty(f))
550 .ToList()!;
551 }
552 catch { return []; }
553 }

다음에 의해서 참조됨 : RunCameraLoopAsync().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ RunCameraLoopAsync()

async Task DreamineVMS.Services.Agent.HlsSegmentPusherService.RunCameraLoopAsync ( string cameraId,
string outputRoot,
CancellationToken ct )
inlineprivate

Run Camera Loop Async 작업을 수행합니다.

매개변수
cameraIdcamera Id에 사용할 string 값입니다.
outputRootoutput Root에 사용할 string 값입니다.
ct취소 요청을 감시하는 토큰입니다.
반환값
Run Camera Loop Async 작업에서 생성한 Task 결과입니다.

HlsSegmentPusherService.cs 파일의 239 번째 라인에서 정의되었습니다.

240 {
241 var pushed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
242 var camDir = Path.Combine(outputRoot, cameraId);
243 byte[]? lastPushedM3u8 = null;
244
245 while (!ct.IsCancellationRequested)
246 {
247 try
248 {
249 if (_api.Token is null)
250 {
251 await EnsureLoggedInAsync(ct);
252 if (_api.Token is null)
253 {
254 await Task.Delay(2000, ct);
255 continue;
256 }
257
258 await SyncCamerasAsync();
259 }
260
261 if (!Directory.Exists(camDir))
262 {
263 await Task.Delay(1000, ct);
264 continue;
265 }
266
267 var m3u8Path = Path.Combine(camDir, "index.m3u8");
268 if (!File.Exists(m3u8Path))
269 {
270 await Task.Delay(500, ct);
271 continue;
272 }
273
274 byte[] m3u8Bytes;
275 List<string> refs;
276 try
277 {
278 m3u8Bytes = await File.ReadAllBytesAsync(m3u8Path, ct);
279 refs = ParseSegmentsFromM3u8(m3u8Bytes);
280 }
281 catch (IOException)
282 {
283 await Task.Delay(100, ct);
284 continue;
285 }
286
287 if (refs.Count == 0)
288 {
289 await Task.Delay(500, ct);
290 continue;
291 }
292
293 bool allRefsPushed = true;
294 foreach (string filename in refs)
295 {
296 if (ct.IsCancellationRequested || _api.Token is null)
297 {
298 allRefsPushed = false;
299 break;
300 }
301
302 if (pushed.Contains(filename))
303 {
304 continue;
305 }
306
307 string segmentPath = Path.Combine(camDir, filename);
308 if (!File.Exists(segmentPath))
309 {
310 allRefsPushed = false;
311 break;
312 }
313
314 bool ok = await TryPushFileAsync(cameraId, segmentPath, filename);
315 if (ok)
316 {
317 pushed.Add(filename);
318 }
319 else
320 {
321 allRefsPushed = false;
322 break;
323 }
324 }
325
326 if (allRefsPushed && !m3u8Bytes.AsSpan().SequenceEqual(lastPushedM3u8))
327 {
328 bool ok = await _api.PushSegmentAsync(cameraId, "index.m3u8", m3u8Bytes);
329 if (ok)
330 {
331 lastPushedM3u8 = m3u8Bytes;
332 }
333 }
334
335 if (pushed.Count > 200)
336 {
337 pushed.RemoveWhere(filename => !refs.Contains(filename, StringComparer.OrdinalIgnoreCase));
338 }
339 }
340 catch (OperationCanceledException) { break; }
341 catch (Exception ex)
342 {
343 _logger.LogWarning(ex, "[Agent] [{Cam}] 스캔 중 오류", cameraId);
344 }
345
346 await Task.Delay(200, ct);
347 }
348 }

다음을 참조함 : _api, _logger, EnsureLoggedInAsync(), ParseSegmentsFromM3u8(), SyncCamerasAsync(), TryPushFileAsync().

다음에 의해서 참조됨 : ExecuteAsync().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ SyncCamerasAsync()

async Task DreamineVMS.Services.Agent.HlsSegmentPusherService.SyncCamerasAsync ( )
inlineprivate

Sync Cameras Async 작업을 수행합니다.

반환값
Sync Cameras Async 작업에서 생성한 Task 결과입니다.

HlsSegmentPusherService.cs 파일의 448 번째 라인에서 정의되었습니다.

449 {
450 var localCams = _repository.GetAll()
451 .Where(c => c.Enabled && !c.IsDirectHls)
452 .Select(c => new AgentCameraInfo
453 {
454 Id = c.Id, Name = c.Name, Host = c.Host,
455 RtspUrl = c.RtspUrl, AutoReconnect = c.AutoReconnect, IsPublic = c.IsPublic
456 }).ToList();
457
458 var ok = await _api.SyncCamerasAsync(localCams);
459 _logger.LogInformation("[Agent] 카메라 {Count}개 서버 동기화 {Result}.",
460 localCams.Count, ok ? "성공" : "실패");
461 }

다음을 참조함 : _api, _logger, _repository.

다음에 의해서 참조됨 : ExecuteAsync(), RunCameraLoopAsync().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ TryPushFileAsync()

async Task< bool > DreamineVMS.Services.Agent.HlsSegmentPusherService.TryPushFileAsync ( string cameraId,
string filePath,
string filename )
inlineprivate

Push File Async 작업을 시도하고 성공 여부를 반환합니다.

매개변수
cameraIdcamera Id에 사용할 string 값입니다.
filePathfile Path에 사용할 string 값입니다.
filenamefilename에 사용할 string 값입니다.
반환값
Try Push File Async 작업에서 생성한 Task<bool> 결과입니다.

HlsSegmentPusherService.cs 파일의 503 번째 라인에서 정의되었습니다.

504 {
505 try
506 {
507 var data = await File.ReadAllBytesAsync(filePath);
508 return await _api.PushSegmentAsync(cameraId, filename, data);
509 }
510 catch (Exception ex)
511 {
512 _logger.LogWarning(ex, "[Agent] [{Cam}] 세그먼트 파일 읽기 실패: {File}", cameraId, filename);
513 return false;
514 }
515 }

다음을 참조함 : _api, _logger.

다음에 의해서 참조됨 : RunCameraLoopAsync().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

멤버 데이터 문서화

◆ _api

readonly AgentApiClient DreamineVMS.Services.Agent.HlsSegmentPusherService._api
private

api 값을 보관합니다.

HlsSegmentPusherService.cs 파일의 27 번째 라인에서 정의되었습니다.

다음에 의해서 참조됨 : EnsureLoggedInAsync(), ExecuteAsync(), HlsSegmentPusherService(), LoginWithRetryAsync(), RunCameraLoopAsync(), SyncCamerasAsync(), TryPushFileAsync().

◆ _config

readonly IConfiguration DreamineVMS.Services.Agent.HlsSegmentPusherService._config
private

config 값을 보관합니다.

HlsSegmentPusherService.cs 파일의 45 번째 라인에서 정의되었습니다.

다음에 의해서 참조됨 : ExecuteAsync(), GetOutputRoot(), HlsSegmentPusherService().

◆ _logger

readonly ILogger<HlsSegmentPusherService> DreamineVMS.Services.Agent.HlsSegmentPusherService._logger
private

logger 값을 보관합니다.

HlsSegmentPusherService.cs 파일의 54 번째 라인에서 정의되었습니다.

다음에 의해서 참조됨 : ExecuteAsync(), HlsSegmentPusherService(), LoginWithRetryAsync(), RunCameraLoopAsync(), SyncCamerasAsync(), TryPushFileAsync().

◆ _loginLock

readonly SemaphoreSlim DreamineVMS.Services.Agent.HlsSegmentPusherService._loginLock = new(1, 1)
private

login Lock 값을 보관합니다.

HlsSegmentPusherService.cs 파일의 63 번째 라인에서 정의되었습니다.

다음에 의해서 참조됨 : EnsureLoggedInAsync().

◆ _repository

readonly IVmsCameraRepository DreamineVMS.Services.Agent.HlsSegmentPusherService._repository
private

repository 값을 보관합니다.

HlsSegmentPusherService.cs 파일의 36 번째 라인에서 정의되었습니다.

다음에 의해서 참조됨 : ExecuteAsync(), HlsSegmentPusherService(), SyncCamerasAsync().


이 클래스에 대한 문서화 페이지는 다음의 파일로부터 생성되었습니다.: