PlayingGroupState.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using System;
  2. using System.Threading;
  3. using MediaBrowser.Controller.Session;
  4. using MediaBrowser.Model.SyncPlay;
  5. using Microsoft.Extensions.Logging;
  6. namespace MediaBrowser.Controller.SyncPlay
  7. {
  8. /// <summary>
  9. /// Class PlayingGroupState.
  10. /// </summary>
  11. /// <remarks>
  12. /// Class is not thread-safe, external locking is required when accessing methods.
  13. /// </remarks>
  14. public class PlayingGroupState : AbstractGroupState
  15. {
  16. /// <summary>
  17. /// Initializes a new instance of the <see cref="PlayingGroupState"/> class.
  18. /// </summary>
  19. /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
  20. public PlayingGroupState(ILogger logger)
  21. : base(logger)
  22. {
  23. // Do nothing.
  24. }
  25. /// <inheritdoc />
  26. public override GroupStateType Type
  27. {
  28. get
  29. {
  30. return GroupStateType.Playing;
  31. }
  32. }
  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(Logger);
  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(IGroupStateContext context, GroupStateType prevState, PlayGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  52. {
  53. // Change state.
  54. var waitingState = new WaitingGroupState(Logger);
  55. context.SetState(waitingState);
  56. waitingState.HandleRequest(context, Type, request, session, cancellationToken);
  57. }
  58. /// <inheritdoc />
  59. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, UnpauseGroupRequest request, 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(IGroupStateContext context, GroupStateType prevState, PauseGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  84. {
  85. // Change state.
  86. var pausedState = new PausedGroupState(Logger);
  87. context.SetState(pausedState);
  88. pausedState.HandleRequest(context, Type, request, session, cancellationToken);
  89. }
  90. /// <inheritdoc />
  91. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, StopGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  92. {
  93. // Change state.
  94. var idleState = new IdleGroupState(Logger);
  95. context.SetState(idleState);
  96. idleState.HandleRequest(context, Type, request, session, cancellationToken);
  97. }
  98. /// <inheritdoc />
  99. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, SeekGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  100. {
  101. // Change state.
  102. var waitingState = new WaitingGroupState(Logger);
  103. context.SetState(waitingState);
  104. waitingState.HandleRequest(context, Type, request, session, cancellationToken);
  105. }
  106. /// <inheritdoc />
  107. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, BufferGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  108. {
  109. if (IgnoreBuffering)
  110. {
  111. return;
  112. }
  113. // Change state.
  114. var waitingState = new WaitingGroupState(Logger);
  115. context.SetState(waitingState);
  116. waitingState.HandleRequest(context, Type, request, session, cancellationToken);
  117. }
  118. /// <inheritdoc />
  119. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, ReadyGroupRequest request, 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(IGroupStateContext context, GroupStateType prevState, NextTrackGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  135. {
  136. // Change state.
  137. var waitingState = new WaitingGroupState(Logger);
  138. context.SetState(waitingState);
  139. waitingState.HandleRequest(context, Type, request, session, cancellationToken);
  140. }
  141. /// <inheritdoc />
  142. public override void HandleRequest(IGroupStateContext context, GroupStateType prevState, PreviousTrackGroupRequest request, SessionInfo session, CancellationToken cancellationToken)
  143. {
  144. // Change state.
  145. var waitingState = new WaitingGroupState(Logger);
  146. context.SetState(waitingState);
  147. waitingState.HandleRequest(context, Type, request, session, cancellationToken);
  148. }
  149. }
  150. }