/// <summary> /// Transfer task. Retrieves data from underlying storage and pushes it down /// to ESPlayer /// </summary> /// <param name="token">CancellationToken</param> private async Task TransferTask(CancellationToken token) { logger.Info($"{streamType}: Transfer task started"); var streamBuffer = streamBufferController.GetStreamBuffer(streamType); barrier.Reset(); try { while (true) { var shouldContinue = await ProcessNextPacket(token); if (!shouldContinue) { break; } if (!barrier.Reached()) { continue; } var delay = barrier.TimeToNextFrame(); logger.Info( $"{streamType}: Halted. Buffer {streamBuffer.BufferFill()}% {streamBuffer.CurrentBufferedDuration()} {streamBuffer.BufferTimeRange}"); DelayTransfer(delay, token); barrier.Reset(); } } catch (InvalidOperationException e) { logger.Error(e, $"{streamType}: Stream completed"); } catch (OperationCanceledException) { logger.Info($"{streamType}: Transfer cancelled"); } catch (PacketSubmitException pse) { logger.Error(pse, $"{streamType}: Submit Error " + pse.SubmitStatus); DisableInput(); } catch (DrmException drme) { logger.Error(drme, $"{streamType}: Decrypt Error"); DisableInput(); playbackErrorSubject.OnNext("Playback Error"); } catch (Exception e) { // Dump unhandled exception. Running as a task so they will not be reported. logger.Error(e, $"{streamType}"); DisableInput(); playbackErrorSubject.OnNext("Playback Error"); } logger.Info( $"{streamType}: Terminated. Buffer {streamBuffer.BufferFill()}% {streamBuffer.CurrentBufferedDuration()} {streamBuffer.BufferTimeRange}"); }