Beispiel #1
0
        private ulong TotalTicks; // For ETA calculation.

        // The object created may modify trackSamples in a destructive way, to save memory.
        // Expects tracks[...].NextStartTime and tracks[...].DownloadedChunkCount to be initialized.
        public DownloadingMediaDataSource(IList <Track> tracks, string manifestParentPath,
                                          ulong timeScale, bool isLive, ulong stopAfter, ulong totalTicks,
                                          DisplayDuration displayDuration)
        {
            int trackCount = tracks.Count;

            this.Tracks             = tracks;
            this.ManifestParentPath = manifestParentPath;
            this.TimeScale          = timeScale;
            this.DisplayDuration    = displayDuration;
            this.IsLive             = isLive;
            this.StopAfter          = stopAfter;
            this.TotalDuration      = 0;
            this.MinStartTime       = ulong.MaxValue;
            this.IsStopped          = false;
            for (int i = 0; i < trackCount; ++i)
            {
                ulong chunkStartTime = tracks[i].NextStartTime;
                if (this.MinStartTime > chunkStartTime)
                {
                    this.MinStartTime = chunkStartTime;
                }
            }
            this.TotalTicks   = totalTicks;
            this.TrackSamples = new IList <MediaSample> [trackCount]; // Items initialized to null.
            for (int i = 0; i < trackCount; ++i)
            {
                this.TrackSamples[i] = new List <MediaSample>();
            }
            this.TrackSampleStartIndexes = new int[trackCount];            // Items initialized to 0.
            this.TrackFirstBlocks        = new MediaDataBlock[trackCount]; // Items initialized to null.
            this.TrackFirstFileDatas     = new byte[trackCount][];         // Items initialized to null.
            this.ChunkStartTimeReceiver  = null;
        }
        public ToastNotificationBuilder SetDuration(DisplayDuration duration)
        {
            CheckChangesAllowed();

            _longDuration = duration == DisplayDuration.Long;
            return(this);
        }
Beispiel #3
0
        /// <summary>
        /// </summary>
        /// <param name="tracks"></param>
        /// <param name="manifestParentPath"></param>
        /// <param name="timeScale"></param>
        /// <param name="isLive"></param>
        /// <param name="stopAfter"></param>
        /// <param name="totalTicks"></param>
        /// <param name="displayDuration"></param>
        public DownloadingMediaDataSource(IList <Track> tracks, string manifestParentPath,
                                          ulong timeScale, bool isLive, ulong stopAfter, ulong totalTicks,
                                          DisplayDuration displayDuration)
        {
            _tracks             = tracks;
            _manifestParentPath = manifestParentPath;
            _timeScale          = timeScale;
            _displayDuration    = displayDuration;
            _isLive             = isLive;
            _stopAfter          = stopAfter;

            for (var i = 0; i < tracks.Count; ++i)
            {
                var chunkStartTime = tracks[i].NextStartTime;
                if (_minStartTime > chunkStartTime)
                {
                    _minStartTime = chunkStartTime;
                }
            }

            _totalTicks   = totalTicks;
            _trackSamples = new IList <MediaSample> [tracks.Count];

            for (var i = 0; i < tracks.Count; ++i)
            {
                _trackSamples[i] = new List <MediaSample>();
            }

            _trackSampleStartIndexes = new int[tracks.Count];            // Items initialized to 0.
            _trackFirstBlocks        = new MediaDataBlock[tracks.Count]; // Items initialized to null.
            _trackFirstFileDatas     = new byte[tracks.Count][];
            _chunkStartTimeReceiver  = null;
        }
Beispiel #4
0
        private void durButtonPressed(HamsterButton oject)
        {
            Debug.WriteLine(_displayDuration.ToString() + "++");
            if (_displayDuration < DisplayDuration.TOTAL)
            {
                _displayDuration++;
            }
            else
            {
                _displayDuration = DisplayDuration.LAST;
            }

            UpdateDisplay();
        }
Beispiel #5
0
        // Exactly one of manifestUri and manifestPath must be set.
        public static void DownloadAndMux(Uri manifestUri, string manifestPath, string mkvPath, bool isDeterministic, TimeSpan stopAfter,
                                          SetupStop setupStop, DisplayDuration displayDuration)
        {
            string       manifestParentPath = null; // A null indicates a remote manifest file.
            ManifestInfo manifestInfo;

            if (manifestPath != null)
            {
                manifestParentPath = Path.GetDirectoryName(manifestPath);
                Console.WriteLine("Parsing local manifest file: " + manifestPath);
                using (FileStream manifestStream = new FileStream(manifestPath, FileMode.Open)) {
                    manifestInfo = ManifestInfo.ParseManifest(manifestStream, /*manifestUri:*/ new Uri(LOCAL_URL_PREFIX));
                }
            }
            else
            {
                Console.WriteLine("Downloading and parsing manifest: " + manifestUri);
                WebClient webClient = new WebClient();
                using (Stream manifestStream = webClient.OpenRead(manifestUri)) {
                    manifestInfo = ManifestInfo.ParseManifest(manifestStream, manifestUri);
                }
            }
            Console.Write(manifestInfo.GetDescription());

            IList <Track> tracks = new List <Track>();

            foreach (StreamInfo streamInfo in manifestInfo.SelectedStreams)
            {
                foreach (TrackInfo trackInfo in streamInfo.SelectedTracks)
                {
                    tracks.Add(new Track(trackInfo));
                }
            }
            IList <TrackEntry>           trackEntries = new List <TrackEntry>();
            IList <IList <MediaSample> > trackSamples = new List <IList <MediaSample> >();

            for (int i = 0; i < tracks.Count; ++i)
            {
                trackEntries.Add(tracks[i].TrackInfo.TrackEntry);
                trackEntries[i].TrackNumber = (ulong)(i + 1);
                trackSamples.Add(new List <MediaSample>());
            }
            for (int i = 0; i < tracks.Count; i++)
            {
                // TODO: Add a facility to start live streams from a later chunk (it was chunkIndex=10 previously).
                // Our design allows for an empty ChunkList, in case live streams are growing.
                tracks[i].NextStartTime = tracks[i].TrackInfo.Stream.ChunkList.Count == 0 ? 0 :
                                          tracks[i].TrackInfo.Stream.ChunkList[0].StartTime;
            }
            // TODO: Test for live streams (see the StackOverflow question).
            Console.WriteLine("Also muxing selected tracks to MKV: " + mkvPath);
            try {
                if (Directory.GetParent(mkvPath) != null &&
                    !Directory.GetParent(mkvPath).Exists)
                {
                    Directory.GetParent(mkvPath).Create();
                }
            } catch (IOException) {
                // TODO: Add nicer error reporting, without a stack trace.
                throw new Exception("Cannot not create the directory of .mkv: " + mkvPath);
            }
            ulong maxTrackEndTimeHint = manifestInfo.Duration;

            for (int i = 0; i < tracks.Count; ++i)
            {
                IList <ChunkInfo> chunkInfos = tracks[i].TrackInfo.Stream.ChunkList;
                int j = chunkInfos.Count - 1;
                if (j >= 0) // Our design allows for an empty ChunkList.
                {
                    ulong trackDuration = chunkInfos[j].StartTime + chunkInfos[j].Duration;
                    if (maxTrackEndTimeHint < trackDuration)
                    {
                        maxTrackEndTimeHint = trackDuration;
                    }
                }
            }
            // The .muxstate file is approximately 1/5441.43 of the size of the .mkv.
            // The .muxstate file is around 28.088 bytes per second. TODO: Update this after n.
            // Sometimes totalDuration of video is 1156420602, audio is 1156818141 (larger), so we just take the maximum.
            string muxStatePath    = Path.ChangeExtension(mkvPath, "muxstate");
            string muxStateOldPath = muxStatePath + ".old";

            byte[] oldMuxState = null;
            if (File.Exists(muxStatePath)) // False for directories.
            {
                using (FileStream fileStream = new FileStream(muxStatePath, FileMode.Open)) {
                    oldMuxState = ReadFileStream(fileStream);
                }
                if (oldMuxState.Length > 0)
                {
                    // File.Move fails with IOException if the destination already exists.
                    // C# and .NET SUXX: There is no atomic overwrite-move.
                    try {
                        File.Move(muxStatePath, muxStateOldPath);
                    } catch (IOException) {
                        File.Replace(muxStatePath, muxStateOldPath, null, true);
                    }
                }
            }
            DownloadingMediaDataSource source = new DownloadingMediaDataSource(
                tracks, manifestParentPath, manifestInfo.TimeScale,
                manifestInfo.IsLive, (ulong)stopAfter.Ticks, manifestInfo.TotalTicks, displayDuration);

            setupStop(manifestInfo.IsLive, source);
            MuxStateWriter muxStateWriter = new MuxStateWriter(new FileStream(muxStatePath, FileMode.Create));

            try {
                MkvUtils.WriteMkv(mkvPath, trackEntries, source, maxTrackEndTimeHint, manifestInfo.TimeScale, isDeterministic,
                                  oldMuxState, muxStateWriter);
            } finally {
                muxStateWriter.Close();
            }
            File.Delete(muxStatePath);
            if (File.Exists(muxStateOldPath))
            {
                File.Delete(muxStateOldPath);
            }
        }
Beispiel #6
0
        /// <summary>
        /// </summary>
        /// <param name="manifestUri"></param>
        /// <param name="manifestPath"></param>
        /// <param name="mkvPath"></param>
        /// <param name="isDeterministic"></param>
        /// <param name="stopAfter"></param>
        /// <param name="setupStop"></param>
        /// <param name="displayDuration"></param>
        public static void DownloadAndMux(Uri manifestUri, string manifestPath, string mkvPath, bool isDeterministic, TimeSpan stopAfter, SetupStop setupStop, DisplayDuration displayDuration)
        {
            string       manifestParentPath = null;
            ManifestInfo manifestInfo;

            if (manifestPath != null)
            {
                manifestParentPath = Path.GetDirectoryName(manifestPath);
                Console.WriteLine($"Parsing local manifest file: {manifestPath}");

                using (var manifestStream = new FileStream(manifestPath, FileMode.Open))
                {
                    manifestInfo = ManifestInfo.ParseManifest(manifestStream, new Uri(LocalUrlPrefix));
                }
            }
            else
            {
                Console.WriteLine($"Downloading and parsing manifest: {manifestUri}");

                using (var manifestStream = new WebClient().OpenRead(manifestUri))
                {
                    manifestInfo = ManifestInfo.ParseManifest(manifestStream, manifestUri);
                }
            }

            Console.Write(manifestInfo.GetDescription());

            var tracks = (
                from streamInfo in manifestInfo.SelectedStreams
                from trackInfo in streamInfo.SelectedTracks
                select new Track(trackInfo)
                ).ToList();

            var trackEntries = new List <TrackEntry>();
            var trackSamples = new List <IList <MediaSample> >();

            for (var i = 0; i < tracks.Count; ++i)
            {
                trackEntries.Add(tracks[i].TrackInfo.TrackEntry);
                trackEntries[i].TrackNumber = (ulong)(i + 1);
                trackSamples.Add(new List <MediaSample>());
            }

            foreach (var track in tracks)
            {
                if (track.TrackInfo.Stream.ChunkList.Count == 0)
                {
                    track.NextStartTime = 0;
                }
                else
                {
                    track.NextStartTime = track.TrackInfo.Stream.ChunkList[0].StartTime;
                }
            }

            Console.WriteLine("Also muxing selected tracks to MKV: " + mkvPath);

            try
            {
                if (Directory.GetParent(mkvPath) != null &&
                    !Directory.GetParent(mkvPath).Exists)
                {
                    Directory.GetParent(mkvPath).Create();
                }
            }
            catch (IOException)
            {
                throw new Exception();
            }

            var maxTrackEndTimeHint = manifestInfo.Duration;

            foreach (var track in tracks)
            {
                var chunkInfos = track.TrackInfo.Stream.ChunkList;
                var lastIndex  = chunkInfos.Count - 1;

                if (lastIndex < 0)
                {
                    continue;
                }

                var trackDuration = chunkInfos[lastIndex].StartTime + chunkInfos[lastIndex].Duration;

                if (maxTrackEndTimeHint < trackDuration)
                {
                    maxTrackEndTimeHint = trackDuration;
                }
            }

            var muxStatePath    = Path.ChangeExtension(mkvPath, "muxstate");
            var muxStateOldPath = muxStatePath + ".old";

            byte[] previousMuxState = null;

            if (File.Exists(muxStatePath))
            {
                using (var stream = new FileStream(muxStatePath ?? throw new Exception(), FileMode.Open))
                {
                    previousMuxState = ReadFileStream(stream);
                }

                if (previousMuxState.Length > 0)
                {
                    try
                    {
                        File.Move(muxStatePath, muxStateOldPath);
                    }
                    catch (IOException)
                    {
                        File.Replace(muxStatePath, muxStateOldPath, null, true);
                    }
                }
            }

            var source = new DownloadingMediaDataSource(tracks, manifestParentPath, manifestInfo.TimeScale,
                                                        manifestInfo.IsLive, (ulong)stopAfter.Ticks, manifestInfo.TotalTicks, displayDuration);

            setupStop(manifestInfo.IsLive, source);

            var muxStateWriter = new MuxStateWriter(new FileStream(muxStatePath, FileMode.Create));

            try
            {
                MkvUtils.WriteMkv(mkvPath, trackEntries, source, maxTrackEndTimeHint, manifestInfo.TimeScale, isDeterministic, previousMuxState, muxStateWriter);
            }
            finally
            {
                muxStateWriter.Close();
            }

            File.Delete(muxStatePath);

            if (File.Exists(muxStateOldPath))
            {
                File.Delete(muxStateOldPath);
            }
        }
Beispiel #7
0
        // Exactly one of manifestUri and manifestPath must be set.
        public static void DownloadAndMux(Uri manifestUri, string manifestPath, string mkvPath, bool isDeterministic, TimeSpan stopAfter,
            SetupStop setupStop, DisplayDuration displayDuration)
        {
            string manifestParentPath = null;  // A null indicates a remote manifest file.
              ManifestInfo manifestInfo;
              if (manifestPath != null) {
            manifestParentPath = Path.GetDirectoryName(manifestPath);
            Console.WriteLine("Parsing local manifest file: " + manifestPath);
            using (FileStream manifestStream = new FileStream(manifestPath, FileMode.Open)) {
              manifestInfo = ManifestInfo.ParseManifest(manifestStream, /*manifestUri:*/new Uri(LOCAL_URL_PREFIX));
            }
                        } else {
            Console.WriteLine("Downloading and parsing manifest: " + manifestUri);
            WebClient webClient = new WebClient();
            using (Stream manifestStream = webClient.OpenRead(manifestUri)) {
              manifestInfo = ManifestInfo.ParseManifest(manifestStream, manifestUri);
            }
              }
              Console.Write(manifestInfo.GetDescription());

              IList<Track> tracks = new List<Track>();
              foreach (StreamInfo streamInfo in manifestInfo.SelectedStreams) {
            foreach (TrackInfo trackInfo in streamInfo.SelectedTracks) {
              tracks.Add(new Track(trackInfo));
            }
              }
              IList<TrackEntry> trackEntries = new List<TrackEntry>();
              IList<IList<MediaSample>> trackSamples = new List<IList<MediaSample>>();
              for (int i = 0; i < tracks.Count; ++i) {
            trackEntries.Add(tracks[i].TrackInfo.TrackEntry);
            trackEntries[i].TrackNumber = (ulong)(i + 1);
            trackSamples.Add(new List<MediaSample>());
              }
              for (int i = 0; i < tracks.Count; i++) {
            // TODO: Add a facility to start live streams from a later chunk (it was chunkIndex=10 previously).
            // Our design allows for an empty ChunkList, in case live streams are growing.
            tracks[i].NextStartTime = tracks[i].TrackInfo.Stream.ChunkList.Count == 0 ? 0 :
                                  tracks[i].TrackInfo.Stream.ChunkList[0].StartTime;
              }
              // TODO: Test for live streams (see the StackOverflow question).
              Console.WriteLine("Also muxing selected tracks to MKV: " + mkvPath);
              try {
            if (Directory.GetParent(mkvPath) != null &&
            !Directory.GetParent(mkvPath).Exists)
              Directory.GetParent(mkvPath).Create();
              } catch (IOException) {
            // TODO: Add nicer error reporting, without a stack trace.
            throw new Exception("Cannot not create the directory of .mkv: " + mkvPath);
              }
              ulong maxTrackEndTimeHint = manifestInfo.Duration;
              for (int i = 0; i < tracks.Count; ++i) {
            IList<ChunkInfo> chunkInfos = tracks[i].TrackInfo.Stream.ChunkList;
            int j = chunkInfos.Count - 1;
            if (j >= 0) {  // Our design allows for an empty ChunkList.
              ulong trackDuration = chunkInfos[j].StartTime + chunkInfos[j].Duration;
              if (maxTrackEndTimeHint < trackDuration) maxTrackEndTimeHint = trackDuration;
            }
              }
              // The .muxstate file is approximately 1/5441.43 of the size of the .mkv.
              // The .muxstate file is around 28.088 bytes per second. TODO: Update this after n.
              // Sometimes totalDuration of video is 1156420602, audio is 1156818141 (larger), so we just take the maximum.
              string muxStatePath = Path.ChangeExtension(mkvPath, "muxstate");
              string muxStateOldPath = muxStatePath + ".old";
              byte[] oldMuxState = null;
              if (File.Exists(muxStatePath)) {  // False for directories.
            using (FileStream fileStream = new FileStream(muxStatePath, FileMode.Open)) {
              oldMuxState = ReadFileStream(fileStream);
            }
            if (oldMuxState.Length > 0) {
              // File.Move fails with IOException if the destination already exists.
              // C# and .NET SUXX: There is no atomic overwrite-move.
              try {
            File.Move(muxStatePath, muxStateOldPath);
              } catch (IOException) {
            File.Replace(muxStatePath, muxStateOldPath, null, true);
              }
            }
              }
              DownloadingMediaDataSource source = new DownloadingMediaDataSource(
              tracks, manifestParentPath, manifestInfo.TimeScale,
                            manifestInfo.IsLive, (ulong)stopAfter.Ticks, manifestInfo.TotalTicks, displayDuration);
              setupStop(manifestInfo.IsLive, source);
              MuxStateWriter muxStateWriter = new MuxStateWriter(new FileStream(muxStatePath, FileMode.Create));
              try {
            MkvUtils.WriteMkv(mkvPath, trackEntries, source, maxTrackEndTimeHint, manifestInfo.TimeScale, isDeterministic,
                          oldMuxState, muxStateWriter);
              } finally {
            muxStateWriter.Close();
              }
              File.Delete(muxStatePath);
              if (File.Exists(muxStateOldPath)) {
            File.Delete(muxStateOldPath);
              }
        }
Beispiel #8
0
 // The object created may modify trackSamples in a destructive way, to save memory.
 // Expects tracks[...].NextStartTime and tracks[...].DownloadedChunkCount to be initialized.
 public DownloadingMediaDataSource(IList<Track> tracks, string manifestParentPath,
     ulong timeScale, bool isLive, ulong stopAfter, ulong totalTicks,
     DisplayDuration displayDuration)
 {
     int trackCount = tracks.Count;
       this.Tracks = tracks;
       this.ManifestParentPath = manifestParentPath;
       this.TimeScale = timeScale;
       this.DisplayDuration = displayDuration;
       this.IsLive = isLive;
       this.StopAfter = stopAfter;
       this.TotalDuration = 0;
       this.MinStartTime = ulong.MaxValue;
       this.IsStopped = false;
       for (int i = 0; i < trackCount; ++i) {
     ulong chunkStartTime = tracks[i].NextStartTime;
     if (this.MinStartTime > chunkStartTime) {
       this.MinStartTime = chunkStartTime;
     }
       }
       this.TotalTicks = totalTicks;
       this.TrackSamples = new IList<MediaSample>[trackCount];  // Items initialized to null.
       for (int i = 0; i < trackCount; ++i) {
     this.TrackSamples[i] = new List<MediaSample>();
       }
       this.TrackSampleStartIndexes = new int[trackCount];  // Items initialized to 0.
       this.TrackFirstBlocks = new MediaDataBlock[trackCount];  // Items initialized to null.
       this.TrackFirstFileDatas = new byte[trackCount][];  // Items initialized to null.
       this.ChunkStartTimeReceiver = null;
 }