public async Task Start() { var streams = M3UPlaylist.Parse(await _client.GetStringAsync(_slideUrl)); // This will typically select a 720p stream with 3500 Kbps bitrate, the best on offer var bestStream = streams.StreamItems.OrderByDescending(s => s.Width * s.Height).First(); var url = _slideUrl.Substring(0, _slideUrl.LastIndexOf('/') + 1); var itemsListSrc = await _client.GetStringAsync(url + bestStream.Path); var itemsList = M3UPlaylist.Parse(itemsListSrc); _client.DefaultRequestHeaders.Remove("accept"); _client.DefaultRequestHeaders.Add("accept", "*/*"); // Prefetch all the AES keys foreach (var key in itemsList.Keys) { key.Key = await _client.GetByteArrayAsync(key.Uri); if (key.Key == null || key.Key.Length != 16) { throw new Exception("Failed to retrieve AES key: " + key.Uri); } } var videoBaseUrl = url + bestStream.Path; videoBaseUrl = videoBaseUrl.Substring(0, videoBaseUrl.LastIndexOf('/') + 1); int i = 0; foreach (var video in itemsList.VideoItems) { var videoUrl = videoBaseUrl + video.Path; var raw = await _client.GetByteArrayAsync(videoUrl); i++; Aes crypto = Aes.Create(); var decryptor = crypto.CreateDecryptor(video.Key.Key, video.Key.IV); byte[] decrypted = decryptor.TransformFinalBlock(raw, 0, raw.Length); if (NewVideoSequence != null) { NewVideoSequence(this, new VideoSequenceData { SourceUrl = videoUrl, RawData = decrypted, SequenceNumber = video.Sequence, PartsInQueue = itemsList.VideoItems.Count - i }); } } }
public static M3UPlaylist Parse(string content) { var lines = content.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); if (lines[0] != "#EXTM3U") { return(null); } var ret = new M3UPlaylist(); int seq = 0; M3UEncryptionKey currentKey = null; for (int i = 1; i < lines.Length; i++) { var line = lines[i]; if (line.StartsWith("#EXT-X-STREAM-INF:") && (i + 1) != lines.Length) { var props = ParseProperties(line); var item = new M3UStreamItem { Path = lines[++i] }; if (props.ContainsKey("RESOLUTION")) { var rect = props["RESOLUTION"].Split('x'); item.Width = int.Parse(rect[0]); item.Height = int.Parse(rect[1]); } if (props.ContainsKey("BANDWIDTH")) { item.Bandwidth = int.Parse(props["BANDWIDTH"]); } if (props.ContainsKey("AVERAGE-BANDWIDTH")) { item.AverageBandwidth = int.Parse(props["AVERAGE-BANDWIDTH"]); } ret.StreamItems.Add(item); } else if (line.StartsWith("#EXT-X-MEDIA-SEQUENCE")) { ret.MediaSequence = int.Parse(line.Split(':')[1]); } else if (line.StartsWith("#EXT-X-KEY")) { var props = ParseProperties(line); currentKey = new M3UEncryptionKey { Method = props["METHOD"], Uri = props["URI"], }; var iv = props["IV"]; iv = iv.Substring(2); currentKey.IV = new byte[iv.Length / 2]; for (int j = 0; j < (iv.Length / 2); j++) { currentKey.IV[j] = byte.Parse(iv[j * 2 + 0].ToString() + iv[j * 2 + 1], NumberStyles.HexNumber); } ret.Keys.Add(currentKey); } else if (line.StartsWith("#EXTINF") && (i + 1) != lines.Length) { ret.VideoItems.Add(new M3UVideoItem { Path = lines[++i], Sequence = ret.MediaSequence + (seq++), Key = currentKey }); } } return(ret); }