Esempio n. 1
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);
            }
        }
Esempio n. 2
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);
            }
        }