PlayingGroupState.cs 7.5 KB

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