示例#1
0
 public async Task Validate(TunerHostInfo info)
 {
     using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
     {
     }
 }
示例#2
0
 protected override Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken)
 {
     return(Task.FromResult(new List <MediaSourceInfo> {
         CreateMediaSourceInfo(info, channelInfo)
     }));
 }
示例#3
0
        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);
        }
示例#4
0
        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"));
            }
示例#5
0
        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));
        }
示例#6
0
        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;
            }
        }
示例#7
0
        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);
        }
示例#8
0
 protected abstract Task <LiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
示例#9
0
 private string GetFullChannelIdPrefix(TunerHostInfo info)
 {
     return(ChannelIdPrefix + info.Url.GetMD5().ToString("N"));
 }
示例#10
0
        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);
        }
示例#11
0
        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));
        }
示例#12
0
 protected abstract Task <IEnumerable <ChannelInfo> > GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
示例#13
0
 protected abstract Task <bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
示例#14
0
        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);
        }
示例#15
0
        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);
        }
示例#16
0
 private string GetFullChannelIdPrefix(TunerHostInfo info)
 {
     return(ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture));
 }
示例#17
0
 protected abstract Task <List <MediaSourceInfo> > GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);