public void EchoSetsParameters() { string sKey, sParameters = "", sValues = ""; foreach (KeyValuePair <string, string> pair in Params) { //sKey = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(pair.Key); sKey = pair.Key; if (pair.Value == "[1]") { sParameters += sKey + " "; } else { sValues += String.Format("<c:DarkCyan>{0,-10}: <c:DarkGreen>{1}\r\n", sKey, pair.Value); } } if (sParameters != "") { Program.Message(String.Format("<c:DarkCyan>Parameters:</c> <c:DarkGreen>{0,-10}", sParameters)); } if (sValues != "") { Program.Message(sValues); } }
private void Error(string msg) { if (Program.redir2Prog != null) { Program.Message(msg); } else { Program.Quit(msg); } }
public void ShowMessageAtTheEnd() { DestroyUpdateStatusTimer(); string msg = string.Format("\r\nFile size=<c:Cyan>{0}</c>KiB Duration=<c:Cyan>{1}</c> Video=<c:Cyan>{2}</c>KiB Audio=<c:Cyan>{3}</c>KiB" , FLVFile.Filesize / 1024 , FormatTS(FLVFile.LastTimestamp) , FLVFile.SizeVideo / 1024 , FLVFile.SizeAudio / 1024); Program.Message(msg); }
public void DisplayHelpAndQuit() { string version = Program.GetFullVersion(); string shrtKey = "", longKey = ""; if (Program.isRedirected) { Console.SetOut(new System.IO.StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }); } Program.Message(version + ". You can use <c:White>hdsdump</c> with following switches:"); Program.Message(""); foreach (KeyValuePair <string, string> pair in ACCEPTED[0]) { shrtKey = pair.Key.Split('|')[0].Trim(); longKey = pair.Key.Split('|')[1].Trim(); if (shrtKey == "") { Program.Message(String.Format(" <c:White>--{0,-17} <c:DarkCyan>{1}", longKey, pair.Value)); } else { Program.Message(String.Format(" <c:White>-{0,-2}</c>|<c:White>--{1,-17}</c> <c:DarkCyan>{2}", shrtKey, longKey, pair.Value)); } } foreach (KeyValuePair <string, string> pair in ACCEPTED[1]) { shrtKey = pair.Key.Split('|')[0].Trim(); longKey = pair.Key.Split('|')[1].Trim(); if (shrtKey == "") { Program.Message(String.Format(" <c:White>--{0,-10}</c><param> <c:DarkCyan>{1,-7}", longKey, pair.Value)); } else { Program.Message(String.Format(" <c:White>-{0,-2}</c>|<c:White>--{1,-10}</c><param> <c:DarkCyan>{2,-7}", shrtKey, longKey, pair.Value)); } } Environment.Exit(0); }
private void ShowDownloadStatus(object state = null) { string msg; if (selectedMedia.Bootstrap.live && HDSDownloader.LiveIsStalled) { msg = "<c:DarkRed>Live is stalled...</c> " + FormatTS((uint)DateTime.Now.Subtract(HDSDownloader.StartedStall).Ticks / 10000); if (!Program.verbose) { msg += "\r"; } Program.Message(msg); return; } msg = string.Format("frag=<c:White>{0}</c>/{1} frames={2} TS={3} size={4}KiB time={5} drop={6}" , selectedMedia.CurrentFragmentIndex - 1 , selectedMedia.TotalFragments , FLVFile.Frames , FormatTS(_currentTime) , FLVFile.Filesize / 1024 , FormatTS(FLVFile.LastTimestamp) , totalDroppedFrames); int fragsToDownload = (int)(selectedMedia.TotalFragments - selectedMedia.CurrentFragmentIndex + 1); string remaining = ""; if (Program.showtime && !selectedMedia.Bootstrap.live && selectedMedia.Downloaded > 0 && fragsToDownload > 0) { TimeSpan remainingTimeSpan = TimeSpan.FromTicks(DateTime.Now.Subtract(startTime).Ticks / selectedMedia.Downloaded * fragsToDownload); remaining = string.Format(" <c:DarkCyan>Time left: </c>{0:00}<c:Cyan>:</c>{1:00}<c:Cyan>:</c>{2:00}", remainingTimeSpan.Hours, remainingTimeSpan.Minutes, remainingTimeSpan.Seconds); } msg += remaining; if (!Program.verbose) { msg += "\r"; } Program.Message(msg); }
public void DownloadFragment(TagsStore tagsStore) { string fragmentUrl = media.GetFragmentUrl(fragIndex); Program.DebugLog("Fragment Url: " + fragmentUrl); byte[] data = HTTP.TryGETData(fragmentUrl, out int retCode, out string status); int retries = 0; while (retCode >= 500 && retries <= MaxRetriesLoad) { System.Threading.Thread.Sleep(1000); retries++; data = HTTP.TryGETData(fragmentUrl, out retCode, out status); } if (retCode != 200) { string msg = "Download fragment failed " + fragIndex + "/" + media.TotalFragments + " code: " + retCode + " status: " + status; Program.DebugLog(msg); if (Program.verbose) { Program.Message(msg); } if (media.Bootstrap.live) { //media.CurrentFragmentIndex = media.TotalFragments; tagsStore.Complete = true; Done(media); return; } else { throw new InvalidOperationException(status); } } Program.DebugLog("Downloaded: fragment=" + fragIndex + "/" + media.TotalFragments + " lenght: " + data.Length); var boxes = Box.GetBoxes(data); if (boxes.Find(i => i.Type == F4FConstants.BOX_TYPE_MDAT) is MediaDataBox mdat) { lock (tagsStore) { FLVTag.GetVideoAndAudioTags(tagsStore, mdat.data); tagsStore.ARFA = boxes.Find(i => i.Type == F4FConstants.BOX_TYPE_AFRA) as AdobeFragmentRandomAccessBox; tagsStore.Complete = true; } HDSDownloader.LiveIsStalled = false; } else if (media.Bootstrap.live) { HDSDownloader.LiveIsStalled = true; } else { throw new InvalidOperationException("No found mdat box in fragment " + fragIndex + "/" + media.TotalFragments); } if (Program.verbose) { Program.Message(string.Format("Media: {0} Downloaded: {1} Data size: {2}", media.label, fragIndex, data.Length)); } media.CurrentFragmentIndex++; media.Downloaded++; Done(media); }
public void DetermineAudioVideo(FLVTagScriptBody metadata, Media media, ref bool isDetermined, ref bool hasVideo, ref bool hasAudio) { if (!isDetermined) { if (FragmentsData.ContainsKey(media) && FragmentsData[media].Count > 0) { var tagStore = FragmentsData[media].Peek(); if (tagStore.Complete) { Program.ClearLine(); hasVideo = tagStore.hasVideo; hasAudio = tagStore.hasAudio; isDetermined = true; string videoInfo, audioInfo; if (hasVideo) { string codec = FLVTagVideo.CodecToString(tagStore.VideoCodec); if (metadata != null) { string res = metadata.GetInfoIfExists("videocodecid"); if (res == "avc1" || res == "7") { codec = "H.264 / AVC / MPEG-4 (Part 10)"; } } videoInfo = "<c:DarkGreen>" + codec; if (metadata != null) { string res = metadata.GetInfoIfExists("width") + "x" + metadata.GetInfoIfExists("height"); if (res != "x") { videoInfo += " " + res; } res = metadata.GetInfoIfExists("videoframerate", "framerate", 5); if (res != "") { videoInfo += " Frame rate: " + res; } res = metadata.GetDuration(); if (res != "") { videoInfo += " Duration: " + res; } } } else { videoInfo = "<c:DarkRed>None"; } if (hasAudio) { string audioSampleRate = string.Empty; if (metadata != null) { audioSampleRate = metadata.GetInfoIfExists("audiosamplerate"); } if (audioSampleRate.Length == 0) { audioSampleRate = FLVTagAudio.RateToString(tagStore.AudioRate); } else { audioSampleRate += "Hz"; } audioInfo = "<c:DarkGreen>" + FLVTagAudio.FormatToString(tagStore.AudioFormat) + " " + audioSampleRate + " " + (tagStore.AudioChannels == FLVTagAudio.Channels.MONO ? "Mono" : "Stereo"); } else { audioInfo = "<c:DarkRed>None"; } Program.Message("<c:DarkCyan>Video: " + videoInfo); Program.Message("<c:DarkCyan>Audio: " + audioInfo); if (tagStore.isAkamaiEncrypted) { Program.Message("<c:Yellow>Encryption: Akamai DRM"); } } } } }
public void StartDownload(string manifestUrl) { GetManifestAndSelectMedia(manifestUrl); Program.Message("Waiting first fragment...\r"); // downloader already running in UpdateBootstrapInfo() DetermineAudioVideoPresentInDownloadedFragment(); var DecoderState = new DecoderLastState(); bool useAltAudio = selectedMediaAlt != null; TagsFilter tagFilter = TagsFilter.ALL; if (UpdateStatusTimer == null && !Program.isRedirected) { UpdateStatusTimer = new Timer(ShowDownloadStatus, null, 0, UpdateStatusInterval); } if (useAltAudio) { tagFilter = TagsFilter.VIDEO; // only video for main media if alternate media is selected } _currentTime = INVALID_TIME; _mediaTime = INVALID_TIME; _alternateTime = INVALID_TIME; droppedAudioFrames = 0; droppedVideoFrames = 0; FLVTag mediaTag = null; FLVTag alternateTag = null; bool needSynchronizationAudio = true; if (!Program.ConsolePresent && Program.isRedirected) { Program.Message("Processing..."); } // --------------- MAIN LOOP DECODE FRAGMENTS ---------------- while (Downloader.TagsAvaliable(selectedMedia) || selectedMedia.Bootstrap.live) { if (mediaTag == null) { mediaTag = Downloader.GetNextTag(selectedMedia); if (mediaTag == null) { Thread.Sleep(100); // for decrease CPU load } } if (useAltAudio && _mediaTime != INVALID_TIME && Downloader.TagsAvaliable(selectedMediaAlt)) { if (alternateTag == null) { alternateTag = Downloader.GetNextTag(selectedMediaAlt); } if (useAltAudio && alternateTag != null && alternateTag.IsAkamaiEncrypted) { if (AD2 == null) // create and init only if need { AD2 = new AkamaiDecryptor(); } AD2.DecryptFLVTag(alternateTag, manifest.baseURL, auth); } if (needSynchronizationAudio && (_mediaTime != INVALID_TIME || _alternateTime != INVALID_TIME)) { uint alternateSynchronizationTime = _alternateTime != INVALID_TIME ? _alternateTime : _mediaTime; alternateTag = Downloader.SeekAudioByTime(selectedMediaAlt, alternateSynchronizationTime); if (alternateTag != null) { needSynchronizationAudio = false; } } } if (mediaTag != null && mediaTag.IsAkamaiEncrypted) { if (AD1 == null) // create and init only if need { AD1 = new AkamaiDecryptor(); } AD1.DecryptFLVTag(mediaTag, manifest.baseURL, auth); } if (useAltAudio && alternateTag != null && alternateTag.IsAkamaiEncrypted) { if (AD2 == null) // create and init only if need { AD2 = new AkamaiDecryptor(); } AD2.DecryptFLVTag(alternateTag, manifest.baseURL, auth); } if (ShouldFilterTag(mediaTag, tagFilter)) { if (mediaTag != null) { if (mediaTag is FLVTagVideo) { droppedVideoFrames++; } totalDroppedFrames++; } mediaTag = null; } else { UpdateTimes(mediaTag); } if (ShouldFilterTag(alternateTag, TagsFilter.AUDIO)) { if (alternateTag != null) { droppedAudioFrames++; totalDroppedFrames++; } alternateTag = null; } else { UpdateTimes(alternateTag); } if (!useAltAudio) { if (mediaTag != null) { _currentTime = mediaTag.Timestamp; FLVFile.Write(mediaTag); mediaTag = null; } } else { if (_mediaTime != INVALID_TIME || _alternateTime != INVALID_TIME) { if (alternateTag != null && (alternateTag.Timestamp >= _currentTime || _currentTime == INVALID_TIME) && (alternateTag.Timestamp <= _mediaTime)) { _currentTime = alternateTag.Timestamp; FLVFile.Write(alternateTag); alternateTag = null; } else if (mediaTag != null && (mediaTag.Timestamp >= _currentTime || _currentTime == INVALID_TIME) && (mediaTag.Timestamp <= _alternateTime)) { _currentTime = mediaTag.Timestamp; FLVFile.Write(mediaTag); mediaTag = null; } } } if ((duration > 0) && (FLVFile.LastTimestamp >= duration)) { Status = "Duration limit reached"; break; } if ((filesize > 0) && (FLVFile.Filesize >= filesize)) { Status = "File size limit reached"; break; } } DestroyUpdateStatusTimer(); ShowDownloadStatus(); }
private void GetManifestAndSelectMedia(string manifestUrl, int nestedBitrate = 0, int level = 0) { if (level > MAX_LEVEL_NESTED_MANIFESTS) { throw new InvalidOperationException("Maximum nesting level reached of multi-level manifests."); } XmlNodeEx xmlManifest = LoadXml(manifestUrl); if (string.IsNullOrEmpty(baseUrl)) { baseUrl = URL.ExtractBaseUrl(manifestUrl); } // parse the manifest manifest = new Manifest(xmlManifest, baseUrl, "", nestedBitrate); if (string.IsNullOrEmpty(manifest.baseURL)) { throw new InvalidOperationException("Not found <c:Magenta>baseURL</c> value in manifest or in parameter <c:White>--urlbase</c>."); } if (manifest.media.Count < 1) { throw new InvalidOperationException("No media entry found in the manifest"); } Program.DebugLog("Manifest entries:\n"); Program.DebugLog(String.Format(" {0,-8}{1}", "Bitrate", "URL")); // TEST for alternate selection if (testalt) { manifest.alternativeMedia.AddRange(manifest.media); } // Quality selection selectedMedia = QualitySelectionForMedia(manifest.media, ref avaliableBitrates, level < 1); // Quality selection for alternative media selectedMediaAlt = QualitySelectionForMedia(manifest.alternativeMedia, ref avaliableAlt, level < 1, true); if (selectedMedia == null) { selectedMedia = manifest.media[0]; } // check for multi-level manifest if (!string.IsNullOrEmpty(selectedMedia.href)) { string nestedManifestUrl = URL.getAbsoluteUrl(manifest.baseURL, selectedMedia.href); baseUrl = URL.ExtractBaseUrl(nestedManifestUrl); nestedBitrate = selectedMedia.bitrate; selectedMedia = null; GetManifestAndSelectMedia(nestedManifestUrl, nestedBitrate, level + 1); return; } string sQuality = selectedMedia.bitrate.ToString(); int n = Math.Max(0, avaliableBitrates.IndexOf(sQuality)); avaliableBitrates = avaliableBitrates.Replace(" " + sQuality + " ", " <c:Cyan>" + sQuality + "</c> "); Program.Message("Quality Selection:"); Program.Message("Available:" + avaliableBitrates); Program.Message("Selected : <c:Cyan>" + sQuality.PadLeft(n + sQuality.Length - 1)); if (manifest.alternativeMedia.Count > 0) { Program.Message("Alternatives:" + avaliableAlt); if (selectedMediaAlt != null) { string label = selectedMediaAlt.label; n = avaliableAlt.IndexOf(label); avaliableAlt = avaliableAlt.Replace(" " + label + " ", " <c:Cyan>" + label + "</c> "); Program.Message("Selected : <c:Cyan>" + label.PadLeft(n + label.Length - 1)); // get bootstrap for media from manifest by id if (!string.IsNullOrEmpty(selectedMediaAlt.bootstrapInfo?.id) && (selectedMediaAlt.bootstrapInfo.data == null)) { selectedMediaAlt.bootstrapInfo = manifest.bootstrapInfos.Find(i => i.id == selectedMediaAlt.bootstrapInfo.id); } } } // get bootstrap for media from manifest by id if (!string.IsNullOrEmpty(selectedMedia.bootstrapInfo?.id) && (selectedMedia.bootstrapInfo.data == null)) { selectedMedia.bootstrapInfo = manifest.bootstrapInfos.Find(i => i.id == selectedMedia.bootstrapInfo.id); } if (selectedMedia.bootstrapInfo == null) { throw new InvalidOperationException("No bootstrapInfo for selected media entry"); } if (!Program.fproxy) { HTTP.notUseProxy = true; } // Use embedded auth information when available int idx = selectedMedia.url.IndexOf('?'); if (idx > 0) { auth = selectedMedia.url.Substring(idx); selectedMedia.url = selectedMedia.url.Substring(0, idx); } if (selectedMedia.metadata != null) { FLVFile.onMetaData = new FLVTagScriptBody(selectedMedia.metadata); } selectedMedia.AfterUpdateBootstrap += Media_AfterUpdateBootstrap; selectedMedia.UpdateBootstrapInfo(); if (selectedMedia.Bootstrap.live) { Program.Message("<c:Magenta>[Live stream]"); } if (selectedMediaAlt == selectedMedia) { selectedMediaAlt = null; } if (selectedMediaAlt != null) { selectedMediaAlt.alternate = true; // Use embedded auth information when available idx = selectedMediaAlt.url.IndexOf('?'); if (idx > 0) { auth = selectedMediaAlt.url.Substring(idx); selectedMediaAlt.url = selectedMediaAlt.url.Substring(0, idx); } selectedMediaAlt.AfterUpdateBootstrap += Media_AfterUpdateBootstrap; selectedMediaAlt.UpdateBootstrapInfo(); } }