private async Task _fillVideoGabs(YoutubeMediaPacketType type) { // compute the broken ranges for both videos packets and audio packets List <Range> LostPacketsRanges = _computeBrokenRanges(type); // fill the missing packets await _fillBrokenRanges(LostPacketsRanges, type); }
public YoutubeMediaPacket(YoutubeMediaPacketType videoPacketType, YoutubeRequestURL requestURL, string packetFilePath) { FilePath = packetFilePath; VideoPacketType = videoPacketType; YoutubeRequestURL = requestURL; VideoId = requestURL.RequestPath.QueryString.HasValue(YoutubeChatterClientHandler.VIDEO_ID_FIELD_NAME) ? requestURL.RequestPath.QueryString.GetValue(YoutubeChatterClientHandler.VIDEO_ID_FIELD_NAME) : null; _populateFromYoutubeRequestURL(requestURL); }
public YoutubeMediaPacket(YoutubeMediaPacketType videoPacketType, YoutubeRequestURL requestURL, byte[] packetData) { FilePath = _writePacketDataToDisk(packetData); VideoPacketType = videoPacketType; YoutubeRequestURL = requestURL; _populateFromYoutubeRequestURL(requestURL); VideoId = requestURL.RequestPath.QueryString.HasValue(YoutubeChatterClientHandler.VIDEO_ID_FIELD_NAME) ? requestURL.RequestPath.QueryString.GetValue(YoutubeChatterClientHandler.VIDEO_ID_FIELD_NAME) : null; }
private List <Range> _computeBrokenRanges(YoutubeMediaPacketType type) { YoutubeMediaPacket[] storedPackets; lock (this.Video.Packets) { storedPackets = this.Video.Packets.Where(x => x.VideoPacketType == type).OrderBy(x => x.Range.Start).ToArray(); } var lostPacketsRanges = new List <Range>(); // checking if there is a missing packets at start or end of media var firstPacket = storedPackets.FirstOrDefault(); var lastPacket = storedPackets.LastOrDefault(); // at start if (firstPacket != null && firstPacket.Range.Start != 0) { lostPacketsRanges.Add(new Range(0, firstPacket.Range.Start - 1)); } // at end if (lastPacket != null && lastPacket.Range.End + 1 != lastPacket.OverAllLength) { lostPacketsRanges.Add(new Range(lastPacket.Range.End + 1, lastPacket.OverAllLength - 1)); } // checking if there is a missing packets at middle of the video packets for (int i = 0; i < storedPackets.Length; i++) { // to make sure the counter don't exceed the array bounds when declaring nextPacket if (i >= storedPackets.Length - 1) { break; } var currentPacket = storedPackets[i]; var nextPacket = storedPackets[i + 1]; // a gap will appear when two consecutive ranges are broken // i.e when the end of one packet range + 1 not equal to the next packet's range start if (currentPacket.Range.End + 1 != nextPacket.Range.Start) { // two cases may happen here // 1- a user seeking to unbuffer location so we have a gab // 2- a buffered location faced an inturrupting ad // case #2 is handled in AddPacket (newComingIntersectWithAlreadyStored flag) so it will not occure here // i will add broken ranges to lostPacketsRanges to handle them later var start = currentPacket.Range.End + 1; var end = nextPacket.Range.Start - 1; lostPacketsRanges.Add(new Range(start, end)); } } return(lostPacketsRanges); }
private YoutubeMediaPacket _getTheLastAddedMediaPacket(YoutubeMediaPacketType typeToSearchFor) { // lock is for "Exception thrown: 'System.ArgumentException' in mscorlib.dll // An exception of type 'System.ArgumentException' occurred in mscorlib.dll but was not handled in user code // Destination array was not long enough. Check destIndex and length, and the array's lower bounds." lock (this.Video) { return(this.Video.Packets.ToList() // ToList to avoid (Collection was modified; enumeration operation may not execute) Exception .. see https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute .Where(x => x.VideoPacketType == typeToSearchFor) .OrderByDescending(x => x.Range.End).FirstOrDefault()); } }
private async Task <bool> _populate(YoutubeMediaPacketType type, string filePath) { return(await Task.Run(() => { Debug.WriteLine(type + " packets clen : " + this.Video.Packets.Where(x => x.VideoPacketType == type).First().OverAllLength); var sorted = this.Video.Packets.Where(x => x.VideoPacketType == type).OrderBy(x => x.Range.Start).ToList(); sorted.ForEach(item => { Debug.WriteLine("Appending Packet : " + item.VideoPacketType.ToString() + " " + item.Range.Start + "-" + item.Range.End + " :: " + item.FilePath); Helpers.Misc.AppendAllBytes(filePath, File.ReadAllBytes(item.FilePath)); }); return true; })); }
private YoutubeMediaPacketType _parseMediaPacketType(string sessionContentType) { // returns the videoMediaType from a contentType header YoutubeMediaPacketType PacketType = YoutubeMediaPacketType.Unknown; if (sessionContentType.StartsWith("video")) { PacketType = YoutubeMediaPacketType.Video; } if (sessionContentType.StartsWith("audio")) { PacketType = YoutubeMediaPacketType.Audio; } return(PacketType); }
private async Task _handleYoutubePacket(SessionEventArgs session) { // check if the packet in session is Audio or Video YoutubeMediaPacketType PacketType = _parseMediaPacketType(session.HttpClient.Response.Headers.Headers["Content-Type"].Value); // if content type is not video nor audio cancel it if (PacketType == YoutubeMediaPacketType.Unknown) { return; } var requestURL = requestURLFromSession(session); // making sure that the packet is valid to parse if (!YoutubeMediaPacket.IsValidYoutubeRequestURL(requestURL)) { return; } byte[] bodyBytes = await session.GetResponseBody(); var newPacket = new YoutubeMediaPacket(PacketType, requestURL, bodyBytes); // if videos list contains a video with the same fingerprint append the packet to it // otherwise create another video and add new packet to bool isVideoAddedBefore = this.VideosManagers.Any(x => x.Video.VideoFingerPrint == newPacket.VideoFingerPrint); if (!isVideoAddedBefore) { var newVideo = new YoutubeVideo(newPacket.VideoFingerPrint); var newVideoManager = new YoutubeVideoManager(newVideo, _client); VideosManagers.Add(newVideoManager); newVideoManager.OnYoutubeLastPacketRecieved += videoLastPacketRecieved; newVideoManager.OnYoutubeStored += NewVideoManager_OnYoutubeStored; newVideoManager.AddPacketFile(newPacket); } else { var packetVideo = VideosManagers.Where(x => x.Video.VideoFingerPrint == newPacket.VideoFingerPrint).FirstOrDefault(); packetVideo.AddPacketFile(newPacket); } }
private double?_computePacketsSum(YoutubeMediaPacketType type) { var higherQualityPacket = _getHigherQualityPacket(); if (higherQualityPacket == null) { return(null); } int?higherQuality = higherQualityPacket.PacketMediaFileDataInfo.VerticalResolution; if (!higherQuality.HasValue) { return(null); } return(this.Packets .Where(x => x.VideoPacketType == type) .Sum(x => x.Range.Length)); }
private async Task _fillBrokenRanges(List <Range> lostPacketsRanges, YoutubeMediaPacketType lostPacketsType) { // debugging purposes if (lostPacketsRanges.Count > 0) { YTrackLogger.Log("\nBroken Ranges for type " + lostPacketsType + " : \n"); lostPacketsRanges.ForEach(x => { YTrackLogger.Log(x.ToString()); }); YTrackLogger.Log("\n\n"); } // constructing the lost packates new requests and fetch them // firstly i will get clone from first packet url // no need for higher quality checks because lower ones are already removed var lostPacketRequestURL = this.Video.Packets.FirstOrDefault(x => x.VideoPacketType == lostPacketsType)?.YoutubeRequestURL?.DeepClone(); // Youtube Contains M4A containers .... the video may contain only media with Audio content type // if no video packets are present return if (lostPacketRequestURL == null) { return; } // now loop throught all lost packets and fill them foreach (var lostRange in lostPacketsRanges) { var newPacket = await _fillGab(lostPacketRequestURL, lostRange, lostPacketsType); _insertGabPacket(newPacket); } }
private async Task <YoutubeMediaPacket> _fillGab(YoutubeRequestURL packetRequestURL, Range lostRange, YoutubeMediaPacketType type) { packetRequestURL.RequestPath.QueryString.SetValue("range", $"{lostRange.Start}-{lostRange.End}"); var requestableURL = packetRequestURL.ToRequestableURL(); // debugging YTrackLogger.Log("\nfilling broken range for type " + type + " : " + lostRange.ToString() + " from : " + requestableURL); long httpSegmentSize = packetRequestURL.RequestPath.QueryString.HasValue("ratebypass") && packetRequestURL.RequestPath.QueryString.GetValue("ratebypass") == "yes" ? (long)lostRange.Length : 9_898_989; YoutubeMediaPacket packet; using (SegmentedHttpStream segmentedHttpStream = new SegmentedHttpStream(_client, requestableURL, (long)lostRange.Length, httpSegmentSize)) { try { string tmpFileName = Path.GetTempFileName(); using (Stream outputStream = new FileStream(tmpFileName, FileMode.Append)) { IProgress <double> progressPercentage = new Progress <double>(b => packetDownloadProgressChanged(b)); await segmentedHttpStream.CopyToStreamAsync(outputStream, progressPercentage); } // no need to check whether the packetRequestURL is a valid requist URL // because we requested it anyway packet = new YoutubeMediaPacket(type, packetRequestURL, tmpFileName); OnPacketDownloadCompleted?.Invoke(this, packet); } catch (Exception e) { // two exception may be thrown // WebException if something went wrong when fetch the new packets // IOException if something went wrong when writing the packet file to disk // I'll raise PacketDownloadExceptionThrown and re-throw exception if (e is WebException) { _onGabPacketDownloadExceptionThrown?.Invoke(this, new PacketDownloadExceptionEventArgumanets() { Exception = e as WebException, FailedPacketRequestURL = packetRequestURL, Range = lostRange }); } // re-throw the error to the caller Helpers.YTrackLogger.Log("Failed to download Packet : " + e.Message + "\n\n" + e.StackTrace); throw; } } return(packet); }
private void _truncateVideoPackets(YoutubeMediaPacketType type = YoutubeMediaPacketType.Video) { this.Video.Packets.RemoveAll(x => x.VideoPacketType == type); }