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(); }
public static void FixTimestamp(DecoderLastState DecoderState, FLVTag tag) { uint lastTS = DecoderState.prevVideoTS >= DecoderState.prevAudioTS ? DecoderState.prevVideoTS : DecoderState.prevAudioTS; uint fixedTS = lastTS + (uint)fixWindow; if ((DecoderState.baseTS == DecoderLastState.INVALID_TIMESTAMP) && ((tag.Type == FLVTag.TagType.AUDIO) || (tag.Type == FLVTag.TagType.VIDEO))) { DecoderState.baseTS = tag.Timestamp; } if ((DecoderState.baseTS > fixWindow) && (tag.Timestamp >= DecoderState.baseTS)) { tag.Timestamp -= DecoderState.baseTS; } if (lastTS != DecoderLastState.INVALID_TIMESTAMP) { int timeShift = (int)(tag.Timestamp - lastTS); if (timeShift > fixWindow) { Program.DebugLog(string.Format("Timestamp gap detected: PacketTS={0} LastTS={1} Timeshift={2}", tag.Timestamp, lastTS, timeShift)); if (DecoderState.baseTS < tag.Timestamp) { DecoderState.baseTS += (uint)(timeShift - fixWindow); } else { DecoderState.baseTS = (uint)(timeShift - fixWindow); } tag.Timestamp = fixedTS; } else { lastTS = tag.Type == FLVTag.TagType.VIDEO ? DecoderState.prevVideoTS : DecoderState.prevAudioTS; if ((lastTS != DecoderLastState.INVALID_TIMESTAMP) && (int)tag.Timestamp < (lastTS - fixWindow)) { if ((DecoderState.negTS != DecoderLastState.INVALID_TIMESTAMP) && ((tag.Timestamp + DecoderState.negTS) < (lastTS - fixWindow))) { DecoderState.negTS = DecoderLastState.INVALID_TIMESTAMP; } if (DecoderState.negTS == DecoderLastState.INVALID_TIMESTAMP) { DecoderState.negTS = fixedTS - tag.Timestamp; Program.DebugLog(string.Format("Negative timestamp detected: PacketTS={0} LastTS={1} NegativeTS={2}", tag.Timestamp, lastTS, DecoderState.negTS)); tag.Timestamp = (uint)fixedTS; } else { if ((tag.Timestamp + DecoderState.negTS) <= (lastTS + fixWindow)) { tag.Timestamp += (uint)DecoderState.negTS; } else { DecoderState.negTS = fixedTS - tag.Timestamp; Program.DebugLog(string.Format("Negative timestamp override: PacketTS={0} LastTS={1} NegativeTS={2}", tag.Timestamp, lastTS, DecoderState.negTS)); tag.Timestamp = (uint)fixedTS; } } } } } if (tag is FLVTagAudio) { DecoderState.prevAudioTS = tag.Timestamp; } else { DecoderState.prevVideoTS = tag.Timestamp; } }