aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Dlna/PlayTo/DlnaController.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Dlna/PlayTo/DlnaController.cs')
-rw-r--r--MediaBrowser.Dlna/PlayTo/DlnaController.cs481
1 files changed, 481 insertions, 0 deletions
diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
new file mode 100644
index 0000000000..894e32599d
--- /dev/null
+++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
@@ -0,0 +1,481 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Dlna.PlayTo.Configuration;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Session;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Timers;
+using Timer = System.Timers.Timer;
+
+namespace MediaBrowser.Dlna.PlayTo
+{
+ public class PlayToController : ISessionController
+ {
+ private Device _device;
+ private BaseItem _currentItem = null;
+ private TranscodeSettings[] _transcodeSettings;
+ private readonly SessionInfo _session;
+ private readonly ISessionManager _sessionManager;
+ private readonly IItemRepository _itemRepository;
+ private readonly ILibraryManager _libraryManager;
+ private readonly INetworkManager _networkManager;
+ private readonly ILogger _logger;
+ private bool _playbackStarted = false;
+
+ public bool SupportsMediaRemoteControl
+ {
+ get { return true; }
+ }
+
+ public bool IsSessionActive
+ {
+ get
+ {
+ if (_device == null || _device.UpdateTime == default(DateTime))
+ return false;
+
+ return DateTime.UtcNow <= _device.UpdateTime.AddSeconds(30);
+ }
+ }
+
+ public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager)
+ {
+ _session = session;
+ _itemRepository = itemRepository;
+ _sessionManager = sessionManager;
+ _libraryManager = libraryManager;
+ _networkManager = networkManager;
+ _logger = logger;
+ }
+
+ public void Init(Device device, TranscodeSettings[] transcodeSettings)
+ {
+ _transcodeSettings = transcodeSettings;
+ _device = device;
+ _device.PlaybackChanged += Device_PlaybackChanged;
+ _device.CurrentIdChanged += Device_CurrentIdChanged;
+ _device.Start();
+
+ _updateTimer = new Timer(1000);
+ _updateTimer.Elapsed += updateTimer_Elapsed;
+ _updateTimer.Start();
+ }
+
+ #region Device EventHandlers & Update Timer
+
+ Timer _updateTimer;
+
+ async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
+ {
+ if (_currentItem == null)
+ return;
+
+ if (e.Stopped == false)
+ await ReportProgress().ConfigureAwait(false);
+
+ else if (e.Stopped && _playbackStarted)
+ {
+ _playbackStarted = false;
+
+ await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
+ {
+ Item = _currentItem,
+ SessionId = _session.Id,
+ PositionTicks = _device.Position.Ticks
+
+ }).ConfigureAwait(false);
+
+ await SetNext().ConfigureAwait(false);
+ }
+ }
+
+ async void Device_CurrentIdChanged(object sender, CurrentIdEventArgs e)
+ {
+ if (e.Id != Guid.Empty)
+ {
+ if (_currentItem != null && _currentItem.Id == e.Id)
+ {
+ return;
+ }
+
+ var item = _libraryManager.GetItemById(e.Id);
+
+ if (item != null)
+ {
+ _logger.Debug("{0} - CurrentId {1}", _session.DeviceName, item.Id);
+ _currentItem = item;
+ _playbackStarted = false;
+
+ await ReportProgress().ConfigureAwait(false);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Handles the Elapsed event of the updateTimer control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="ElapsedEventArgs"/> instance containing the event data.</param>
+ async void updateTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ if (_disposed)
+ return;
+
+ ((Timer)sender).Stop();
+
+ await ReportProgress().ConfigureAwait(false);
+
+ if (!_disposed && IsSessionActive)
+ ((Timer)sender).Start();
+ }
+
+ /// <summary>
+ /// Reports the playback progress.
+ /// </summary>
+ /// <returns></returns>
+ private async Task ReportProgress()
+ {
+ if (_currentItem == null || _device.IsStopped)
+ return;
+
+ if (!_playbackStarted)
+ {
+ await _sessionManager.OnPlaybackStart(new PlaybackInfo { Item = _currentItem, SessionId = _session.Id, CanSeek = true, QueueableMediaTypes = new List<string> { "Audio", "Video" } }).ConfigureAwait(false);
+ _playbackStarted = true;
+ }
+
+ if ((_device.IsPlaying || _device.IsPaused))
+ {
+ var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
+ if (playlistItem != null && playlistItem.Transcode)
+ {
+ await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+ {
+ Item = _currentItem,
+ SessionId = _session.Id,
+ PositionTicks = _device.Position.Ticks + playlistItem.StartPositionTicks,
+ IsMuted = _device.IsMuted,
+ IsPaused = _device.IsPaused
+
+ }).ConfigureAwait(false);
+ }
+ else if (_currentItem != null)
+ {
+ await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+ {
+ Item = _currentItem,
+ SessionId = _session.Id,
+ PositionTicks = _device.Position.Ticks,
+ IsMuted = _device.IsMuted,
+ IsPaused = _device.IsPaused
+
+ }).ConfigureAwait(false);
+ }
+ }
+ }
+
+ #endregion
+
+ #region SendCommands
+
+ public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
+ {
+ _logger.Debug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
+
+ var items = new List<BaseItem>();
+ foreach (string id in command.ItemIds)
+ {
+ AddItemFromId(Guid.Parse(id), items);
+ }
+
+ var playlist = new List<PlaylistItem>();
+ var isFirst = true;
+
+ var serverAddress = GetServerAddress();
+
+ foreach (var item in items)
+ {
+ if (isFirst && command.StartPositionTicks.HasValue)
+ {
+ playlist.Add(CreatePlaylistItem(item, command.StartPositionTicks.Value, serverAddress));
+ isFirst = false;
+ }
+ else
+ {
+ playlist.Add(CreatePlaylistItem(item, 0, serverAddress));
+ }
+ }
+
+ _logger.Debug("{0} - Playlist created", _session.DeviceName);
+
+ if (command.PlayCommand == PlayCommand.PlayLast)
+ {
+ AddItemsToPlaylist(playlist);
+ return Task.FromResult(true);
+ }
+ if (command.PlayCommand == PlayCommand.PlayNext)
+ {
+ AddItemsToPlaylist(playlist);
+ return Task.FromResult(true);
+ }
+
+ _logger.Debug("{0} - Playing {1} items", _session.DeviceName, playlist.Count);
+ return PlayItems(playlist);
+ }
+
+ public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
+ {
+ switch (command.Command)
+ {
+ case PlaystateCommand.Stop:
+ Playlist.Clear();
+ return _device.SetStop();
+
+ case PlaystateCommand.Pause:
+ return _device.SetPause();
+
+ case PlaystateCommand.Unpause:
+ return _device.SetPlay();
+
+ case PlaystateCommand.Seek:
+ var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
+ if (playlistItem != null && playlistItem.Transcode && playlistItem.IsVideo && _currentItem != null)
+ {
+ var newItem = CreatePlaylistItem(_currentItem, command.SeekPositionTicks ?? 0, GetServerAddress());
+ playlistItem.StartPositionTicks = newItem.StartPositionTicks;
+ playlistItem.StreamUrl = newItem.StreamUrl;
+ playlistItem.Didl = newItem.Didl;
+ return _device.SetAvTransport(playlistItem.StreamUrl, playlistItem.DlnaHeaders, playlistItem.Didl);
+
+ }
+ return _device.Seek(TimeSpan.FromTicks(command.SeekPositionTicks ?? 0));
+
+
+ case PlaystateCommand.NextTrack:
+ _currentItem = null;
+ return SetNext();
+
+ case PlaystateCommand.PreviousTrack:
+ _currentItem = null;
+ return SetPrevious();
+ }
+
+ return Task.FromResult(true);
+ }
+
+ public Task SendSystemCommand(SystemCommand command, CancellationToken cancellationToken)
+ {
+ switch (command)
+ {
+ case SystemCommand.VolumeDown:
+ return _device.VolumeDown();
+ case SystemCommand.VolumeUp:
+ return _device.VolumeUp();
+ case SystemCommand.Mute:
+ return _device.VolumeDown(true);
+ case SystemCommand.Unmute:
+ return _device.VolumeUp(true);
+ case SystemCommand.ToggleMute:
+ return _device.ToggleMute();
+ default:
+ return Task.FromResult(true);
+ }
+ }
+
+ public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendServerRestartNotification(CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendServerShutdownNotification(CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendBrowseCommand(BrowseRequest command, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task SendMessageCommand(MessageCommand command, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(true);
+ }
+
+ #endregion
+
+ #region Playlist
+
+ private List<PlaylistItem> _playlist = new List<PlaylistItem>();
+
+ private List<PlaylistItem> Playlist
+ {
+ get
+ {
+ return _playlist;
+ }
+ set
+ {
+ _playlist = value;
+ }
+ }
+
+ private void AddItemFromId(Guid id, List<BaseItem> list)
+ {
+ var item = _libraryManager.GetItemById(id);
+ if (item.IsFolder)
+ {
+ foreach (var childId in _itemRepository.GetChildren(item.Id))
+ {
+ AddItemFromId(childId, list);
+ }
+ }
+ else
+ {
+ if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
+ {
+ list.Add(item);
+ }
+ }
+ }
+
+ private string GetServerAddress()
+ {
+ return string.Format("{0}://{1}:{2}/mediabrowser",
+
+ "http",
+ _networkManager.GetLocalIpAddresses().FirstOrDefault() ?? "localhost",
+ "8096"
+ );
+ }
+
+ private PlaylistItem CreatePlaylistItem(BaseItem item, long startPostionTicks, string serverAddress)
+ {
+ var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList();
+
+ var playlistItem = PlaylistItem.GetBasicConfig(item, _transcodeSettings);
+ playlistItem.StartPositionTicks = startPostionTicks;
+
+ if (playlistItem.IsAudio)
+ playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
+ else
+ {
+ playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
+ }
+
+ var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams);
+ playlistItem.Didl = didl;
+
+ var header = StreamHelper.GetDlnaHeaders(playlistItem);
+ playlistItem.DlnaHeaders = header;
+ return playlistItem;
+ }
+
+ /// <summary>
+ /// Plays the items.
+ /// </summary>
+ /// <param name="items">The items.</param>
+ /// <returns></returns>
+ private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items)
+ {
+ Playlist.Clear();
+ Playlist.AddRange(items);
+ await SetNext();
+ return true;
+ }
+
+ /// <summary>
+ /// Adds the items to playlist.
+ /// </summary>
+ /// <param name="items">The items.</param>
+ private void AddItemsToPlaylist(IEnumerable<PlaylistItem> items)
+ {
+ Playlist.AddRange(items);
+ }
+
+ private async Task<bool> SetNext()
+ {
+ if (!Playlist.Any() || Playlist.All(i => i.PlayState != 0))
+ {
+ return true;
+ }
+ var currentitem = Playlist.FirstOrDefault(i => i.PlayState == 1);
+
+ if (currentitem != null)
+ {
+ currentitem.PlayState = 2;
+ }
+
+ var nextTrack = Playlist.FirstOrDefault(i => i.PlayState == 0);
+ if (nextTrack == null)
+ {
+ await _device.SetStop();
+ return true;
+ }
+ nextTrack.PlayState = 1;
+ await _device.SetAvTransport(nextTrack.StreamUrl, nextTrack.DlnaHeaders, nextTrack.Didl);
+ if (nextTrack.StartPositionTicks > 0 && !nextTrack.Transcode)
+ await _device.Seek(TimeSpan.FromTicks(nextTrack.StartPositionTicks));
+ return true;
+ }
+
+ public Task<bool> SetPrevious()
+ {
+ if (!Playlist.Any() || Playlist.All(i => i.PlayState != 2))
+ return Task.FromResult(false);
+
+ var currentitem = Playlist.FirstOrDefault(i => i.PlayState == 1);
+
+ var prevTrack = Playlist.LastOrDefault(i => i.PlayState == 2);
+
+ if (currentitem != null)
+ {
+ currentitem.PlayState = 0;
+ }
+
+ if (prevTrack == null)
+ return Task.FromResult(false);
+
+ prevTrack.PlayState = 1;
+ return _device.SetAvTransport(prevTrack.StreamUrl, prevTrack.DlnaHeaders, prevTrack.Didl);
+ }
+
+ #endregion
+
+ private bool _disposed;
+
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+ _updateTimer.Stop();
+ _disposed = true;
+ _device.Dispose();
+ _logger.Log(LogSeverity.Debug, "PlayTo - Controller disposed");
+ }
+ }
+ }
+}