Exemple #1
0
        public static SampleSizeTable createVariable(Mp4Reader reader, int count)
        {
            // That's not a small amount of data, maybe a megabyte or 2. Bypassing the GC with malloc/free.
            IntPtr nativePointer = Marshal.AllocHGlobal(count * 4);

            try
            {
                Span <int> entries = Unsafe.writeSpan <int>(nativePointer, count);

                reader.read(entries.asBytes());
                ComputeUtils.flipEndiannessAndComputeMax(entries, out uint maxValue);

                if (maxValue > 0xFFFFFF)
                {
                    return(new SampleSizeVariable32(entries.ToArray(), (int)maxValue));
                }

                if (maxValue > 0xFFFF)
                {
                    return(new SampleSizeVariable24(entries, (int)maxValue));
                }

                return(new SampleSizeVariable16(entries, (int)maxValue));
            }
            finally
            {
                Marshal.FreeHGlobal(nativePointer);
            }
        }
Exemple #2
0
        static void readInfoHeader(Mp4Reader reader, out DateTime creationTime, out DateTime modificationTime, out TimeSpan duration, out CultureInfo culture, out uint timeScale)
        {
            uint ver = reader.readUInt();

            switch (ver & 0xFF)
            {
            case 0:
                var v0 = reader.readStructure <MediaInfoV0>();
                creationTime     = v0.creationTime;
                modificationTime = v0.modificationTime;
                duration         = v0.duration;
                culture          = v0.culture;
                timeScale        = v0.timeScale;
                return;

            case 1:
                var v1 = reader.readStructure <MediaInfoV1>();
                creationTime     = v1.creationTime;
                modificationTime = v1.modificationTime;
                duration         = v1.duration;
                culture          = v1.culture;
                timeScale        = v1.timeScale;
                return;
            }
            throw new ApplicationException("Unsupported format version");
        }
Exemple #3
0
        internal TrackHeader(Mp4Reader reader, uint timescale)
        {
            Debug.Assert(reader.currentBox == eBoxType.tkhd);
            uint versionAndFlags = reader.readUInt();

            flags = (eTrackFlags)BinaryPrimitives.ReverseEndianness(versionAndFlags & 0xFFFFFF00u);

            switch (versionAndFlags & 0xFF)
            {
            case 0:
                var ver0 = reader.readStructure <TrackHeaderVersion0>();
                ver0.parseHeader(timescale, out creationTime, out modificationTime, out id, out duration);
                ver0.parseCommon(out layer, out alternateGroup, out volume, out size);
                break;

            case 1:
                var ver1 = reader.readStructure <TrackHeaderVersion1>();
                ver1.parseHeader(timescale, out creationTime, out modificationTime, out id, out duration);
                ver1.parseCommon(out layer, out alternateGroup, out volume, out size);
                break;

            default:
                throw new ArgumentException("Unsupported track version");
            }
        }
Exemple #4
0
        static T[] readArray <T>(Mp4Reader reader) where T : unmanaged
        {
            sHeader header = reader.readStructure <sHeader>();
            int     count  = header.entry_count.endian();

            return(readArray <T>(reader, count));
        }
Exemple #5
0
        public static SampleSizeTable readSampleSizeCompact(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.stz2);

            Span <int> header = stackalloc int[3];

            reader.read(header.asBytes());
            int fieldSize = (header[1] & 0xFF);
            int count     = header[2].endian();

            switch (fieldSize)
            {
            case 4:
                // bits / sample, i.e. each value is in [ 0 .. 15 ] interval. I wonder which codec they have designed it for..
                throw new NotImplementedException();

            case 8:
                return(new SampleSizeVariable8(reader, count));

            case 16:
                return(new SampleSizeVariable16(reader, count));

            default:
                throw new ArgumentException();
            }
        }
Exemple #6
0
        static int parseSampleTableHeader(Mp4Reader reader)
        {
            Span <byte> span = stackalloc byte[8];

            reader.read(span);
            return(BitConverter.ToInt32(span.Slice(4)).endian());
        }
Exemple #7
0
        internal MediaHandler(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.hdlr);

            // 8.4.3.2
            int         cb   = checked ((int)reader.remainingBytes);
            Span <byte> data = stackalloc byte[cb];

            reader.read(data);

            uint handlerType = BitConverter.ToUInt32(data.Slice(8));

            mediaHandler = (eMediaHandler)handlerType;

            ReadOnlySpan <byte> utf8 = data.Slice(8 + 4 + 4 * 3);

            // Trim the null
            for (int i = 0; i < utf8.Length; i++)
            {
                if (0 == utf8[i])
                {
                    utf8 = utf8.Slice(0, i);
                    break;
                }
            }

            name = Encoding.UTF8.GetString(utf8);
        }
 public Ac3AudioSampleEntry(Mp4Reader mp4, int bytesLeft) :
     base(mp4, ref bytesLeft)
 {
     // configBlob = new byte[ bytesLeft ];
     // mp4.read( configBlob.AsSpan() );
     mp4.skipCurrentBox();
 }
Exemple #9
0
        internal static uint readUInt(this Mp4Reader reader)
        {
            Span <byte> bytes = stackalloc byte[4];

            reader.read(bytes);
            return(bytes.cast <uint>()[0]);
        }
Exemple #10
0
        public Metadata(Mp4Reader reader)
        {
            reader.moveToBox(eBoxType.moov);
            Debug.Assert(1 == reader.level);

            movieHeader = default;
            List <TrackMetadata> list = new List <TrackMetadata>();

            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                case eBoxType.mvhd:
                    movieHeader = new MovieHeader(reader);
                    break;

                case eBoxType.trak:
                    list.Add(parseTrack(reader, movieHeader.timescale));
                    break;

                default:                                // e.g. udta, for optional user data.
                    reader.skipCurrentBox();
                    break;
                }
            }

            tracks = list.ToArray();
        }
Exemple #11
0
        public static IEnumerable <eBoxType> readChildren(this Mp4Reader reader)
        {
            int lvl = reader.level;

            Debug.Assert(lvl > 0);
            Debug.Assert(reader.currentBox.isContainer());
            lvl--;

            while (true)
            {
                eBoxType boxType = reader.readBox();
                // Console.WriteLine( "{0}:   {1}", reader.currentBoxNames, boxType );
                if (boxType == eBoxType.ChildContainerEnd)
                {
                    if (reader.level == lvl)
                    {
                        yield break;
                    }
                }
                else
                {
                    yield return(boxType);
                }
            }
        }
Exemple #12
0
        static TrackMetadata parseTrack(Mp4Reader reader, uint timescale)
        {
            TrackHeader header   = default;
            MediaInfo   info     = default;
            EditListBox editList = null;

            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                case eBoxType.tkhd:
                    header = new TrackHeader(reader, timescale);
                    break;

                case eBoxType.mdia:
                    info = new MediaInfo(reader);
                    break;

                case eBoxType.edts:
                    editList = EditListBox.load(reader);
                    break;

                default:
                    reader.skipCurrentBox();
                    break;
                }
            }
            iEditList el = Mpeg4EditList.create(editList, timescale, info.timeScale);

            return(new TrackMetadata(header, info, el));
        }
Exemple #13
0
 public SampleSizeVariable8(Mp4Reader mp4, int count) : base(count)
 {
     Debug.Assert(mp4.currentBox == eBoxType.stz2);
     entries = new byte[count];
     mp4.read(entries.AsSpan());
     maxSampleSize = ComputeUtils.computeMax(entries.AsSpan());
 }
Exemple #14
0
 public SampleSizeVariable16(Mp4Reader mp4, int count) :
     base(count)
 {
     Debug.Assert(mp4.currentBox == eBoxType.stz2);
     entries = new ushort[count];
     mp4.read(entries.AsSpan().asBytes());
     maxSampleSize = ComputeUtils.swapEndiannessAndComputeMax(entries.AsSpan());
 }
Exemple #15
0
        internal static T readStructure <T>(this Mp4Reader reader) where T : unmanaged
        {
            T           result = default;
            Span <T>    span1  = MemoryMarshal.CreateSpan(ref result, 1);
            Span <byte> span2  = MemoryMarshal.Cast <T, byte>(span1);

            reader.read(span2);
            return(result);
        }
Exemple #16
0
        internal SampleTable(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.stbl);
            entries             = null;
            timeToSample        = null;
            sampleToChunk       = null;
            sampleSize          = null;
            chunkOffset         = null;
            compositionToSample = null;
            syncSampleTable     = null;

            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                case eBoxType.stsd:
                    entries = parseSampleTable(reader);
                    break;

                case eBoxType.stts:
                    timeToSample = TimingTables.readTimeToSample(reader);
                    break;

                case eBoxType.ctts:
                    compositionToSample = CompositionTimeDeltas.read(reader);
                    break;

                case eBoxType.stsc:
                    sampleToChunk = TimingTables.readSampleToChunk(reader);
                    break;

                case eBoxType.stsz:
                    sampleSize = TimingTables.readSampleSize(reader);
                    break;

                case eBoxType.stz2:
                    sampleSize = TimingTables.readSampleSizeCompact(reader);
                    break;

                case eBoxType.stco:
                    chunkOffset = TimingTables.readOffsets32(reader);
                    break;

                case eBoxType.co64:
                    chunkOffset = TimingTables.readOffsets64(reader);
                    break;

                case eBoxType.stss:
                    syncSampleTable = TimingTables.readSyncSample(reader);
                    break;

                default:
                    reader.skipCurrentBox();
                    break;
                }
            }
        }
Exemple #17
0
        public static ChunkOffsetTable readOffsets32(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.stco);

            sHeader header = reader.readStructure <sHeader>();
            int     count  = header.entry_count.endian();

            uint[] entries = readArray <uint>(reader, count);
            return(new ChunkOffsetTable32(entries));
        }
Exemple #18
0
        static float readHeader(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.smhd);
            Span <byte> bytes = stackalloc byte[8];

            reader.read(bytes);
            short balance = BitConverter.ToInt16(bytes.Slice(4));

            balance = BinaryPrimitives.ReverseEndianness(balance);
            return(balance * (1.0f / 0x100));
        }
Exemple #19
0
        public static SampleSizeTable readSampleSize(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.stsz);
            sSampleSizeBox box = reader.readStructure <sSampleSizeBox>();

            if (box.sample_size != 0)
            {
                return(new SampleSizeFixed(box.sample_size.endian(), box.sample_count.endian()));
            }

            return(SampleSizeTable.createVariable(reader, box.sample_count.endian()));
        }
Exemple #20
0
        public AudioSampleEntry(Mp4Reader reader, ref int bytesLeft)
        {
            var ss = reader.readStructure <Structures.AudioSampleEntry>();

            bytesLeft -= Marshal.SizeOf <Structures.AudioSampleEntry>();

            checked
            {
                channelCount  = (byte)ss.channelcount.endian();
                bitsPerSample = ss.samplesize.endian();
                sampleRateInt = ss.sampleRate.endian();
            }
        }
Exemple #21
0
        internal static CompositionTimeDeltas read(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.ctts);

            sHeader header = reader.readStructure <sHeader>();
            int     count  = header.entry_count.endian();

            if (count <= 0)
            {
                return(null);
            }

            bool signedIntegers;

            switch (header.version)
            {
            case 0:
                signedIntegers = false;
                break;

            case 1:
                signedIntegers = true;
                break;

            default:
                // Warning because videos actually play OK even when ignoring the data from that box.
                Logger.logWarning("ctts box has unexpected version {0}", header.version);
                return(null);
            }

            // That thing takes couple MB of RAM. Bypassing the GC with malloc/free.
            IntPtr nativePointer = Marshal.AllocHGlobal(count * 8);

            try
            {
                Span <CttsEntryUnsigned> span = Unsafe.writeSpan <CttsEntryUnsigned>(nativePointer, count);
                reader.read(span.asBytes());

                if (signedIntegers)
                {
                    var signed = MemoryMarshal.Cast <CttsEntryUnsigned, CttsEntrySigned>(span);
                    return(parseSigned(signed));
                }
                return(parseUnsigned(span));
            }
            finally
            {
                Marshal.FreeHGlobal(nativePointer);
            }
        }
Exemple #22
0
        internal VideoSampleEntry(Mp4Reader reader, ref int bytesLeft)
        {
            var ss = reader.readStructure <Structures.VisualSampleEntry>();

            bytesLeft -= Marshal.SizeOf <Structures.VisualSampleEntry>();

            sizePixels      = ss.size;
            pixelsPerInch   = ss.resolution;
            framesPerSample = ss.frameCount;
            unsafe
            {
                byte *comp = ss.compressorname;
                compressorName = StringMarshal.copy(comp, 32);
            }
        }
Exemple #23
0
 public static void moveToBox(this Mp4Reader reader, eBoxType boxType)
 {
     while (true)
     {
         var bt = reader.readBox();
         if (bt == boxType)
         {
             return;
         }
         if (bt == eBoxType.Empty)
         {
             throw new EndOfStreamException();
         }
     }
 }
Exemple #24
0
        public static T[] readArray <T>(Mp4Reader reader, int count) where T : unmanaged
        {
            T[] result = new T[count];
            var span   = result.AsSpan().asBytes();

            reader.read(span);

            Span <uint> values = span.cast <uint>();

            for (int i = 0; i < values.Length; i++)
            {
                values[i] = values[i].endian();
            }

            return(result);
        }
Exemple #25
0
        public static ChunkOffsetTable readOffsets64(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.co64);

            sHeader header = reader.readStructure <sHeader>();
            int     count  = header.entry_count.endian();

            long[] entries = new long[count];
            reader.read(entries.AsSpan().asBytes());
            for (int i = 0; i < entries.Length; i++)
            {
                entries[i] = entries[i].endian();
            }

            return(new ChunkOffsetTable64(entries));
        }
Exemple #26
0
        internal VideoInformation(Mp4Reader reader)
        {
            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                default:
                    // dinf is useless but mandatory in the spec.
                    // vmhd is useless, as well.
                    reader.skipCurrentBox();
                    break;

                case eBoxType.stbl:
                    sampleTable = new SampleTable(reader);
                    break;
                }
            }
        }
Exemple #27
0
        public Mp4File(Mp4Reader reader)
        {
            this.reader = reader;

            reader.moveToBox(eBoxType.ftyp);

            int         cb   = checked ((int)reader.remainingBytes);
            Span <byte> data = stackalloc byte[cb];

            reader.read(data);

            List <eFileType> types = new List <eFileType>();
            var values             = data.cast <uint>();

            types.Add((eFileType)values[0]);
            minorVersion = (int)BinaryPrimitives.ReverseEndianness(values[1]);
            for (int i = 2; i < values.Length; i++)
            {
                uint v = values[i];
                if (v == values[0])
                {
                    continue;
                }
                if (Enum.IsDefined(typeof(eFileType), v))
                {
                    types.Add((eFileType)v);
                }
            }
            fileType = types.ToArray();

            metadata = new Metadata(reader);

            findDefaultTracks();

            // Fast forward to the movie data; that box has the actual content we gonna play.
            // if( reader.currentBox != eBoxType.mdat )
            //	reader.moveToBox( eBoxType.mdat );

            m_videoTrack = new VideoTrack(this);
            m_audio      = new Audio(this);
        }
Exemple #28
0
        internal AudioInformation(Mp4Reader reader)
        {
            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                case eBoxType.smhd:
                    balance = readHeader(reader);
                    break;

                default:
                    // dinf is useless but mandatory in the spec
                    reader.skipCurrentBox();
                    break;

                case eBoxType.stbl:
                    sampleTable = new SampleTable(reader);
                    break;
                }
            }
        }
Exemple #29
0
        internal MovieHeader(Mp4Reader reader)
        {
            Debug.Assert(reader.currentBox == eBoxType.mvhd);
            uint versionAndFlags = reader.readUInt();

            switch (versionAndFlags & 0xFF)
            {
            case 0:
                var ver0 = reader.readStructure <MovieHeaderVersion0>();
                ver0.parseHeader(out creationTime, out modificationTime, out duration, out timescale);
                ver0.parseCommon(out rate, out volume, out nextTrackId);
                break;

            case 1:
                var ver1 = reader.readStructure <MovieHeaderVersion1>();
                ver1.parseHeader(out creationTime, out modificationTime, out duration, out timescale);
                ver1.parseCommon(out rate, out volume, out nextTrackId);
                break;

            default:
                throw new ArgumentException("Unsupported track version");
            }
        }
Exemple #30
0
        internal MediaInfo(Mp4Reader reader)
        {
            creationTime     = modificationTime = default;
            duration         = default;
            culture          = null;
            mediaHandler     = default;
            mediaInformation = null;
            timeScale        = 0;

            foreach (eBoxType boxType in reader.readChildren())
            {
                switch (boxType)
                {
                case eBoxType.mdhd:
                    readInfoHeader(reader, out creationTime, out modificationTime, out duration, out culture, out timeScale);
                    break;

                case eBoxType.hdlr:
                    mediaHandler = new MediaHandler(reader);
                    break;

                case eBoxType.minf:
                    switch (mediaHandler.mediaHandler)
                    {
                    case eMediaHandler.vide:
                    case eMediaHandler.auxv:
                        mediaInformation = new VideoInformation(reader);
                        break;

                    case eMediaHandler.soun:
                        mediaInformation = new AudioInformation(reader);
                        break;
                    }
                    break;
                }
            }
        }