/// <summary> /// Creates a MediaItem that represents a TV stream. The MediaItem also holds information about stream indices to provide PiP /// functions (<paramref name="slotIndex"/>). /// </summary> /// <param name="slotIndex">Index of the slot (0/1)</param> /// <param name="path">Path or URL of the stream</param> /// <param name="channel"></param> /// <returns></returns> public static LiveTvMediaItem CreateMediaItem(int slotIndex, string path, IChannel channel) { if (!String.IsNullOrEmpty(path)) { ISystemResolver systemResolver = ServiceRegistration.Get <ISystemResolver>(); IDictionary <Guid, MediaItemAspect> aspects = new Dictionary <Guid, MediaItemAspect>(); MediaItemAspect providerResourceAspect; MediaItemAspect mediaAspect; SlimTvResourceAccessor resourceAccessor = new SlimTvResourceAccessor(slotIndex, path); aspects[ProviderResourceAspect.ASPECT_ID] = providerResourceAspect = new MediaItemAspect(ProviderResourceAspect.Metadata); aspects[MediaAspect.ASPECT_ID] = mediaAspect = new MediaItemAspect(MediaAspect.Metadata); // VideoAspect needs to be included to associate VideoPlayer later! aspects[VideoAspect.ASPECT_ID] = new MediaItemAspect(VideoAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId); String raPath = resourceAccessor.CanonicalLocalResourcePath.Serialize(); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, raPath); mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, "Live TV"); mediaAspect.SetAttribute(MediaAspect.ATTR_MIME_TYPE, "video/livetv"); // Custom mimetype for LiveTv LiveTvMediaItem tvStream = new LiveTvMediaItem(new Guid(), aspects); tvStream.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX] = slotIndex; tvStream.AdditionalProperties[LiveTvMediaItem.CHANNEL] = channel; tvStream.AdditionalProperties[LiveTvMediaItem.TUNING_TIME] = DateTime.Now; return(tvStream); } return(null); }
private void UpdateExistingMediaItem(MediaItem timeshiftMediaItem) { IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext == null) { continue; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvMediaItem newLiveTvMediaItem = timeshiftMediaItem as LiveTvMediaItem; // Check if this is "our" media item to update. if (liveTvMediaItem == null || newLiveTvMediaItem == null || !IsSameSlot(liveTvMediaItem, newLiveTvMediaItem)) { continue; } if (!IsSameLiveTvItem(liveTvMediaItem, newLiveTvMediaItem)) { // Switch MediaItem in current slot, the LiveTvPlayer implements IReusablePlayer and will change its source without need to change full player. playerContext.DoPlay(newLiveTvMediaItem); // Copy old channel history into new item liveTvMediaItem.TimeshiftContexes.ToList().ForEach(tc => newLiveTvMediaItem.TimeshiftContexes.Add(tc)); // Use new MediaItem, so new context will be added to new instance. liveTvMediaItem = newLiveTvMediaItem; } // Add new timeshift context AddOrUpdateTimeshiftContext(liveTvMediaItem, newLiveTvMediaItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel); } }
/// <summary> /// Checks if both <see cref="LiveTvMediaItem"/> are of same type, which includes mimeType and streaming url. This check is used to detected /// card changes on server and switching between radio and tv channels. /// </summary> /// <param name="oldItem"></param> /// <param name="newItem"></param> /// <returns></returns> protected bool IsSameLiveTvItem(LiveTvMediaItem oldItem, LiveTvMediaItem newItem) { string oldPath = oldItem.Aspects[ProviderResourceAspect.ASPECT_ID].GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH).ToString(); string newPath = newItem.Aspects[ProviderResourceAspect.ASPECT_ID].GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH).ToString(); if (oldPath != newPath) { return(false); } IChannel oldChannel = oldItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel; IChannel newChannel = newItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel; if (oldChannel != null && newChannel != null && oldChannel.MediaType != newChannel.MediaType) { return(false); } string oldMimeType; string oldTitle; string newMimeType; string newTitle; return(oldItem.GetPlayData(out oldMimeType, out oldTitle) && newItem.GetPlayData(out newMimeType, out newTitle) && oldMimeType == newMimeType); }
/// <summary> /// Creates a MediaItem that represents a TV stream. The MediaItem also holds information about stream indices to provide PiP /// functions (<paramref name="slotIndex"/>). /// </summary> /// <param name="slotIndex">Index of the slot (0/1)</param> /// <param name="path">Path or URL of the stream</param> /// <param name="channel"></param> /// <returns></returns> public static LiveTvMediaItem CreateMediaItem(int slotIndex, string path, IChannel channel) { if (!String.IsNullOrEmpty(path)) { ISystemResolver systemResolver = ServiceRegistration.Get<ISystemResolver>(); IDictionary<Guid, MediaItemAspect> aspects = new Dictionary<Guid, MediaItemAspect>(); MediaItemAspect providerResourceAspect; MediaItemAspect mediaAspect; SlimTvResourceAccessor resourceAccessor = new SlimTvResourceAccessor(slotIndex, path); aspects[ProviderResourceAspect.ASPECT_ID] = providerResourceAspect = new MediaItemAspect(ProviderResourceAspect.Metadata); aspects[MediaAspect.ASPECT_ID] = mediaAspect = new MediaItemAspect(MediaAspect.Metadata); // VideoAspect needs to be included to associate VideoPlayer later! aspects[VideoAspect.ASPECT_ID] = new MediaItemAspect(VideoAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId); String raPath = resourceAccessor.CanonicalLocalResourcePath.Serialize(); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, raPath); mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, "Live TV"); mediaAspect.SetAttribute(MediaAspect.ATTR_MIME_TYPE, "video/livetv"); // Custom mimetype for LiveTv LiveTvMediaItem tvStream = new LiveTvMediaItem(new Guid(), aspects); tvStream.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX] = slotIndex; tvStream.AdditionalProperties[LiveTvMediaItem.CHANNEL] = channel; tvStream.AdditionalProperties[LiveTvMediaItem.TUNING_TIME] = DateTime.Now; return tvStream; } return null; }
private void UpdateExistingMediaItem(MediaItem timeshiftMediaItem) { IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext != null) { LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvMediaItem newLiveTvMediaItem = timeshiftMediaItem as LiveTvMediaItem; if (liveTvMediaItem != null && newLiveTvMediaItem != null) { // check if this is "our" media item to update. if ((int)liveTvMediaItem.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX] == (int)newLiveTvMediaItem.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX]) { if (liveTvMediaItem.Aspects[ProviderResourceAspect.ASPECT_ID].GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH).ToString() != newLiveTvMediaItem.Aspects[ProviderResourceAspect.ASPECT_ID].GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH).ToString()) { //switch MediaItem in current slot playerContext.DoPlay(newLiveTvMediaItem); //clear former timeshift contexes (card change cause loss of buffer in rtsp mode). liveTvMediaItem.TimeshiftContexes.Clear(); } // add new timeshift context AddTimeshiftContext(liveTvMediaItem, newLiveTvMediaItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel); } } } } }
public MediaItem CreateMediaItem(int slotIndex, string streamUrl, IChannel channel) { // Channel is usually only passed as placeholder with ID only, so query the details here IChannelService channelService = GlobalServiceProvider.Get <IChannelService>(); Channel fullChannel = channelService.GetChannel(channel.ChannelId); bool isTv = fullChannel.MediaType == 0; LiveTvMediaItem tvStream = isTv ? SlimTvMediaItemBuilder.CreateMediaItem(slotIndex, streamUrl, fullChannel.ToChannel()) : SlimTvMediaItemBuilder.CreateRadioMediaItem(slotIndex, streamUrl, fullChannel.ToChannel()); if (tvStream != null) { // Add program infos to the LiveTvMediaItem IProgram currentProgram; IProgram nextProgram; if (GetNowNextProgram(channel, out currentProgram, out nextProgram)) { tvStream.AdditionalProperties[LiveTvMediaItem.CURRENT_PROGRAM] = currentProgram; tvStream.AdditionalProperties[LiveTvMediaItem.NEXT_PROGRAM] = nextProgram; } return(tvStream); } return(null); }
private async Task <bool> InitActionsList() { IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); IPlayerContext playerContext = playerContextManager.GetPlayerContext(PlayerChoice.PrimaryPlayer); if (playerContext == null) { return(false); } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; ILivePlayer player = playerContext.CurrentPlayer as ILivePlayer; if (liveTvMediaItem == null || player == null) { return(false); } ITimeshiftContext context = liveTvMediaItem.TimeshiftContexes.LastOrDefault(); if (context == null || context.Channel == null) { return(false); } ILocalization localization = ServiceRegistration.Get <ILocalization>(); var result = await _tvHandler.ProgramInfo.GetNowNextProgramAsync(context.Channel); return(await InitActionsList(result.Success?result.Result[0] : null, context.Channel)); }
protected async override void Update() { // Don't update the current channel and program information if we are in zap osd. if (_tvHandler == null || (_zapTimer != null && _zapTimer.IsEventPending)) { return; } // Update current programs for all channels of current group (visible inside MiniGuide). await UpdateAllCurrentPrograms(); _zapChannelIndex = ChannelContext.Instance.Channels.CurrentIndex; if (_tvHandler.NumberOfActiveSlots < 1) { PiPAvailable = false; PiPEnabled = false; return; } PiPAvailable = true; // get the current channel and program out of the LiveTvMediaItems' TimeshiftContexes IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); IPlayerContext playerContext = playerContextManager.GetPlayerContext(PlayerChoice.PrimaryPlayer); if (playerContext != null) { ILivePlayer player = playerContext.CurrentPlayer as ILivePlayer; if (player != null) { LiveTvMediaItem item = player.CurrentItem; ITimeshiftContext context = item == null ? null : item.TimeshiftContexes.LastOrDefault(); IProgram currentProgram = null; IProgram nextProgram = null; IChannel channel = null; if (context != null && context.Channel != null) { channel = context.Channel; ChannelName = channel.Name; ChannelLogoType = channel.GetFanArtMediaType(); if (_tvHandler.ProgramInfo != null) { var result = await _tvHandler.ProgramInfo.GetNowNextProgramAsync(channel); if (result.Success) { currentProgram = result.Result[0]; nextProgram = result.Result[1]; double progress = currentProgram == null ? 100d : (DateTime.Now - currentProgram.StartTime).TotalSeconds / (currentProgram.EndTime - currentProgram.StartTime).TotalSeconds * 100; _programProgressProperty.SetValue(progress); } } } CurrentProgram.SetProgram(currentProgram, channel); NextProgram.SetProgram(nextProgram, channel); } } }
private void UpdateExistingMediaItem(MediaItem timeshiftMediaItem) { IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext == null) { continue; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvMediaItem newLiveTvMediaItem = timeshiftMediaItem as LiveTvMediaItem; // Check if this is "our" media item to update. if (liveTvMediaItem == null || newLiveTvMediaItem == null || !IsSameSlot(liveTvMediaItem, newLiveTvMediaItem)) { continue; } if (!IsSameLiveTvItem(liveTvMediaItem, newLiveTvMediaItem)) { // Switch MediaItem in current slot playerContext.DoPlay(newLiveTvMediaItem); // Clear former timeshift contexes (card change cause loss of buffer in rtsp mode). liveTvMediaItem.TimeshiftContexes.Clear(); } // Add new timeshift context AddTimeshiftContext(liveTvMediaItem, newLiveTvMediaItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel); } }
private bool InitActionsList() { _dialogActionsList.Clear(); DialogHeader = "[SlimTvClient.RecordActions]"; IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); IPlayerContext playerContext = playerContextManager.GetPlayerContext(PlayerChoice.PrimaryPlayer); if (playerContext == null) { return(false); } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvPlayer player = playerContext.CurrentPlayer as LiveTvPlayer; if (liveTvMediaItem == null || player == null) { return(false); } ITimeshiftContext context = player.TimeshiftContexes.LastOrDefault(); if (context == null || context.Channel == null) { return(false); } IProgram programNow; IProgram programNext; ListItem item; ILocalization localization = ServiceRegistration.Get <ILocalization>(); bool isRecording = false; if (_tvHandler.ProgramInfo.GetNowNextProgram(context.Channel, out programNow, out programNext)) { var recStatus = GetRecordingStatus(programNow); isRecording = recStatus.HasValue && recStatus.Value.HasFlag(RecordingStatus.Scheduled | RecordingStatus.Recording); item = new ListItem(Consts.KEY_NAME, localization.ToString(isRecording ? "[SlimTvClient.StopCurrentRecording]" : "[SlimTvClient.RecordCurrentProgram]", programNow.Title)) { Command = new MethodDelegateCommand(() => CreateOrDeleteSchedule(programNow)) }; _dialogActionsList.Add(item); } if (!isRecording) { item = new ListItem(Consts.KEY_NAME, "[SlimTvClient.RecordManual]") { Command = new MethodDelegateCommand(() => CreateOrDeleteSchedule(new Program { Title = localization.ToString("[SlimTvClient.ManualRecordingTitle]"), ChannelId = context.Channel.ChannelId, StartTime = DateTime.Now, EndTime = DateTime.Now.AddDays(1) })) }; _dialogActionsList.Add(item); } _dialogActionsList.FireChange(); return(true); }
private async Task <bool> InitActionsList() { _dialogActionsList.Clear(); DialogHeader = "[SlimTvClient.RecordActions]"; IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); IPlayerContext playerContext = playerContextManager.GetPlayerContext(PlayerChoice.PrimaryPlayer); if (playerContext == null) { return(false); } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvPlayer player = playerContext.CurrentPlayer as LiveTvPlayer; if (liveTvMediaItem == null || player == null) { return(false); } ITimeshiftContext context = player.TimeshiftContexes.LastOrDefault(); if (context == null || context.Channel == null) { return(false); } ListItem item; ILocalization localization = ServiceRegistration.Get <ILocalization>(); bool isRecording = false; var result = await _tvHandler.ProgramInfo.GetNowNextProgramAsync(context.Channel); if (result.Success) { IProgram programNow = result.Result[0]; var recStatus = await GetRecordingStatusAsync(programNow); isRecording = recStatus.HasValue && recStatus.Value.HasFlag(RecordingStatus.Scheduled | RecordingStatus.Recording); item = new ListItem(Consts.KEY_NAME, localization.ToString(isRecording ? "[SlimTvClient.StopCurrentRecording]" : "[SlimTvClient.RecordCurrentProgram]", programNow.Title)) { Command = new AsyncMethodDelegateCommand(() => CreateOrDeleteSchedule(programNow)) }; _dialogActionsList.Add(item); } if (!isRecording) { item = new ListItem(Consts.KEY_NAME, "[SlimTvClient.RecordManual]") { Command = new AsyncMethodDelegateCommand(() => CreateOrDeleteScheduleByTimeAsync(context.Channel, DateTime.Now, DateTime.Now.AddDays(1))) }; _dialogActionsList.Add(item); } _dialogActionsList.FireChange(); return(true); }
protected override void Update() { // Don't update the current channel and program information if we are in zap osd. if (_tvHandler == null || !_active || _zapTimer != null) { return; } if (_tvHandler.NumberOfActiveSlots < 1) { PiPAvailable = false; PiPEnabled = false; return; } PiPAvailable = true; // get the current channel and program out of the LiveTvMediaItems' TimeshiftContexes IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); IPlayerContext playerContext = playerContextManager.GetPlayerContext(PlayerChoice.PrimaryPlayer); if (playerContext != null) { LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; LiveTvPlayer player = playerContext.CurrentPlayer as LiveTvPlayer; if (liveTvMediaItem != null && player != null) { ITimeshiftContext context = player.CurrentTimeshiftContext; IProgram currentProgram = null; IProgram nextProgram = null; if (context != null) { ChannelName = context.Channel.Name; currentProgram = context.Program; if (currentProgram != null) { currentProgram = context.Program; double progress = (DateTime.Now - currentProgram.StartTime).TotalSeconds / (currentProgram.EndTime - currentProgram.StartTime).TotalSeconds * 100; _programProgressProperty.SetValue(progress); IList <IProgram> nextPrograms; DateTime nextTime = currentProgram.EndTime.Add(TimeSpan.FromSeconds(10)); if (_tvHandler.ProgramInfo.GetPrograms(context.Channel, nextTime, nextTime, out nextPrograms)) { nextProgram = nextPrograms[0]; } } } CurrentProgram.SetProgram(currentProgram); NextProgram.SetProgram(nextProgram); } } }
/// <summary> /// Creates a new <see cref="TimeshiftContext"/> and fills the <see cref="LiveTvMediaItem.TimeshiftContexes"/> with it. /// A new context is created for each channel change or for changed programs on same channel. /// </summary> /// <param name="timeshiftMediaItem">MediaItem</param> /// <param name="channel">Current channel.</param> private void AddOrUpdateTimeshiftContext(LiveTvMediaItem timeshiftMediaItem, IChannel channel) { TimeshiftContext tsContext = new TimeshiftContext { Channel = channel }; // Remove the newly tuned channel from history if present timeshiftMediaItem.TimeshiftContexes.Where(tc => tc.Channel.ChannelId == channel.ChannelId).ToList(). ForEach(context => timeshiftMediaItem.TimeshiftContexes.Remove(context)); // Then add the new context to the end timeshiftMediaItem.TimeshiftContexes.Add(tsContext); timeshiftMediaItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] = channel; }
private void AddTimeshiftContext(LiveTvMediaItem timeshiftMediaItem, IChannel channel) { IProgram program = GetCurrentProgram(channel); TimeshiftContext tsContext = new TimeshiftContext { Channel = channel, Program = program, TuneInTime = DateTime.Now }; int tc = timeshiftMediaItem.TimeshiftContexes.Count; if (tc > 0) { ITimeshiftContext lastContext = timeshiftMediaItem.TimeshiftContexes[tc - 1]; lastContext.TimeshiftDuration = DateTime.Now - lastContext.TuneInTime; } timeshiftMediaItem.TimeshiftContexes.Add(tsContext); }
protected virtual async Task <MediaItem> CreateMediaItem(int slotIndex, string streamUrl, IChannel channel, bool isTv, IChannel fullChannel) { LiveTvMediaItem tvStream = isTv ? SlimTvMediaItemBuilder.CreateMediaItem(slotIndex, streamUrl, fullChannel) : SlimTvMediaItemBuilder.CreateRadioMediaItem(slotIndex, streamUrl, fullChannel); if (tvStream != null) { // Add program infos to the LiveTvMediaItem var result = await GetNowNextProgramAsync(channel); if (result.Success) { tvStream.AdditionalProperties[LiveTvMediaItem.CURRENT_PROGRAM] = result.Result[0]; tvStream.AdditionalProperties[LiveTvMediaItem.NEXT_PROGRAM] = result.Result[1]; } return(tvStream); } return(null); }
protected virtual MediaItem CreateMediaItem(int slotIndex, string streamUrl, IChannel channel, bool isTv, IChannel fullChannel) { LiveTvMediaItem tvStream = isTv ? SlimTvMediaItemBuilder.CreateMediaItem(slotIndex, streamUrl, fullChannel) : SlimTvMediaItemBuilder.CreateRadioMediaItem(slotIndex, streamUrl, fullChannel); if (tvStream != null) { // Add program infos to the LiveTvMediaItem IProgram currentProgram; IProgram nextProgram; if (GetNowNextProgram(channel, out currentProgram, out nextProgram)) { tvStream.AdditionalProperties[LiveTvMediaItem.CURRENT_PROGRAM] = currentProgram; tvStream.AdditionalProperties[LiveTvMediaItem.NEXT_PROGRAM] = nextProgram; } return(tvStream); } return(null); }
private IPlayerContext existingPlayerContext(int slotIndex) { IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext == null) { continue; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; // Check if this is "our" media item to update. if (liveTvMediaItem == null || (int)liveTvMediaItem.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX] != slotIndex) { continue; } return(playerContext); } return(null); }
public Task <MetadataContainer> ParseChannelStreamAsync(int channelId, LiveTvMediaItem channelMediaItem) { var info = new MetadataContainer(); var edition = 0; info.AddEdition(edition); info.Metadata[edition].VideoContainerType = VideoContainer.Mp4; info.Video[edition] = new VideoStream { Codec = VideoCodec.H264, AspectRatio = 1.777777777F, Width = 1920, Height = 1080, Framerate = 25, }; info.Audio[edition].Add(new AudioStream { Codec = AudioCodec.Aac, Channels = 2 }); return(Task.FromResult(info)); }
protected override void EnumerateChapters(bool forceRefresh) { StreamInfoHandler chapterInfo; lock (SyncObj) chapterInfo = _chapterInfo; if (chapterInfo != null && !forceRefresh) { return; } IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext == null || playerContext.CurrentPlayer != this) { continue; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; if (liveTvMediaItem == null) { continue; } _timeshiftContexes = liveTvMediaItem.TimeshiftContexes; chapterInfo = new StreamInfoHandler(); int i = 0; foreach (ITimeshiftContext timeshiftContext in _timeshiftContexes) { chapterInfo.AddUnique(new StreamInfo(null, i++, GetContextTitle(timeshiftContext), 0)); } } lock (SyncObj) _chapterInfo = chapterInfo; }
private async Task <MetadataContainer> ParseSlimTvItemAsync(LiveTvMediaItem liveMedia) { try { LiveTvMediaItem liveMediaItem = new LiveTvMediaItem(liveMedia.MediaItemId, liveMedia.Aspects); //Preserve current aspects IChannel channel = (IChannel)liveMedia.AdditionalProperties[LiveTvMediaItem.CHANNEL]; var container = await ParseChannelStreamAsync(channel.ChannelId, liveMediaItem).ConfigureAwait(false); if (container == null) { return(null); } CopyAspects(liveMediaItem, liveMedia); return(container); } catch (Exception ex) { _logger.Error("MediaAnalyzer: Live mediaitem {0} could not be parsed", ex, liveMedia.MediaItemId); } return(null); }
public MediaItem CreateMediaItem(int slotIndex, string streamUrl, IChannel channel) { LiveTvMediaItem tvStream = SlimTvMediaItemBuilder.CreateMediaItem(slotIndex, streamUrl, channel); if (tvStream != null) { // Add program infos to the LiveTvMediaItem IProgram currentProgram; if (GetCurrentProgram(channel, out currentProgram)) { tvStream.AdditionalProperties[LiveTvMediaItem.CURRENT_PROGRAM] = currentProgram; } IProgram nextProgram; if (GetNextProgram(channel, out nextProgram)) { tvStream.AdditionalProperties[LiveTvMediaItem.NEXT_PROGRAM] = nextProgram; } return(tvStream); } return(null); }
private void UpdateExistingMediaItem(MediaItem timeshiftMediaItem, int slotIndex) { LiveTvMediaItem newLiveTvMediaItem = timeshiftMediaItem as LiveTvMediaItem; IPlayerContext playerContext = existingPlayerContext(slotIndex); if (playerContext == null) { return; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; if (!IsSameLiveTvItem(liveTvMediaItem, newLiveTvMediaItem)) { // Switch MediaItem in current slot, the LiveTvPlayer implements IReusablePlayer and will change its source without need to change full player. playerContext.DoPlay(newLiveTvMediaItem); // Copy old channel history into new item liveTvMediaItem.TimeshiftContexes.ToList().ForEach(tc => newLiveTvMediaItem.TimeshiftContexes.Add(tc)); // Use new MediaItem, so new context will be added to new instance. liveTvMediaItem = newLiveTvMediaItem; } // Add new timeshift context AddOrUpdateTimeshiftContext(liveTvMediaItem, newLiveTvMediaItem.AdditionalProperties[LiveTvMediaItem.CHANNEL] as IChannel); }
protected override void EnumerateChapters(bool forceRefresh) { IList <IChannel> channelHistory; lock (SyncObj) channelHistory = _channelHistory; if (channelHistory != null && !forceRefresh) { return; } IPlayerContextManager playerContextManager = ServiceRegistration.Get <IPlayerContextManager>(); for (int index = 0; index < playerContextManager.NumActivePlayerContexts; index++) { IPlayerContext playerContext = playerContextManager.GetPlayerContext(index); if (playerContext == null || playerContext.CurrentPlayer != this) { continue; } LiveTvMediaItem liveTvMediaItem = playerContext.CurrentMediaItem as LiveTvMediaItem; if (liveTvMediaItem == null) { continue; } _timeshiftContexes = liveTvMediaItem.TimeshiftContexes; var reversedList = new List <ITimeshiftContext>(_timeshiftContexes); reversedList.Reverse(); channelHistory = reversedList.Select(timeshiftContext => timeshiftContext.Channel).ToList(); } lock (SyncObj) _channelHistory = channelHistory; }
protected override void EnumerateChapters(bool forceRefresh) { IList <IChannel> channelHistory; lock (SyncObj) channelHistory = _channelHistory; if (channelHistory != null && !forceRefresh) { return; } LiveTvMediaItem liveTvMediaItem = CurrentItem; if (liveTvMediaItem != null) { _timeshiftContexes = liveTvMediaItem.TimeshiftContexes; var reversedList = new List <ITimeshiftContext>(_timeshiftContexes); reversedList.Reverse(); channelHistory = reversedList.Select(timeshiftContext => timeshiftContext.Channel).ToList(); } lock (SyncObj) _channelHistory = channelHistory; }
public static async Task <WebMediaInfo> ProcessAsync(IOwinContext context, string itemId, WebMediaType type) { if (itemId == null) { throw new BadRequestException("GetMediaInfo: itemId is null"); } Guid mediaItemId; MediaItem item; long duration = 0; string container = string.Empty; MetadataContainer info; List <WebVideoStream> webVideoStreams = new List <WebVideoStream>(); List <WebAudioStream> webAudioStreams = new List <WebAudioStream>(); List <WebSubtitleStream> webSubtitleStreams = new List <WebSubtitleStream>(); if (type == WebMediaType.TV || type == WebMediaType.Radio) { int channelIdInt; if (int.TryParse(itemId, out channelIdInt)) { item = new LiveTvMediaItem(Guid.Empty); info = MediaAnalyzer.ParseChannelStreamAsync(channelIdInt, (LiveTvMediaItem)item).Result; if (info == null) { throw new BadRequestException(String.Format("GetMediaInfo: Channel {0} stream not available", itemId)); } } else { throw new BadRequestException(String.Format("GetMediaInfo: Channel {0} not found", itemId)); } } else if (Guid.TryParse(itemId, out mediaItemId) == true) { ISet <Guid> necessaryMIATypes = new HashSet <Guid>(); necessaryMIATypes.Add(MediaAspect.ASPECT_ID); necessaryMIATypes.Add(ProviderResourceAspect.ASPECT_ID); necessaryMIATypes.Add(ImporterAspect.ASPECT_ID); ISet <Guid> optionalMIATypes = new HashSet <Guid>(); optionalMIATypes.Add(VideoAspect.ASPECT_ID); optionalMIATypes.Add(VideoStreamAspect.ASPECT_ID); optionalMIATypes.Add(VideoAudioStreamAspect.ASPECT_ID); optionalMIATypes.Add(AudioAspect.ASPECT_ID); optionalMIATypes.Add(ImageAspect.ASPECT_ID); item = MediaLibraryAccess.GetMediaItemById(context, itemId, necessaryMIATypes, optionalMIATypes); if (item == null) { throw new BadRequestException(String.Format("GetMediaInfo: No MediaItem found with id: {0}", itemId)); } } else { throw new BadRequestException(String.Format("GetMediaInfo: Media not found with id: {0}", itemId)); } // decide which type of media item we have info = await MediaAnalyzer.ParseMediaItemAsync(item); if (item.Aspects.ContainsKey(VideoAspect.ASPECT_ID)) { var videoAspect = item.GetAspect(VideoAspect.Metadata); var videoStreamAspect = item.GetAspect(VideoStreamAspect.Metadata); duration = Convert.ToInt64(videoStreamAspect.GetAttributeValue <long?>(VideoStreamAspect.ATTR_DURATION) ?? 0); bool interlaced = (videoStreamAspect.GetAttributeValue <string>(VideoStreamAspect.ATTR_VIDEO_TYPE) ?? "").ToLowerInvariant().Contains("interlaced"); foreach (var data in info.Metadata) { int edition = data.Key; int editionOffset = 0; if (edition >= 0) { editionOffset = (edition + 1) * ProfileMediaItem.EDITION_OFFSET; } if (info.IsVideo(edition)) { // Video WebVideoStream webVideoStream = new WebVideoStream(); webVideoStream.Codec = Convert.ToString(info.Video[edition].Codec); webVideoStream.DisplayAspectRatio = Convert.ToDecimal(info.Video[edition].AspectRatio ?? 0); webVideoStream.DisplayAspectRatioString = AspectRatioHelper.AspectRatioToString(Convert.ToDecimal(info.Video[edition].AspectRatio ?? 0)); webVideoStream.Height = Convert.ToInt32(info.Video[edition].Height ?? 0); webVideoStream.Width = Convert.ToInt32(info.Video[edition].Width ?? 0); webVideoStreams.Add(webVideoStream); webVideoStream.ID = info.Video[edition].StreamIndex + editionOffset; webVideoStream.Index = 0; webVideoStream.Interlaced = interlaced; container = info.Metadata[edition].VideoContainerType.ToString(); // Audio for (int i = 0; i < info.Audio[edition].Count; i++) { WebAudioStream webAudioStream = new WebAudioStream(); if (info.Audio[edition][i].Channels != null) { webAudioStream.Channels = info.Audio[edition][i].Channels.Value; } webAudioStream.Codec = info.Audio[edition][i].Codec.ToString(); webAudioStream.ID = info.Audio[edition][i].StreamIndex + editionOffset; webAudioStream.Index = i; if (info.Audio[edition][i].Language != null) { string language = info.Audio[edition][i].Language == string.Empty ? UNDEFINED : info.Audio[edition][i].Language; webAudioStream.Language = language; if (language != UNDEFINED) { webAudioStream.LanguageFull = new CultureInfo(language).EnglishName; if (item.HasEditions && item.Editions.Any(e => e.Value.SetNo == edition)) { webAudioStream.Title = item.Editions.First(e => e.Key == edition).Value.Name; } else if (item.GetPlayData(out var mime, out var mediaName)) { webAudioStream.Title = mediaName; } else { webAudioStream.Title = "?"; } if (string.IsNullOrEmpty(webAudioStream.Codec) == false) { webAudioStream.Title += webAudioStream.Title?.Length > 0 ? $" ({webAudioStream.Codec.ToUpperInvariant()})" : webAudioStream.Codec.ToUpperInvariant(); } } } webAudioStreams.Add(webAudioStream); } // Subtitles if (info.Subtitles[edition].Any()) { int firstMediaIdx = info.Subtitles[edition].Keys.First(); for (int i = 0; i < info.Subtitles[edition][firstMediaIdx].Count; i++) { WebSubtitleStream webSubtitleStream = new WebSubtitleStream(); webSubtitleStream.Filename = info.Subtitles[edition][firstMediaIdx][i].IsEmbedded ? "Embedded" : info.Subtitles[edition][firstMediaIdx][i].SourcePath; webSubtitleStream.ID = info.Subtitles[edition][firstMediaIdx][i].StreamIndex + editionOffset; webSubtitleStream.Index = i; if (info.Subtitles[edition][firstMediaIdx][i].Language != null) { string language = info.Subtitles[edition][firstMediaIdx][i].Language == string.Empty ? UNDEFINED : info.Subtitles[edition][firstMediaIdx][i].Language; webSubtitleStream.Language = language; webSubtitleStream.LanguageFull = language; if (language != UNDEFINED) { webSubtitleStream.LanguageFull = new CultureInfo(language).EnglishName; } } webSubtitleStreams.Add(webSubtitleStream); } } } } } else if (item.Aspects.ContainsKey(AudioAspect.ASPECT_ID)) { int edition = info.Metadata.Min(d => d.Key); if (info.IsAudio(edition)) { var audioAspect = item.GetAspect(AudioAspect.Metadata); duration = (long)audioAspect[AudioAspect.ATTR_DURATION]; container = info.Metadata[edition].AudioContainerType.ToString(); } } else if (item.Aspects.ContainsKey(ImageAspect.ASPECT_ID)) { int edition = info.Metadata.Min(d => d.Key); if (info.IsImage(edition)) { container = info.Metadata[edition].ImageContainerType.ToString(); } } WebMediaInfo webMediaInfo = new WebMediaInfo { Duration = duration * 1000, Container = container, VideoStreams = webVideoStreams, AudioStreams = webAudioStreams, SubtitleStreams = webSubtitleStreams }; return(webMediaInfo); }
public static async Task <WebBoolResult> ProcessAsync(IOwinContext context, string itemId, string clientDescription, string identifier, WebMediaType type, int?idleTimeout) { if (itemId == null) { throw new BadRequestException("InitStream: itemId is null"); } if (clientDescription == null) { throw new BadRequestException("InitStream: clientDescription is null"); } if (identifier == null) { throw new BadRequestException("InitStream: identifier is null"); } StreamItem streamItem = new StreamItem { ItemType = type, ClientDescription = clientDescription, IdleTimeout = idleTimeout ?? -1, ClientIp = context.Request.RemoteIpAddress }; MediaItem mediaItem = new LiveTvMediaItem(Guid.Empty); if (streamItem.ItemType == WebMediaType.TV || streamItem.ItemType == WebMediaType.Radio) { int channelIdInt; if (!int.TryParse(itemId, out channelIdInt)) { throw new BadRequestException(string.Format("InitStream: Couldn't convert channelId to int: {0}", itemId)); } streamItem.Title = "Live TV"; if (streamItem.ItemType == WebMediaType.Radio) { streamItem.Title = "Live Radio"; } streamItem.LiveChannelId = channelIdInt; var info = await MediaAnalyzer.ParseChannelStreamAsync(channelIdInt, (LiveTvMediaItem)mediaItem); if (info == null) { throw new BadRequestException(string.Format("InitStream: Couldn't parse channel stream: {0}", channelIdInt)); } } else { Guid itemGuid; if (!Guid.TryParse(itemId, out itemGuid)) { throw new BadRequestException(string.Format("InitStream: Couldn't parse itemId: {0}", itemId)); } ISet <Guid> necessaryMIATypes = new HashSet <Guid>(); necessaryMIATypes.Add(MediaAspect.ASPECT_ID); necessaryMIATypes.Add(ProviderResourceAspect.ASPECT_ID); ISet <Guid> optionalMIATypes = new HashSet <Guid>(); optionalMIATypes.Add(VideoAspect.ASPECT_ID); optionalMIATypes.Add(AudioAspect.ASPECT_ID); optionalMIATypes.Add(ImageAspect.ASPECT_ID); mediaItem = MediaLibraryAccess.GetMediaItemById(context, itemGuid, necessaryMIATypes, optionalMIATypes); if (mediaItem == null) { throw new NotFoundException(string.Format("InitStream: Couldn't init stream! No MediaItem found with id: {0}", itemId)); } streamItem.Title = mediaItem[MediaAspect.Metadata.AspectId][0].GetAttributeValue <string>(MediaAspect.ATTR_TITLE); } streamItem.RequestedMediaItem = mediaItem; // Add the stream to the stream controller bool result = await StreamControl.AddStreamItemAsync(identifier, streamItem); return(new WebBoolResult { Result = result }); }
public async Task <LiveTvMediaItem> InitializeChannel(int channelId) { IsLive = true; MediaItemId = Guid.Empty; MediaItemTitle = "Channel " + channelId; LiveTvMediaItem item = new LiveTvMediaItem(Guid.Empty); var info = await MediaAnalyzer.ParseChannelStreamAsync(channelId, item); if (info == null) { Logger.Warn("MediaServer: Channel {0} couldn't be analyzed", channelId); return(null); } if (item.Aspects.ContainsKey(AudioAspect.ASPECT_ID)) { IsAudio = true; } else if (item.Aspects.ContainsKey(VideoAspect.ASPECT_ID)) { IsVideo = true; } else { Logger.Warn("MediaServer: Channel {0} contains no required aspect information", channelId); return(null); } if (MediaItemAspect.TryGetAttribute(item.Aspects, MediaAspect.ATTR_TITLE, out string title)) { MediaItemTitle = title; } _edition = info.Metadata.Min(m => m.Key); string transcodeId = Guid.NewGuid().ToString() + "_" + Client.Profile.ID; if (MediaServerPlugin.Settings.TranscodingAllowed) { if (IsAudio) { AudioTranscoding audio = TranscodeProfileManager.GetAudioTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID, info, _edition, IsLive, transcodeId); TranscodingParameter = audio; } else if (IsVideo) { VideoTranscoding video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID, info, _edition, Client.PreferredAudioLanguages, IsLive, transcodeId); if (video != null) { if (video.TargetVideoContainer == VideoContainer.Hls) { IsSegmented = true; } if (MediaServerPlugin.Settings.HardcodedSubtitlesAllowed == false) { video.TargetSubtitleSupport = SubtitleSupport.None; } } TranscodingParameter = video; } } if (TranscodingParameter == null) { if (IsVideo) { TranscodingParameter = TranscodeProfileManager.GetLiveVideoTranscoding(info, Client.PreferredAudioLanguages, transcodeId); } else if (IsAudio) { TranscodingParameter = TranscodeProfileManager.GetLiveAudioTranscoding(info, transcodeId); } } AssignDlnaMetadata(info, _edition); return(item); }
/// <summary> /// Checks if both <see cref="LiveTvMediaItem"/> are representing the same player slot. /// </summary> /// <param name="oldItem"></param> /// <param name="newItem"></param> /// <returns><c>true</c> if same</returns> protected bool IsSameSlot(LiveTvMediaItem oldItem, LiveTvMediaItem newItem) { return((int)oldItem.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX] == (int)newItem.AdditionalProperties[LiveTvMediaItem.SLOT_INDEX]); }