/// <summary> /// QBox.Read /// Read-in a QBox /// NOTE: stream position is NOT assumed to start at the beginning of a qbox. /// </summary> /// <param name="br"></param> public void Read(BinaryReader br) { //ODS.Processing.Core.Logger.Instance.Info("Reading QBox"); mHeaderSize = 0; if (br.BaseStream.CanSeek) { mHeaderPosition = br.BaseStream.Position; // needed for flashback only if (br.BaseStream.Position + 4 > br.BaseStream.Length) throw new Exception("QBox:Read, Not enough data to read qbox length"); } mBoxSize = QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; if (mBoxSize == 0) throw new Exception("QBox:Read, Invalid box size"); if (br.BaseStream.CanSeek) { // subtract 4 as the size includes the 4 bytes we read already which was the length... if (br.BaseStream.Position + (long)(mBoxSize - 4) > br.BaseStream.Length) { // reset the location to the start of the qbox // this allows the reader to try again later... br.BaseStream.Position = mHeaderPosition; throw new Exception("QBox:Read, Not enough data to read qbox content"); } } try { mBoxType = QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; if (mBoxType != QBOX_TYPE) throw new Exception("QBox:Read, Invalid box type (not a qbox)"); mBoxFlags.value = (uint) QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mSampleStreamType = QBox.BE16(br.ReadUInt16()); mHeaderSize += 2; mSampleStreamId = QBox.BE16(br.ReadUInt16()); mHeaderSize += 2; // replacement for all of commented lines below mSampleFlags = QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; // version 0 only uses a single int for the CTS, version 1 uses 64 bit high/low value _SampleCTSHigh = QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; if (mBoxFlags.version == 1 || mBoxFlags.version == 2) { _SampleCTSLow = QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; } if (mBoxFlags.version == 2) { mBoxContinuityCounter = (uint)QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mFrameCounter = (uint)QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mSampleDuration = (uint)QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mStreamDuration = (ulong)QBox.BE64(br.ReadUInt64()); mHeaderSize += 8; mOffsetFromLastSyncPoint = (uint)QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mOffsetFromLastFrame = (uint)QBox.BE32(br.ReadUInt32()); mHeaderSize += 4; mReserved2 = (ulong)QBox.BE64(br.ReadUInt64()); mHeaderSize += 8; mReserved3 = (ulong)QBox.BE64(br.ReadUInt64()); mHeaderSize += 8; mReserved4 = (ulong)QBox.BE64(br.ReadUInt64()); mHeaderSize += 8; } //// adjust time stamp if 120KHz bit is ON (doesn't matter which track) //if ((mSampleFlags & QBox.QBOX_SAMPLE_FLAGS_120HZ_CLOCK) != 0U) { // mSampleCTS = (mSampleCTS * QBox.BASE_TIME_SCALE) / 120000U; // mSampleDuration = (mSampleDuration * QBox.BASE_TIME_SCALE) / 120000U; // mStreamDuration = (mStreamDuration * QBox.BASE_TIME_SCALE) / 120000U; //} mSampleSize = (int) mBoxSize - mHeaderSize; if (mSampleSize == 0) return; mSample = new QBoxSample(); mSampleSize -= mSample.Read(br, mSampleSize, mBoxFlags.flags, mSampleFlags, mSampleStreamType, mSampleStreamId); mSamplePosition += mSample.mSampleHeaderSize; } catch (Exception ex) { // reset the location to the start of the qbox // this allows the reader to try again later... if (br.BaseStream.CanSeek) br.BaseStream.Position = mHeaderPosition; throw ex; } #if REMOVE_EXTRA_SPS // deal with the special case in which the box is a Meta V box if ((mSample.v != null) && (mSample.mSampleHeaderSize > 0)) mSamplePosition += 6; // nalu delimiter length is always 6 #endif }
public static int GetSampleSize(QBoxSample audioMetaSample) { if (audioMetaSample == null) return 0; if (audioMetaSample.a != null) return (int)audioMetaSample.a.samplesize; if (audioMetaSample.qmed != null) { QMed.QMedAAC aacQ = (QMed.QMedAAC)audioMetaSample.qmed; // FIXME: for now we assume it's a QMedAAC return (int)aacQ.sampleSize; } return 0; }
public static byte[] GetAudioSpecificConfig(QBoxSample audioMetaSample) { if (audioMetaSample == null) return null; if (audioMetaSample.qmed != null) { QMed.QMedAAC aacQ = (QMed.QMedAAC)audioMetaSample.qmed; // FIXME: for now we assume it's a QMedAAC return aacQ.audioSpecificConfig; } return null; }
public static int GetChannelCount(QBoxSample audioMetaSample) { if (audioMetaSample == null) return 0; if (audioMetaSample.a != null) return (int)audioMetaSample.a.channels; if (audioMetaSample.qmed != null) { QMed.QMedAAC aacQ = (QMed.QMedAAC)audioMetaSample.qmed; // FIXME: for now we assume it's a QMedAAC return (int)aacQ.channels; } return 0; }
/// <summary> /// Constructor for writing to a qbox file. /// FIXME: Need to add v2 properties like mFrameCounter, mContinuityCounter, mSampleDuration, and mStreamDuration. /// </summary> /// <param name="dataSize"></param> /// <param name="flags"></param> /// <param name="timeStamp"></param> /// <param name="sampleStreamType"></param> /// <param name="sampleFlags"></param> public QBox(int dataSize, uint qboxFlags, ulong timeStamp, string sampleStreamType, ulong sampleFlags) : this(QBOX_FLAGS_SAMPLE_DATA_PRESENT) { mBoxFlags.flags |= qboxFlags; //mSampleCTS = timeStamp; // set stream type: at this time we only accept AAC and H264 payload mSampleStreamType = (ushort)((sampleStreamType == "Audio") ? QBOX_SAMPLE_TYPE_AAC : ((sampleStreamType == "Video") ? QBOX_SAMPLE_TYPE_H264 : 0u)); mSampleStreamId = (ushort)((sampleStreamType == "Audio") ? QBOX_AUDIO_TRACK : ((sampleStreamType == "Video") ? QBOX_VIDEO_TRACK : 0u)); mSampleFlags |= sampleFlags; if (mBoxFlags.version == 0) mHeaderSize = 24; else if (mBoxFlags.version == 1) mHeaderSize = 28; else // version 2; mHeaderSize = 80; mBoxSize = (ulong)mHeaderSize; if ((mBoxFlags.flags & QBOX_FLAGS_SAMPLE_DATA_PRESENT) != 0) { mSample = new QBoxSample(dataSize, sampleFlags, mSampleStreamType); mBoxSize = (ulong)(mHeaderSize + mSample.mSampleHeaderSize + dataSize); mSampleSize = dataSize; } }