public async Task<IDictionary<long, Program>> LoadAsync(ContentType contentType, CancellationToken cancellationToken) { var playlists = Playlists; foreach (var playlist in playlists) { try { var parser = new M3U8Parser(); if (null != _playlistWebReader) _playlistWebReader.Dispose(); _playlistWebReader = _webReaderManager.CreateReader(playlist, ContentKind.Playlist, contentType: contentType); var actualPlaylist = await parser.ParseAsync(_playlistWebReader, _retryManager, playlist, cancellationToken) .ConfigureAwait(false); return await LoadAsync(_playlistWebReader, parser, contentType, cancellationToken).ConfigureAwait(false); } catch (StatusCodeWebException e) { // This one didn't work, so try the next playlist url. Debug.WriteLine("HlsProgramManager.LoadAsync: " + e.Message); } } return NoPrograms; }
public static void Parse(this M3U8Parser parser, Uri baseUrl, Stream stream, Encoding encoding = null) { if (null == encoding) { encoding = UriExtensions.HasExtension(baseUrl, ".m3u") ? SmEncodings.Latin1 : Encoding.UTF8; } using (StreamReader streamReader = new StreamReader(stream, encoding)) M3U8ParserExtensions.Parse(parser, baseUrl, (TextReader)streamReader); }
public static Task <Uri> ParseAsync(this M3U8Parser parser, IWebReader webReader, IRetryManager retryManager, Uri playlist, CancellationToken cancellationToken) { IRetry retry = RetryManagerExtensions.CreateWebRetry(retryManager, 2, 250); return(retry.CallAsync <Uri>((Func <Task <Uri> >)(() => WebReaderExtensions.ReadStreamAsync <Uri>(webReader, playlist, retry, (Func <Uri, Stream, Uri>)((actualPlaylist, stream) => { M3U8ParserExtensions.Parse(parser, actualPlaylist, stream, (Encoding)null); return actualPlaylist; }), cancellationToken)), cancellationToken)); }
/// <summary> /// A playlist is dynamic if it does not have an #EXT-X-ENDLIST tag and every segment has an #EXTINF with a valid /// duration. /// </summary> /// <param name="parser"></param> /// <returns></returns> public static bool IsDynamicPlayist(M3U8Parser parser) { if (null != parser.GlobalTags.Tag(M3U8Tags.ExtXEndList)) return false; var validDuration = parser.Playlist.All( p => { var extInf = M3U8Tags.ExtXInf.Find(p.Tags); return null != extInf && extInf.Duration >= 0; }); return validDuration; }
public HlsStreamSegments(M3U8Parser parser, IWebReader webReader, IRetryManager retryManager, IPlatformServices platformServices) { if (null == parser) throw new ArgumentNullException(nameof(parser)); if (null == webReader) throw new ArgumentNullException(nameof(webReader)); if (null == retryManager) throw new ArgumentNullException(nameof(retryManager)); if (null == platformServices) throw new ArgumentNullException(nameof(platformServices)); _parser = parser; _webReader = webReader; _retryManager = retryManager; _platformServices = platformServices; _mediaSequence = M3U8Tags.ExtXMediaSequence.GetValue<long>(parser.GlobalTags); }
public IDictionary<long, Program> Load(Uri playlist) { using (var f = new WebClient().OpenRead(playlist)) { var parser = new M3U8Parser(); parser.Parse(playlist, f); var audioStreams = new Dictionary<string, MediaGroup>(); foreach (var gt in parser.GlobalTags) { if (M3U8Tags.ExtXMedia == gt.Tag) { try { var audioAttribute = gt.Attribute(ExtMediaSupport.AttrType, "AUDIO"); if (null != audioAttribute) { var group = gt.Attribute(ExtMediaSupport.AttrGroupId).Value; var urlAttribute = gt.AttributeObject(ExtMediaSupport.AttrUri); Uri playlistUrl = null; if (null != urlAttribute) playlistUrl = new Uri(playlist, new Uri(urlAttribute, UriKind.RelativeOrAbsolute)); var audioStream = new PlaylistSubStream { Name = group, Playlist = playlistUrl }; MediaGroup mediaGroup; if (!audioStreams.TryGetValue(group, out mediaGroup)) { mediaGroup = new MediaGroup { Default = audioStream }; audioStreams[group] = mediaGroup; } var isDefault = 0 == string.CompareOrdinal("YES", gt.Attribute(ExtMediaSupport.AttrDefault).Value); if (isDefault) mediaGroup.Default = audioStream; var name = gt.Attribute(ExtMediaSupport.AttrName).Value; mediaGroup.Streams[name] = audioStream; } } catch (NullReferenceException) { // DynamicObject isn't welcome on the phone or this would be a binding exception. } } } var programs = new Dictionary<long, Program>(); SimpleSubProgram simpleSubProgram = null; foreach (var p in parser.Playlist) { var streamInf = p.Tags.FirstOrDefault(t => M3U8Tags.ExtXStreamInf == t.Tag); var programId = long.MinValue; MediaGroup audioGroup = null; if (null != streamInf) { var programIdAttribute = streamInf.Attribute(ExtStreamInfSupport.AttrProgramId); if (null != programIdAttribute) programId = programIdAttribute.Value; var audioAttribute = streamInf.AttributeObject(ExtStreamInfSupport.AttrAudio); if (null != audioAttribute) audioStreams.TryGetValue(audioAttribute, out audioGroup); var subProgram = new PlaylistSubProgram { Bandwidth = streamInf.Attribute(ExtStreamInfSupport.AttrBandwidth).Value, Playlist = new Uri(playlist, new Uri(p.Uri, UriKind.RelativeOrAbsolute)), Audio = audioGroup }; Program program; if (!programs.TryGetValue(programId, out program)) { program = new Program { ProgramId = programId }; programs[programId] = program; } program.SubPrograms.Add(subProgram); } else { var extInf = (ExtinfTagInstance)p.Tags.FirstOrDefault(t => M3U8Tags.ExtXInf == t.Tag); if (null != extInf) { if (null == simpleSubProgram) { simpleSubProgram = new SimpleSubProgram(); var program = new Program { ProgramId = long.MinValue }; program.SubPrograms.Add(simpleSubProgram); programs[program.ProgramId] = program; } simpleSubProgram.Segments.Add(new XSegment { Url = new Uri(playlist, new Uri(p.Uri, UriKind.RelativeOrAbsolute)), Duration = TimeSpan.FromSeconds((double)extInf.Duration) }); } } } return programs; } }
public override IEnumerable<XSegment> GetPlaylist(SubStream audio = null) { var playlist = Playlist; if (null == playlist) yield break; var parser = new M3U8Parser(); using (var f = new WebClient().OpenRead(playlist)) { parser.Parse(playlist, f); Uri previous = null; foreach (var p in parser.Playlist) { var url = new Uri(playlist, new Uri(p.Uri, UriKind.RelativeOrAbsolute)); if (null != previous && url == previous) continue; yield return new XSegment { Url = url }; previous = url; } } }
public IHlsStreamSegments Create(M3U8Parser parser, IWebReader webReader) { return new HlsStreamSegments(parser, webReader, _retryManager, _platformServices); }
public Task<ICollection<ISegment>> CreateSegmentsAsync(M3U8Parser parser, IWebReader webReader, CancellationToken cancellationToken) { var streamSegments = _streamSegmentsFactory.Create(parser, webReader); return streamSegments.CreateSegmentsAsync(cancellationToken); }
async Task<M3U8Parser> FetchPlaylistAsync(CancellationToken cancellationToken) { var urls = Urls; if (null == urls || urls.Count < 1) return null; foreach (var playlist in urls) { UpdateSubPlaylistCache(playlist); cancellationToken.ThrowIfCancellationRequested(); WebResponse webResponse = null; if (null == StreamMetadata) webResponse = new WebResponse(); var parsedPlaylist = await _subPlaylistCache.ReadAsync( (actualUri, bytes) => { cancellationToken.ThrowIfCancellationRequested(); if (bytes.Length < 1) return null; var parser = new M3U8Parser(); using (var ms = new MemoryStream(bytes)) { parser.Parse(actualUri, ms); } return parser; }, cancellationToken, webResponse) .ConfigureAwait(false); if (null != parsedPlaylist) { if (null != webResponse) StreamMetadata = _webMetadataFactory.CreateStreamMetadata(webResponse); return parsedPlaylist; } } return null; }
async Task UpdateAsync(M3U8Parser parser, CancellationToken cancellationToken) { _segments = await _segmentsFactory.CreateSegmentsAsync(parser, _subPlaylistCache.WebReader, cancellationToken).ConfigureAwait(false); _isDynamicPlaylist = HlsPlaylistSettings.Parameters.IsDynamicPlaylist(parser); _actualUrl = parser.BaseUrl; }
ISegment CreateStreamSegment(M3U8Parser.M3U8Uri uri, CancellationToken cancellationToken) { var url = _parser.ResolveUrl(uri.Uri); var segment = new SubStreamSegment(url, _parser.BaseUrl); if (_mediaSequence.HasValue) segment.MediaSequence = _mediaSequence + _segmentIndex; ++_segmentIndex; var tags = uri.Tags; if (null == tags || 0 == tags.Length) return segment; var info = M3U8Tags.ExtXInf.Find(tags); if (null != info) segment.Duration = TimeSpan.FromSeconds((double)info.Duration); var byteRange = M3U8Tags.ExtXByteRange.Find(tags); if (null != byteRange) HandleByteRange(segment, byteRange); var extKeys = M3U8Tags.ExtXKey.FindAll(tags); if (null != extKeys) HandleKey(segment, extKeys, cancellationToken); return segment; }
async Task<IDictionary<long, Program>> LoadAsync(IWebReader webReader, M3U8Parser parser, ContentType contentType, CancellationToken cancellationToken) { var audioStreams = new Dictionary<string, MediaGroup>(); foreach (var gt in parser.GlobalTags) { if (M3U8Tags.ExtXMedia == gt.Tag) { try { var audioAttribute = gt.Attribute(ExtMediaSupport.AttrType, "AUDIO"); if (null != audioAttribute) AddMedia(parser.BaseUrl, gt, audioStreams); } catch (NullReferenceException) { // DynamicObject isn't welcome on the phone or this would be a binding exception. } } } var programs = new Dictionary<long, Program>(); var hasSegments = false; foreach (var p in parser.Playlist) { if (null == p.Tags || p.Tags.Length < 1) { hasSegments = true; continue; } var streamInf = M3U8Tags.ExtXStreamInf.Find(p.Tags); var programId = long.MinValue; MediaGroup audioGroup = null; if (null != streamInf) { var programIdAttribute = streamInf.Attribute(ExtStreamInfSupport.AttrProgramId); if (null != programIdAttribute) programId = programIdAttribute.Value; var audioAttribute = streamInf.AttributeObject(ExtStreamInfSupport.AttrAudio); if (null != audioAttribute) audioStreams.TryGetValue(audioAttribute, out audioGroup); var playlistUrl = parser.ResolveUrl(p.Uri); var bandwidth = streamInf.Attribute(ExtStreamInfSupport.AttrBandwidth); var resolution = streamInf.AttributeInstance<ResolutionAttributeInstance>(ExtStreamInfSupport.AttrResolution); var programUrl = parser.BaseUrl; var program = GetProgram(programs, programId, programUrl); var hlsProgramStream = _programStreamFactory.Create(new[] { playlistUrl }, webReader, ContentType, StreamContentType); var subProgram = new PlaylistSubProgram(program, hlsProgramStream) { Bandwidth = null == bandwidth ? 0 : bandwidth.Value, Playlist = playlistUrl, AudioGroup = audioGroup }; if (null != resolution) { subProgram.Width = resolution.X; subProgram.Height = resolution.Y; } program.SubPrograms.Add(subProgram); } else hasSegments = true; } if (hasSegments) { var program = GetProgram(programs, long.MinValue, parser.BaseUrl); var hlsProgramStream = _programStreamFactory.Create(new[] { webReader.RequestUri }, webReader, ContentType, StreamContentType); await hlsProgramStream.SetParserAsync(parser, cancellationToken).ConfigureAwait(false); var subProgram = new PlaylistSubProgram(program, hlsProgramStream); program.SubPrograms.Add(subProgram); } return programs; }
public static void Parse(this M3U8Parser parser, Uri baseUrl, TextReader textReader) { parser.Parse(baseUrl, M3U8ParserExtensions.GetExtendedLines(textReader)); }
static void Main(string[] args) { if (args.Length < 1) return; try { using (var programManager = new ProgramManager()) { foreach (var arg in args) { var programs = programManager.Load(new Uri(arg)); foreach (var program in programs.Values) { foreach (var subProgram in program.SubPrograms) { foreach (var segment in subProgram.GetPlaylist()) { Debug.WriteLine("Segment: {0}", segment.Url); } } } Console.WriteLine("Reading {0}", arg); var url = new Uri(arg); using (var f = new WebClient().OpenRead(url)) { var parser = new M3U8Parser(); parser.Parse(url, f); } } } } catch (Exception ex) { Console.WriteLine(ex); } }
public Task SetParserAsync(M3U8Parser parser, CancellationToken cancellationToken) { UpdateSubPlaylistCache(parser.BaseUrl); return UpdateAsync(parser, cancellationToken); }
public static Uri ResolveUrl(this M3U8Parser parser, string url) { return(parser.ResolveUrl(new Uri(url, UriKind.RelativeOrAbsolute))); }