aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations')
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelManager.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs1
-rw-r--r--MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs36
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs20
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs6
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs34
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs13
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs33
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs4
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs74
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs46
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs28
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs188
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs18
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs79
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs68
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs59
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs203
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs37
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs68
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs105
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs57
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs65
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs60
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs160
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs116
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs32
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs25
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs90
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj15
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs56
-rw-r--r--MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs5
-rw-r--r--MediaBrowser.Server.Implementations/Udp/UdpServer.cs65
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs62
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs16
38 files changed, 1760 insertions, 234 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
index bb7e142b6..b76cf46b0 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
@@ -530,6 +530,19 @@ namespace MediaBrowser.Server.Implementations.Channels
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
}
+ public bool SupportsSync(string channelId)
+ {
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
+ //var channel = GetChannel(channelId);
+ var channelProvider = GetChannelProvider(channelId);
+
+ return channelProvider.GetChannelFeatures().SupportsContentDownloading;
+ }
+
public ChannelFeatures GetChannelFeaturesDto(Channel channel,
IChannel provider,
InternalChannelFeatures features)
@@ -1450,6 +1463,24 @@ namespace MediaBrowser.Server.Implementations.Channels
return result;
}
+ internal IChannel GetChannelProvider(string internalChannelId)
+ {
+ if (internalChannelId == null)
+ {
+ throw new ArgumentNullException("internalChannelId");
+ }
+
+ var result = GetAllChannels()
+ .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), internalChannelId, StringComparison.OrdinalIgnoreCase));
+
+ if (result == null)
+ {
+ throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId);
+ }
+
+ return result;
+ }
+
private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user)
{
foreach (var filter in filters.OrderByDescending(f => (int)f))
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
index 6cd9e9620..50bb6c559 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
+++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using System.IO;
using CommonIO;
+using MediaBrowser.Controller.Collections;
namespace MediaBrowser.Server.Implementations.Collections
{
diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs
deleted file mode 100644
index 3e33066ae..000000000
--- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Server.Implementations.Collections
-{
- public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay
- {
- public ManualCollectionsFolder()
- {
- Name = "Collections";
- DisplayMediaType = "CollectionFolder";
- }
-
- public override bool IsHidden
- {
- get
- {
- return true;
- }
- }
-
- public bool IsHiddenFromUser(User user)
- {
- return !ConfigurationManager.Configuration.DisplayCollectionsView;
- }
-
- public override string CollectionType
- {
- get { return Model.Entities.CollectionType.BoxSets; }
- }
-
- public override string GetClientTypeName()
- {
- return typeof(CollectionFolder).Name;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
index 28a62c012..2b2373a47 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
@@ -43,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.Connect
{
LoadCachedAddress();
- _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3));
+ _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
((ConnectManager)_connectManager).Start();
}
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index be68162ca..9284f4fc7 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -1168,6 +1168,26 @@ namespace MediaBrowser.Server.Implementations.Dto
};
})
.ToList();
+
+ // Include artists that are not in the database yet, e.g., just added via metadata editor
+ var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
+ dto.ArtistItems.AddRange(hasArtist.Artists
+ .Except(foundArtists, new DistinctNameComparer())
+ .Select(i =>
+ {
+ var artist = _libraryManager.GetArtist(i);
+ if (artist != null)
+ {
+ return new NameIdPair
+ {
+ Name = artist.Name,
+ Id = artist.Id.ToString("N")
+ };
+ }
+
+ return null;
+
+ }).Where(i => i != null));
}
var hasAlbumArtist = item as IHasAlbumArtist;
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
index f82bb01bb..d14bd4368 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
@@ -92,11 +92,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
DeviceId = session.DeviceId
};
- // Report usage to remote server, except for web client, since we already have data on that
- if (!string.Equals(info.AppName, "Dashboard", StringComparison.OrdinalIgnoreCase))
- {
- ReportNewSession(info);
- }
+ ReportNewSession(info);
return info;
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
index 51a53fe21..8e46f8f03 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -71,14 +71,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer
HostConfig.Instance.MapExceptionToStatusCode = new Dictionary<Type, int>
{
- {typeof (InvalidOperationException), 422},
+ {typeof (InvalidOperationException), 500},
+ {typeof (NotImplementedException), 500},
{typeof (ResourceNotFoundException), 404},
{typeof (FileNotFoundException), 404},
{typeof (DirectoryNotFoundException), 404},
{typeof (SecurityException), 401},
{typeof (PaymentRequiredException), 402},
{typeof (UnauthorizedAccessException), 500},
- {typeof (ApplicationException), 500}
+ {typeof (ApplicationException), 500},
+ {typeof (PlatformNotSupportedException), 500},
+ {typeof (NotSupportedException), 500}
};
HostConfig.Instance.GlobalResponseHeaders = new Dictionary<string, string>();
@@ -99,14 +102,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer
// new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
//}));
- PreRequestFilters.Add((httpReq, httpRes) =>
- {
- //Handles Request and closes Responses after emitting global HTTP Headers
- if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.EndRequest(); //add a 'using ServiceStack;'
- }
- });
+ //PreRequestFilters.Add((httpReq, httpRes) =>
+ //{
+ // //Handles Request and closes Responses after emitting global HTTP Headers
+ // if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
+ // {
+ // httpRes.EndRequest(); //add a 'using ServiceStack;'
+ // }
+ //});
HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
}
@@ -400,6 +403,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer
return;
}
+ if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.StatusCode = 200;
+ httpRes.AddHeader("Access-Control-Allow-Origin", "*");
+ httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
+ httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
+ httpRes.ContentType = "text/html";
+
+ httpRes.Close();
+ }
+
var operationName = httpReq.OperationName;
var localPath = url.LocalPath;
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 7ed4dc71e..c87d10ef4 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -46,6 +46,14 @@ namespace MediaBrowser.Server.Implementations.IO
"TempSBE"
};
+ private readonly IReadOnlyList<string> _alwaysIgnoreSubstrings = new List<string>
+ {
+ // Synology
+ "@eaDir",
+ ".wd_tv",
+ ".actors"
+ };
+
private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string>
{
// thumbs.db
@@ -421,10 +429,11 @@ namespace MediaBrowser.Server.Implementations.IO
}
var filename = Path.GetFileName(path);
-
+
var monitorPath = !string.IsNullOrEmpty(filename) &&
!_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
- !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1);
// Ignore certain files
var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList();
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 5d556e3a6..442e2ebe5 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -401,7 +401,7 @@ namespace MediaBrowser.Server.Implementations.Library
var locationType = item.LocationType;
var children = item.IsFolder
- ? ((Folder)item).GetRecursiveChildren().ToList()
+ ? ((Folder)item).GetRecursiveChildren(false).ToList()
: new List<BaseItem>();
foreach (var metadataPath in GetMetadataPaths(item, children))
@@ -621,9 +621,38 @@ namespace MediaBrowser.Server.Implementations.Library
return ResolveItem(args, resolvers);
}
+ private readonly List<string> _ignoredPaths = new List<string>();
+
+ public void RegisterIgnoredPath(string path)
+ {
+ lock (_ignoredPaths)
+ {
+ _ignoredPaths.Add(path);
+ }
+ }
+ public void UnRegisterIgnoredPath(string path)
+ {
+ lock (_ignoredPaths)
+ {
+ _ignoredPaths.Remove(path);
+ }
+ }
+
public bool IgnoreFile(FileSystemMetadata file, BaseItem parent)
{
- return EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent));
+ if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)))
+ {
+ return true;
+ }
+
+ //lock (_ignoredPaths)
+ {
+ if (_ignoredPaths.Contains(file.FullName, StringComparer.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ return false;
}
public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index aefb29f1a..4c6254330 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -54,14 +54,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
if (args.IsDirectory)
{
+ if (args.HasParent<Series>())
+ {
+ return null;
+ }
+
var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
- if (args.HasParent<Series>())
- {
- return null;
- }
-
var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
@@ -76,11 +76,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
if (string.IsNullOrWhiteSpace(collectionType))
{
- if (args.HasParent<Series>())
- {
- return null;
- }
-
if (args.Parent.IsRoot)
{
return null;
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index 5fffa3d1f..2cbee7c97 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -120,8 +120,8 @@ namespace MediaBrowser.Server.Implementations.Library
}, cancellationToken).ConfigureAwait(false);
var channels = channelResult.Items;
-
- if (user.Configuration.EnableChannelView && channels.Length > 0)
+
+ if (_config.Configuration.EnableChannelView && channels.Length > 0)
{
list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false));
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 6acb0783e..8fa34109d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -33,7 +33,7 @@ using Microsoft.Win32;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
- public class EmbyTV : ILiveTvService, ISupportsNewTimerIds, IHasRegistrationInfo, IDisposable
+ public class EmbyTV : ILiveTvService, ISupportsNewTimerIds, IDisposable
{
private readonly IApplicationHost _appHpst;
private readonly ILogger _logger;
@@ -46,7 +46,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly LiveTvManager _liveTvManager;
private readonly IFileSystem _fileSystem;
- private readonly ISecurityManager _security;
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
@@ -62,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
- public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement)
+ public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement)
{
Current = this;
@@ -71,7 +70,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_httpClient = httpClient;
_config = config;
_fileSystem = fileSystem;
- _security = security;
_libraryManager = libraryManager;
_libraryMonitor = libraryMonitor;
_providerManager = providerManager;
@@ -851,29 +849,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var recordPath = RecordingPath;
var config = GetConfiguration();
- if (info.IsMovie)
- {
- var customRecordingPath = config.MovieRecordingPath;
- var allowSubfolder = true;
- if (!string.IsNullOrWhiteSpace(customRecordingPath))
- {
- allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase);
- recordPath = customRecordingPath;
- }
-
- if (allowSubfolder && config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Movies");
- }
-
- var folderName = _fileSystem.GetValidFilename(info.Name).Trim();
- if (info.ProductionYear.HasValue)
- {
- folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
- }
- recordPath = Path.Combine(recordPath, folderName);
- }
- else if (info.IsSeries)
+ if (info.IsSeries)
{
var customRecordingPath = config.SeriesRecordingPath;
var allowSubfolder = true;
@@ -910,6 +886,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recordPath = Path.Combine(recordPath, folderName);
}
}
+ else if (info.IsMovie)
+ {
+ var customRecordingPath = config.MovieRecordingPath;
+ var allowSubfolder = true;
+ if (!string.IsNullOrWhiteSpace(customRecordingPath))
+ {
+ allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase);
+ recordPath = customRecordingPath;
+ }
+
+ if (allowSubfolder && config.EnableRecordingSubfolders)
+ {
+ recordPath = Path.Combine(recordPath, "Movies");
+ }
+
+ var folderName = _fileSystem.GetValidFilename(info.Name).Trim();
+ if (info.ProductionYear.HasValue)
+ {
+ folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
+ }
+ recordPath = Path.Combine(recordPath, folderName);
+ }
else if (info.IsKids)
{
if (config.EnableRecordingSubfolders)
@@ -995,6 +993,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id);
+ _libraryManager.RegisterIgnoredPath(recordPath);
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
_fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
activeRecordingInfo.Path = recordPath;
@@ -1046,6 +1045,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
semaphore.Release();
}
+ _libraryManager.UnRegisterIgnoredPath(recordPath);
_libraryMonitor.ReportFileSystemChangeComplete(recordPath, true);
ActiveRecordingInfo removed;
@@ -1114,7 +1114,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
if (config.EnableRecordingEncoding)
{
- var regInfo = await _security.GetRegistrationStatus("embytvrecordingconversion").ConfigureAwait(false);
+ var regInfo = await _liveTvManager.GetRegistrationInfo("embytvrecordingconversion").ConfigureAwait(false);
if (regInfo.IsValid)
{
@@ -1171,8 +1171,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
{
var newTimers = GetTimersForSeries(seriesTimer, epgData, true).ToList();
-
- var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
+
+ var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
if (registration.IsValid)
{
@@ -1349,20 +1349,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
- public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
- {
- if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
- {
- return _security.GetRegistrationStatus("embytvseriesrecordings");
- }
-
- return Task.FromResult(new MBRegistrationRecord
- {
- IsValid = true,
- IsRegistered = true
- });
- }
-
public List<VirtualFolderInfo> GetRecordingFolders()
{
var list = new List<VirtualFolderInfo>();
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index fc3a507d1..75ad3de59 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -46,18 +46,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_httpClient = httpClient;
}
+ private string OutputFormat
+ {
+ get
+ {
+ var format = _liveTvOptions.RecordingEncodingFormat;
+
+ if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase))
+ {
+ return "mkv";
+ }
+
+ return "mp4";
+ }
+ }
+
public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
{
- return Path.ChangeExtension(targetFile, ".mp4");
+ return Path.ChangeExtension(targetFile, "." + OutputFormat);
}
public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
+ if (mediaSource.Path.IndexOf("m3u8", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ await RecordWithoutTempFile(mediaSource, targetFile, duration, onStarted, cancellationToken)
+ .ConfigureAwait(false);
+
+ return;
+ }
+
var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts");
try
{
- await RecordInternal(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
+ await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
.ConfigureAwait(false);
}
finally
@@ -73,7 +96,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
- public async Task RecordInternal(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+ private async Task RecordWithoutTempFile(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+ {
+ var durationToken = new CancellationTokenSource(duration);
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+
+ await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
+
+ _logger.Info("Recording completed to file {0}", targetFile);
+ }
+
+ private async Task RecordWithTempFile(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
var httpRequestOptions = new HttpRequestOptions()
{
@@ -215,15 +248,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private string GetAudioArgs(MediaSourceInfo mediaSource)
{
- // do not copy aac because many players have difficulty with aac_latm
- var copyAudio = new[] { "mp3" };
var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty;
- if (copyAudio.Contains(inputAudioCodec, StringComparer.OrdinalIgnoreCase))
- {
- return "-codec:a:0 copy";
- }
+ // do not copy aac because many players have difficulty with aac_latm
if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings && !string.Equals(inputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
return "-codec:a:0 copy";
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index e37109c14..1a5ebedc2 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -194,14 +194,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
return station;
}
- if (string.IsNullOrWhiteSpace(channelName))
+ if (!string.IsNullOrWhiteSpace(channelName))
{
- return null;
- }
+ channelName = NormalizeName(channelName);
+
+ var result = channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
- channelName = NormalizeName(channelName);
+ if (result != null)
+ {
+ return result;
+ }
+ }
- return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
+ if (!string.IsNullOrWhiteSpace(channelNumber))
+ {
+ return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
+ }
}
return null;
@@ -348,7 +356,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
if (programInfo.audioProperties != null)
{
- if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
+ if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
+ {
+ audioType = ProgramAudio.Atmos;
+ }
+ else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.DolbyDigital;
}
@@ -405,6 +417,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
if (programInfo.videoProperties != null)
{
info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
+ info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
}
if (details.contentRating != null && details.contentRating.Count > 0)
@@ -785,9 +798,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
get { return "Schedules Direct"; }
}
+ public static string TypeName = "SchedulesDirect";
public string Type
{
- get { return "SchedulesDirect"; }
+ get { return TypeName; }
}
private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 88017aa59..b3ced55a5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -31,7 +31,11 @@ using CommonIO;
using IniParser;
using IniParser.Model;
using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Events;
+using MediaBrowser.Server.Implementations.LiveTv.Listings;
namespace MediaBrowser.Server.Implementations.LiveTv
{
@@ -49,6 +53,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ITaskManager _taskManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly IProviderManager _providerManager;
+ private readonly ISecurityManager _security;
private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization;
@@ -71,7 +76,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
- public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem)
+ public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security)
{
_config = config;
_logger = logger;
@@ -83,6 +88,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_jsonSerializer = jsonSerializer;
_providerManager = providerManager;
_fileSystem = fileSystem;
+ _security = security;
_dtoService = dtoService;
_userDataManager = userDataManager;
@@ -1423,6 +1429,49 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return new QueryResult<BaseItem>();
}
+ var includeItemTypes = new List<string>();
+ var excludeItemTypes = new List<string>();
+ var genres = new List<string>();
+
+ if (query.IsMovie.HasValue)
+ {
+ if (query.IsMovie.Value)
+ {
+ includeItemTypes.Add(typeof(Movie).Name);
+ }
+ else
+ {
+ excludeItemTypes.Add(typeof(Movie).Name);
+ }
+ }
+ if (query.IsSeries.HasValue)
+ {
+ if (query.IsSeries.Value)
+ {
+ includeItemTypes.Add(typeof(Episode).Name);
+ }
+ else
+ {
+ excludeItemTypes.Add(typeof(Episode).Name);
+ }
+ }
+ if (query.IsSports.HasValue)
+ {
+ if (query.IsSports.Value)
+ {
+ genres.Add("Sports");
+ }
+ }
+ if (query.IsKids.HasValue)
+ {
+ if (query.IsKids.Value)
+ {
+ genres.Add("Kids");
+ genres.Add("Children");
+ genres.Add("Family");
+ }
+ }
+
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
MediaTypes = new[] { MediaType.Video },
@@ -1430,13 +1479,75 @@ namespace MediaBrowser.Server.Implementations.LiveTv
AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
IsFolder = false,
ExcludeLocationTypes = new[] { LocationType.Virtual },
- Limit = Math.Min(200, query.Limit ?? int.MaxValue),
+ Limit = query.Limit,
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
- EnableTotalRecordCount = query.EnableTotalRecordCount
+ EnableTotalRecordCount = query.EnableTotalRecordCount,
+ IncludeItemTypes = includeItemTypes.ToArray(),
+ ExcludeItemTypes = excludeItemTypes.ToArray(),
+ Genres = genres.ToArray()
});
}
+ public async Task<QueryResult<BaseItemDto>> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
+ {
+ var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
+ if (user != null && !IsLiveTvEnabled(user))
+ {
+ return new QueryResult<BaseItemDto>();
+ }
+
+ if (_services.Count > 1)
+ {
+ return new QueryResult<BaseItemDto>();
+ }
+
+ if (user == null || (query.IsInProgress ?? false))
+ {
+ return new QueryResult<BaseItemDto>();
+ }
+
+ var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
+ .SelectMany(i => i.Locations)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Select(i => _libraryManager.FindByPath(i, true))
+ .Where(i => i != null)
+ .Where(i => i.IsVisibleStandalone(user))
+ .ToList();
+
+ if (folders.Count == 0)
+ {
+ return new QueryResult<BaseItemDto>();
+ }
+
+ var includeItemTypes = new List<string>();
+ var excludeItemTypes = new List<string>();
+
+ includeItemTypes.Add(typeof(Series).Name);
+
+ var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+ {
+ Recursive = true,
+ AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
+ Limit = query.Limit,
+ SortBy = new[] { ItemSortBy.DateCreated },
+ SortOrder = SortOrder.Descending,
+ EnableTotalRecordCount = query.EnableTotalRecordCount,
+ IncludeItemTypes = includeItemTypes.ToArray(),
+ ExcludeItemTypes = excludeItemTypes.ToArray()
+ });
+
+ RemoveFields(options);
+
+ var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
+
+ return new QueryResult<BaseItemDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = internalResult.TotalRecordCount
+ };
+ }
+
public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@@ -1492,6 +1603,30 @@ namespace MediaBrowser.Server.Implementations.LiveTv
recordings = recordings.Where(i => i.Status == val);
}
+ if (query.IsMovie.HasValue)
+ {
+ var val = query.IsMovie.Value;
+ recordings = recordings.Where(i => i.IsMovie == val);
+ }
+
+ if (query.IsSeries.HasValue)
+ {
+ var val = query.IsSeries.Value;
+ recordings = recordings.Where(i => i.IsSeries == val);
+ }
+
+ if (query.IsKids.HasValue)
+ {
+ var val = query.IsKids.Value;
+ recordings = recordings.Where(i => i.IsKids == val);
+ }
+
+ if (query.IsSports.HasValue)
+ {
+ var val = query.IsSports.Value;
+ recordings = recordings.Where(i => i.IsSports == val);
+ }
+
if (!string.IsNullOrEmpty(query.SeriesTimerId))
{
var guid = new Guid(query.SeriesTimerId);
@@ -1950,16 +2085,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.Number = channel.Number;
dto.ChannelNumber = channel.Number;
dto.ChannelType = channel.ChannelType;
- dto.ServiceName = GetService(channel).Name;
+ dto.ServiceName = channel.ServiceName;
if (options.Fields.Contains(ItemFields.MediaSources))
{
dto.MediaSources = channel.GetMediaSources(true).ToList();
}
- var channelIdString = channel.Id.ToString("N");
if (options.AddCurrentProgram)
{
+ var channelIdString = channel.Id.ToString("N");
var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString));
if (currentProgram != null)
@@ -2091,6 +2226,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
{
+ var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
+
+ if (!registration.IsValid)
+ {
+ _logger.Info("Creating series recordings requires an active Emby Premiere subscription.");
+ return;
+ }
+
var service = GetService(timer.ServiceName);
var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
@@ -2653,33 +2796,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
}
- public Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature)
+ public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
{
- ILiveTvService service;
-
- if (string.IsNullOrWhiteSpace(programId))
- {
- var channel = GetInternalChannel(channelId);
- service = GetService(channel);
- }
- else
+ if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
{
- var program = GetInternalProgram(programId);
- service = GetService(program);
+ feature = "embytvseriesrecordings";
}
- var hasRegistration = service as IHasRegistrationInfo;
-
- if (hasRegistration != null)
+ if (string.Equals(feature, "dvr", StringComparison.OrdinalIgnoreCase))
{
- return hasRegistration.GetRegistrationInfo(feature);
+ var config = GetConfiguration();
+ if (config.TunerHosts.Count(i => i.IsEnabled) > 0 &&
+ config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
+ {
+ return Task.FromResult(new MBRegistrationRecord
+ {
+ IsRegistered = true,
+ IsValid = true
+ });
+ }
}
- return Task.FromResult(new MBRegistrationRecord
- {
- IsValid = true,
- IsRegistered = true
- });
+ return _security.GetRegistrationStatus(feature);
}
public List<NameValuePair> GetSatIniMappings()
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index ffe95c862..8095a6989 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -70,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
}
else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
{
- var channel = GetChannelnfo(extInf, tunerHostId);
+ var channel = GetChannelnfo(extInf, tunerHostId, line);
channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
channel.Path = line;
channels.Add(channel);
@@ -79,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
}
return channels;
}
- private M3UChannel GetChannelnfo(string extInf, string tunerHostId)
+ private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
{
var titleIndex = extInf.LastIndexOf(',');
var channel = new M3UChannel();
@@ -87,8 +88,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
channel.Number = extInf.Trim().Split(' ')[0] ?? "0";
channel.Name = extInf.Substring(titleIndex + 1);
-
- if(channel.Number == "-1") { channel.Number = "0"; }
//Check for channel number with the format from SatIp
int number;
@@ -101,6 +100,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
channel.Name = channel.Name.Substring(numberIndex + 1);
}
}
+
+ if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(mediaUrl))
+ {
+ channel.Number = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
+ }
+
+ if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase))
+ {
+ channel.Number = "0";
+ }
+
channel.ImageUrl = FindProperty("tvg-logo", extInf, null);
channel.Number = FindProperty("tvg-id", extInf, channel.Number);
channel.Number = FindProperty("channel-id", extInf, channel.Number);
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs
new file mode 100644
index 000000000..dddd77179
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public class ReportBlock
+ {
+ /// <summary>
+ /// Get the length of the block.
+ /// </summary>
+ public int BlockLength { get { return (24); } }
+ /// <summary>
+ /// Get the synchronization source.
+ /// </summary>
+ public string SynchronizationSource { get; private set; }
+ /// <summary>
+ /// Get the fraction lost.
+ /// </summary>
+ public int FractionLost { get; private set; }
+ /// <summary>
+ /// Get the cumulative packets lost.
+ /// </summary>
+ public int CumulativePacketsLost { get; private set; }
+ /// <summary>
+ /// Get the highest number received.
+ /// </summary>
+ public int HighestNumberReceived { get; private set; }
+ /// <summary>
+ /// Get the inter arrival jitter.
+ /// </summary>
+ public int InterArrivalJitter { get; private set; }
+ /// <summary>
+ /// Get the timestamp of the last report.
+ /// </summary>
+ public int LastReportTimeStamp { get; private set; }
+ /// <summary>
+ /// Get the delay since the last report.
+ /// </summary>
+ public int DelaySinceLastReport { get; private set; }
+
+ /// <summary>
+ /// Initialize a new instance of the ReportBlock class.
+ /// </summary>
+ public ReportBlock() { }
+
+ /// <summary>
+ /// Unpack the data in a packet.
+ /// </summary>
+ /// <param name="buffer">The buffer containing the packet.</param>
+ /// <param name="offset">The offset to the first byte of the packet within the buffer.</param>
+ /// <returns>An ErrorSpec instance if an error occurs; null otherwise.</returns>
+ public void Process(byte[] buffer, int offset)
+ {
+ SynchronizationSource = Utils.ConvertBytesToString(buffer, offset, 4);
+ FractionLost = buffer[offset + 4];
+ CumulativePacketsLost = Utils.Convert3BytesToInt(buffer, offset + 5);
+ HighestNumberReceived = Utils.Convert4BytesToInt(buffer, offset + 8);
+ InterArrivalJitter = Utils.Convert4BytesToInt(buffer, offset + 12);
+ LastReportTimeStamp = Utils.Convert4BytesToInt(buffer, offset + 16);
+ DelaySinceLastReport = Utils.Convert4BytesToInt(buffer, offset + 20);
+
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs
new file mode 100644
index 000000000..990b6dd94
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ class RtcpAppPacket : RtcpPacket
+ {
+ /// <summary>
+ /// Get the synchronization source.
+ /// </summary>
+ public int SynchronizationSource { get; private set; }
+ /// <summary>
+ /// Get the name.
+ /// </summary>
+ public string Name { get; private set; }
+ /// <summary>
+ /// Get the identity.
+ /// </summary>
+ public int Identity { get; private set; }
+ /// <summary>
+ /// Get the variable data portion.
+ /// </summary>
+ public string Data { get; private set; }
+
+ public override void Parse(byte[] buffer, int offset)
+ {
+ base.Parse(buffer, offset);
+ SynchronizationSource = Utils.Convert4BytesToInt(buffer, offset + 4);
+ Name = Utils.ConvertBytesToString(buffer, offset + 8, 4);
+ Identity = Utils.Convert2BytesToInt(buffer, offset + 12);
+
+ int dataLength = Utils.Convert2BytesToInt(buffer, offset + 14);
+ if (dataLength != 0)
+ Data = Utils.ConvertBytesToString(buffer, offset + 16, dataLength);
+ }
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("Application Specific.\n");
+ sb.AppendFormat("Version : {0} .\n", Version);
+ sb.AppendFormat("Padding : {0} .\n", Padding);
+ sb.AppendFormat("Report Count : {0} .\n", ReportCount);
+ sb.AppendFormat("PacketType: {0} .\n", Type);
+ sb.AppendFormat("Length : {0} .\n", Length);
+ sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource);
+ sb.AppendFormat("Name : {0} .\n", Name);
+ sb.AppendFormat("Identity : {0} .\n", Identity);
+ sb.AppendFormat("Data : {0} .\n", Data);
+ sb.AppendFormat(".\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs
new file mode 100644
index 000000000..c79ea31a8
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs
@@ -0,0 +1,59 @@
+using System.Collections.ObjectModel;
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public class RtcpByePacket :RtcpPacket
+ {
+ public Collection<string> SynchronizationSources { get; private set; }
+ public string ReasonForLeaving { get; private set; }
+ public override void Parse(byte[] buffer, int offset)
+ {
+ base.Parse(buffer, offset);
+ SynchronizationSources = new Collection<string>();
+ int index = 4;
+
+ while (SynchronizationSources.Count < ReportCount)
+ {
+ SynchronizationSources.Add(Utils.ConvertBytesToString(buffer, offset + index, 4));
+ index += 4;
+ }
+
+ if (index < Length)
+ {
+ int reasonLength = buffer[offset + index];
+ ReasonForLeaving = Utils.ConvertBytesToString(buffer, offset + index + 1, reasonLength);
+ }
+ }
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("ByeBye .\n");
+ sb.AppendFormat("Version : {0} .\n", Version);
+ sb.AppendFormat("Padding : {0} .\n", Padding);
+ sb.AppendFormat("Report Count : {0} .\n", ReportCount);
+ sb.AppendFormat("PacketType: {0} .\n", Type);
+ sb.AppendFormat("Length : {0} .\n", Length);
+ sb.AppendFormat("SynchronizationSources : {0} .\n", SynchronizationSources);
+ sb.AppendFormat("ReasonForLeaving : {0} .\n", ReasonForLeaving);
+ sb.AppendFormat(".\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs
new file mode 100644
index 000000000..2c54f0665
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs
@@ -0,0 +1,203 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using MediaBrowser.Model.Logging;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public class RtcpListener
+ {
+ private readonly ILogger _logger;
+ private Thread _rtcpListenerThread;
+ private AutoResetEvent _rtcpListenerThreadStopEvent = null;
+ private UdpClient _udpClient;
+ private IPEndPoint _multicastEndPoint;
+ private IPEndPoint _serverEndPoint;
+ private TransmissionMode _transmissionMode;
+
+ public RtcpListener(String address, int port, TransmissionMode mode,ILogger logger)
+ {
+ _logger = logger;
+ _transmissionMode = mode;
+ switch (mode)
+ {
+ case TransmissionMode.Unicast:
+ _udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(address), port));
+ _serverEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ break;
+ case TransmissionMode.Multicast:
+ _multicastEndPoint = new IPEndPoint(IPAddress.Parse(address), port);
+ _serverEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ _udpClient = new UdpClient();
+ _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
+ _udpClient.ExclusiveAddressUse = false;
+ _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port));
+ _udpClient.JoinMulticastGroup(_multicastEndPoint.Address);
+ break;
+ }
+ //StartRtcpListenerThread();
+ }
+
+ public void StartRtcpListenerThread()
+ {
+ // Kill the existing thread if it is in "zombie" state.
+ if (_rtcpListenerThread != null && !_rtcpListenerThread.IsAlive)
+ {
+ StopRtcpListenerThread();
+ }
+
+ if (_rtcpListenerThread == null)
+ {
+ _logger.Info("SAT>IP : starting new RTCP listener thread");
+ _rtcpListenerThreadStopEvent = new AutoResetEvent(false);
+ _rtcpListenerThread = new Thread(new ThreadStart(RtcpListenerThread));
+ _rtcpListenerThread.Name = string.Format("SAT>IP tuner RTCP listener");
+ _rtcpListenerThread.IsBackground = true;
+ _rtcpListenerThread.Priority = ThreadPriority.Lowest;
+ _rtcpListenerThread.Start();
+ }
+ }
+
+ public void StopRtcpListenerThread()
+ {
+ if (_rtcpListenerThread != null)
+ {
+ if (!_rtcpListenerThread.IsAlive)
+ {
+ _logger.Info("SAT>IP : aborting old RTCP listener thread");
+ _rtcpListenerThread.Abort();
+ }
+ else
+ {
+ _rtcpListenerThreadStopEvent.Set();
+ if (!_rtcpListenerThread.Join(400 * 2))
+ {
+ _logger.Info("SAT>IP : failed to join RTCP listener thread, aborting thread");
+ _rtcpListenerThread.Abort();
+ }
+ }
+ _rtcpListenerThread = null;
+ if (_rtcpListenerThreadStopEvent != null)
+ {
+ _rtcpListenerThreadStopEvent.Close();
+ _rtcpListenerThreadStopEvent = null;
+ }
+ }
+ }
+
+ private void RtcpListenerThread()
+ {
+ try
+ {
+ bool receivedGoodBye = false;
+ try
+ {
+ _udpClient.Client.ReceiveTimeout = 400;
+ IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ while (!receivedGoodBye && !_rtcpListenerThreadStopEvent.WaitOne(1))
+ {
+ byte[] packets = _udpClient.Receive(ref serverEndPoint);
+ if (packets == null)
+ {
+ continue;
+ }
+
+ int offset = 0;
+ while (offset < packets.Length)
+ {
+ switch (packets[offset + 1])
+ {
+ case 200: //sr
+ var sr = new RtcpSenderReportPacket();
+ sr.Parse(packets, offset);
+ offset += sr.Length;
+ break;
+ case 201: //rr
+ var rr = new RtcpReceiverReportPacket();
+ rr.Parse(packets, offset);
+ offset += rr.Length;
+ break;
+ case 202: //sd
+ var sd = new RtcpSourceDescriptionPacket();
+ sd.Parse(packets, offset);
+ offset += sd.Length;
+ break;
+ case 203: // bye
+ var bye = new RtcpByePacket();
+ bye.Parse(packets, offset);
+ receivedGoodBye = true;
+ OnPacketReceived(new RtcpPacketReceivedArgs(bye));
+ offset += bye.Length;
+ break;
+ case 204: // app
+ var app = new RtcpAppPacket();
+ app.Parse(packets, offset);
+ OnPacketReceived(new RtcpPacketReceivedArgs(app));
+ offset += app.Length;
+ break;
+ }
+ }
+
+ }
+ }
+ finally
+ {
+ switch (_transmissionMode)
+ {
+ case TransmissionMode.Multicast:
+ _udpClient.DropMulticastGroup(_multicastEndPoint.Address);
+ _udpClient.Close();
+ break;
+ case TransmissionMode.Unicast:
+ _udpClient.Close();
+ break;
+ }
+ }
+ }
+ catch (ThreadAbortException)
+ {
+ }
+ catch (Exception ex)
+ {
+ _logger.Info(string.Format("SAT>IP : RTCP listener thread exception"), ex);
+ return;
+ }
+ _logger.Info("SAT>IP : RTCP listener thread stopping");
+ }
+ public delegate void PacketReceivedHandler(object sender, RtcpPacketReceivedArgs e);
+ public event PacketReceivedHandler PacketReceived;
+ public class RtcpPacketReceivedArgs : EventArgs
+ {
+ public Object Packet { get; private set; }
+
+ public RtcpPacketReceivedArgs(Object packet)
+ {
+ Packet = packet;
+ }
+ }
+ protected void OnPacketReceived(RtcpPacketReceivedArgs args)
+ {
+ if (PacketReceived != null)
+ {
+ PacketReceived(this, args);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs
new file mode 100644
index 000000000..0a949eb7e
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public abstract class RtcpPacket
+ {
+ public int Version { get; private set; }
+ public bool Padding { get; private set; }
+ public int ReportCount { get; private set; }
+ public int Type { get; private set; }
+ public int Length { get; private set; }
+
+ public virtual void Parse(byte[] buffer, int offset)
+ {
+ Version = buffer[offset] >> 6;
+ Padding = (buffer[offset] & 0x20) != 0;
+ ReportCount = buffer[offset] & 0x1f;
+ Type = buffer[offset + 1];
+ Length = (Utils.Convert2BytesToInt(buffer, offset + 2) * 4) + 4;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs
new file mode 100644
index 000000000..abb863652
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs
@@ -0,0 +1,68 @@
+using System.Collections.ObjectModel;
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public class RtcpReceiverReportPacket :RtcpPacket
+ {
+ public string SynchronizationSource { get; private set; }
+ public Collection<ReportBlock> ReportBlocks { get; private set; }
+ public byte[] ProfileExtension { get; private set; }
+ public override void Parse(byte[] buffer, int offset)
+ {
+ base.Parse(buffer, offset);
+ SynchronizationSource = Utils.ConvertBytesToString(buffer, offset + 4, 4);
+
+ ReportBlocks = new Collection<ReportBlock>();
+ int index = 8;
+
+ while (ReportBlocks.Count < ReportCount)
+ {
+ ReportBlock reportBlock = new ReportBlock();
+ reportBlock.Process(buffer, offset + index);
+ ReportBlocks.Add(reportBlock);
+ index += reportBlock.BlockLength;
+ }
+
+ if (index < Length)
+ {
+ ProfileExtension = new byte[Length - index];
+
+ for (int extensionIndex = 0; index < Length; index++)
+ {
+ ProfileExtension[extensionIndex] = buffer[offset + index];
+ extensionIndex++;
+ }
+ }
+ }
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("Receiver Report.\n");
+ sb.AppendFormat("Version : {0} .\n", Version);
+ sb.AppendFormat("Padding : {0} .\n", Padding);
+ sb.AppendFormat("Report Count : {0} .\n", ReportCount);
+ sb.AppendFormat("PacketType: {0} .\n", Type);
+ sb.AppendFormat("Length : {0} .\n", Length);
+ sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource);
+ sb.AppendFormat(".\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs
new file mode 100644
index 000000000..dda5d6a03
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs
@@ -0,0 +1,105 @@
+using System.Collections.ObjectModel;
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ public class RtcpSenderReportPacket : RtcpPacket
+ {
+ #region Properties
+ /// <summary>
+ /// Get the synchronization source.
+ /// </summary>
+ public int SynchronizationSource { get; private set; }
+ /// <summary>
+ /// Get the NPT timestamp.
+ /// </summary>
+ public long NPTTimeStamp { get; private set; }
+ /// <summary>
+ /// Get the RTP timestamp.
+ /// </summary>
+ public int RTPTimeStamp { get; private set; }
+ /// <summary>
+ /// Get the packet count.
+ /// </summary>
+ public int SenderPacketCount { get; private set; }
+ /// <summary>
+ /// Get the octet count.
+ /// </summary>
+ public int SenderOctetCount { get; private set; }
+ /// <summary>
+ /// Get the list of report blocks.
+ /// </summary>
+ public Collection<ReportBlock> ReportBlocks { get; private set; }
+ /// <summary>
+ /// Get the profile extension data.
+ /// </summary>
+ public byte[] ProfileExtension { get; private set; }
+ #endregion
+
+ public override void Parse(byte[] buffer, int offset)
+ {
+ base.Parse(buffer, offset);
+ SynchronizationSource = Utils.Convert4BytesToInt(buffer, offset + 4);
+ NPTTimeStamp = Utils.Convert8BytesToLong(buffer, offset + 8);
+ RTPTimeStamp = Utils.Convert4BytesToInt(buffer, offset + 16);
+ SenderPacketCount = Utils.Convert4BytesToInt(buffer, offset + 20);
+ SenderOctetCount = Utils.Convert4BytesToInt(buffer, offset + 24);
+
+ ReportBlocks = new Collection<ReportBlock>();
+ int index = 28;
+
+ while (ReportBlocks.Count < ReportCount)
+ {
+ ReportBlock reportBlock = new ReportBlock();
+ reportBlock.Process(buffer, offset + index);
+ ReportBlocks.Add(reportBlock);
+ index += reportBlock.BlockLength;
+ }
+
+ if (index < Length)
+ {
+ ProfileExtension = new byte[Length - index];
+
+ for (int extensionIndex = 0; index < Length; index++)
+ {
+ ProfileExtension[extensionIndex] = buffer[offset + index];
+ extensionIndex++;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("Sender Report.\n");
+ sb.AppendFormat("Version : {0} .\n", Version);
+ sb.AppendFormat("Padding : {0} .\n", Padding);
+ sb.AppendFormat("Report Count : {0} .\n", ReportCount);
+ sb.AppendFormat("PacketType: {0} .\n", Type);
+ sb.AppendFormat("Length : {0} .\n", Length);
+ sb.AppendFormat("SynchronizationSource : {0} .\n", SynchronizationSource);
+ sb.AppendFormat("NTP Timestamp : {0} .\n", Utils.NptTimestampToDateTime(NPTTimeStamp));
+ sb.AppendFormat("RTP Timestamp : {0} .\n", RTPTimeStamp);
+ sb.AppendFormat("Sender PacketCount : {0} .\n", SenderPacketCount);
+ sb.AppendFormat("Sender Octet Count : {0} .\n", SenderOctetCount);
+ sb.AppendFormat(".\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs
new file mode 100644
index 000000000..0a95a4413
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs
@@ -0,0 +1,57 @@
+using System.Collections.ObjectModel;
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ class RtcpSourceDescriptionPacket :RtcpPacket
+ { /// <summary>
+ /// Get the list of source descriptions.
+ /// </summary>
+ public Collection<SourceDescriptionBlock> Descriptions;
+ public override void Parse(byte[] buffer, int offset)
+ {
+ base.Parse(buffer, offset);
+ Descriptions = new Collection<SourceDescriptionBlock>();
+
+ int index = 4;
+
+ while (Descriptions.Count < ReportCount)
+ {
+ SourceDescriptionBlock descriptionBlock = new SourceDescriptionBlock();
+ descriptionBlock.Process(buffer, offset + index);
+ Descriptions.Add(descriptionBlock);
+ index += descriptionBlock.BlockLength;
+ }
+ }
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("Source Description.\n");
+ sb.AppendFormat("Version : {0} .\n", Version);
+ sb.AppendFormat("Padding : {0} .\n", Padding);
+ sb.AppendFormat("Report Count : {0} .\n", ReportCount);
+ sb.AppendFormat("PacketType: {0} .\n", Type);
+ sb.AppendFormat("Length : {0} .\n", Length);
+ sb.AppendFormat("Descriptions : {0} .\n", Descriptions);
+
+ sb.AppendFormat(".\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs
new file mode 100644
index 000000000..bf56087cd
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System.Collections.ObjectModel;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ class SourceDescriptionBlock
+ {
+ /// <summary>
+ /// Get the length of the block.
+ /// </summary>
+ public int BlockLength { get { return (blockLength + (blockLength % 4)); } }
+
+ /// <summary>
+ /// Get the synchronization source.
+ /// </summary>
+ public string SynchronizationSource { get; private set; }
+ /// <summary>
+ /// Get the list of source descriptioni items.
+ /// </summary>
+ public Collection<SourceDescriptionItem> Items;
+
+ private int blockLength;
+
+ public void Process(byte[] buffer, int offset)
+ {
+ SynchronizationSource = Utils.ConvertBytesToString(buffer, offset, 4);
+ Items = new Collection<SourceDescriptionItem>();
+ int index = 4;
+ bool done = false;
+ do
+ {
+ SourceDescriptionItem item = new SourceDescriptionItem();
+ item.Process(buffer, offset + index);
+
+ if (item.Type != 0)
+ {
+ Items.Add(item);
+ index += item.ItemLength;
+ blockLength += item.ItemLength;
+ }
+ else
+ {
+ blockLength++;
+ done = true;
+ }
+ }
+ while (!done);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs
new file mode 100644
index 000000000..5dd033642
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtcp
+{
+ /// <summary>
+ /// The class that describes a source description item.
+ /// </summary>
+ public class SourceDescriptionItem
+ {
+ /// <summary>
+ /// Get the type.
+ /// </summary>
+ public int Type { get; private set; }
+ /// <summary>
+ /// Get the text.
+ /// </summary>
+ public string Text { get; private set; }
+
+ /// <summary>
+ /// Get the length of the item.
+ /// </summary>
+ public int ItemLength { get { return (Text.Length + 2); } }
+
+ /// <summary>
+ /// Initialize a new instance of the SourceDescriptionItem class.
+ /// </summary>
+ public SourceDescriptionItem() { }
+
+ /// <summary>
+ /// Unpack the data in a packet.
+ /// </summary>
+ /// <param name="buffer">The buffer containing the packet.</param>
+ /// <param name="offset">The offset to the first byte of the packet within the buffer.</param>
+ /// <returns>An ErrorSpec instance if an error occurs; null otherwise.</returns>
+ public void Process(byte[] buffer, int offset)
+ {
+ Type = buffer[offset];
+ if (Type != 0)
+ {
+ int length = buffer[offset + 1];
+ Text = Utils.ConvertBytesToString(buffer, offset + 2, length);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs
new file mode 100644
index 000000000..ea6a9ba6a
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using MediaBrowser.Model.Logging;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtp
+{
+ public class RtpListener
+ {
+ private readonly ILogger _logger;
+ private AutoResetEvent _rtpListenerThreadStopEvent;
+ private Thread _rtpListenerThread;
+ private UdpClient _udpClient;
+ private IPEndPoint _multicastEndPoint;
+ private IPEndPoint _serverEndPoint;
+ private TransmissionMode _transmissionMode;
+ public RtpListener(String address, int port,TransmissionMode mode,ILogger logger)
+ {
+ _logger = logger;
+ _transmissionMode = mode;
+ switch (mode)
+ {
+ case TransmissionMode.Unicast:
+ _udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(address), port));
+ _serverEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ break;
+ case TransmissionMode.Multicast:
+ _multicastEndPoint = new IPEndPoint(IPAddress.Parse(address), port);
+ _serverEndPoint = null;
+ _udpClient = new UdpClient();
+ _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
+ _udpClient.ExclusiveAddressUse = false;
+ _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _multicastEndPoint.Port));
+ _udpClient.JoinMulticastGroup(_multicastEndPoint.Address);
+ break;
+ }
+ //StartRtpListenerThread();
+ }
+ public void StartRtpListenerThread()
+ {
+ // Kill the existing thread if it is in "zombie" state.
+ if (_rtpListenerThread != null && !_rtpListenerThread.IsAlive)
+ {
+ StopRtpListenerThread();
+ }
+
+ if (_rtpListenerThread == null)
+ {
+ _logger.Info("SAT>IP : starting new RTP listener thread");
+ _rtpListenerThreadStopEvent = new AutoResetEvent(false);
+ _rtpListenerThread = new Thread(new ThreadStart(RtpListenerThread));
+ _rtpListenerThread.Name = string.Format("SAT>IP tuner RTP listener");
+ _rtpListenerThread.IsBackground = true;
+ _rtpListenerThread.Priority = ThreadPriority.Lowest;
+ _rtpListenerThread.Start();
+ }
+ }
+
+ public void StopRtpListenerThread()
+ {
+ if (_rtpListenerThread != null)
+ {
+ if (!_rtpListenerThread.IsAlive)
+ {
+ _logger.Info("SAT>IP : aborting old RTP listener thread");
+ _rtpListenerThread.Abort();
+ }
+ else
+ {
+ _rtpListenerThreadStopEvent.Set();
+ if (!_rtpListenerThread.Join(400 * 2))
+ {
+ _logger.Info("SAT>IP : failed to join RTP listener thread, aborting thread");
+ _rtpListenerThread.Abort();
+ }
+ }
+ _rtpListenerThread = null;
+ if (_rtpListenerThreadStopEvent != null)
+ {
+ _rtpListenerThreadStopEvent.Close();
+ _rtpListenerThreadStopEvent = null;
+ }
+ }
+ }
+
+ private void RtpListenerThread()
+ {
+ try
+ {
+ try
+ {
+
+ while (!_rtpListenerThreadStopEvent.WaitOne(1))
+ {
+ byte[] receivedbytes = _udpClient.Receive(ref _serverEndPoint);
+ RtpPacket packet = RtpPacket.Decode(receivedbytes);
+ OnPacketReceived(new RtpPacketReceivedArgs(packet));
+ }
+ }
+ finally
+ {
+ switch (_transmissionMode)
+ {
+ case TransmissionMode.Multicast:
+ _udpClient.DropMulticastGroup(_multicastEndPoint.Address);
+ _udpClient.Close();
+ break;
+ case TransmissionMode.Unicast:
+ _udpClient.Close();
+ break;
+ }
+ }
+ }
+ catch (ThreadAbortException)
+ {
+ }
+ catch (Exception ex)
+ {
+ _logger.Info(string.Format("SAT>IP : RTP listener thread exception"), ex);
+ return;
+ }
+ _logger.Info("SAT>IP : RTP listener thread stopping");
+ }
+ public delegate void PacketReceivedHandler(object sender, RtpPacketReceivedArgs e);
+ public event PacketReceivedHandler PacketReceived;
+ public class RtpPacketReceivedArgs : EventArgs
+ {
+ public RtpPacket Packet { get; private set; }
+
+ public RtpPacketReceivedArgs(RtpPacket packet)
+ {
+ Packet = packet;
+ }
+ }
+ protected void OnPacketReceived(RtpPacketReceivedArgs args)
+ {
+ if (PacketReceived != null)
+ {
+ PacketReceived(this, args);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs
new file mode 100644
index 000000000..489d7f087
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs
@@ -0,0 +1,116 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using System;
+using System.Collections.ObjectModel;
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtp
+{
+ public class RtpPacket
+ {
+ private static int MinHeaderLength = 12;
+ public int HeaderSize = MinHeaderLength;
+ public int Version { get; set; }
+ public Boolean Padding { get; set; }
+ public Boolean Extension { get; set; }
+ public int ContributingSourceCount { get; set; }
+ public Boolean Marker { get; set; }
+ public int PayloadType { get; set; }
+ public int SequenceNumber { get; set; }
+ public long TimeStamp { get; set; }
+ public long SynchronizationSource { get; set; }
+ public Collection<string> ContributingSources { get; private set; }
+ public int ExtensionHeaderId = 0;
+ public int ExtensionHeaderLength = 0;
+ public bool HasPayload { get; set; }
+ public byte[] Payload { get; set; }
+ public RtpPacket()
+ {
+
+ }
+ public static RtpPacket Decode(byte[] buffer)
+ {
+ var packet = new RtpPacket();
+ packet.Version = buffer[0] >> 6;
+ packet.Padding = (buffer[0] & 0x20) != 0;
+ packet.Extension = (buffer[0] & 0x10) != 0;
+ packet.ContributingSourceCount = buffer[0] & 0x0f;
+
+ packet.Marker = (buffer[1] & 0x80) != 0;
+ packet.PayloadType = buffer[1] & 0x7f;
+
+ packet.SequenceNumber = Utils.Convert2BytesToInt(buffer, 2);
+ packet.TimeStamp = Utils.Convert4BytesToLong(buffer, 4);
+ packet.SynchronizationSource = Utils.Convert4BytesToLong(buffer, 8);
+
+ int index = 12;
+
+ if (packet.ContributingSourceCount != 0)
+ {
+ packet.ContributingSources = new Collection<string>();
+
+ while (packet.ContributingSources.Count < packet.ContributingSourceCount)
+ {
+ packet.ContributingSources.Add(Utils.ConvertBytesToString(buffer, index, 4));
+ index += 4;
+ }
+ }
+ var dataoffset = 0;
+ if (!packet.Extension)
+ dataoffset = index;
+ else
+ {
+ packet.ExtensionHeaderId = Utils.Convert2BytesToInt(buffer, index);
+ packet.ExtensionHeaderLength = Utils.Convert2BytesToInt(buffer, index + 2);
+ dataoffset = index + packet.ExtensionHeaderLength + 4;
+ }
+
+ var dataLength = buffer.Length - dataoffset;
+ if (dataLength > dataoffset)
+ {
+ packet.HasPayload = true;
+ packet.Payload = new byte[dataLength];
+ Array.Copy(buffer, dataoffset, packet.Payload, 0, dataLength);
+ }
+ else
+ {
+ packet.HasPayload = false;
+ }
+ return packet;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("RTP Packet");
+ sb.AppendFormat("Version: {0} \n", Version);
+ sb.AppendFormat("Padding: {0} \n", Padding);
+ sb.AppendFormat("Extension: {0} \n", Extension);
+ sb.AppendFormat("Contributing Source Identifiers Count: {0} \n", ContributingSourceCount);
+ sb.AppendFormat("Marker: {0} \n", Marker);
+ sb.AppendFormat("Payload Type: {0} \n", PayloadType);
+ sb.AppendFormat("Sequence Number: {0} \n", SequenceNumber);
+ sb.AppendFormat("Timestamp: {0} .\n", TimeStamp);
+ sb.AppendFormat("Synchronization Source Identifier: {0} \n", SynchronizationSource);
+ sb.AppendFormat("\n");
+ return sb.ToString();
+ }
+
+ }
+
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
index ba5c604ed..cb0e573da 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
@@ -237,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
string modelurl = "";
string serialnumber = "";
string presentationurl = "";
- string capabilities = "";
+ //string capabilities = "";
string m3u = "";
var document = XDocument.Load(locationUri.AbsoluteUri);
var xnm = new XmlNamespaceManager(new NameTable());
@@ -284,22 +284,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
var capabilitiesElement = deviceElement.Element(n1 + "X_SATIPCAP");
if (capabilitiesElement != null)
{
- //_capabilities = capabilitiesElement.Value;
- //if (capabilitiesElement.Value.Contains(','))
- //{
- // string[] capabilities = capabilitiesElement.Value.Split(',');
- // foreach (var capability in capabilities)
- // {
- // ReadCapability(capability);
- // }
- //}
- //else
- //{
- // ReadCapability(capabilitiesElement.Value);
- //}
+ //_capabilities = capabilitiesElement.Value;
+ if (capabilitiesElement.Value.Contains(','))
+ {
+ string[] capabilities = capabilitiesElement.Value.Split(',');
+ foreach (var capability in capabilities)
+ {
+ ReadCapability(capability);
+ }
}
else
{
+ ReadCapability(capabilitiesElement.Value);
+ }
+ }
+ else
+ {
_supportsDVBS = true;
_tunerCountDVBS =1;
}
@@ -314,8 +314,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
Id = uniquedevicename,
IsEnabled = true,
Type = SatIpHost.DeviceType,
- Tuners = 1,
- TunersAvailable = 1,
+ Tuners = _tunerCountDVBS,
+ TunersAvailable = _tunerCountDVBS,
M3UUrl = m3u
};
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs
new file mode 100644
index 000000000..71d7656d9
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
+{
+ public enum TransmissionMode
+ {
+ Unicast,
+ Multicast
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs
new file mode 100644
index 000000000..3595e4b0a
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) <2007-2016> <Kay Diefenthal>
+
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SatIp.RtspSample is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
+*/
+using System;
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
+{
+ public class Utils
+ {
+ public static int Convert2BytesToInt(byte[] buffer, int offset)
+ {
+ int temp = (int)buffer[offset];
+ temp = (temp * 256) + buffer[offset + 1];
+
+ return (temp);
+ }
+ public static int Convert3BytesToInt(byte[] buffer, int offset)
+ {
+ int temp = (int)buffer[offset];
+ temp = (temp * 256) + buffer[offset + 1];
+ temp = (temp * 256) + buffer[offset + 2];
+
+ return (temp);
+ }
+ public static int Convert4BytesToInt(byte[] buffer, int offset)
+ {
+ int temp =(int)buffer[offset];
+ temp = (temp * 256) + buffer[offset + 1];
+ temp = (temp * 256) + buffer[offset + 2];
+ temp = (temp * 256) + buffer[offset + 3];
+
+ return (temp);
+ }
+ public static long Convert4BytesToLong(byte[] buffer, int offset)
+ {
+ long temp = 0;
+
+ for (int index = 0; index < 4; index++)
+ temp = (temp * 256) + buffer[offset + index];
+
+ return (temp);
+ }
+ public static long Convert8BytesToLong(byte[] buffer, int offset)
+ {
+ long temp = 0;
+
+ for (int index = 0; index < 8; index++)
+ temp = (temp * 256) + buffer[offset + index];
+
+ return (temp);
+ }
+ public static string ConvertBytesToString(byte[] buffer, int offset, int length)
+ {
+ StringBuilder reply = new StringBuilder(4);
+ for (int index = 0; index < length; index++)
+ reply.Append((char)buffer[offset + index]);
+ return (reply.ToString());
+ }
+ public static DateTime NptTimestampToDateTime(long nptTimestamp) { return NptTimestampToDateTime((uint)((nptTimestamp >> 32) & 0xFFFFFFFF), (uint)(nptTimestamp & 0xFFFFFFFF),null); }
+
+ public static DateTime NptTimestampToDateTime(uint seconds, uint fractions, DateTime? epoch )
+ {
+ ulong ticks =(ulong)((seconds * TimeSpan.TicksPerSecond) + ((fractions * TimeSpan.TicksPerSecond) / 0x100000000L));
+ if (epoch.HasValue) return epoch.Value + TimeSpan.FromTicks((Int64)ticks);
+ return (seconds & 0x80000000L) == 0 ? UtcEpoch2036 + TimeSpan.FromTicks((Int64)ticks) : UtcEpoch1900 + TimeSpan.FromTicks((Int64)ticks);
+ }
+
+ //When the First Epoch will wrap (The real Y2k)
+ public static DateTime UtcEpoch2036 = new DateTime(2036, 2, 7, 6, 28, 16, DateTimeKind.Utc);
+
+ public static DateTime UtcEpoch1900 = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ public static DateTime UtcEpoch1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 6879c3f40..8850f3d35 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -124,7 +124,6 @@
<Compile Include="Channels\RefreshChannelsScheduledTask.cs" />
<Compile Include="Collections\CollectionManager.cs" />
<Compile Include="Collections\CollectionsDynamicFolder.cs" />
- <Compile Include="Collections\ManualCollectionsFolder.cs" />
<Compile Include="Collections\CollectionImageProvider.cs" />
<Compile Include="Configuration\ServerConfigurationManager.cs" />
<Compile Include="Connect\ConnectData.cs" />
@@ -251,6 +250,18 @@
<Compile Include="LiveTv\RecordingImageProvider.cs" />
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpByePacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpListener.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpReceiverReportPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSenderReportPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSourceDescriptionPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionBlock.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionItem.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpListener.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpPacket.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" />
@@ -258,6 +269,8 @@
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\TransmissionMode.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Utils.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
<Compile Include="Logging\PatternsLogger.cs" />
<Compile Include="MediaEncoder\EncodingManager.cs" />
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
index 67ddcc5cc..7d0841fa6 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -14,6 +14,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Library;
namespace MediaBrowser.Server.Implementations.MediaEncoder
{
@@ -24,16 +25,18 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
private readonly ILogger _logger;
private readonly IMediaEncoder _encoder;
private readonly IChapterManager _chapterManager;
+ private readonly ILibraryManager _libraryManager;
public EncodingManager(IFileSystem fileSystem,
ILogger logger,
IMediaEncoder encoder,
- IChapterManager chapterManager)
+ IChapterManager chapterManager, ILibraryManager libraryManager)
{
_fileSystem = fileSystem;
_logger = logger;
_encoder = encoder;
_chapterManager = chapterManager;
+ _libraryManager = libraryManager;
}
/// <summary>
@@ -57,27 +60,38 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
return false;
}
- var options = _chapterManager.GetConfiguration();
-
- if (video is Movie)
+ var libraryOptions = _libraryManager.GetLibraryOptions(video);
+ if (libraryOptions != null && libraryOptions.SchemaVersion >= 2)
{
- if (!options.EnableMovieChapterImageExtraction)
+ if (!libraryOptions.EnableChapterImageExtraction)
{
return false;
}
}
- else if (video is Episode)
+ else
{
- if (!options.EnableEpisodeChapterImageExtraction)
+ var options = _chapterManager.GetConfiguration();
+
+ if (video is Movie)
{
- return false;
+ if (!options.EnableMovieChapterImageExtraction)
+ {
+ return false;
+ }
}
- }
- else
- {
- if (!options.EnableOtherVideoChapterImageExtraction)
+ else if (video is Episode)
{
- return false;
+ if (!options.EnableEpisodeChapterImageExtraction)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!options.EnableOtherVideoChapterImageExtraction)
+ {
+ return false;
+ }
}
}
@@ -135,16 +149,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
}
}
- // Add some time for the first chapter to make sure we don't end up with a black image
- var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
+ try
+ {
+ // Add some time for the first chapter to make sure we don't end up with a black image
+ var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
- var protocol = MediaProtocol.File;
+ var protocol = MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);
+ var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
var tempFile = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
File.Copy(tempFile, path, true);
@@ -164,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
}
catch (Exception ex)
{
- _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", inputPath));
+ _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", video.Path));
success = false;
break;
}
diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
index 175dbbc01..f40b64498 100644
--- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
+++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.Sync
if (supportsDca)
{
- mkvAudio += ",dca";
+ mkvAudio += ",dca,dts";
}
var videoProfile = "high|main|baseline|constrained baseline";
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index ffba60af8..1278a40a4 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -541,6 +541,11 @@ namespace MediaBrowser.Server.Implementations.Sync
return true;
}
+ if (item.SourceType == SourceType.Channel)
+ {
+ return BaseItem.ChannelManager.SupportsSync(item.ChannelId);
+ }
+
return item.LocationType == LocationType.FileSystem || item is Season;
}
diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
index 32992b9b2..6dd5de548 100644
--- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
+++ b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
@@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Udp
private bool _isDisposed;
- private readonly List<Tuple<string, byte[], Action<string, Encoding>>> _responders = new List<Tuple<string, byte[], Action<string, Encoding>>>();
+ private readonly List<Tuple<string, bool, Func<string, string, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, string, Encoding, Task>>>();
private readonly IServerApplicationHost _appHost;
private readonly IJsonSerializer _json;
@@ -49,43 +49,35 @@ namespace MediaBrowser.Server.Implementations.Udp
_appHost = appHost;
_json = json;
- AddMessageResponder("who is EmbyServer?", RespondToV2Message);
- AddMessageResponder("who is MediaBrowserServer_v2?", RespondToV2Message);
- AddMessageResponder("who is MediaBrowserServer?", RespondToV1Message);
+ AddMessageResponder("who is EmbyServer?", true, RespondToV2Message);
+ AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
}
- private void AddMessageResponder(string message, Action<string, Encoding> responder)
+ private void AddMessageResponder(string message, bool isSubstring, Func<string, string, Encoding, Task> responder)
{
- var expectedMessageBytes = Encoding.UTF8.GetBytes(message);
-
- _responders.Add(new Tuple<string, byte[], Action<string, Encoding>>(message, expectedMessageBytes, responder));
+ _responders.Add(new Tuple<string, bool, Func<string, string, Encoding, Task>>(message, isSubstring, responder));
}
/// <summary>
/// Raises the <see cref="E:MessageReceived" /> event.
/// </summary>
/// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param>
- private void OnMessageReceived(UdpMessageReceivedEventArgs e)
+ private async void OnMessageReceived(UdpMessageReceivedEventArgs e)
{
- var responder = _responders.FirstOrDefault(i => i.Item2.SequenceEqual(e.Bytes));
var encoding = Encoding.UTF8;
+ var responder = GetResponder(e.Bytes, encoding);
if (responder == null)
{
- var text = Encoding.Unicode.GetString(e.Bytes);
- responder = _responders.FirstOrDefault(i => string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase));
-
- if (responder != null)
- {
- encoding = Encoding.Unicode;
- }
+ encoding = Encoding.Unicode;
+ responder = GetResponder(e.Bytes, encoding);
}
if (responder != null)
{
try
{
- responder.Item3(e.RemoteEndPoint, encoding);
+ await responder.Item2.Item3(responder.Item1, e.RemoteEndPoint, encoding).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -94,33 +86,29 @@ namespace MediaBrowser.Server.Implementations.Udp
}
}
- private async void RespondToV1Message(string endpoint, Encoding encoding)
+ private Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>> GetResponder(byte[] bytes, Encoding encoding)
{
- var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
-
- if (!string.IsNullOrEmpty(localUrl))
+ var text = encoding.GetString(bytes);
+ var responder = _responders.FirstOrDefault(i =>
{
- // This is how we did the old v1 search, so need to strip off the protocol
- var index = localUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
+ if (i.Item2)
{
- localUrl = localUrl.Substring(index + 3);
+ return text.IndexOf(i.Item1, StringComparison.OrdinalIgnoreCase) != -1;
}
+ return string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase);
+ });
- // Send a response back with our ip address and port
- var response = String.Format("MediaBrowserServer|{0}", localUrl);
-
- await SendAsync(Encoding.UTF8.GetBytes(response), endpoint);
- }
- else
+ if (responder == null)
{
- _logger.Warn("Unable to respond to udp request because the local ip address could not be determined.");
+ return null;
}
+ return new Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>>(text, responder);
}
- private async void RespondToV2Message(string endpoint, Encoding encoding)
+ private async Task RespondToV2Message(string messageText, string endpoint, Encoding encoding)
{
+ var parts = messageText.Split('|');
+
var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
if (!string.IsNullOrEmpty(localUrl))
@@ -132,7 +120,12 @@ namespace MediaBrowser.Server.Implementations.Udp
Name = _appHost.FriendlyName
};
- await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint);
+ await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint).ConfigureAwait(false);
+
+ if (parts.Length > 1)
+ {
+ _appHost.EnableLoopback(parts[1]);
+ }
}
else
{
diff --git a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
index 29716d33e..2cff4a14f 100644
--- a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
@@ -13,6 +13,10 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Querying;
namespace MediaBrowser.Server.Implementations.UserViews
{
@@ -109,4 +113,62 @@ namespace MediaBrowser.Server.Implementations.UserViews
return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
}
}
+
+ public class ManualCollectionFolderImageProvider : BaseDynamicImageProvider<ManualCollectionsFolder>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public ManualCollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ {
+ return new List<ImageType>
+ {
+ ImageType.Primary
+ };
+ }
+
+ protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
+ {
+ var view = (ManualCollectionsFolder)item;
+
+ var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+
+ var items = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ Recursive = recursive,
+ IncludeItemTypes = new[] { typeof(BoxSet).Name },
+ Limit = 20,
+ SortBy = new[] { ItemSortBy.Random }
+ });
+
+ return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
+ }
+
+ protected override bool Supports(IHasImages item)
+ {
+ return item is ManualCollectionsFolder;
+ }
+
+ protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ {
+ var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
+
+ if (imageType == ImageType.Primary)
+ {
+ if (itemsWithImages.Count == 0)
+ {
+ return null;
+ }
+
+ return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
+ }
+
+ return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
+ }
+ }
+
}
diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
index 3c75c8a48..f40072897 100644
--- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -14,17 +14,20 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.LiveTv;
namespace MediaBrowser.Server.Implementations.UserViews
{
public class DynamicImageProvider : BaseDynamicImageProvider<UserView>
{
private readonly IUserManager _userManager;
+ private readonly ILibraryManager _libraryManager;
- public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager)
+ public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager)
: base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
_userManager = userManager;
+ _libraryManager = libraryManager;
}
public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
@@ -50,7 +53,15 @@ namespace MediaBrowser.Server.Implementations.UserViews
if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
{
- return new List<BaseItem>();
+ var programs = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ ImageTypes = new[] { ImageType.Primary },
+ Limit = 30,
+ IsMovie = true
+ }).ToList();
+
+ return GetFinalItems(programs).ToList();
}
if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) ||
@@ -147,6 +158,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
CollectionType.MusicVideos,
CollectionType.HomeVideos,
CollectionType.BoxSets,
+ CollectionType.LiveTv,
CollectionType.Playlists,
CollectionType.Photos,
string.Empty