aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/LiveTv
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/LiveTv')
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs32
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs121
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs96
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs95
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs34
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs183
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs53
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs8
12 files changed, 356 insertions, 334 deletions
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 4a2836d59..be5e57539 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1222,7 +1222,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.Info("Closing live stream {0}", id);
- await stream.Close().ConfigureAwait(false);
+ stream.Close();
_logger.Info("Live stream {0} closed successfully", id);
}
}
@@ -1286,9 +1286,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Id = timer.Id
};
- if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
+ if (!_activeRecordings.ContainsKey(timer.Id))
{
- await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
+ await RecordStream(timer, recordingEndDate, activeRecordingInfo).ConfigureAwait(false);
}
else
{
@@ -1397,8 +1397,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Path.Combine(recordPath, recordingFileName);
}
- private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate,
- ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
+ private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo)
{
if (timer == null)
{
@@ -1420,19 +1419,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (programInfo != null)
{
CopyProgramInfoToTimerInfo(programInfo, timer);
- //activeRecordingInfo.Program = programInfo;
}
string seriesPath = null;
var recordPath = GetRecordingPath(timer, out seriesPath);
var recordingStatus = RecordingStatus.New;
+ var recorder = await GetRecorder().ConfigureAwait(false);
+
string liveStreamId = null;
try
{
- var recorder = await GetRecorder().ConfigureAwait(false);
-
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
_logger.Info("Opening recording stream from tuner provider");
@@ -1442,14 +1440,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var mediaStreamInfo = liveStreamInfo.Item2;
liveStreamId = mediaStreamInfo.Id;
- // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
- //await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
-
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id);
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
- activeRecordingInfo.Path = recordPath;
var duration = recordingEndDate - DateTime.UtcNow;
@@ -1459,15 +1453,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Action onStarted = async () =>
{
+ activeRecordingInfo.Path = recordPath;
+
+ _activeRecordings.TryAdd(timer.Id, activeRecordingInfo);
+
timer.Status = RecordingStatus.InProgress;
_timerProvider.AddOrUpdate(timer, false);
await SaveRecordingMetadata(timer, recordPath, seriesPath).ConfigureAwait(false);
+
+ CreateRecordingFolders();
+
TriggerRefresh(recordPath);
EnforceKeepUpTo(timer, seriesPath);
};
- await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
+ await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
recordingStatus = RecordingStatus.Completed;
_logger.Info("Recording completed: {0}", recordPath);
@@ -1507,6 +1508,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
timer.Status = RecordingStatus.New;
+ timer.PrePaddingSeconds = 0;
timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
timer.RetryCount++;
_timerProvider.AddOrUpdate(timer);
@@ -1526,13 +1528,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void TriggerRefresh(string path)
{
- _logger.Debug("Triggering refresh on {0}", path);
+ _logger.Info("Triggering refresh on {0}", path);
var item = GetAffectedBaseItem(_fileSystem.GetDirectoryName(path));
if (item != null)
{
- _logger.Debug("Refreshing recording parent {0}", item.Path);
+ _logger.Info("Refreshing recording parent {0}", item.Path);
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
{
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 149f69e5b..d6f5e0d9f 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -272,11 +272,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private bool EncodeVideo(MediaSourceInfo mediaSource)
{
- if (string.Equals(_liveTvOptions.RecordedVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced);
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index b7cfdea1b..b3c7ecc9f 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -541,27 +541,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
- using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
+ using (var httpResponse = await Get(options, false, info).ConfigureAwait(false))
{
- var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
-
- if (root != null)
+ using (Stream responce = httpResponse.Content)
{
- foreach (ScheduleDirect.Headends headend in root)
+ var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
+
+ if (root != null)
{
- foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+ foreach (ScheduleDirect.Headends headend in root)
{
- lineups.Add(new NameIdPair
+ foreach (ScheduleDirect.Lineup lineup in headend.lineups)
{
- Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
- Id = lineup.uri.Substring(18)
- });
+ lineups.Add(new NameIdPair
+ {
+ Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
+ Id = lineup.uri.Substring(18)
+ });
+ }
}
}
- }
- else
- {
- _logger.Info("No lineups available");
+ else
+ {
+ _logger.Info("No lineups available");
+ }
}
}
}
@@ -647,6 +650,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
bool enableRetry,
ListingsProviderInfo providerInfo)
{
+ // Schedules direct requires that the client support compression and will return a 400 response without it
+ options.EnableHttpCompression = true;
+
try
{
return await _httpClient.Post(options).ConfigureAwait(false);
@@ -671,13 +677,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return await Post(options, false, providerInfo).ConfigureAwait(false);
}
- private async Task<Stream> Get(HttpRequestOptions options,
+ private async Task<HttpResponseInfo> Get(HttpRequestOptions options,
bool enableRetry,
ListingsProviderInfo providerInfo)
{
+ // Schedules direct requires that the client support compression and will return a 400 response without it
+ options.EnableHttpCompression = true;
+
try
{
- return await _httpClient.Get(options).ConfigureAwait(false);
+ return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
}
catch (HttpException ex)
{
@@ -797,11 +806,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
- using (var response = await Get(options, false, null).ConfigureAwait(false))
+ using (var httpResponse = await Get(options, false, null).ConfigureAwait(false))
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+ using (var response = httpResponse.Content)
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ }
}
}
catch (HttpException ex)
@@ -879,53 +891,56 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var list = new List<ChannelInfo>();
- using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
+ using (var httpResponse = await Get(httpOptions, true, info).ConfigureAwait(false))
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
- _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
- _logger.Info("Mapping Stations to Channel");
-
- var allStations = root.stations ?? new List<ScheduleDirect.Station>();
-
- foreach (ScheduleDirect.Map map in root.map)
+ using (var response = httpResponse.Content)
{
- var channelNumber = GetChannelNumber(map);
-
- var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
- if (station == null)
- {
- station = new ScheduleDirect.Station
- {
- stationID = map.stationID
- };
- }
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
+ _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
+ _logger.Info("Mapping Stations to Channel");
- var name = channelNumber;
+ var allStations = root.stations ?? new List<ScheduleDirect.Station>();
- var channelInfo = new ChannelInfo
+ foreach (ScheduleDirect.Map map in root.map)
{
- Number = channelNumber,
- Name = name
- };
+ var channelNumber = GetChannelNumber(map);
- if (station != null)
- {
- if (!string.IsNullOrWhiteSpace(station.name))
+ var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
+ if (station == null)
{
- channelInfo.Name = station.name;
+ station = new ScheduleDirect.Station
+ {
+ stationID = map.stationID
+ };
}
- channelInfo.Id = station.stationID;
- channelInfo.CallSign = station.callsign;
+ var name = channelNumber;
+
+ var channelInfo = new ChannelInfo
+ {
+ Number = channelNumber,
+ Name = name
+ };
- if (station.logo != null)
+ if (station != null)
{
- channelInfo.ImageUrl = station.logo.URL;
- channelInfo.HasImage = true;
+ if (!string.IsNullOrWhiteSpace(station.name))
+ {
+ channelInfo.Name = station.name;
+ }
+
+ channelInfo.Id = station.stationID;
+ channelInfo.CallSign = station.callsign;
+
+ if (station.logo != null)
+ {
+ channelInfo.ImageUrl = station.logo.URL;
+ channelInfo.HasImage = true;
+ }
}
- }
- list.Add(channelInfo);
+ list.Add(channelInfo);
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index 8ea98879a..95ec1dee0 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -110,7 +110,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
_fileSystem.CreateDirectory(tempFolder);
- _zipClient.ExtractAllFromGz(stream, tempFolder, true);
+ try
+ {
+ _zipClient.ExtractAllFromGz(stream, tempFolder, true);
+ }
+ catch
+ {
+ // If the extraction fails just return the original file, it could be a gz
+ return file;
+ }
return _fileSystem.GetFiles(tempFolder, true)
.Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index e6479feaa..15bbca136 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -89,6 +89,11 @@ namespace Emby.Server.Implementations.LiveTv
if (channel != null)
{
dto.ChannelName = channel.Name;
+
+ if (channel.HasImage(ImageType.Primary))
+ {
+ dto.ChannelPrimaryImageTag = GetImageTag(channel);
+ }
}
return dto;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 857afa378..718620ab5 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -173,11 +173,11 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- public async Task<QueryResult<BaseItem>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
+ public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var topFolder = GetInternalLiveTvFolder(cancellationToken);
var internalQuery = new InternalItemsQuery(user)
{
@@ -527,18 +527,18 @@ namespace Emby.Server.Implementations.LiveTv
item.ChannelType = channelInfo.ChannelType;
item.ServiceName = serviceName;
+
+ if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
+ {
+ forceUpdate = true;
+ }
item.Number = channelInfo.Number;
- //if (!string.Equals(item.ProviderImageUrl, channelInfo.ImageUrl, StringComparison.OrdinalIgnoreCase))
- //{
- // isNew = true;
- // replaceImages.Add(ImageType.Primary);
- //}
- //if (!string.Equals(item.ProviderImagePath, channelInfo.ImagePath, StringComparison.OrdinalIgnoreCase))
- //{
- // isNew = true;
- // replaceImages.Add(ImageType.Primary);
- //}
+ if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal))
+ {
+ forceUpdate = true;
+ }
+ item.Name = channelInfo.Name;
if (!item.HasImage(ImageType.Primary))
{
@@ -554,18 +554,13 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- if (string.IsNullOrEmpty(item.Name))
- {
- item.Name = channelInfo.Name;
- }
-
if (isNew)
{
_libraryManager.CreateItem(item, cancellationToken);
}
else if (forceUpdate)
{
- await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken);
}
await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
@@ -760,7 +755,7 @@ namespace Emby.Server.Implementations.LiveTv
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
}
- private async Task<Guid> CreateRecordingRecord(RecordingInfo info, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
+ private Guid CreateRecordingRecord(RecordingInfo info, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
{
var isNew = false;
@@ -892,7 +887,7 @@ namespace Emby.Server.Implementations.LiveTv
else if (dataChanged || info.DateLastUpdated > recording.DateLastSaved || statusChanged)
{
metadataRefreshMode = MetadataRefreshMode.FullRefresh;
- await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken);
}
if (info.Status != RecordingStatus.InProgress)
@@ -928,7 +923,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var topFolder = GetInternalLiveTvFolder(cancellationToken);
if (query.OrderBy.Length == 0)
{
@@ -1007,11 +1002,11 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public async Task<QueryResult<BaseItem>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public QueryResult<BaseItem> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
{
var user = _userManager.GetUserById(query.UserId);
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var topFolder = GetInternalLiveTvFolder(cancellationToken);
var internalQuery = new InternalItemsQuery(user)
{
@@ -1072,11 +1067,11 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public async Task<QueryResult<BaseItemDto>> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public QueryResult<BaseItemDto> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
{
RemoveFields(options);
- var internalResult = await GetRecommendedProgramsInternal(query, options, cancellationToken).ConfigureAwait(false);
+ var internalResult = GetRecommendedProgramsInternal(query, options, cancellationToken);
var user = _userManager.GetUserById(query.UserId);
@@ -1302,7 +1297,7 @@ namespace Emby.Server.Implementations.LiveTv
var list = new List<LiveTvChannel>();
var numComplete = 0;
- var parentFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var parentFolder = GetInternalLiveTvFolder(cancellationToken);
var parentFolderId = parentFolder.Id;
foreach (var channelInfo in allChannelsList)
@@ -1425,7 +1420,7 @@ namespace Emby.Server.Implementations.LiveTv
// TODO: Do this in bulk
foreach (var program in updatedPrograms)
{
- await _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken);
}
currentChannel.IsMovie = isMovie;
@@ -1434,7 +1429,7 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.IsKids = isKids;
currentChannel.IsSeries = iSSeries;
- await currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
}
catch (OperationCanceledException)
{
@@ -1549,9 +1544,8 @@ namespace Emby.Server.Implementations.LiveTv
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var recordingTasks = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, internalLiveTvFolderId, cancellationToken));
-
- var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
+ var idList = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, internalLiveTvFolderId, cancellationToken))
+ .ToArray();
await CleanDatabaseInternal(idList, new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
@@ -1726,7 +1720,7 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItem>();
}
- var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var folder = GetInternalLiveTvFolder(cancellationToken);
// TODO: Figure out how to merge emby recordings + service recordings
if (_services.Length == 1)
@@ -2143,18 +2137,6 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- public Task OnRecordingFileDeleted(BaseItem recording)
- {
- var service = GetService(recording);
-
- if (service is EmbyTV.EmbyTV)
- {
- return service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None);
- }
-
- return Task.FromResult(true);
- }
-
public async Task DeleteRecording(string recordingId)
{
var recording = await GetInternalRecording(recordingId, CancellationToken.None).ConfigureAwait(false);
@@ -2171,13 +2153,17 @@ namespace Emby.Server.Implementations.LiveTv
{
var service = GetService(recording.ServiceName);
- try
- {
- await service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None).ConfigureAwait(false);
- }
- catch (ResourceNotFoundException)
+ if (service != null)
{
+ // handle the service being uninstalled and the item hanging around in the database
+ try
+ {
+ await service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (ResourceNotFoundException)
+ {
+ }
}
_lastRecordingRefreshTime = DateTime.MinValue;
@@ -2387,7 +2373,7 @@ namespace Emby.Server.Implementations.LiveTv
MinEndDate = now,
Limit = channelIds.Length,
OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
- TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") },
+ TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id.ToString("N") },
DtoOptions = options
}) : new List<BaseItem>();
@@ -2910,11 +2896,11 @@ namespace Emby.Server.Implementations.LiveTv
return service.ResetTuner(parts[1], cancellationToken);
}
- public async Task<BaseItemDto> GetLiveTvFolder(string userId, CancellationToken cancellationToken)
+ public BaseItemDto GetLiveTvFolder(string userId, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(userId);
- var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+ var folder = GetInternalLiveTvFolder(cancellationToken);
return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
}
@@ -2930,10 +2916,10 @@ namespace Emby.Server.Implementations.LiveTv
options.Fields = fields.ToArray(fields.Count);
}
- public async Task<Folder> GetInternalLiveTvFolder(CancellationToken cancellationToken)
+ public Folder GetInternalLiveTvFolder(CancellationToken cancellationToken)
{
- var name = _localization.GetLocalizedString("ViewTypeLiveTV");
- return await _libraryManager.GetNamedView(name, CollectionType.LiveTv, name, cancellationToken).ConfigureAwait(false);
+ var name = _localization.GetLocalizedString("HeaderLiveTV");
+ return _libraryManager.GetNamedView(name, CollectionType.LiveTv, name, cancellationToken);
}
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index bb11dac5f..59346cdec 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -86,16 +86,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
CancellationToken = cancellationToken,
BufferContent = false
};
- using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+ using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
{
- var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
-
- if (info.ImportFavoritesOnly)
+ using (var stream = response.Content)
{
- lineup = lineup.Where(i => i.Favorite).ToList();
- }
+ var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
+
+ if (info.ImportFavoritesOnly)
+ {
+ lineup = lineup.Where(i => i.Favorite).ToList();
+ }
- return lineup.Where(i => !i.DRM).ToList();
+ return lineup.Where(i => !i.DRM).ToList();
+ }
}
}
@@ -143,26 +146,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
- using (var stream = await _httpClient.Get(new HttpRequestOptions()
+ using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
{
Url = string.Format("{0}/discover.json", GetApiUrl(info, false)),
CancellationToken = cancellationToken,
TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
- }).ConfigureAwait(false))
+ }, "GET").ConfigureAwait(false))
{
- var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
-
- if (!string.IsNullOrWhiteSpace(info.Id))
+ using (var stream = response.Content)
{
- lock (_modelCache)
+ var discoverResponse = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
+
+ if (!string.IsNullOrWhiteSpace(info.Id))
{
- _modelCache[info.Id] = response;
+ lock (_modelCache)
+ {
+ _modelCache[info.Id] = discoverResponse;
+ }
}
- }
- return response;
+ return discoverResponse;
+ }
}
}
catch (HttpException ex)
@@ -197,7 +203,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var uri = new Uri(GetApiUrl(info, false));
- using (var manager = new HdHomerunManager(_socketFactory))
+ using (var manager = new HdHomerunManager(_socketFactory, Logger))
{
// Legacy HdHomeruns are IPv4 only
var ipInfo = _networkManager.ParseIpAddress(uri.Host);
@@ -299,6 +305,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
int? videoBitrate = null;
int? audioBitrate = null;
+ var isHd = channelInfo.IsHD ?? true;
+
if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase))
{
width = 1280;
@@ -350,7 +358,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
else
{
// This is for android tv's 1200 condition. Remove once not needed anymore so that we can avoid possible side effects of dummying up this data
- if ((channelInfo.IsHD ?? true))
+ if (isHd)
{
width = 1920;
height = 1080;
@@ -367,9 +375,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!videoBitrate.HasValue)
{
- videoBitrate = (channelInfo.IsHD ?? true) ? 15000000 : 2000000;
+ videoBitrate = isHd ? 15000000 : 2000000;
}
- audioBitrate = (channelInfo.IsHD ?? true) ? 448000 : 192000;
+ audioBitrate = isHd ? 448000 : 192000;
}
// normalize
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
index 604aa74f0..7e0e5fc5c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
@@ -22,9 +22,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
- private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
-
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
: base(mediaSource, environment, fileSystem, logger, appPaths)
{
@@ -33,19 +30,34 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
OriginalStreamId = originalStreamId;
}
- protected override Task OpenInternal(CancellationToken openCancellationToken)
+ public override async Task Open(CancellationToken openCancellationToken)
{
- _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
+ LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
var mediaSource = OriginalMediaSource;
var url = mediaSource.Path;
+ FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
+
Logger.Info("Opening HDHR Live stream from {0}", url);
- var taskCompletionSource = new TaskCompletionSource<bool>();
+ var response = await _httpClient.SendAsync(new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = CancellationToken.None,
+ BufferContent = false,
+
+ // Increase a little bit
+ TimeoutMs = 30000,
+
+ EnableHttpCompression = false
+
+ }, "GET").ConfigureAwait(false);
+
+ Logger.Info("Opened HDHR stream from {0}", url);
- StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
+ StartStreaming(response, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
@@ -59,77 +71,42 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
-
- return taskCompletionSource.Task;
-
- //await Task.Delay(5000).ConfigureAwait(false);
}
- public override Task Close()
+ public override void Close()
{
Logger.Info("Closing HDHR live stream");
- _liveStreamCancellationTokenSource.Cancel();
-
- return _liveStreamTaskCompletionSource.Task;
+ LiveStreamCancellationTokenSource.Cancel();
}
- private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private Task StartStreaming(HttpResponseInfo response, CancellationToken cancellationToken)
{
return Task.Run(async () =>
{
- var isFirstAttempt = true;
-
- while (!cancellationToken.IsCancellationRequested)
+ try
{
- try
+ using (response)
{
- using (var response = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = false,
-
- // Increase a little bit
- TimeoutMs = 30000,
-
- EnableHttpCompression = false
-
- }, "GET").ConfigureAwait(false))
+ using (var stream = response.Content)
{
- Logger.Info("Opened HDHR stream from {0}", url);
+ Logger.Info("Beginning HdHomerunHttpStream stream to file");
- if (!cancellationToken.IsCancellationRequested)
+ FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
+ using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
{
- Logger.Info("Beginning multicastStream.CopyUntilCancelled");
-
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
- using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
- {
- StreamHelper.CopyTo(response.Content, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
- }
+ StreamHelper.CopyTo(stream, fileStream, 81920, null, cancellationToken);
}
}
}
- catch (OperationCanceledException)
- {
- break;
- }
- catch (Exception ex)
- {
- if (isFirstAttempt)
- {
- Logger.ErrorException("Error opening live stream:", ex);
- openTaskCompletionSource.TrySetException(ex);
- break;
- }
-
- Logger.ErrorException("Error copying live stream, will reopen", ex);
- }
-
- isFirstAttempt = false;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error copying live stream.", ex);
}
- _liveStreamTaskCompletionSource.TrySetResult(true);
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
});
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index c737c4cba..5156f1744 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
@@ -89,9 +90,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly ISocketFactory _socketFactory;
private IpAddressInfo _remoteIp;
- public HdHomerunManager(ISocketFactory socketFactory)
+ private ILogger _logger;
+
+ public HdHomerunManager(ISocketFactory socketFactory, ILogger logger)
{
_socketFactory = socketFactory;
+ _logger = logger;
}
public void Dispose()
@@ -140,6 +144,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_lockkey = (uint)rand.Next();
}
+ var lockKeyValue = _lockkey.Value;
+
var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
for (int i = 0; i < numTuners; ++i)
@@ -148,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
continue;
_activeTuner = i;
- var lockKeyString = String.Format("{0:d}", _lockkey.Value);
+ var lockKeyString = String.Format("{0:d}", lockKeyValue);
var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
@@ -160,27 +166,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var commandList = commands.GetCommands();
foreach(Tuple<string,string> command in commandList)
{
- var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value);
+ var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
{
- await ReleaseLockkey(tcpClient).ConfigureAwait(false);
+ await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
continue;
}
}
var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
- var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value);
+ var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
{
- await ReleaseLockkey(tcpClient).ConfigureAwait(false);
+ await ReleaseLockkey(tcpClient, lockKeyValue).ConfigureAwait(false);
continue;
}
@@ -201,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
foreach (Tuple<string, string> command in commandList)
{
- var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey.Value);
+ var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
@@ -216,24 +222,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task StopStreaming()
{
- if (!_lockkey.HasValue)
+ var lockKey = _lockkey;
+
+ if (!lockKey.HasValue)
return;
using (var socket = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
{
- await ReleaseLockkey(socket).ConfigureAwait(false);
+ await ReleaseLockkey(socket, lockKey.Value).ConfigureAwait(false);
}
}
- private async Task ReleaseLockkey(ISocket tcpClient)
+ private async Task ReleaseLockkey(ISocket tcpClient, uint lockKeyValue)
{
- var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", _lockkey);
+ _logger.Info("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
+
+ var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
- var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", _lockkey);
+ var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue);
_lockkey = null;
await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index ff8fd1bc4..06326d26c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -1,23 +1,16 @@
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Emby.Server.Implementations.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
-using System.Globalization;
-using MediaBrowser.Controller.IO;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
@@ -26,8 +19,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
- private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
- private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
private readonly IHdHomerunChannelCommands _channelCommands;
private readonly int _numTuners;
private readonly INetworkManager _networkManager;
@@ -43,20 +34,62 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_numTuners = numTuners;
}
- protected override Task OpenInternal(CancellationToken openCancellationToken)
+ public override async Task Open(CancellationToken openCancellationToken)
{
- _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
+ LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
var mediaSource = OriginalMediaSource;
var uri = new Uri(mediaSource.Path);
var localPort = _networkManager.GetRandomUnusedUdpPort();
+ FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
+
Logger.Info("Opening HDHR UDP Live stream from {0}", uri.Host);
+ var remoteAddress = _networkManager.ParseIpAddress(uri.Host);
+ IpAddressInfo localAddress = null;
+ using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false))
+ {
+ try
+ {
+ tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort));
+ localAddress = tcpSocket.LocalEndPoint.IpAddress;
+ tcpSocket.Close();
+ }
+ catch (Exception)
+ {
+ Logger.Error("Unable to determine local ip address for Legacy HDHomerun stream.");
+ return;
+ }
+ }
+
+ var udpClient = _socketFactory.CreateUdpSocket(localPort);
+ var hdHomerunManager = new HdHomerunManager(_socketFactory, Logger);
+
+ try
+ {
+ // send url to start streaming
+ await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ using (udpClient)
+ {
+ using (hdHomerunManager)
+ {
+ if (!(ex is OperationCanceledException))
+ {
+ Logger.ErrorException("Error opening live stream:", ex);
+ }
+ throw;
+ }
+ }
+ }
+
var taskCompletionSource = new TaskCompletionSource<bool>();
- StartStreaming(uri.Host, localPort, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
+ StartStreaming(udpClient, hdHomerunManager, remoteAddress, localAddress, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
@@ -68,86 +101,47 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
- return taskCompletionSource.Task;
-
//await Task.Delay(5000).ConfigureAwait(false);
+ await taskCompletionSource.Task.ConfigureAwait(false);
}
- public override Task Close()
+ public override void Close()
{
Logger.Info("Closing HDHR UDP live stream");
- _liveStreamCancellationTokenSource.Cancel();
-
- return _liveStreamTaskCompletionSource.Task;
+ LiveStreamCancellationTokenSource.Cancel();
}
- private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private Task StartStreaming(ISocket udpClient, HdHomerunManager hdHomerunManager, IpAddressInfo remoteAddress, IpAddressInfo localAddress, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
{
- var isFirstAttempt = true;
- using (var udpClient = _socketFactory.CreateUdpSocket(localPort))
+ using (udpClient)
{
- using (var hdHomerunManager = new HdHomerunManager(_socketFactory))
+ using (hdHomerunManager)
{
- var remoteAddress = _networkManager.ParseIpAddress(remoteIp);
- IpAddressInfo localAddress = null;
- using (var tcpSocket = _socketFactory.CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, false))
+ try
+ {
+ await CopyTo(udpClient, TempFilePath, openTaskCompletionSource, cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException ex)
{
- try
- {
- tcpSocket.Connect(new IpEndPointInfo(remoteAddress, HdHomerunManager.HdHomeRunPort));
- localAddress = tcpSocket.LocalEndPoint.IpAddress;
- tcpSocket.Close();
- }
- catch (Exception)
- {
- Logger.Error("Unable to determine local ip address for Legacy HDHomerun stream.");
- return;
- }
+ Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
+ openTaskCompletionSource.TrySetException(ex);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error opening live stream:", ex);
+ openTaskCompletionSource.TrySetException(ex);
}
- while (!cancellationToken.IsCancellationRequested)
+ try
{
- try
- {
- // send url to start streaming
- await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, cancellationToken).ConfigureAwait(false);
-
- Logger.Info("Opened HDHR UDP stream from {0}", remoteAddress);
-
- if (!cancellationToken.IsCancellationRequested)
- {
- FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
- using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
- {
- CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken);
- }
- }
- }
- catch (OperationCanceledException ex)
- {
- Logger.Info("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
- openTaskCompletionSource.TrySetException(ex);
- break;
- }
- catch (Exception ex)
- {
- if (isFirstAttempt)
- {
- Logger.ErrorException("Error opening live stream:", ex);
- openTaskCompletionSource.TrySetException(ex);
- break;
- }
-
- Logger.ErrorException("Error copying live stream, will reopen", ex);
- }
-
- isFirstAttempt = false;
+ await hdHomerunManager.StopStreaming().ConfigureAwait(false);
}
+ catch
+ {
- await hdHomerunManager.StopStreaming().ConfigureAwait(false);
- _liveStreamTaskCompletionSource.TrySetResult(true);
+ }
}
}
@@ -158,36 +152,45 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
{
Task.Run(() =>
- {
- openTaskCompletionSource.TrySetResult(true);
- });
+ {
+ openTaskCompletionSource.TrySetResult(true);
+ });
}
private static int RtpHeaderBytes = 12;
- private void CopyTo(ISocket udpClient, Stream target, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+ private async Task CopyTo(ISocket udpClient, string file, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
- var source = _socketFactory.CreateNetworkStream(udpClient, false);
var bufferSize = 81920;
byte[] buffer = new byte[bufferSize];
int read;
var resolved = false;
- while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
+ using (var source = _socketFactory.CreateNetworkStream(udpClient, false))
{
- cancellationToken.ThrowIfCancellationRequested();
+ using (var fileStream = FileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
+ {
+ var currentCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token).Token;
- read -= RtpHeaderBytes;
+ while ((read = await source.ReadAsync(buffer, 0, buffer.Length, currentCancellationToken).ConfigureAwait(false)) != 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
- if (read > 0)
- {
- target.Write(buffer, RtpHeaderBytes, read);
- }
+ currentCancellationToken = cancellationToken;
- if (!resolved)
- {
- resolved = true;
- Resolve(openTaskCompletionSource);
+ read -= RtpHeaderBytes;
+
+ if (read > 0)
+ {
+ fileStream.Write(buffer, RtpHeaderBytes, read);
+ }
+
+ if (!resolved)
+ {
+ resolved = true;
+ Resolve(openTaskCompletionSource);
+ }
+ }
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 4641a1c91..12695cd8e 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly string TempFilePath;
protected readonly ILogger Logger;
+ protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
{
@@ -46,19 +47,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
TempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
}
- public Task Open(CancellationToken cancellationToken)
- {
- return OpenInternal(cancellationToken);
- }
-
- protected virtual Task OpenInternal(CancellationToken cancellationToken)
+ public virtual Task Open(CancellationToken openCancellationToken)
{
return Task.FromResult(true);
}
- public virtual Task Close()
+ public virtual void Close()
{
- return Task.FromResult(true);
}
protected Stream GetInputStream(string path, bool allowAsyncFileRead)
@@ -75,11 +70,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected async Task DeleteTempFile(string path, int retryCount = 0)
{
+ if (retryCount == 0)
+ {
+ Logger.Info("Deleting temp file {0}", path);
+ }
+
try
{
FileSystem.DeleteFile(path);
return;
}
+ catch (DirectoryNotFoundException)
+ {
+ return;
+ }
+ catch (FileNotFoundException)
+ {
+ return;
+ }
catch
{
@@ -96,6 +104,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
+
var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
@@ -110,16 +120,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
{
byte[] buffer = new byte[bufferSize];
- while (true)
+
+ var eofCount = 0;
+ var emptyReadLimit = 1000;
+
+ while (eofCount < emptyReadLimit)
{
cancellationToken.ThrowIfCancellationRequested();
- var read = source.Read(buffer, 0, buffer.Length);
+ var bytesRead = source.Read(buffer, 0, buffer.Length);
- if (read > 0)
+ if (bytesRead == 0)
{
+ eofCount++;
+ await Task.Delay(10, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ eofCount = 0;
+
//await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
- destination.Write(buffer, 0, read);
+ destination.Write(buffer, 0, bytesRead);
if (onStarted != null)
{
@@ -127,10 +148,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
onStarted = null;
}
}
- else
- {
- await Task.Delay(10).ConfigureAwait(false);
- }
}
}
@@ -140,6 +157,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
stream.Seek(offset, SeekOrigin.End);
}
+ catch (IOException)
+ {
+
+ }
catch (ArgumentException)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 8d1854f4b..9fc6687d1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -93,13 +93,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
{
- var channelIdPrefix = GetFullChannelIdPrefix(info);
-
- if (!channelId.StartsWith(channelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
var channel = channels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
if (channel != null)
@@ -165,7 +158,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
RequiresOpening = true,
RequiresClosing = true,
RequiresLooping = info.EnableStreamLooping,
- EnableMpDecimate = info.EnableMpDecimate,
ReadAtNativeFramerate = false,