Beispiel #1
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");
            }
        }
Beispiel #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");
        }
Beispiel #3
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));
        }
Beispiel #4
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));
        }
Beispiel #5
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()));
        }
Beispiel #6
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();
            }
        }
Beispiel #7
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);
            }
        }
Beispiel #8
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");
            }
        }
Beispiel #9
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);
            }
        }
Beispiel #10
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));
        }
Beispiel #11
0
        public AVC1SampleEntry(Mp4Reader reader, int bytesLeft) :
            base(reader, ref bytesLeft)
        {
            var avcc = reader.readStructure <Structures.AVCDecoderConfigurationRecord>();

            if (avcc.boxType != eAVC1BoxType.avcC)
            {
                throw new NotImplementedException();
            }
            bytesLeft -= decoderConfigSizeof;

            profile = avcc.profileCode;
            profileCompatibility = avcc.profileCompatibility;
            levelCode            = avcc.levelCode;
            naluLengthSize       = checked ((byte)(avcc.lengthSizeMinusOne + 1));

            Span <byte> remainingStuff = stackalloc byte[bytesLeft];

            reader.read(remainingStuff);

            int readOffset = 0;

            sps = ContainerUtils.copyBlobs(avcc.numOfSequenceParameterSets, remainingStuff, ref readOffset);

            if (null == sps)
            {
                throw new ArgumentException("The file doesn't have an SPS");
            }
            // SpsData spsData = new SpsData( sps[ 0 ] );
            // File.WriteAllBytes( @"C:\Temp\2remove\h264\sps.bin", sps[ 0 ] );

            int ppsCount = remainingStuff[readOffset++];

            pps = ContainerUtils.copyBlobs(ppsCount, remainingStuff, ref readOffset);

            if (null == sps || null == pps)
            {
                throw new NotImplementedException("Vrmac Video only supports mp4 files with out-of-band SPS and PPS blobs, in the `avcC` atom of the file.");
            }
            if (sps.Length > 1 || pps.Length > 1)
            {
                throw new NotImplementedException("Vrmac Video only supports mp4 files with a single out-of-band SPS and PPS for the complete video.");                     // The video payload may include other PPS-es, these are fine.
            }
            if (readOffset >= remainingStuff.Length)
            {
                return;
            }

            remainingStuff = remainingStuff.Slice(readOffset);

            if (readOffset + decoderConfigSizeof < avcc.length)
            {
                // The spec I have says the files with profile IDs 100, 110, 122, 144 have this.
                // The mp4 file I use to test this code has 100, but misses this data.
                chromaFormat   = (eChromaFormat)(remainingStuff[0] & 3);
                bitDepthLuma   = (byte)((remainingStuff[1] & 7) + 8);
                bitDepthChroma = (byte)((remainingStuff[2] & 7) + 8);
                int numPpsEx = remainingStuff[3];
                readOffset = 4;                 // Resetting because sliced the span
                ppsExt     = ContainerUtils.copyBlobs(numPpsEx, remainingStuff, ref readOffset);

                remainingStuff = remainingStuff.Slice(readOffset);
            }
            else
            {
                // https://en.wikipedia.org/wiki/Advanced_Video_Coding#Feature_support_in_particular_profiles
                chromaFormat   = eChromaFormat.c420;
                bitDepthLuma   = 8;
                bitDepthChroma = 8;
            }

            while (!remainingStuff.IsEmpty)
            {
                int          size = BitConverter.ToInt32(remainingStuff).endian();
                eAVC1BoxType code = (eAVC1BoxType)BitConverter.ToUInt32(remainingStuff.Slice(4));
                switch (code)
                {
                case eAVC1BoxType.btrt:
                    bitRate           = new MPEG4BitRateBox(remainingStuff);
                    m_maxBytesInFrame = bitRate.decodingBufferSize;
                    break;
                }
                remainingStuff = remainingStuff.Slice(size);
            }
        }