PlayingGroupState.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #nullable disable
  2. using System;
  3. using System.Threading;
  4. using MediaBrowser.Controller.Session;
  5. using MediaBrowser.Controller.SyncPlay.PlaybackRequests;
  6. using MediaBrowser.Model.SyncPlay;
  7. using Microsoft.Extensions.Logging;
  8. namespace MediaBrowser.Controller.SyncPlay.GroupStates
  9. {
  10. /// <summary>
  11. /// Class PlayingGroupState.
  12. /// </summary>
  13. /// <remarks>
  14. /// Class is not thread-safe, external locking is required when accessing methods.
  15. /// </remarks>
  16. public class PlayingGroupState : AbstractGroupState
  17. {
  18. /// <summary>
  19. /// The logger.
  20. /// </summary>
  21. private readonly ILogger<PlayingGroupState> _logger;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="PlayingGroupState"/> class.
  24. /// </summary>
  25. /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
  26. public PlayingGroupState(ILoggerFactory loggerFactory)
  27. : base(loggerFactory)
  28. {
  29. _logger = LoggerFactory.CreateLogger<PlayingGroupState>();
  30. }
  31. /// <inheritdoc />
  32. public override GroupStateType Type { get; } = GroupStateType.Playing;
  33. /// <summary>
  34. /// Gets or sets a value indicating whether requests for buffering should be ignored.
  35. /// </summary>
  36. public bool IgnoreBuffering { get; set; }
  37. /// <inheritdoc />
  38. public override void SessionJoined(IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  39. {
  40. // Wait for session to be ready.
  41. var waitingState = new WaitingGroupState(LoggerFactory);
  42. context.SetState(waitingState);
  43. waitingState.SessionJoined(context, Type, session, cancellationToken);
  44. }
  45. /// <inheritdoc />
  46. public override void SessionLeaving(IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  47. {
  48. // Do nothing.
  49. }
  50. /// <inheritdoc />
  51. public override void HandleRequest(PlayGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  52. {
  53. // Change state.
  54. var waitingState = new WaitingGroupState(LoggerFactory);
  55. context.SetState(waitingState);
  56. waitingState.HandleRequest(request, context, Type, session, cancellationToken);
  57. }
  58. /// <inheritdoc />
  59. public override void HandleRequest(UnpauseGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  60. {
  61. if (!prevState.Equals(Type))
  62. {
  63. // Pick a suitable time that accounts for latency.
  64. var delayMillis = Math.Max(context.GetHighestPing() * 2, context.DefaultPing);
  65. // Unpause group and set starting point in future.
  66. // Clients will start playback at LastActivity (datetime) from PositionTicks (playback position).
  67. // The added delay does not guarantee, of course, that the command will be received in time.
  68. // Playback synchronization will mainly happen client side.
  69. context.LastActivity = DateTime.UtcNow.AddMilliseconds(delayMillis);
  70. var command = context.NewSyncPlayCommand(SendCommandType.Unpause);
  71. context.SendCommand(session, SyncPlayBroadcastType.AllGroup, command, cancellationToken);
  72. // Notify relevant state change event.
  73. SendGroupStateUpdate(context, request, session, cancellationToken);
  74. }
  75. else
  76. {
  77. // Client got lost, sending current state.
  78. var command = context.NewSyncPlayCommand(SendCommandType.Unpause);
  79. context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
  80. }
  81. }
  82. /// <inheritdoc />
  83. public override void HandleRequest(PauseGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  84. {
  85. // Change state.
  86. var pausedState = new PausedGroupState(LoggerFactory);
  87. context.SetState(pausedState);
  88. pausedState.HandleRequest(request, context, Type, session, cancellationToken);
  89. }
  90. /// <inheritdoc />
  91. public override void HandleRequest(StopGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  92. {
  93. // Change state.
  94. var idleState = new IdleGroupState(LoggerFactory);
  95. context.SetState(idleState);
  96. idleState.HandleRequest(request, context, Type, session, cancellationToken);
  97. }
  98. /// <inheritdoc />
  99. public override void HandleRequest(SeekGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  100. {
  101. // Change state.
  102. var waitingState = new WaitingGroupState(LoggerFactory);
  103. context.SetState(waitingState);
  104. waitingState.HandleRequest(request, context, Type, session, cancellationToken);
  105. }
  106. /// <inheritdoc />
  107. public override void HandleRequest(BufferGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  108. {
  109. if (IgnoreBuffering)
  110. {
  111. return;
  112. }
  113. // Change state.
  114. var waitingState = new WaitingGroupState(LoggerFactory);
  115. context.SetState(waitingState);
  116. waitingState.HandleRequest(request, context, Type, session, cancellationToken);
  117. }
  118. /// <inheritdoc />
  119. public override void HandleRequest(ReadyGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  120. {
  121. if (prevState.Equals(Type))
  122. {
  123. // Group was not waiting, make sure client has latest state.
  124. var command = context.NewSyncPlayCommand(SendCommandType.Unpause);
  125. context.SendCommand(session, SyncPlayBroadcastType.CurrentSession, command, cancellationToken);
  126. }
  127. else if (prevState.Equals(GroupStateType.Waiting))
  128. {
  129. // Notify relevant state change event.
  130. SendGroupStateUpdate(context, request, session, cancellationToken);
  131. }
  132. }
  133. /// <inheritdoc />
  134. public override void HandleRequest(NextItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  135. {
  136. // Change state.
  137. var waitingState = new WaitingGroupState(LoggerFactory);
  138. context.SetState(waitingState);
  139. waitingState.HandleRequest(request, context, Type, session, cancellationToken);
  140. }
  141. /// <inheritdoc />
  142. public override void HandleRequest(PreviousItemGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
  143. {
  144. // Change state.
  145. var waitingState = new WaitingGroupState(LoggerFactory);
  146. context.SetState(waitingState);
  147. waitingState.HandleRequest(request, context, Type, session, cancellationToken);
  148. }
  149. }
  150. }