Beispiel #1
0
        /// <summary>
        /// Reads the chapter information from the specified file.
        /// </summary>
        /// <param name="fileHandle">The handle to the file from which to read the chapter information.</param>
        /// <returns>A new instance of a <see cref="ChapterList"/> object containing the information
        /// about the chapters for the file.</returns>
        internal static ChapterList ReadFromFile(IntPtr fileHandle)
        {
            ChapterList list = new ChapterList();
            IntPtr      chapterListPointer = IntPtr.Zero;
            int         chapterCount       = 0;

            NativeMethods.MP4ChapterType chapterType = NativeMethods.MP4GetChapters(fileHandle, ref chapterListPointer, ref chapterCount, NativeMethods.MP4ChapterType.Qt);
            if (chapterType != NativeMethods.MP4ChapterType.None && chapterCount != 0)
            {
                IntPtr currentChapterPointer = chapterListPointer;
                for (int i = 0; i < chapterCount; i++)
                {
                    NativeMethods.MP4Chapter currentChapter = currentChapterPointer.ReadStructure <NativeMethods.MP4Chapter>();
                    TimeSpan duration = TimeSpan.FromMilliseconds(currentChapter.duration);
                    string   title    = Encoding.UTF8.GetString(currentChapter.title);
                    if ((currentChapter.title[0] == 0xFE && currentChapter.title[1] == 0xFF) ||
                        (currentChapter.title[0] == 0xFF && currentChapter.title[1] == 0xFE))
                    {
                        title = Encoding.Unicode.GetString(currentChapter.title);
                    }

                    title = title.Substring(0, title.IndexOf('\0'));
                    list.AddInternal(new Chapter()
                    {
                        Duration = duration, Title = title
                    });
                    currentChapterPointer = IntPtr.Add(currentChapterPointer, Marshal.SizeOf(currentChapter));
                }
            }
            else
            {
                int  timeScale = NativeMethods.MP4GetTimeScale(fileHandle);
                long duration  = NativeMethods.MP4GetDuration(fileHandle);
                list.AddInternal(new Chapter()
                {
                    Duration = TimeSpan.FromSeconds(duration / timeScale), Title = "Chapter 1"
                });
            }

            if (chapterListPointer != IntPtr.Zero)
            {
                NativeMethods.MP4Free(chapterListPointer);
            }

            return(list);
        }
Beispiel #2
0
        /// <summary>
        /// Writes the chapter information to the file.
        /// </summary>
        /// <param name="fileHandle">The handle to the file to which to write the chapter information.</param>
        internal void WriteToFile(IntPtr fileHandle)
        {
            // Only write to the file if there have been changes since the chapters were read.
            // Note that a happy side effect of this is that if there were no chapters specified
            // in the file at read time, and no manipulation was done before write, we will not
            // write chapters into the file, even though our internal representation will contain
            // a single chapter with the full duration of the file, and the title of "Chapter 1".
            if (this.IsDirty)
            {
                // Find the first video track, so that we make sure the total duration
                // of the chapters we add does not exceed the length of the file.
                int referenceTrackId = -1;
                for (short i = 0; i < NativeMethods.MP4GetNumberOfTracks(fileHandle, null, 0); i++)
                {
                    int    currentTrackId = NativeMethods.MP4FindTrackId(fileHandle, i, null, 0);
                    string trackType      = NativeMethods.MP4GetTrackType(fileHandle, currentTrackId);
                    if (trackType == NativeMethods.MP4VideoTrackType)
                    {
                        referenceTrackId = currentTrackId;
                        break;
                    }
                }

                // If we don't have a video track, then we have an audio file, which has
                // only one track, and we can use it to find the duration.
                referenceTrackId = referenceTrackId <= 0 ? 1 : referenceTrackId;
                long referenceTrackDuration = NativeMethods.MP4ConvertFromTrackDuration(fileHandle, referenceTrackId, NativeMethods.MP4GetTrackDuration(fileHandle, referenceTrackId), NativeMethods.MP4TimeScale.Milliseconds);

                long runningTotal = 0;
                List <NativeMethods.MP4Chapter> nativeChapters = new List <NativeMethods.MP4Chapter>();
                foreach (Chapter chapter in this.chapters)
                {
                    NativeMethods.MP4Chapter nativeChapter = new NativeMethods.MP4Chapter();

                    // Set the title
                    nativeChapter.title = new byte[1024];
                    byte[] titleByteArray = Encoding.UTF8.GetBytes(chapter.Title);
                    Array.Copy(titleByteArray, nativeChapter.title, titleByteArray.Length);

                    // Set the duration, making sure that we only use durations up to
                    // the length of the reference track.
                    long chapterLength = (long)chapter.Duration.TotalMilliseconds;
                    if (runningTotal + chapterLength > referenceTrackDuration)
                    {
                        nativeChapter.duration = referenceTrackDuration - runningTotal;
                    }
                    else
                    {
                        nativeChapter.duration = chapterLength;
                    }

                    runningTotal += chapterLength;
                    nativeChapters.Add(nativeChapter);
                    if (runningTotal > referenceTrackDuration)
                    {
                        break;
                    }
                }

                NativeMethods.MP4Chapter[] chapterArray = nativeChapters.ToArray();
                NativeMethods.MP4SetChapters(fileHandle, chapterArray, chapterArray.Length, NativeMethods.MP4ChapterType.Qt);
            }
        }