public async Task Validate(TunerHostInfo info) { using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false)) { } }
protected override Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken) { return(Task.FromResult(new List <MediaSourceInfo> { CreateMediaSourceInfo(info, channelInfo) })); }
private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, ChannelInfo channelInfo, string profile) { int? width = null; int? height = null; bool isInterlaced = true; string videoCodec = null; int?videoBitrate = null; var isHd = channelInfo.IsHD ?? true; if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase)) { width = 1920; height = 1080; isInterlaced = false; videoCodec = "h264"; videoBitrate = 15000000; } else if (string.Equals(profile, "internet720", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 8000000; } else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase)) { width = 960; height = 540; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2500000; } else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase)) { width = 848; height = 480; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase)) { width = 640; height = 360; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1500000; } else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase)) { width = 432; height = 240; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1000000; } 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 (isHd) { width = 1920; height = 1080; } } if (string.IsNullOrWhiteSpace(videoCodec)) { videoCodec = channelInfo.VideoCodec; } string audioCodec = channelInfo.AudioCodec; videoBitrate ??= isHd ? 15000000 : 2000000; int?audioBitrate = isHd ? 448000 : 192000; // normalize if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase)) { videoCodec = "mpeg2video"; } string nal = null; if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { nal = "0"; } var url = GetApiUrl(info); var id = profile; if (string.IsNullOrWhiteSpace(id)) { id = "native"; } id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture); var mediaSource = new MediaSourceInfo { Path = url, Protocol = MediaProtocol.Udp, MediaStreams = new MediaStream[] { new MediaStream { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container Index = -1, IsInterlaced = isInterlaced, Codec = videoCodec, Width = width, Height = height, BitRate = videoBitrate, NalLengthSize = nal }, new MediaStream { Type = MediaStreamType.Audio, // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1, Codec = audioCodec, BitRate = audioBitrate } }, RequiresOpening = true, RequiresClosing = true, BufferMs = 0, Container = "ts", Id = id, SupportsDirectPlay = false, SupportsDirectStream = true, SupportsTranscoding = true, IsInfiniteStream = true, IgnoreDts = true, // IgnoreIndex = true, // ReadAtNativeFramerate = true }; mediaSource.InferTotalBitrate(); return(mediaSource); }
protected override async Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo tuner, ChannelInfo channel, CancellationToken cancellationToken) { var list = new List <MediaSourceInfo>(); var channelId = channel.Id; var hdhrId = GetHdHrIdFromChannelId(channelId); if (channel is HdHomerunChannelInfo hdHomerunChannelInfo && hdHomerunChannelInfo.IsLegacyTuner) { list.Add(GetMediaSource(tuner, hdhrId, channel, "native")); }
protected override async Task <bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) { var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false); return(info.Any(i => i.Status == LiveTvTunerStatus.Available)); }
internal async Task <DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken) { var cacheKey = info.Id; lock (_modelCache) { if (!string.IsNullOrEmpty(cacheKey)) { if (_modelCache.TryGetValue(cacheKey, out DiscoverResponse response)) { return(response); } } } try { using var response = await _httpClientFactory.CreateClient(NamedClient.Default) .GetAsync(GetApiUrl(info) + "/discover.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken) .ConfigureAwait(false); response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); var discoverResponse = await JsonSerializer.DeserializeAsync <DiscoverResponse>(stream, _jsonOptions, cancellationToken) .ConfigureAwait(false); if (!string.IsNullOrEmpty(cacheKey)) { lock (_modelCache) { _modelCache[cacheKey] = discoverResponse; } } return(discoverResponse); } catch (HttpRequestException ex) { if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) { const string DefaultValue = "HDHR"; var response = new DiscoverResponse { ModelNumber = DefaultValue }; if (!string.IsNullOrEmpty(cacheKey)) { // HDHR4 doesn't have this api lock (_modelCache) { _modelCache[cacheKey] = response; } } return(response); } throw; } }
private MediaSourceInfo GetLegacyMediaSource(TunerHostInfo info, string channelId, ChannelInfo channel) { int? width = null; int? height = null; bool isInterlaced = true; string videoCodec = null; string audioCodec = null; int?videoBitrate = null; int?audioBitrate = null; if (channel != null) { if (string.IsNullOrWhiteSpace(videoCodec)) { videoCodec = channel.VideoCodec; } audioCodec = channel.AudioCodec; } // normalize if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase)) { videoCodec = "mpeg2video"; } string nal = null; var url = GetApiUrl(info, false); var id = channelId; id += "_" + url.GetMD5().ToString("N"); var mediaSource = new MediaSourceInfo { Path = url, Protocol = MediaProtocol.Udp, MediaStreams = new List <MediaStream> { new MediaStream { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container Index = -1, IsInterlaced = isInterlaced, Codec = videoCodec, Width = width, Height = height, BitRate = videoBitrate, NalLengthSize = nal }, new MediaStream { Type = MediaStreamType.Audio, // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1, Codec = audioCodec, BitRate = audioBitrate } }, RequiresOpening = true, RequiresClosing = true, BufferMs = 0, Container = "ts", Id = id, SupportsDirectPlay = false, SupportsDirectStream = true, SupportsTranscoding = true, IsInfiniteStream = true }; mediaSource.InferTotalBitrate(); return(mediaSource); }
protected abstract Task <LiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
private string GetFullChannelIdPrefix(TunerHostInfo info) { return(ChannelIdPrefix + info.Url.GetMD5().ToString("N")); }
private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, ChannelInfo channelInfo, string profile) { int? width = null; int? height = null; bool isInterlaced = true; string videoCodec = null; string audioCodec = null; int?videoBitrate = null; int?audioBitrate = null; if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase)) { width = 1920; height = 1080; isInterlaced = false; videoCodec = "h264"; videoBitrate = 15000000; } else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase)) { width = 960; height = 546; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2500000; } else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase)) { width = 848; height = 480; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase)) { width = 640; height = 360; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1500000; } else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase)) { width = 432; height = 240; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1000000; } if (channelInfo != null) { if (string.IsNullOrWhiteSpace(videoCodec)) { videoCodec = channelInfo.VideoCodec; } audioCodec = channelInfo.AudioCodec; if (!videoBitrate.HasValue) { videoBitrate = (channelInfo.IsHD ?? true) ? 15000000 : 2000000; } audioBitrate = (channelInfo.IsHD ?? true) ? 448000 : 192000; } // normalize if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase)) { videoCodec = "mpeg2video"; } string nal = null; if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { nal = "0"; } var url = GetApiUrl(info, true) + "/auto/v" + channelId; // If raw was used, the tuner doesn't support params if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { url += "?transcode=" + profile; } var id = profile; if (string.IsNullOrWhiteSpace(id)) { id = "native"; } id += "_" + url.GetMD5().ToString("N"); var mediaSource = new MediaSourceInfo { Path = url, Protocol = MediaProtocol.Http, MediaStreams = new List <MediaStream> { new MediaStream { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container Index = -1, IsInterlaced = isInterlaced, Codec = videoCodec, Width = width, Height = height, BitRate = videoBitrate, NalLengthSize = nal }, new MediaStream { Type = MediaStreamType.Audio, // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1, Codec = audioCodec, BitRate = audioBitrate } }, RequiresOpening = true, RequiresClosing = false, BufferMs = 0, Container = "ts", Id = id, SupportsDirectPlay = false, SupportsDirectStream = true, SupportsTranscoding = true, IsInfiniteStream = true }; mediaSource.InferTotalBitrate(); return(mediaSource); }
protected override async Task <List <ChannelInfo> > GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var channelIdPrefix = GetFullChannelIdPrefix(info); return(await new M3uParser(Logger, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false)); }
protected abstract Task <IEnumerable <ChannelInfo> > GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
protected abstract Task <bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
protected virtual MediaSourceInfo CreateMediaSourceInfo(TunerHostInfo info, ChannelInfo channel) { var path = channel.Path; var supportsDirectPlay = !info.EnableStreamLooping && info.TunerCount == 0; var supportsDirectStream = !info.EnableStreamLooping; var protocol = _mediaSourceManager.GetPathProtocol(path); var isRemote = true; if (Uri.TryCreate(path, UriKind.Absolute, out var uri)) { isRemote = !_networkManager.IsInLocalNetwork(uri.Host); } var httpHeaders = new Dictionary <string, string>(); if (protocol == MediaProtocol.Http) { // Use user-defined user-agent. If there isn't one, make it look like a browser. httpHeaders[HeaderNames.UserAgent] = string.IsNullOrWhiteSpace(info.UserAgent) ? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.85 Safari/537.36" : info.UserAgent; } var mediaSource = new MediaSourceInfo { Path = path, Protocol = protocol, MediaStreams = new List <MediaStream> { new MediaStream { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container Index = -1, IsInterlaced = true }, new MediaStream { Type = MediaStreamType.Audio, // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1 } }, RequiresOpening = true, RequiresClosing = true, RequiresLooping = info.EnableStreamLooping, ReadAtNativeFramerate = false, Id = channel.Path.GetMD5().ToString("N", CultureInfo.InvariantCulture), IsInfiniteStream = true, IsRemote = isRemote, IgnoreDts = true, SupportsDirectPlay = supportsDirectPlay, SupportsDirectStream = supportsDirectStream, RequiredHttpHeaders = httpHeaders }; mediaSource.InferTotalBitrate(); return(mediaSource); }
protected override async Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var list = new List <MediaSourceInfo>(); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { return(list); } var hdhrId = GetHdHrIdFromChannelId(channelId); var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false); var channelInfo = channels.FirstOrDefault(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)); var hdHomerunChannelInfo = channelInfo as HdHomerunChannelInfo; var isLegacyTuner = hdHomerunChannelInfo != null && hdHomerunChannelInfo.IsLegacyTuner; if (isLegacyTuner) { list.Add(GetLegacyMediaSource(info, hdhrId, channelInfo)); } else { try { var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); var model = modelInfo == null ? string.Empty : (modelInfo.ModelNumber ?? string.Empty); if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)) { list.Add(GetMediaSource(info, hdhrId, channelInfo, "native")); if (info.AllowHWTranscoding) { list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy")); list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540")); list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480")); list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360")); list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240")); list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile")); } } } catch { } if (list.Count == 0) { list.Add(GetMediaSource(info, hdhrId, channelInfo, "native")); } } return(list); }
private string GetFullChannelIdPrefix(TunerHostInfo info) { return(ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture)); }
protected abstract Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);