aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2016-04-02 12:52:33 -0400
committerLuke <luke.pulverenti@gmail.com>2016-04-02 12:52:33 -0400
commitd2050ac305289fc98517468571c9ec68d7786385 (patch)
tree53dab07da3aff1dcec437d995915a17ecb0c8f15
parentba192144ecdcca0081a41834df680ed9125d0047 (diff)
parentcbcadbf892b9546ec6b6d1ce821401dfa463a090 (diff)
Merge pull request #1617 from MediaBrowser/dev
Dev
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs16
-rw-r--r--MediaBrowser.Common.Implementations/Security/MbAdmin.cs4
-rw-r--r--MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs24
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs17
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs2
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpHandler.cs47
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs4
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs225
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs2
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs12
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs105
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs88
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs140
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs149
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs688
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs251
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs169
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs3
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj11
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs2
-rw-r--r--MediaBrowser.Server.Implementations/packages.config1
-rw-r--r--MediaBrowser.Server.Mono/Native/BaseMonoApp.cs95
-rw-r--r--MediaBrowser.Server.Startup.Common/ApplicationHost.cs2
-rw-r--r--MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs142
-rw-r--r--MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs21
-rw-r--r--MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs (renamed from MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs)62
-rw-r--r--MediaBrowser.Server.Startup.Common/INativeApp.cs3
-rw-r--r--MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj4
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj8
-rw-r--r--MediaBrowser.ServerApplication/Native/WindowsApp.cs30
-rw-r--r--MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id1
-rw-r--r--MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id1
-rw-r--r--MediaBrowser.ServerApplication/packages.config1
36 files changed, 1839 insertions, 497 deletions
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 5b7bc78a8..ebcf8fbea 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -482,7 +482,14 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated(AllowBeforeStartupWizard = true)]
public class GetSatIniMappings : IReturn<List<NameValuePair>>
{
-
+
+ }
+
+ [Route("/LiveTv/TunerHosts/Satip/ChannelScan", "GET", Summary = "Scans for available channels")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class GetSatChannnelScanResult : TunerHostInfo
+ {
+
}
public class LiveTvService : BaseApiService
@@ -504,6 +511,13 @@ namespace MediaBrowser.Api.LiveTv
_dtoService = dtoService;
}
+ public async Task<object> Get(GetSatChannnelScanResult request)
+ {
+ var result = await _liveTvManager.GetSatChannelScanResult(request, CancellationToken.None).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
public async Task<object> Get(GetLiveTvRegistrationInfo request)
{
var result = await _liveTvManager.GetRegistrationInfo(request.ChannelId, request.ProgramId, request.Feature).ConfigureAwait(false);
diff --git a/MediaBrowser.Common.Implementations/Security/MbAdmin.cs b/MediaBrowser.Common.Implementations/Security/MbAdmin.cs
index ab4a83257..76ff92c2e 100644
--- a/MediaBrowser.Common.Implementations/Security/MbAdmin.cs
+++ b/MediaBrowser.Common.Implementations/Security/MbAdmin.cs
@@ -3,11 +3,11 @@ namespace MediaBrowser.Common.Implementations.Security
{
public class MbAdmin
{
- public const string HttpUrl = "http://www.mb3admin.com/admin/";
+ public const string HttpUrl = "https://www.mb3admin.com/admin/";
/// <summary>
/// Leaving as http for now until we get it squared away
/// </summary>
- public const string HttpsUrl = "http://www.mb3admin.com/admin/";
+ public const string HttpsUrl = "https://www.mb3admin.com/admin/";
}
}
diff --git a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
index af58c3731..4e01041bc 100644
--- a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
+++ b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Common.Implementations.Security
public class PluginSecurityManager : ISecurityManager
{
private const string MBValidateUrl = MbAdmin.HttpsUrl + "service/registration/validate";
- private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "http://mb3admin.com/admin/service/appstore/register";
+ private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "https://mb3admin.com/admin/service/appstore/register";
/// <summary>
/// The _is MB supporter
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index bf25da2ac..ba5da03d1 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -255,6 +255,11 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrWhiteSpace(Path))
{
+ if (SourceType == SourceType.Channel)
+ {
+ return LocationType.Remote;
+ }
+
return LocationType.Virtual;
}
@@ -494,7 +499,19 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- return _sortName ?? (_sortName = CreateSortName());
+ if (_sortName == null)
+ {
+ if (!string.IsNullOrWhiteSpace(ForcedSortName))
+ {
+ // Need the ToLower because that's what CreateSortName does
+ _sortName = ModifySortChunks(ForcedSortName).ToLower();
+ }
+ else
+ {
+ _sortName = CreateSortName();
+ }
+ }
+ return _sortName;
}
set
{
@@ -529,11 +546,6 @@ namespace MediaBrowser.Controller.Entities
/// <returns>System.String.</returns>
protected virtual string CreateSortName()
{
- if (!string.IsNullOrWhiteSpace(ForcedSortName))
- {
- return ModifySortChunks(ForcedSortName).ToLower();
- }
-
if (Name == null) return null; //some items may not have name filled in properly
if (!EnableAlphaNumericSorting)
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 88dae3c7c..d016392e8 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -175,7 +175,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <returns>System.String.</returns>
protected override string CreateSortName()
{
- return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000-") : "")
+ return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ") : "")
+ (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 511606efc..2c7d3856b 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -80,23 +80,6 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public override LocationType LocationType
- {
- get
- {
- if (SourceType == SourceType.Channel)
- {
- if (string.IsNullOrEmpty(Path))
- {
- return LocationType.Remote;
- }
- }
-
- return base.LocationType;
- }
- }
-
- [IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 56b7a307a..a4bd32fff 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -383,5 +383,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <returns>List&lt;NameValuePair&gt;.</returns>
List<NameValuePair> GetSatIniMappings();
+
+ Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index 2d1ec1273..3cdeb1afd 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Dlna.Ssdp
private Timer _notificationTimer;
private bool _isDisposed;
- private readonly ConcurrentDictionary<string, List<UpnpDevice>> _devices = new ConcurrentDictionary<string, List<UpnpDevice>>();
+ private readonly Dictionary<string, List<UpnpDevice>> _devices = new Dictionary<string, List<UpnpDevice>>();
private readonly IApplicationHost _appHost;
@@ -172,9 +172,12 @@ namespace MediaBrowser.Dlna.Ssdp
{
get
{
- var devices = _devices.ToList();
+ lock (_devices)
+ {
+ var devices = _devices.ToList();
- return devices.SelectMany(i => i.Value).ToList();
+ return devices.SelectMany(i => i.Value).ToList();
+ }
}
}
@@ -482,26 +485,42 @@ namespace MediaBrowser.Dlna.Ssdp
public void RegisterNotification(string uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
{
- var list = _devices.GetOrAdd(uuid, new List<UpnpDevice>());
+ lock (_devices)
+ {
+ List<UpnpDevice> list;
+ List<UpnpDevice> dl;
+ if (_devices.TryGetValue(uuid, out dl))
+ {
+ list = dl;
+ }
+ else
+ {
+ list = new List<UpnpDevice>();
+ _devices[uuid] = list;
+ }
- list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
+ list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
- NotifyAll();
- _logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri);
+ NotifyAll();
+ _logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri);
+ }
}
public void UnregisterNotification(string uuid)
{
- List<UpnpDevice> dl;
- if (_devices.TryRemove(uuid, out dl))
+ lock (_devices)
{
-
- foreach (var d in dl.ToList())
+ List<UpnpDevice> dl;
+ if (_devices.TryGetValue(uuid, out dl))
{
- NotifyDevice(d, "byebye", true);
- }
+ _devices.Remove(uuid);
+ foreach (var d in dl.ToList())
+ {
+ NotifyDevice(d, "byebye", true);
+ }
- _logger.Debug("Unregistered mount {0}", uuid);
+ _logger.Debug("Unregistered mount {0}", uuid);
+ }
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index 71f87ac3a..660f30cc9 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -37,6 +37,10 @@ namespace MediaBrowser.Model.LiveTv
public string FriendlyName { get; set; }
public int Tuners { get; set; }
public string DiseqC { get; set; }
+ public string SourceA { get; set; }
+ public string SourceB { get; set; }
+ public string SourceC { get; set; }
+ public string SourceD { get; set; }
public int DataVersion { get; set; }
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 95763c43f..a7e5396eb 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
-using Mono.Nat;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -11,6 +10,9 @@ using System.IO;
using System.Net;
using System.Text;
using MediaBrowser.Common.Threading;
+using Open.Nat;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.EntryPoints
{
@@ -20,9 +22,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly ISsdpHandler _ssdp;
-
- private PeriodicTimer _timer;
- private bool _isStarted;
+ private CancellationTokenSource _currentCancellationTokenSource;
+ private TimeSpan _interval = TimeSpan.FromHours(1);
public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp)
{
@@ -30,225 +31,97 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_appHost = appHost;
_config = config;
_ssdp = ssdp;
- }
-
- private string _lastConfigIdentifier;
- private string GetConfigIdentifier()
- {
- var values = new List<string>();
- var config = _config.Configuration;
-
- values.Add(config.EnableUPnP.ToString());
- values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
- values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
- values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
- values.Add(config.EnableHttps.ToString());
- values.Add(_appHost.EnableHttps.ToString());
- return string.Join("|", values.ToArray());
+ _config.ConfigurationUpdated += _config_ConfigurationUpdated;
}
- void _config_ConfigurationUpdated(object sender, EventArgs e)
+ private void _config_ConfigurationUpdated(object sender, EventArgs e)
{
- _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
-
- if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
- {
- if (_isStarted)
- {
- DisposeNat();
- }
-
- Run();
- }
}
public void Run()
{
- //NatUtility.Logger = new LogWriter(_logger);
-
- if (_config.Configuration.EnableUPnP)
- {
- Start();
- }
-
- _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
- _config.ConfigurationUpdated += _config_ConfigurationUpdated;
+ Discover();
}
- private void Start()
+ private async void Discover()
{
- _logger.Debug("Starting NAT discovery");
- NatUtility.EnabledProtocols = new List<NatProtocol>
+ if (!_config.Configuration.EnableUPnP)
{
- NatProtocol.Pmp
- };
- NatUtility.DeviceFound += NatUtility_DeviceFound;
-
- // Mono.Nat does never rise this event. The event is there however it is useless.
- // You could remove it with no risk.
- NatUtility.DeviceLost += NatUtility_DeviceLost;
-
-
- // it is hard to say what one should do when an unhandled exception is raised
- // because there isn't anything one can do about it. Probably save a log or ignored it.
- NatUtility.UnhandledException += NatUtility_UnhandledException;
- NatUtility.StartDiscovery();
-
- _timer = new PeriodicTimer(s => _createdRules = new List<string>(), null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
-
- _ssdp.MessageReceived += _ssdp_MessageReceived;
-
- _lastConfigIdentifier = GetConfigIdentifier();
+ return;
+ }
- _isStarted = true;
- }
+ var discoverer = new NatDiscoverer();
- void _ssdp_MessageReceived(object sender, SsdpMessageEventArgs e)
- {
- var endpoint = e.EndPoint as IPEndPoint;
+ var cancellationTokenSource = new CancellationTokenSource(10000);
+ _currentCancellationTokenSource = cancellationTokenSource;
- if (endpoint != null && e.LocalEndPoint != null)
+ try
{
- NatUtility.Handle(e.LocalEndPoint.Address, e.Message, endpoint, NatProtocol.Upnp);
- }
- }
+ var device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cancellationTokenSource).ConfigureAwait(false);
- void NatUtility_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- var ex = e.ExceptionObject as Exception;
-
- if (ex == null)
- {
- //_logger.Error("Unidentified error reported by Mono.Nat");
+ await CreateRules(device).ConfigureAwait(false);
}
- else
+ catch (OperationCanceledException)
{
- // Seeing some blank exceptions coming through here
- //_logger.ErrorException("Error reported by Mono.Nat: ", ex);
- }
- }
-
- void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
- {
- try
- {
- var device = e.Device;
- _logger.Debug("NAT device found: {0}", device.LocalAddress.ToString());
- CreateRules(device);
}
catch (Exception ex)
{
- // I think it could be a good idea to log the exception because
- // you are using permanent portmapping here (never expire) and that means that next time
- // CreatePortMap is invoked it can fails with a 718-ConflictInMappingEntry or not. That depends
- // on the router's upnp implementation (specs says it should fail however some routers don't do it)
- // It also can fail with others like 727-ExternalPortOnlySupportsWildcard, 728-NoPortMapsAvailable
- // and those errors (upnp errors) could be useful for diagnosting.
-
- // Commenting out because users are reporting problems out of our control
- //_logger.ErrorException("Error creating port forwarding rules", ex);
+ _logger.ErrorException("Error discovering NAT devices", ex);
}
- }
-
- private List<string> _createdRules = new List<string>();
- private void CreateRules(INatDevice device)
- {
- // On some systems the device discovered event seems to fire repeatedly
- // This check will help ensure we're not trying to port map the same device over and over
-
- var address = device.LocalAddress.ToString();
-
- if (!_createdRules.Contains(address))
+ finally
{
- _createdRules.Add(address);
-
- CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
- CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
+ _currentCancellationTokenSource = null;
}
- }
- private void CreatePortMap(INatDevice device, int privatePort, int publicPort)
- {
- _logger.Debug("Creating port map on port {0}", privatePort);
- device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
+ if (_config.Configuration.EnableUPnP)
{
- Description = _appHost.Name
- });
+ await Task.Delay(_interval).ConfigureAwait(false);
+ Discover();
+ }
}
- // As I said before, this method will be never invoked. You can remove it.
- void NatUtility_DeviceLost(object sender, DeviceEventArgs e)
+ private async Task CreateRules(NatDevice device)
{
- var device = e.Device;
- _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
- }
+ // On some systems the device discovered event seems to fire repeatedly
+ // This check will help ensure we're not trying to port map the same device over and over
- public void Dispose()
- {
- DisposeNat();
+ await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
+ await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
}
- private void DisposeNat()
+ private async Task CreatePortMap(NatDevice device, int privatePort, int publicPort)
{
- _logger.Debug("Stopping NAT discovery");
-
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
-
- _ssdp.MessageReceived -= _ssdp_MessageReceived;
+ _logger.Debug("Creating port map on port {0}", privatePort);
try
{
- // This is not a significant improvement
- NatUtility.StopDiscovery();
- NatUtility.DeviceFound -= NatUtility_DeviceFound;
- NatUtility.DeviceLost -= NatUtility_DeviceLost;
- NatUtility.UnhandledException -= NatUtility_UnhandledException;
+ await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, privatePort, publicPort, _appHost.Name)).ConfigureAwait(false);
}
- // Statements in try-block will no fail because StopDiscovery is a one-line
- // method that was no chances to fail.
- // public static void StopDiscovery ()
- // {
- // searching.Reset();
- // }
- // IMO you could remove the catch-block
catch (Exception ex)
{
- _logger.ErrorException("Error stopping NAT Discovery", ex);
- }
- finally
- {
- _isStarted = false;
+ _logger.ErrorException("Error creating port map", ex);
}
}
- private class LogWriter : TextWriter
+ public void Dispose()
{
- private readonly ILogger _logger;
-
- public LogWriter(ILogger logger)
- {
- _logger = logger;
- }
-
- public override Encoding Encoding
- {
- get { return Encoding.UTF8; }
- }
-
- public override void WriteLine(string format, params object[] arg)
- {
- _logger.Debug(format, arg);
- }
+ DisposeNat();
+ }
- public override void WriteLine(string value)
+ private void DisposeNat()
+ {
+ if (_currentCancellationTokenSource != null)
{
- _logger.Debug(value);
+ try
+ {
+ _currentCancellationTokenSource.Cancel();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error calling _currentCancellationTokenSource.Cancel", ex);
+ }
}
}
}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
index 7e22efb23..7b3a7a30d 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly IHttpClient _httpClient;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
- private const string MbAdminUrl = "http://www.mb3admin.com/admin/";
+ private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 3849f44ab..d40f2a141 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -2450,7 +2450,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public List<NameValuePair> GetSatIniMappings()
{
- var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini.satellite", StringComparison.OrdinalIgnoreCase) != -1).ToList();
+ var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList();
return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList();
}
@@ -2472,13 +2472,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return null;
}
+ var srch = "SatIp.ini.";
+ var filename = Path.GetFileName(resource);
+
return new NameValuePair
{
Name = satType1 + " " + satType2,
- Value = satType2 + "|" + Path.GetFileName(resource)
+ Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length)
};
}
}
}
+
+ public Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken)
+ {
+ return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken);
+ }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs
new file mode 100644
index 000000000..fdeae25b0
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using IniParser;
+using IniParser.Model;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
+{
+ public class ChannelScan
+ {
+ private readonly ILogger _logger;
+
+ public ChannelScan(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task<List<ChannelInfo>> Scan(TunerHostInfo info, CancellationToken cancellationToken)
+ {
+ var ini = info.SourceA.Split('|')[1];
+ var resource = GetType().Assembly.GetManifestResourceNames().FirstOrDefault(i => i.EndsWith(ini, StringComparison.OrdinalIgnoreCase));
+
+ _logger.Info("Opening ini file {0}", resource);
+ var list = new List<ChannelInfo>();
+
+ using (var stream = GetType().Assembly.GetManifestResourceStream(resource))
+ {
+ using (var reader = new StreamReader(stream))
+ {
+ var parser = new StreamIniDataParser();
+ var data = parser.ReadData(reader);
+
+ var count = GetInt(data, "DVB", "0", 0);
+
+ _logger.Info("DVB Count: {0}", count);
+
+ var index = 1;
+ var source = "1";
+
+ while (index <= count)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var rtspSession = new RtspSession(info.Url, _logger))
+ {
+ float percent = count == 0 ? 0 : (float)(index) / count;
+ percent = Math.Max(percent * 100, 100);
+
+ //SetControlPropertyThreadSafe(pgbSearchResult, "Value", (int)percent);
+ var strArray = data["DVB"][index.ToString(CultureInfo.InvariantCulture)].Split(',');
+
+ string tuning;
+ if (strArray[4] == "S2")
+ {
+ tuning = string.Format("src={0}&freq={1}&pol={2}&sr={3}&fec={4}&msys=dvbs2&mtype={5}&plts=on&ro=0.35&pids=0,16,17,18,20", source, strArray[0], strArray[1].ToLower(), strArray[2].ToLower(), strArray[3], strArray[5].ToLower());
+ }
+ else
+ {
+ tuning = string.Format("src={0}&freq={1}&pol={2}&sr={3}&fec={4}&msys=dvbs&mtype={5}&pids=0,16,17,18,20", source, strArray[0], strArray[1].ToLower(), strArray[2], strArray[3], strArray[5].ToLower());
+ }
+
+ rtspSession.Setup(tuning, "unicast");
+
+ rtspSession.Play(string.Empty);
+
+ int signallevel;
+ int signalQuality;
+ rtspSession.Describe(out signallevel, out signalQuality);
+
+ await Task.Delay(500).ConfigureAwait(false);
+ index++;
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private int GetInt(IniData data, string s1, string s2, int defaultValue)
+ {
+ var value = data[s1][s2];
+ int numericValue;
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out numericValue))
+ {
+ return numericValue;
+ }
+
+ return defaultValue;
+ }
+ }
+
+ public class SatChannel
+ {
+ // TODO: Add properties
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs
new file mode 100644
index 000000000..5f286f1db
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs
@@ -0,0 +1,88 @@
+/*
+ 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.Generic;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
+{
+ /// <summary>
+ /// Standard RTSP request methods.
+ /// </summary>
+ public sealed class RtspMethod
+ {
+ public override int GetHashCode()
+ {
+ return (_name != null ? _name.GetHashCode() : 0);
+ }
+
+ private readonly string _name;
+ private static readonly IDictionary<string, RtspMethod> _values = new Dictionary<string, RtspMethod>();
+
+ public static readonly RtspMethod Describe = new RtspMethod("DESCRIBE");
+ public static readonly RtspMethod Announce = new RtspMethod("ANNOUNCE");
+ public static readonly RtspMethod GetParameter = new RtspMethod("GET_PARAMETER");
+ public static readonly RtspMethod Options = new RtspMethod("OPTIONS");
+ public static readonly RtspMethod Pause = new RtspMethod("PAUSE");
+ public static readonly RtspMethod Play = new RtspMethod("PLAY");
+ public static readonly RtspMethod Record = new RtspMethod("RECORD");
+ public static readonly RtspMethod Redirect = new RtspMethod("REDIRECT");
+ public static readonly RtspMethod Setup = new RtspMethod("SETUP");
+ public static readonly RtspMethod SetParameter = new RtspMethod("SET_PARAMETER");
+ public static readonly RtspMethod Teardown = new RtspMethod("TEARDOWN");
+
+ private RtspMethod(string name)
+ {
+ _name = name;
+ _values.Add(name, this);
+ }
+
+ public override string ToString()
+ {
+ return _name;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var method = obj as RtspMethod;
+ if (method != null && this == method)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public static ICollection<RtspMethod> Values
+ {
+ get { return _values.Values; }
+ }
+
+ public static explicit operator RtspMethod(string name)
+ {
+ RtspMethod value;
+ if (!_values.TryGetValue(name, out value))
+ {
+ return null;
+ }
+ return value;
+ }
+
+ public static implicit operator string(RtspMethod method)
+ {
+ return method._name;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs
new file mode 100644
index 000000000..600eda02d
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs
@@ -0,0 +1,140 @@
+/*
+ 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.Generic;
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
+{
+ /// <summary>
+ /// A simple class that can be used to serialise RTSP requests.
+ /// </summary>
+ public class RtspRequest
+ {
+ private readonly RtspMethod _method;
+ private readonly string _uri;
+ private readonly int _majorVersion;
+ private readonly int _minorVersion;
+ private IDictionary<string, string> _headers = new Dictionary<string, string>();
+ private string _body = string.Empty;
+
+ /// <summary>
+ /// Initialise a new instance of the <see cref="RtspRequest"/> class.
+ /// </summary>
+ /// <param name="method">The request method.</param>
+ /// <param name="uri">The request URI</param>
+ /// <param name="majorVersion">The major version number.</param>
+ /// <param name="minorVersion">The minor version number.</param>
+ public RtspRequest(RtspMethod method, string uri, int majorVersion, int minorVersion)
+ {
+ _method = method;
+ _uri = uri;
+ _majorVersion = majorVersion;
+ _minorVersion = minorVersion;
+ }
+
+ /// <summary>
+ /// Get the request method.
+ /// </summary>
+ public RtspMethod Method
+ {
+ get
+ {
+ return _method;
+ }
+ }
+
+ /// <summary>
+ /// Get the request URI.
+ /// </summary>
+ public string Uri
+ {
+ get
+ {
+ return _uri;
+ }
+ }
+
+ /// <summary>
+ /// Get the request major version number.
+ /// </summary>
+ public int MajorVersion
+ {
+ get
+ {
+ return _majorVersion;
+ }
+ }
+
+ /// <summary>
+ /// Get the request minor version number.
+ /// </summary>
+ public int MinorVersion
+ {
+ get
+ {
+ return _minorVersion;
+ }
+ }
+
+ /// <summary>
+ /// Get or set the request headers.
+ /// </summary>
+ public IDictionary<string, string> Headers
+ {
+ get
+ {
+ return _headers;
+ }
+ set
+ {
+ _headers = value;
+ }
+ }
+
+ /// <summary>
+ /// Get or set the request body.
+ /// </summary>
+ public string Body
+ {
+ get
+ {
+ return _body;
+ }
+ set
+ {
+ _body = value;
+ }
+ }
+
+ /// <summary>
+ /// Serialise this request.
+ /// </summary>
+ /// <returns>raw request bytes</returns>
+ public byte[] Serialise()
+ {
+ var request = new StringBuilder();
+ request.AppendFormat("{0} {1} RTSP/{2}.{3}\r\n", _method, _uri, _majorVersion, _minorVersion);
+ foreach (var header in _headers)
+ {
+ request.AppendFormat("{0}: {1}\r\n", header.Key, header.Value);
+ }
+ request.AppendFormat("\r\n{0}", _body);
+ return Encoding.UTF8.GetBytes(request.ToString());
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs
new file mode 100644
index 000000000..97290623b
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs
@@ -0,0 +1,149 @@
+/*
+ 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.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
+{
+ /// <summary>
+ /// A simple class that can be used to deserialise RTSP responses.
+ /// </summary>
+ public class RtspResponse
+ {
+ private static readonly Regex RegexStatusLine = new Regex(@"RTSP/(\d+)\.(\d+)\s+(\d+)\s+([^.]+?)\r\n(.*)", RegexOptions.Singleline);
+
+ private int _majorVersion = 1;
+ private int _minorVersion;
+ private RtspStatusCode _statusCode;
+ private string _reasonPhrase;
+ private IDictionary<string, string> _headers;
+ private string _body;
+
+ /// <summary>
+ /// Initialise a new instance of the <see cref="RtspResponse"/> class.
+ /// </summary>
+ private RtspResponse()
+ {
+ }
+
+ /// <summary>
+ /// Get the response major version number.
+ /// </summary>
+ public int MajorVersion
+ {
+ get
+ {
+ return _majorVersion;
+ }
+ }
+
+ /// <summary>
+ /// Get the response minor version number.
+ /// </summary>
+ public int MinorVersion
+ {
+ get
+ {
+ return _minorVersion;
+ }
+ }
+
+ /// <summary>
+ /// Get the response status code.
+ /// </summary>
+ public RtspStatusCode StatusCode
+ {
+ get
+ {
+ return _statusCode;
+ }
+ }
+
+ /// <summary>
+ /// Get the response reason phrase.
+ /// </summary>
+ public string ReasonPhrase
+ {
+ get
+ {
+ return _reasonPhrase;
+ }
+ }
+
+ /// <summary>
+ /// Get the response headers.
+ /// </summary>
+ public IDictionary<string, string> Headers
+ {
+ get
+ {
+ return _headers;
+ }
+ }
+
+ /// <summary>
+ /// Get the response body.
+ /// </summary>
+ public string Body
+ {
+ get
+ {
+ return _body;
+ }
+ set
+ {
+ _body = value;
+ }
+ }
+
+ /// <summary>
+ /// Deserialise/parse an RTSP response.
+ /// </summary>
+ /// <param name="responseBytes">The raw response bytes.</param>
+ /// <param name="responseByteCount">The number of valid bytes in the response.</param>
+ /// <returns>a response object</returns>
+ public static RtspResponse Deserialise(byte[] responseBytes, int responseByteCount)
+ {
+ var response = new RtspResponse();
+ var responseString = Encoding.UTF8.GetString(responseBytes, 0, responseByteCount);
+
+ var m = RegexStatusLine.Match(responseString);
+ if (m.Success)
+ {
+ response._majorVersion = int.Parse(m.Groups[1].Captures[0].Value);
+ response._minorVersion = int.Parse(m.Groups[2].Captures[0].Value);
+ response._statusCode = (RtspStatusCode)int.Parse(m.Groups[3].Captures[0].Value);
+ response._reasonPhrase = m.Groups[4].Captures[0].Value;
+ responseString = m.Groups[5].Captures[0].Value;
+ }
+
+ var sections = responseString.Split(new[] { "\r\n\r\n" }, StringSplitOptions.None);
+ response._body = sections[1];
+ var headers = sections[0].Split(new[] { "\r\n" }, StringSplitOptions.None);
+ response._headers = new Dictionary<string, string>();
+ foreach (var headerInfo in headers.Select(header => header.Split(':')))
+ {
+ response._headers.Add(headerInfo[0], headerInfo[1].Trim());
+ }
+ return response;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs
new file mode 100644
index 000000000..71b3f8a18
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs
@@ -0,0 +1,688 @@
+/*
+ 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.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Text.RegularExpressions;
+using MediaBrowser.Model.Logging;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
+{
+ public class RtspSession : IDisposable
+ {
+ #region Private Fields
+ private static readonly Regex RegexRtspSessionHeader = new Regex(@"\s*([^\s;]+)(;timeout=(\d+))?");
+ private const int DefaultRtspSessionTimeout = 30; // unit = s
+ private static readonly Regex RegexDescribeResponseSignalInfo = new Regex(@";tuner=\d+,(\d+),(\d+),(\d+),", RegexOptions.Singleline | RegexOptions.IgnoreCase);
+ private string _address;
+ private string _rtspSessionId;
+
+ public string RtspSessionId
+ {
+ get { return _rtspSessionId; }
+ set { _rtspSessionId = value; }
+ }
+ private int _rtspSessionTimeToLive = 0;
+ private string _rtspStreamId;
+ private int _clientRtpPort;
+ private int _clientRtcpPort;
+ private int _serverRtpPort;
+ private int _serverRtcpPort;
+ private int _rtpPort;
+ private int _rtcpPort;
+ private string _rtspStreamUrl;
+ private string _destination;
+ private string _source;
+ private string _transport;
+ private int _signalLevel;
+ private int _signalQuality;
+ private Socket _rtspSocket;
+ private int _rtspSequenceNum = 1;
+ private bool _disposed = false;
+ private readonly ILogger _logger;
+ #endregion
+
+ #region Constructor
+
+ public RtspSession(string address, ILogger logger)
+ {
+ if (string.IsNullOrWhiteSpace(address))
+ {
+ throw new ArgumentNullException("address");
+ }
+
+ _address = address;
+ _logger = logger;
+
+ _logger.Info("Creating RtspSession with url {0}", address);
+ }
+ ~RtspSession()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ #region Properties
+
+ #region Rtsp
+
+ public string RtspStreamId
+ {
+ get { return _rtspStreamId; }
+ set { if (_rtspStreamId != value) { _rtspStreamId = value; OnPropertyChanged("RtspStreamId"); } }
+ }
+ public string RtspStreamUrl
+ {
+ get { return _rtspStreamUrl; }
+ set { if (_rtspStreamUrl != value) { _rtspStreamUrl = value; OnPropertyChanged("RtspStreamUrl"); } }
+ }
+
+ public int RtspSessionTimeToLive
+ {
+ get
+ {
+ if (_rtspSessionTimeToLive == 0)
+ _rtspSessionTimeToLive = DefaultRtspSessionTimeout;
+ return _rtspSessionTimeToLive * 1000 - 20;
+ }
+ set { if (_rtspSessionTimeToLive != value) { _rtspSessionTimeToLive = value; OnPropertyChanged("RtspSessionTimeToLive"); } }
+ }
+
+ #endregion
+
+ #region Rtp Rtcp
+
+ /// <summary>
+ /// The LocalEndPoint Address
+ /// </summary>
+ public string Destination
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(_destination))
+ {
+ var result = "";
+ var host = Dns.GetHostName();
+ var hostentry = Dns.GetHostEntry(host);
+ foreach (var ip in hostentry.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork))
+ {
+ result = ip.ToString();
+ }
+
+ _destination = result;
+ }
+ return _destination;
+ }
+ set
+ {
+ if (_destination != value)
+ {
+ _destination = value;
+ OnPropertyChanged("Destination");
+ }
+ }
+ }
+
+ /// <summary>
+ /// The RemoteEndPoint Address
+ /// </summary>
+ public string Source
+ {
+ get { return _source; }
+ set
+ {
+ if (_source != value)
+ {
+ _source = value;
+ OnPropertyChanged("Source");
+ }
+ }
+ }
+
+ /// <summary>
+ /// The Media Data Delivery RemoteEndPoint Port if we use Unicast
+ /// </summary>
+ public int ServerRtpPort
+ {
+ get
+ {
+ return _serverRtpPort;
+ }
+ set { if (_serverRtpPort != value) { _serverRtpPort = value; OnPropertyChanged("ServerRtpPort"); } }
+ }
+
+ /// <summary>
+ /// The Media Metadata Delivery RemoteEndPoint Port if we use Unicast
+ /// </summary>
+ public int ServerRtcpPort
+ {
+ get { return _serverRtcpPort; }
+ set { if (_serverRtcpPort != value) { _serverRtcpPort = value; OnPropertyChanged("ServerRtcpPort"); } }
+ }
+
+ /// <summary>
+ /// The Media Data Delivery LocalEndPoint Port if we use Unicast
+ /// </summary>
+ public int ClientRtpPort
+ {
+ get { return _clientRtpPort; }
+ set { if (_clientRtpPort != value) { _clientRtpPort = value; OnPropertyChanged("ClientRtpPort"); } }
+ }
+
+ /// <summary>
+ /// The Media Metadata Delivery LocalEndPoint Port if we use Unicast
+ /// </summary>
+ public int ClientRtcpPort
+ {
+ get { return _clientRtcpPort; }
+ set { if (_clientRtcpPort != value) { _clientRtcpPort = value; OnPropertyChanged("ClientRtcpPort"); } }
+ }
+
+ /// <summary>
+ /// The Media Data Delivery RemoteEndPoint Port if we use Multicast
+ /// </summary>
+ public int RtpPort
+ {
+ get { return _rtpPort; }
+ set { if (_rtpPort != value) { _rtpPort = value; OnPropertyChanged("RtpPort"); } }
+ }
+
+ /// <summary>
+ /// The Media Meta Delivery RemoteEndPoint Port if we use Multicast
+ /// </summary>
+ public int RtcpPort
+ {
+ get { return _rtcpPort; }
+ set { if (_rtcpPort != value) { _rtcpPort = value; OnPropertyChanged("RtcpPort"); } }
+ }
+
+ #endregion
+
+ public string Transport
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(_transport))
+ {
+ _transport = "unicast";
+ }
+ return _transport;
+ }
+ set
+ {
+ if (_transport != value)
+ {
+ _transport = value;
+ OnPropertyChanged("Transport");
+ }
+ }
+ }
+ public int SignalLevel
+ {
+ get { return _signalLevel; }
+ set { if (_signalLevel != value) { _signalLevel = value; OnPropertyChanged("SignalLevel"); } }
+ }
+ public int SignalQuality
+ {
+ get { return _signalQuality; }
+ set { if (_signalQuality != value) { _signalQuality = value; OnPropertyChanged("SignalQuality"); } }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void ProcessSessionHeader(string sessionHeader, string response)
+ {
+ if (!string.IsNullOrEmpty(sessionHeader))
+ {
+ var m = RegexRtspSessionHeader.Match(sessionHeader);
+ if (!m.Success)
+ {
+ _logger.Error("Failed to tune, RTSP {0} response session header {1} format not recognised", response, sessionHeader);
+ }
+ _rtspSessionId = m.Groups[1].Captures[0].Value;
+ _rtspSessionTimeToLive = m.Groups[3].Captures.Count == 1 ? int.Parse(m.Groups[3].Captures[0].Value) : DefaultRtspSessionTimeout;
+ }
+ }
+ private void ProcessTransportHeader(string transportHeader)
+ {
+ if (!string.IsNullOrEmpty(transportHeader))
+ {
+ var transports = transportHeader.Split(',');
+ foreach (var transport in transports)
+ {
+ if (transport.Trim().StartsWith("RTP/AVP"))
+ {
+ var sections = transport.Split(';');
+ foreach (var section in sections)
+ {
+ var parts = section.Split('=');
+ if (parts[0].Equals("server_port"))
+ {
+ var ports = parts[1].Split('-');
+ _serverRtpPort = int.Parse(ports[0]);
+ _serverRtcpPort = int.Parse(ports[1]);
+ }
+ else if (parts[0].Equals("destination"))
+ {
+ _destination = parts[1];
+ }
+ else if (parts[0].Equals("port"))
+ {
+ var ports = parts[1].Split('-');
+ _rtpPort = int.Parse(ports[0]);
+ _rtcpPort = int.Parse(ports[1]);
+ }
+ else if (parts[0].Equals("ttl"))
+ {
+ _rtspSessionTimeToLive = int.Parse(parts[1]);
+ }
+ else if (parts[0].Equals("source"))
+ {
+ _source = parts[1];
+ }
+ else if (parts[0].Equals("client_port"))
+ {
+ var ports = parts[1].Split('-');
+ var rtp = int.Parse(ports[0]);
+ var rtcp = int.Parse(ports[1]);
+ //if (!rtp.Equals(_rtpPort))
+ //{
+ // Logger.Error("SAT>IP base: server specified RTP client port {0} instead of {1}", rtp, _rtpPort);
+ //}
+ //if (!rtcp.Equals(_rtcpPort))
+ //{
+ // Logger.Error("SAT>IP base: server specified RTCP client port {0} instead of {1}", rtcp, _rtcpPort);
+ //}
+ _rtpPort = rtp;
+ _rtcpPort = rtcp;
+ }
+ }
+ }
+ }
+ }
+ }
+ private void Connect()
+ {
+ _rtspSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ var ip = IPAddress.Parse(_address);
+ var rtspEndpoint = new IPEndPoint(ip, 554);
+ _rtspSocket.Connect(rtspEndpoint);
+ }
+ private void Disconnect()
+ {
+ if (_rtspSocket != null && _rtspSocket.Connected)
+ {
+ _rtspSocket.Shutdown(SocketShutdown.Both);
+ _rtspSocket.Close();
+ }
+ }
+ private void SendRequest(RtspRequest request)
+ {
+ if (_rtspSocket == null)
+ {
+ Connect();
+ }
+ try
+ {
+ request.Headers.Add("CSeq", _rtspSequenceNum.ToString());
+ _rtspSequenceNum++;
+ byte[] requestBytes = request.Serialise();
+ if (_rtspSocket != null)
+ {
+ var requestBytesCount = _rtspSocket.Send(requestBytes, requestBytes.Length, SocketFlags.None);
+ if (requestBytesCount < 1)
+ {
+
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e.Message);
+ }
+ }
+ private void ReceiveResponse(out RtspResponse response)
+ {
+ response = null;
+ var responseBytesCount = 0;
+ byte[] responseBytes = new byte[1024];
+ try
+ {
+ responseBytesCount = _rtspSocket.Receive(responseBytes, responseBytes.Length, SocketFlags.None);
+ response = RtspResponse.Deserialise(responseBytes, responseBytesCount);
+ string contentLengthString;
+ int contentLength = 0;
+ if (response.Headers.TryGetValue("Content-Length", out contentLengthString))
+ {
+ contentLength = int.Parse(contentLengthString);
+ if ((string.IsNullOrEmpty(response.Body) && contentLength > 0) || response.Body.Length < contentLength)
+ {
+ if (response.Body == null)
+ {
+ response.Body = string.Empty;
+ }
+ while (responseBytesCount > 0 && response.Body.Length < contentLength)
+ {
+ responseBytesCount = _rtspSocket.Receive(responseBytes, responseBytes.Length, SocketFlags.None);
+ response.Body += System.Text.Encoding.UTF8.GetString(responseBytes, 0, responseBytesCount);
+ }
+ }
+ }
+ }
+ catch (SocketException)
+ {
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public RtspStatusCode Setup(string query, string transporttype)
+ {
+
+ RtspRequest request;
+ RtspResponse response;
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
+ if ((_rtspSocket == null))
+ {
+ Connect();
+ }
+ if (string.IsNullOrEmpty(_rtspSessionId))
+ {
+ request = new RtspRequest(RtspMethod.Setup, string.Format("rtsp://{0}:{1}/?{2}", _address, 554, query), 1, 0);
+ switch (transporttype)
+ {
+ case "multicast":
+ request.Headers.Add("Transport", string.Format("RTP/AVP;multicast"));
+ break;
+ case "unicast":
+ var activeTcpConnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
+ var usedPorts = new HashSet<int>();
+ foreach (var connection in activeTcpConnections)
+ {
+ usedPorts.Add(connection.LocalEndPoint.Port);
+ }
+ for (var port = 40000; port <= 65534; port += 2)
+ {
+ if (!usedPorts.Contains(port) && !usedPorts.Contains(port + 1))
+ {
+
+ _clientRtpPort = port;
+ _clientRtcpPort = port + 1;
+ break;
+ }
+ }
+ request.Headers.Add("Transport", string.Format("RTP/AVP;unicast;client_port={0}-{1}", _clientRtpPort, _clientRtcpPort));
+ break;
+ }
+ }
+ else
+ {
+ request = new RtspRequest(RtspMethod.Setup, string.Format("rtsp://{0}:{1}/?{2}", _address, 554, query), 1, 0);
+ switch (transporttype)
+ {
+ case "multicast":
+ request.Headers.Add("Transport", string.Format("RTP/AVP;multicast"));
+ break;
+ case "unicast":
+ request.Headers.Add("Transport", string.Format("RTP/AVP;unicast;client_port={0}-{1}", _clientRtpPort, _clientRtcpPort));
+ break;
+ }
+
+ }
+ SendRequest(request);
+ ReceiveResponse(out response);
+
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
+ //{
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
+ //}
+ if (!response.Headers.TryGetValue("com.ses.streamID", out _rtspStreamId))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate Stream ID header in RTSP SETUP response"));
+ }
+ string sessionHeader;
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate Session header in RTSP SETUP response"));
+ }
+ ProcessSessionHeader(sessionHeader, "Setup");
+ string transportHeader;
+ if (!response.Headers.TryGetValue("Transport", out transportHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate Transport header in RTSP SETUP response"));
+ }
+ ProcessTransportHeader(transportHeader);
+ return response.StatusCode;
+ }
+
+ public RtspStatusCode Play(string query)
+ {
+ if ((_rtspSocket == null))
+ {
+ Connect();
+ }
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
+ RtspResponse response;
+ string data;
+ if (string.IsNullOrEmpty(query))
+ {
+ data = string.Format("rtsp://{0}:{1}/stream={2}", _address,
+ 554, _rtspStreamId);
+ }
+ else
+ {
+ data = string.Format("rtsp://{0}:{1}/stream={2}?{3}", _address,
+ 554, _rtspStreamId, query);
+ }
+ var request = new RtspRequest(RtspMethod.Play, data, 1, 0);
+ request.Headers.Add("Session", _rtspSessionId);
+ SendRequest(request);
+ ReceiveResponse(out response);
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
+ //{
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
+ //}
+ //Logger.Info("RtspSession-Play : \r\n {0}", response);
+ string sessionHeader;
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate Session header in RTSP Play response"));
+ }
+ ProcessSessionHeader(sessionHeader, "Play");
+ string rtpinfoHeader;
+ if (!response.Headers.TryGetValue("RTP-Info", out rtpinfoHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate Rtp-Info header in RTSP Play response"));
+ }
+ return response.StatusCode;
+ }
+
+ public RtspStatusCode Options()
+ {
+ if ((_rtspSocket == null))
+ {
+ Connect();
+ }
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
+ RtspRequest request;
+ RtspResponse response;
+
+
+ if (string.IsNullOrEmpty(_rtspSessionId))
+ {
+ request = new RtspRequest(RtspMethod.Options, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
+ }
+ else
+ {
+ request = new RtspRequest(RtspMethod.Options, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
+ request.Headers.Add("Session", _rtspSessionId);
+ }
+ SendRequest(request);
+ ReceiveResponse(out response);
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
+ //{
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
+ //}
+ //Logger.Info("RtspSession-Options : \r\n {0}", response);
+ string sessionHeader;
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate session header in RTSP Options response"));
+ }
+ ProcessSessionHeader(sessionHeader, "Options");
+ string optionsHeader;
+ if (!response.Headers.TryGetValue("Public", out optionsHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to Options header in RTSP Options response"));
+ }
+ return response.StatusCode;
+ }
+
+ public RtspStatusCode Describe(out int level, out int quality)
+ {
+ if ((_rtspSocket == null))
+ {
+ Connect();
+ }
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
+ RtspRequest request;
+ RtspResponse response;
+ level = 0;
+ quality = 0;
+
+ if (string.IsNullOrEmpty(_rtspSessionId))
+ {
+ request = new RtspRequest(RtspMethod.Describe, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
+ request.Headers.Add("Accept", "application/sdp");
+
+ }
+ else
+ {
+ request = new RtspRequest(RtspMethod.Describe, string.Format("rtsp://{0}:{1}/stream={2}", _address, 554, _rtspStreamId), 1, 0);
+ request.Headers.Add("Accept", "application/sdp");
+ request.Headers.Add("Session", _rtspSessionId);
+ }
+ SendRequest(request);
+ ReceiveResponse(out response);
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
+ //{
+ // Logger.Error("Failed to tune, non-OK RTSP Describe status code {0} {1}", response.StatusCode, response.ReasonPhrase);
+ //}
+ //Logger.Info("RtspSession-Describe : \r\n {0}", response);
+ string sessionHeader;
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
+ {
+ _logger.Error(string.Format("Failed to tune, not able to locate session header in RTSP Describe response"));
+ }
+ ProcessSessionHeader(sessionHeader, "Describe");
+ var m = RegexDescribeResponseSignalInfo.Match(response.Body);
+ if (m.Success)
+ {
+
+ //isSignalLocked = m.Groups[2].Captures[0].Value.Equals("1");
+ level = int.Parse(m.Groups[1].Captures[0].Value) * 100 / 255; // level: 0..255 => 0..100
+ quality = int.Parse(m.Groups[3].Captures[0].Value) * 100 / 15; // quality: 0..15 => 0..100
+
+ }
+ /*
+ v=0
+ o=- 1378633020884883 1 IN IP4 192.168.2.108
+ s=SatIPServer:1 4
+ t=0 0
+ a=tool:idl4k
+ m=video 52780 RTP/AVP 33
+ c=IN IP4 0.0.0.0
+ b=AS:5000
+ a=control:stream=4
+ a=fmtp:33 ver=1.0;tuner=1,0,0,0,12344,h,dvbs2,,off,,22000,34;pids=0,100,101,102,103,106
+ =sendonly
+ */
+
+
+ return response.StatusCode;
+ }
+
+ public RtspStatusCode TearDown()
+ {
+ if ((_rtspSocket == null))
+ {
+ Connect();
+ }
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
+ RtspResponse response;
+
+ var request = new RtspRequest(RtspMethod.Teardown, string.Format("rtsp://{0}:{1}/stream={2}", _address, 554, _rtspStreamId), 1, 0);
+ request.Headers.Add("Session", _rtspSessionId);
+ SendRequest(request);
+ ReceiveResponse(out response);
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
+ //{
+ // Logger.Error("Failed to tune, non-OK RTSP Teardown status code {0} {1}", response.StatusCode, response.ReasonPhrase);
+ //}
+ return response.StatusCode;
+ }
+
+ #endregion
+
+ #region Public Events
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion
+
+ #region Protected Methods
+
+ protected void OnPropertyChanged(string name)
+ {
+ //var handler = PropertyChanged;
+ //if (handler != null)
+ //{
+ // handler(this, new PropertyChangedEventArgs(name));
+ //}
+ }
+
+ #endregion
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);//Disconnect();
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ {
+ TearDown();
+ Disconnect();
+ }
+ }
+ _disposed = true;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs
new file mode 100644
index 000000000..6d6d50623
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs
@@ -0,0 +1,251 @@
+/*
+ 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.ComponentModel;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
+{
+ /// <summary>
+ /// Standard RTSP status codes.
+ /// </summary>
+ public enum RtspStatusCode
+ {
+ /// <summary>
+ /// 100 continue
+ /// </summary>
+ Continue = 100,
+
+ /// <summary>
+ /// 200 OK
+ /// </summary>
+ [Description("Okay")]
+ Ok = 200,
+ /// <summary>
+ /// 201 created
+ /// </summary>
+ Created = 201,
+
+ /// <summary>
+ /// 250 low on storage space
+ /// </summary>
+ [Description("Low On Storage Space")]
+ LowOnStorageSpace = 250,
+
+ /// <summary>
+ /// 300 multiple choices
+ /// </summary>
+ [Description("Multiple Choices")]
+ MultipleChoices = 300,
+ /// <summary>
+ /// 301 moved permanently
+ /// </summary>
+ [Description("Moved Permanently")]
+ MovedPermanently = 301,
+ /// <summary>
+ /// 302 moved temporarily
+ /// </summary>
+ [Description("Moved Temporarily")]
+ MovedTemporarily = 302,
+ /// <summary>
+ /// 303 see other
+ /// </summary>
+ [Description("See Other")]
+ SeeOther = 303,
+ /// <summary>
+ /// 304 not modified
+ /// </summary>
+ [Description("Not Modified")]
+ NotModified = 304,
+ /// <summary>
+ /// 305 use proxy
+ /// </summary>
+ [Description("Use Proxy")]
+ UseProxy = 305,
+
+ /// <summary>
+ /// 400 bad request
+ /// </summary>
+ [Description("Bad Request")]
+ BadRequest = 400,
+ /// <summary>
+ /// 401 unauthorised
+ /// </summary>
+ Unauthorised = 401,
+ /// <summary>
+ /// 402 payment required
+ /// </summary>
+ [Description("Payment Required")]
+ PaymentRequired = 402,
+ /// <summary>
+ /// 403 forbidden
+ /// </summary>
+ Forbidden = 403,
+ /// <summary>
+ /// 404 not found
+ /// </summary>
+ [Description("Not Found")]
+ NotFound = 404,
+ /// <summary>
+ /// 405 method not allowed
+ /// </summary>
+ [Description("Method Not Allowed")]
+ MethodNotAllowed = 405,
+ /// <summary>
+ /// 406 not acceptable
+ /// </summary>
+ [Description("Not Acceptable")]
+ NotAcceptable = 406,
+ /// <summary>
+ /// 407 proxy authentication required
+ /// </summary>
+ [Description("Proxy Authentication Required")]
+ ProxyAuthenticationRequred = 407,
+ /// <summary>
+ /// 408 request time-out
+ /// </summary>
+ [Description("Request Time-Out")]
+ RequestTimeOut = 408,
+
+ /// <summary>
+ /// 410 gone
+ /// </summary>
+ Gone = 410,
+ /// <summary>
+ /// 411 length required
+ /// </summary>
+ [Description("Length Required")]
+ LengthRequired = 411,
+ /// <summary>
+ /// 412 precondition failed
+ /// </summary>
+ [Description("Precondition Failed")]
+ PreconditionFailed = 412,
+ /// <summary>
+ /// 413 request entity too large
+ /// </summary>
+ [Description("Request Entity Too Large")]
+ RequestEntityTooLarge = 413,
+ /// <summary>
+ /// 414 request URI too large
+ /// </summary>
+ [Description("Request URI Too Large")]
+ RequestUriTooLarge = 414,
+ /// <summary>
+ /// 415 unsupported media type
+ /// </summary>
+ [Description("Unsupported Media Type")]
+ UnsupportedMediaType = 415,
+
+ /// <summary>
+ /// 451 parameter not understood
+ /// </summary>
+ [Description("Parameter Not Understood")]
+ ParameterNotUnderstood = 451,
+ /// <summary>
+ /// 452 conference not found
+ /// </summary>
+ [Description("Conference Not Found")]
+ ConferenceNotFound = 452,
+ /// <summary>
+ /// 453 not enough bandwidth
+ /// </summary>
+ [Description("Not Enough Bandwidth")]
+ NotEnoughBandwidth = 453,
+ /// <summary>
+ /// 454 session not found
+ /// </summary>
+ [Description("Session Not Found")]
+ SessionNotFound = 454,
+ /// <summary>
+ /// 455 method not valid in this state
+ /// </summary>
+ [Description("Method Not Valid In This State")]
+ MethodNotValidInThisState = 455,
+ /// <summary>
+ /// 456 header field not valid for this resource
+ /// </summary>
+ [Description("Header Field Not Valid For This Resource")]
+ HeaderFieldNotValidForThisResource = 456,
+ /// <summary>
+ /// 457 invalid range
+ /// </summary>
+ [Description("Invalid Range")]
+ InvalidRange = 457,
+ /// <summary>
+ /// 458 parameter is read-only
+ /// </summary>
+ [Description("Parameter Is Read-Only")]
+ ParameterIsReadOnly = 458,
+ /// <summary>
+ /// 459 aggregate operation not allowed
+ /// </summary>
+ [Description("Aggregate Operation Not Allowed")]
+ AggregateOperationNotAllowed = 459,
+ /// <summary>
+ /// 460 only aggregate operation allowed
+ /// </summary>
+ [Description("Only Aggregate Operation Allowed")]
+ OnlyAggregateOperationAllowed = 460,
+ /// <summary>
+ /// 461 unsupported transport
+ /// </summary>
+ [Description("Unsupported Transport")]
+ UnsupportedTransport = 461,
+ /// <summary>
+ /// 462 destination unreachable
+ /// </summary>
+ [Description("Destination Unreachable")]
+ DestinationUnreachable = 462,
+
+ /// <summary>
+ /// 500 internal server error
+ /// </summary>
+ [Description("Internal Server Error")]
+ InternalServerError = 500,
+ /// <summary>
+ /// 501 not implemented
+ /// </summary>
+ [Description("Not Implemented")]
+ NotImplemented = 501,
+ /// <summary>
+ /// 502 bad gateway
+ /// </summary>
+ [Description("Bad Gateway")]
+ BadGateway = 502,
+ /// <summary>
+ /// 503 service unavailable
+ /// </summary>
+ [Description("Service Unavailable")]
+ ServiceUnavailable = 503,
+ /// <summary>
+ /// 504 gateway time-out
+ /// </summary>
+ [Description("Gateway Time-Out")]
+ GatewayTimeOut = 504,
+ /// <summary>
+ /// 505 RTSP version not supported
+ /// </summary>
+ [Description("RTSP Version Not Supported")]
+ RtspVersionNotSupported = 505,
+
+ /// <summary>
+ /// 551 option not supported
+ /// </summary>
+ [Description("Option Not Supported")]
+ OptionNotSupported = 551
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
index da1894bb7..d0a55966f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
@@ -15,6 +15,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Extensions;
+using System.Xml.Linq;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
{
@@ -171,58 +172,86 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
public async Task<SatIpTunerHostInfo> GetInfo(string url, CancellationToken cancellationToken)
{
+ Uri locationUri = new Uri(url);
+ string devicetype = "";
+ string friendlyname = "";
+ string uniquedevicename = "";
+ string manufacturer = "";
+ string manufacturerurl = "";
+ string modelname = "";
+ string modeldescription = "";
+ string modelnumber = "";
+ string modelurl = "";
+ string serialnumber = "";
+ string presentationurl = "";
+ string capabilities = "";
+ string m3u = "";
+ var document = XDocument.Load(locationUri.AbsoluteUri);
+ var xnm = new XmlNamespaceManager(new NameTable());
+ XNamespace n1 = "urn:ses-com:satip";
+ XNamespace n0 = "urn:schemas-upnp-org:device-1-0";
+ xnm.AddNamespace("root", n0.NamespaceName);
+ xnm.AddNamespace("satip:", n1.NamespaceName);
+ if (document.Root != null)
+ {
+ var deviceElement = document.Root.Element(n0 + "device");
+ if (deviceElement != null)
+ {
+ var devicetypeElement = deviceElement.Element(n0 + "deviceType");
+ if (devicetypeElement != null)
+ devicetype = devicetypeElement.Value;
+ var friendlynameElement = deviceElement.Element(n0 + "friendlyName");
+ if (friendlynameElement != null)
+ friendlyname = friendlynameElement.Value;
+ var manufactureElement = deviceElement.Element(n0 + "manufacturer");
+ if (manufactureElement != null)
+ manufacturer = manufactureElement.Value;
+ var manufactureurlElement = deviceElement.Element(n0 + "manufacturerURL");
+ if (manufactureurlElement != null)
+ manufacturerurl = manufactureurlElement.Value;
+ var modeldescriptionElement = deviceElement.Element(n0 + "modelDescription");
+ if (modeldescriptionElement != null)
+ modeldescription = modeldescriptionElement.Value;
+ var modelnameElement = deviceElement.Element(n0 + "modelName");
+ if (modelnameElement != null)
+ modelname = modelnameElement.Value;
+ var modelnumberElement = deviceElement.Element(n0 + "modelNumber");
+ if (modelnumberElement != null)
+ modelnumber = modelnumberElement.Value;
+ var modelurlElement = deviceElement.Element(n0 + "modelURL");
+ if (modelurlElement != null)
+ modelurl = modelurlElement.Value;
+ var serialnumberElement = deviceElement.Element(n0 + "serialNumber");
+ if (serialnumberElement != null)
+ serialnumber = serialnumberElement.Value;
+ var uniquedevicenameElement = deviceElement.Element(n0 + "UDN");
+ if (uniquedevicenameElement != null) uniquedevicename = uniquedevicenameElement.Value;
+ var presentationUrlElement = deviceElement.Element(n0 + "presentationURL");
+ if (presentationUrlElement != null) presentationurl = presentationUrlElement.Value;
+ var capabilitiesElement = deviceElement.Element(n1 + "X_SATIPCAP");
+ if (capabilitiesElement != null) capabilities = capabilitiesElement.Value;
+ var m3uElement = deviceElement.Element(n1 + "X_SATIPM3U");
+ if (m3uElement != null) m3u = m3uElement.Value;
+ }
+ }
+
var result = new SatIpTunerHostInfo
{
Url = url,
+ Id = uniquedevicename,
IsEnabled = true,
Type = SatIpHost.DeviceType,
Tuners = 1,
- TunersAvailable = 1
+ TunersAvailable = 1,
+ M3UUrl = m3u
};
- using (var stream = await _httpClient.Get(url, cancellationToken).ConfigureAwait(false))
- {
- using (var streamReader = new StreamReader(stream))
- {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader))
- {
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "device":
- using (var subtree = reader.ReadSubtree())
- {
- FillFromDeviceNode(result, subtree);
- }
- break;
- default:
- reader.Skip();
- break;
- }
- }
- }
- }
- }
- }
-
- if (string.IsNullOrWhiteSpace(result.DeviceId))
+ result.FriendlyName = friendlyname;
+ if (string.IsNullOrWhiteSpace(result.Id))
{
throw new NotImplementedException();
}
- // Device hasn't implemented an m3u list
- if (string.IsNullOrWhiteSpace(result.M3UUrl))
- {
- result.IsEnabled = false;
- }
-
else if (!result.M3UUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
var fullM3uUrl = url.Substring(0, url.LastIndexOf('/'));
@@ -233,66 +262,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
return result;
}
-
- private void FillFromDeviceNode(SatIpTunerHostInfo info, XmlReader reader)
- {
- reader.MoveToContent();
-
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.LocalName)
- {
- case "UDN":
- {
- info.DeviceId = reader.ReadElementContentAsString();
- break;
- }
-
- case "friendlyName":
- {
- info.FriendlyName = reader.ReadElementContentAsString();
- break;
- }
-
- case "satip:X_SATIPCAP":
- case "X_SATIPCAP":
- {
- // <satip:X_SATIPCAP xmlns:satip="urn:ses-com:satip">DVBS2-2</satip:X_SATIPCAP>
- var value = reader.ReadElementContentAsString() ?? string.Empty;
- var parts = value.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 2)
- {
- int intValue;
- if (int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out intValue))
- {
- info.TunersAvailable = intValue;
- }
-
- if (int.TryParse(parts[0].Substring(parts[0].Length - 1), NumberStyles.Any, CultureInfo.InvariantCulture, out intValue))
- {
- info.Tuners = intValue;
- }
- }
- break;
- }
-
- case "satip:X_SATIPM3U":
- case "X_SATIPM3U":
- {
- // <satip:X_SATIPM3U xmlns:satip="urn:ses-com:satip">/channellist.lua?select=m3u</satip:X_SATIPM3U>
- info.M3UUrl = reader.ReadElementContentAsString();
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
- }
}
public class SatIpTunerHostInfo : TunerHostInfo
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
index 46a2a8524..ffd85fd18 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
@@ -40,7 +40,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false);
}
- return new List<ChannelInfo>();
+ var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false);
+ return channels;
}
public static string DeviceType
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 97f090ab2..ae39d3eb9 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -62,6 +62,10 @@
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
+ <Reference Include="Open.Nat, Version=2.0.15.0, Culture=neutral, PublicKeyToken=f22a6a4582336c76, processorArchitecture=MSIL">
+ <HintPath>..\packages\Open.NAT.2.0.15.0\lib\net45\Open.Nat.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
</Reference>
@@ -99,6 +103,7 @@
<Reference Include="ServiceStack.Text">
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
</Reference>
+ <Reference Include="System.Xml.Linq" />
<Reference Include="UniversalDetector">
<HintPath>..\ThirdParty\UniversalDetector\UniversalDetector.dll</HintPath>
</Reference>
@@ -242,6 +247,12 @@
<Compile Include="LiveTv\ProgramImageProvider.cs" />
<Compile Include="LiveTv\RecordingImageProvider.cs" />
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.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" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspSession.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
index 3c8a0ffeb..031333f2c 100644
--- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
private readonly ILocalizationManager _localization;
private readonly ITaskManager _taskManager;
- public const int MigrationVersion = 20;
+ public const int MigrationVersion = 23;
public static bool EnableUnavailableMessage = false;
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IHttpServer httpServer, ILocalizationManager localization, ITaskManager taskManager)
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index de914b773..eda0a263a 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
private IDbCommand _updateInheritedRatingCommand;
- private const int LatestSchemaVersion = 56;
+ private const int LatestSchemaVersion = 58;
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
index 66aede029..814a67643 100644
--- a/MediaBrowser.Server.Implementations/packages.config
+++ b/MediaBrowser.Server.Implementations/packages.config
@@ -7,6 +7,7 @@
<package id="MediaBrowser.Naming" version="1.0.0.49" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
+ <package id="Open.NAT" version="2.0.15.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="SocketHttpListener" version="1.0.0.29" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
index e54bc0b4a..6d19c3275 100644
--- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
+++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
@@ -9,6 +9,8 @@ using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using MediaBrowser.Controller.Power;
+using MediaBrowser.Server.Startup.Common.FFMpeg;
+using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
namespace MediaBrowser.Server.Mono.Native
{
@@ -209,6 +211,99 @@ namespace MediaBrowser.Server.Mono.Native
{
return new NullPowerManagement();
}
+
+ public FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ return GetInfo(Environment);
+ }
+
+ public static FFMpegInstallInfo GetInfo(NativeEnvironment environment)
+ {
+ var info = new FFMpegInstallInfo();
+
+ // Windows builds: http://ffmpeg.zeranoe.com/builds/
+ // Linux builds: http://johnvansickle.com/ffmpeg/
+ // OS X builds: http://ffmpegmac.net/
+ // OS X x64: http://www.evermeet.cx/ffmpeg/
+
+ switch (environment.OperatingSystem)
+ {
+ case OperatingSystem.Bsd:
+ break;
+ case OperatingSystem.Linux:
+
+ info.ArchiveType = "7z";
+ info.Version = "20160215";
+ break;
+ case OperatingSystem.Osx:
+
+ info.ArchiveType = "7z";
+
+ switch (environment.SystemArchitecture)
+ {
+ case Architecture.X86_X64:
+ info.Version = "20160124";
+ break;
+ case Architecture.X86:
+ info.Version = "20150110";
+ break;
+ }
+ break;
+ }
+
+ info.DownloadUrls = GetDownloadUrls(environment);
+
+ return info;
+ }
+
+ private static string[] GetDownloadUrls(NativeEnvironment environment)
+ {
+ switch (environment.OperatingSystem)
+ {
+ case OperatingSystem.Osx:
+
+ switch (environment.SystemArchitecture)
+ {
+ case Architecture.X86_X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.5.3.7z"
+ };
+ }
+ break;
+
+ case OperatingSystem.Linux:
+
+ switch (environment.SystemArchitecture)
+ {
+ case Architecture.X86_X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
+ };
+ case Architecture.Arm:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-arm.7z"
+ };
+ }
+ break;
+ }
+
+ // No version available
+ return new string[] { };
+ }
+
}
public class NullPowerManagement : IPowerManagement
diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
index dd7e3cc01..93dbe2945 100644
--- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
+++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
@@ -618,7 +618,7 @@ namespace MediaBrowser.Server.Startup.Common
/// <returns>Task.</returns>
private async Task RegisterMediaEncoder(IProgress<double> progress)
{
- var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment)
+ var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetType().Assembly, NativeApp.GetFfmpegInstallInfo())
.GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false);
var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"),
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
deleted file mode 100644
index 60cb50e30..000000000
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-
-namespace MediaBrowser.Server.Startup.Common.FFMpeg
-{
- public class FFMpegDownloadInfo
- {
- public string Version { get; set; }
- public string FFMpegFilename { get; set; }
- public string FFProbeFilename { get; set; }
- public string ArchiveType { get; set; }
- public string[] DownloadUrls { get; set; }
-
- public FFMpegDownloadInfo()
- {
- DownloadUrls = new string[] { };
- Version = "Path";
- FFMpegFilename = "ffmpeg";
- FFProbeFilename = "ffprobe";
- }
-
- public static FFMpegDownloadInfo GetInfo(NativeEnvironment environment)
- {
- var info = new FFMpegDownloadInfo();
-
- // Windows builds: http://ffmpeg.zeranoe.com/builds/
- // Linux builds: http://johnvansickle.com/ffmpeg/
- // OS X builds: http://ffmpegmac.net/
- // OS X x64: http://www.evermeet.cx/ffmpeg/
-
- switch (environment.OperatingSystem)
- {
- case OperatingSystem.Bsd:
- break;
- case OperatingSystem.Linux:
-
- info.ArchiveType = "7z";
- info.Version = "20160215";
- break;
- case OperatingSystem.Osx:
-
- info.ArchiveType = "7z";
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X86_X64:
- info.Version = "20160124";
- break;
- case Architecture.X86:
- info.Version = "20150110";
- break;
- }
- break;
-
- case OperatingSystem.Windows:
-
- info.FFMpegFilename = "ffmpeg.exe";
- info.FFProbeFilename = "ffprobe.exe";
- info.Version = "20160131";
- info.ArchiveType = "7z";
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X86_X64:
- break;
- case Architecture.X86:
- break;
- }
- break;
- }
-
- info.DownloadUrls = GetDownloadUrls(environment);
-
- return info;
- }
-
- private static string[] GetDownloadUrls(NativeEnvironment environment)
- {
- switch (environment.OperatingSystem)
- {
- case OperatingSystem.Windows:
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X86_X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160131-win64.7z",
- "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20151109-git-480bad7-win64-static.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160131-win32.7z",
- "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20151109-git-480bad7-win32-static.7z"
- };
- }
- break;
-
- case OperatingSystem.Osx:
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X86_X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.5.3.7z"
- };
- }
- break;
-
- case OperatingSystem.Linux:
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X86_X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
- };
- case Architecture.Arm:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-arm.7z"
- };
- }
- break;
- }
-
- // No version available
- return new string[] { };
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs
new file mode 100644
index 000000000..1ce1b55c2
--- /dev/null
+++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs
@@ -0,0 +1,21 @@
+
+namespace MediaBrowser.Server.Startup.Common.FFMpeg
+{
+ public class FFMpegInstallInfo
+ {
+ public string Version { get; set; }
+ public string FFMpegFilename { get; set; }
+ public string FFProbeFilename { get; set; }
+ public string ArchiveType { get; set; }
+ public string[] DownloadUrls { get; set; }
+ public bool IsEmbedded { get; set; }
+
+ public FFMpegInstallInfo()
+ {
+ DownloadUrls = new string[] { };
+ Version = "Path";
+ FFMpegFilename = "ffmpeg";
+ FFProbeFilename = "ffprobe";
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs
index 000568c15..ee284fdc5 100644
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs
+++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -15,7 +16,7 @@ using CommonIO;
namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
- public class FFMpegDownloader
+ public class FFMpegLoader
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
@@ -23,13 +24,15 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
private readonly NativeEnvironment _environment;
+ private readonly Assembly _ownerAssembly;
+ private readonly FFMpegInstallInfo _ffmpegInstallInfo;
private readonly string[] _fontUrls =
{
"https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z"
};
- public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment)
+ public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment, Assembly ownerAssembly, FFMpegInstallInfo ffmpegInstallInfo)
{
_logger = logger;
_appPaths = appPaths;
@@ -37,6 +40,8 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
_zipClient = zipClient;
_fileSystem = fileSystem;
_environment = environment;
+ _ownerAssembly = ownerAssembly;
+ _ffmpegInstallInfo = ffmpegInstallInfo;
}
public async Task<FFMpegInfo> GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress<double> progress)
@@ -54,7 +59,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
};
}
- var downloadInfo = FFMpegDownloadInfo.GetInfo(environment);
+ var downloadInfo = _ffmpegInstallInfo;
var version = downloadInfo.Version;
@@ -78,11 +83,11 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
Version = version
};
- _fileSystem.CreateDirectory(versionedDirectoryPath);
+ _fileSystem.CreateDirectory(versionedDirectoryPath);
var excludeFromDeletions = new List<string> { versionedDirectoryPath };
- if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(info.EncoderPath))
+ if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(info.EncoderPath))
{
// ffmpeg not present. See if there's an older version we can start with
var existingVersion = GetExistingVersion(info, rootEncoderPath);
@@ -106,7 +111,10 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
}
}
- await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false);
+ if (_environment.OperatingSystem == OperatingSystem.Windows)
+ {
+ await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false);
+ }
DeleteOlderFolders(Path.GetDirectoryName(versionedDirectoryPath), excludeFromDeletions);
@@ -175,7 +183,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
return null;
}
- private async void DownloadFFMpegInBackground(FFMpegDownloadInfo downloadinfo, string directory)
+ private async void DownloadFFMpegInBackground(FFMpegInstallInfo downloadinfo, string directory)
{
try
{
@@ -187,8 +195,24 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
}
}
- private async Task DownloadFFMpeg(FFMpegDownloadInfo downloadinfo, string directory, IProgress<double> progress)
+ private async Task DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress<double> progress)
{
+ if (downloadinfo.IsEmbedded)
+ {
+ var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(tempFile));
+
+ using (var stream = _ownerAssembly.GetManifestResourceStream(downloadinfo.DownloadUrls[0]))
+ {
+ using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ {
+ await stream.CopyToAsync(fs).ConfigureAwait(false);
+ }
+ }
+ ExtractFFMpeg(downloadinfo, tempFile, directory);
+ return;
+ }
+
foreach (var url in downloadinfo.DownloadUrls)
{
progress.Report(0);
@@ -216,19 +240,17 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
throw new ApplicationException("ffmpeg unvailable. Please install it and start the server with two command line arguments: -ffmpeg \"{PATH}\" and -ffprobe \"{PATH}\"");
}
- else
- {
- throw new ApplicationException("Unable to download required components. Please try again later.");
- }
+
+ throw new ApplicationException("Unable to download required components. Please try again later.");
}
- private void ExtractFFMpeg(FFMpegDownloadInfo downloadinfo, string tempFile, string targetFolder)
+ private void ExtractFFMpeg(FFMpegInstallInfo downloadinfo, string tempFile, string targetFolder)
{
_logger.Info("Extracting ffmpeg from {0}", tempFile);
var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
- _fileSystem.CreateDirectory(tempFolder);
+ _fileSystem.CreateDirectory(tempFolder);
try
{
@@ -247,7 +269,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
}))
{
var targetFile = Path.Combine(targetFolder, Path.GetFileName(file));
- _fileSystem.CopyFile(file, targetFile, true);
+ _fileSystem.CopyFile(file, targetFile, true);
SetFilePermissions(targetFile);
}
}
@@ -268,7 +290,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
}
}
- private void ExtractArchive(FFMpegDownloadInfo downloadinfo, string archivePath, string targetPath)
+ private void ExtractArchive(FFMpegInstallInfo downloadinfo, string archivePath, string targetPath)
{
_logger.Info("Extracting {0} to {1}", archivePath, targetPath);
@@ -311,13 +333,13 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
var fontsDirectory = Path.Combine(targetPath, "fonts");
- _fileSystem.CreateDirectory(fontsDirectory);
+ _fileSystem.CreateDirectory(fontsDirectory);
const string fontFilename = "ARIALUNI.TTF";
var fontFile = Path.Combine(fontsDirectory, fontFilename);
- if (_fileSystem.FileExists(fontFile))
+ if (_fileSystem.FileExists(fontFile))
{
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}
@@ -360,7 +382,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
try
{
- _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
+ _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
return;
}
catch (IOException ex)
@@ -422,7 +444,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
const string fontConfigFilename = "fonts.conf";
var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
- if (!_fileSystem.FileExists(fontConfigFile))
+ if (!_fileSystem.FileExists(fontConfigFile))
{
var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs
index 9df670bda..121d4192e 100644
--- a/MediaBrowser.Server.Startup.Common/INativeApp.cs
+++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Reflection;
using MediaBrowser.Controller.Power;
+using MediaBrowser.Server.Startup.Common.FFMpeg;
namespace MediaBrowser.Server.Startup.Common
{
@@ -97,5 +98,7 @@ namespace MediaBrowser.Server.Startup.Common
/// </summary>
/// <returns>IPowerManagement.</returns>
IPowerManagement GetPowerManagement();
+
+ FFMpegInstallInfo GetFfmpegInstallInfo();
}
}
diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
index 80ce88fa3..19ce9ed9e 100644
--- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
+++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
@@ -65,8 +65,8 @@
<Compile Include="Browser\BrowserLauncher.cs" />
<Compile Include="EntryPoints\KeepServerAwake.cs" />
<Compile Include="EntryPoints\StartupWizard.cs" />
- <Compile Include="FFMpeg\FFMpegDownloader.cs" />
- <Compile Include="FFMpeg\FFMpegDownloadInfo.cs" />
+ <Compile Include="FFMpeg\FFMpegLoader.cs" />
+ <Compile Include="FFMpeg\FFMpegInstallInfo.cs" />
<Compile Include="FFMpeg\FFMpegInfo.cs" />
<Compile Include="FFMpeg\FFmpegValidator.cs" />
<Compile Include="INativeApp.cs" />
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 6ba91c06f..a654bd2b5 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -69,16 +69,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
- <Reference Include="MediaBrowser.IsoMounter">
- <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\MediaBrowser.IsoMounter.dll</HintPath>
- </Reference>
<Reference Include="Patterns.Logging, Version=1.0.5494.41209, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
</Reference>
- <Reference Include="pfmclrapi">
- <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\pfmclrapi.dll</HintPath>
- </Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
@@ -144,6 +138,8 @@
<None Include="App.config" />
<None Include="app.manifest" />
<EmbeddedResource Include="Native\RegisterServer.bat" />
+ <EmbeddedResource Include="ffmpeg\ffmpegx64.7z" />
+ <EmbeddedResource Include="ffmpeg\ffmpegx86.7z" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs
index 164037dc5..056258f96 100644
--- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs
+++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Reflection;
using CommonIO;
using MediaBrowser.Controller.Power;
+using MediaBrowser.Server.Startup.Common.FFMpeg;
namespace MediaBrowser.ServerApplication.Native
{
@@ -30,7 +31,7 @@ namespace MediaBrowser.ServerApplication.Native
}
list.Add(GetType().Assembly);
-
+
return list;
}
@@ -124,5 +125,32 @@ namespace MediaBrowser.ServerApplication.Native
{
return new WindowsPowerManagement(_logger);
}
+
+ public FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ info.FFMpegFilename = "ffmpeg.exe";
+ info.FFProbeFilename = "ffprobe.exe";
+ info.Version = "20160401";
+ info.ArchiveType = "7z";
+ info.IsEmbedded = true;
+ info.DownloadUrls = GetDownloadUrls();
+
+ return info;
+ }
+
+ private string[] GetDownloadUrls()
+ {
+ switch (Environment.SystemArchitecture)
+ {
+ case Architecture.X86_X64:
+ return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx64.7z" };
+ case Architecture.X86:
+ return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx86.7z" };
+ }
+
+ return new string[] { };
+ }
}
}
diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id
new file mode 100644
index 000000000..b0542b75f
--- /dev/null
+++ b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id
@@ -0,0 +1 @@
+9dc10b022537738edce7eb71aa8dd4adbfee2c7b \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id
new file mode 100644
index 000000000..3939ec44d
--- /dev/null
+++ b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id
@@ -0,0 +1 @@
+00fa1afa35fbd0a7e97ad7956e42ae17f6882f64 \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index 5187a1db3..16acb0c81 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -2,7 +2,6 @@
<packages>
<package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
- <package id="MediaBrowser.IsoMounting" version="3.0.69" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="System.Data.SQLite.Core" version="1.0.94.0" targetFramework="net45" />
</packages> \ No newline at end of file