Example #1
0
        public static ReadOnlyMemory <byte> DecryptSegmentData(byte[] encryptedSegmentData, HlsMediaSegment segment, byte[] key, byte[] iv)
        {
            AesEngine                 engine         = new AesEngine(); // TODO: See if this +2 can be made static.
            CbcBlockCipher            blockCipher    = new CbcBlockCipher(engine);
            PaddedBufferedBlockCipher cipher         = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
            KeyParameter              keyParam       = new KeyParameter(key);
            ParametersWithIV          keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);

            cipher.Init(false, keyParamWithIv);
            byte[] decryptedSegmentData = new byte[cipher.GetOutputSize(encryptedSegmentData.Length)];
            int    length = cipher.ProcessBytes(encryptedSegmentData, decryptedSegmentData, 0);

            length += cipher.DoFinal(decryptedSegmentData, length);

            return(new ReadOnlyMemory <byte>(decryptedSegmentData, 0, length));
        }
Example #2
0
        public async Task <ReadOnlyMemory <byte> > GetNextSegmentAsync()
        {
            if (_nextSegment >= _mediaPlaylist.MediaSegments.Count)
            {
                throw new Exception();
            }

            HlsMediaSegment segment = _mediaPlaylist.MediaSegments[_nextSegment];

            if (segment.Key != null && segment.Key.Method != HlsKeyMethod.None && !_keyCache.ContainsKey(segment.Key.Uri))
            {
                byte[] keyData;
                if (OnKeyFetch != null)
                {
                    keyData = await OnKeyFetch(segment.Key);
                }
                else
                {
                    keyData = await HttpClient.GetByteArrayAsync(segment.Key.Uri);
                }

                if (keyData.Length != 16)
                {
                    throw new Exception($"Key length not 16 ({keyData.Length}).");
                }

                _keyCache[segment.Key.Uri] = keyData;
            }

            byte[] segmentBytes;
            if (OnSegmentFetch != null)
            {
                segmentBytes = await OnSegmentFetch(segment);
            }
            else
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, segment.Uri.ToString());
                if (segment.ByteRange != null)
                {
                    string[] rangeParts = segment.ByteRange.Split('@');
                    int      len = Int32.Parse(rangeParts[0]), start = Int32.Parse(rangeParts[1]);
                    request.Headers.Range = new RangeHeaderValue(start, start + len - 1);
                }

                HttpResponseMessage response = await HttpClient.SendAsync(request);

                segmentBytes = await response.Content.ReadAsByteArrayAsync();
            }

            ReadOnlyMemory <byte> segmentData = segmentBytes;

            if (segment.Key != null && segment.Key.Method == HlsKeyMethod.Aes128)
            {
                byte[] iv = new byte[16];
                if (segment.Key.KeyFormat == "identity")
                {
                    Buffer.BlockCopy(BitConverter.GetBytes(segment.SequenceNumber).Reverse().ToArray(), 0, iv, 12, 4);
                }
                segmentData = DecryptSegmentData(segmentBytes, segment, _keyCache[segment.Key.Uri], iv);
            }

            _nextSegment++;
            return(segmentData);
        }
Example #3
0
        private void HandleMediaPlaylistLine(string line)
        {
            if (_isMasterPlaylist.HasValue && _isMasterPlaylist.Value)
            {
                throw new HlsException("Unexpected tag in media playlist.");
            }

            if (!_isMasterPlaylist.HasValue)
            {
                _isMasterPlaylist = false;
                _mediaPlaylist    = new HlsMediaPlaylist {
                    Version = _version, IsIndependentSegments = _isIndependentSegments, Start = _start
                };
            }

            if (line[0] != '#')
            {
                // A URI line for a media segment should be the only case when this happens.
                _mediaSegment.Uri = new Uri(_baseUri, line);
                _mediaSegment     = null;
                return;
            }

            int    firstColonIndex = line.IndexOf(':');
            string tag             = (firstColonIndex > -1 ? line.Substring(1, firstColonIndex - 1) : line.Substring(1));
            string value           = (firstColonIndex > -1 ? line.Substring(firstColonIndex + 1, line.Length - firstColonIndex - 1) : null);

            switch (tag)
            {
            // Media segment tags.
            case "EXTINF":
                int commaIndex = value.IndexOf(',');
                _mediaSegment = new HlsMediaSegment
                {
                    SequenceNumber = _mediaPlaylist.MediaSequence + _mediaPlaylist._mediaSegments.Count,
                    Duration       = Convert.ToSingle((commaIndex != -1 ? value.Substring(0, commaIndex) : value)),
                    Title          = (commaIndex != -1 ? value.Substring(commaIndex + 1) : null),
                    Key            = _mediaSegmentKey
                };
                _mediaPlaylist._mediaSegments.Add(_mediaSegment);
                break;

            case "EXT-X-BYTERANGE":
                _mediaSegment.ByteRange = value;
                break;

            case "EXT-X-DISCONTINUITY":
                _mediaSegment.HasDiscontinuityAfter = true;
                break;

            case "EXT-X-KEY":
                IReadOnlyDictionary <string, string> keyAttributes = AttributeList.Parse(value);
                _mediaSegmentKey = new HlsMediaSegmentKey
                {
                    Method    = AttributeValueUtils.ParseEnum <HlsKeyMethod>(keyAttributes["METHOD"]),
                    Uri       = AttributeValueUtils.GetUriOrNull(keyAttributes, _baseUri, "URI"),
                    KeyFormat = (AttributeValueUtils.GetStringOrNull(keyAttributes, "KEYFORMAT") ?? "identity"),
                };
                break;

            case "EXT-X-MAP":
                break;

            case "EXT-X-PROGRAM-DATE-TIME":
                _mediaSegment.ProgramDateTime = value;
                break;

            case "EXT-X-ALLOW-CACHE":
                break;

            case "EXT-X-DATERANGE":
                break;

            // Media playlist tags.
            case "EXT-X-TARGETDURATION":
                _mediaPlaylist.TargetDuration = Convert.ToInt32(value);
                break;

            case "EXT-X-MEDIA-SEQUENCE":
                _mediaPlaylist.MediaSequence = Convert.ToInt32(value);
                break;

            case "EXT-X-DISCONTINUITY-SEQUENCE":
                _mediaPlaylist.DiscontinuitySequence = Convert.ToInt32(value);
                break;

            case "EXT-X-ENDLIST":
                _mediaSegment = null;
                HasEndlist    = true;
                break;

            case "EXT-X-PLAYLIST-TYPE":
                _mediaPlaylist.PlaylistType = value;
                break;

            case "EXT-X-I-FRAMES-ONLY":
                _mediaPlaylist.IsIFramesOnly = true;
                break;
            }
        }