PlayingGroupState.cs 7.4 KB

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