public bool Parse(byte[] h264Stream) { profile = 0; level = 0; refFrames = 0; currrentPos = 0; buffer = h264Stream; byte[] nal = new byte[0]; while (nal != null) { nal = ReadNALUnit(); if (nal != null) { NALUnit nu = NALUnit.Read(nal); if (nu.Type == NALUnitType.SequenceParameterSet) { SeqParameterSet param = SeqParameterSet.Read(nu.Data); if (Enum.IsDefined(typeof(H264HeaderProfile), param.profile_idc) == true) { profile = param.profile_idc; level = param.level_idc; refFrames = param.num_ref_frames; return(true); } } } } return(false); }
private NALUnit[] ReadNALUnitArray(MemoryStream stream, int cnt) { return(Enumerable.Range(0, cnt) .Select(_ => { var len = stream.ReadUInt16BE(); return NALUnit.ReadFrom(stream, len); }) .ToArray()); }
private void NALUnitAudioAdd(NALUnit cNALUnit) { if (cNALUnit.nStart + cNALUnit.nBytesQty > _cStream.nBytesBuffered) { _cStream.Cache(cNALUnit.nStart + cNALUnit.nBytesQty - _cStream.nBytesBuffered); } lock (_cSyncRoot) _aAudioNALs.Add(cNALUnit); }
public static NALUnit Read(byte[] nalData) { int nalu = nalData[0] & 0xFF; int refIdc = (nalu >> 5) & 0x03; int type = nalu & 0x1F; if (Enum.IsDefined(typeof(NALUnitType), type) == false) { type = 0; } NALUnit nal = new NALUnit(); nal.Type = (NALUnitType)type; nal.RefIDC = refIdc; nal.Data = new byte[nalData.Length - 1]; Array.Copy(nalData, 1, nal.Data, 0, nal.Data.Length); return(nal); }
private void NALUnitVideoAdd(NALUnit cNALUnit) { if (cNALUnit.nStart + cNALUnit.nBytesQty > _cStream.nBytesBuffered) { _cStream.Cache(cNALUnit.nStart + cNALUnit.nBytesQty - _cStream.nBytesBuffered); } double nProgress = 0; lock (_cSyncRoot) { _aVideoNALs.Add(cNALUnit); if (cNALUnit.bFrameStart) { _nFramesBuffered++; if (_bAudioFrameNeeded) { if (1 < (nProgress = (double)(_nFramesBuffered - _nCurrentFrameVideo) / (_nBufferSeconds * 25))) { nProgress = 1; } ReportGetSampleProgress(nProgress); } } } if (0.9 < nProgress) { if (_bAudioFrameNeeded) { GetSampleAsync(MediaStreamType.Audio); } if (_bVideoFrameNeeded) { GetSampleAsync(MediaStreamType.Video); } } }
private void OnAVCBody(RTMPMessage msg, bool keyframe) { var pts = msg.Timestamp - Math.Max(0, ptsBase); var cts = msg.Body.Skip(2).Take(3).Aggregate(0, (r, v) => (r << 8) | v); if (cts >= 0x800000) { cts = 0x1000000 - cts; } var dts = pts; pts = pts + cts; var access_unit_delimiter = false; var idr = false; var nalbytestream = new MemoryStream(); int units = 0; using (nalbytestream) using (var body = new MemoryStream(msg.Body, 0, msg.Body.Length)) { body.Seek(5, SeekOrigin.Begin); while (body.Position < body.Length) { var len = body.ReadBytes(nalSizeLen).Aggregate(0, (r, v) => (r << 8) | v); var nalu = NALUnit.ReadFrom(body, len); if (nalu.NALUnitType == 9) { access_unit_delimiter = true; } if (!access_unit_delimiter) { NALUnit.WriteToByteStream(nalbytestream, NALUnit.AccessUnitDelimiter); access_unit_delimiter = true; } if (nalu.NALUnitType == 5) { idr = true; foreach (var unit in sps) { NALUnit.WriteToByteStream(nalbytestream, unit); } foreach (var unit in pps) { NALUnit.WriteToByteStream(nalbytestream, unit); } foreach (var unit in spsExt) { NALUnit.WriteToByteStream(nalbytestream, unit); } } NALUnit.WriteToByteStream(nalbytestream, nalu); units += 1; } } var pes = new PESPacket( 0xE0, TSTimeStamp.FromMilliseconds(pts), TSTimeStamp.FromMilliseconds(dts), nalbytestream.ToArray() ); var pes_packet = new MemoryStream(); using (pes_packet) { PESPacket.WriteTo(pes_packet, pes); } writer.WriteTSPackets( VideoPID, msg.IsKeyFrame() || idr, idr ? (TSTimeStamp?)TSTimeStamp.FromMilliseconds(dts) : null, pes_packet.ToArray() ); }
public static void WriteToByteStream(Stream s, NALUnit unit) { s.Write(WritePrefix, 0, WritePrefix.Length); s.WriteByte((byte)((unit.NALRefIdc << 5) | (unit.NALUnitType & 0x1F))); s.Write(unit.Payload, 0, unit.Payload.Length); }
private void NALUnitParse() { //3. NumBytesInNALunit is set equal to the number of bytes starting with the byte at the current position in the byte //stream up to and including the last byte that precedes the location of any of the following: //– A subsequent byte-aligned three-byte sequence equal to 0x000000, //– A subsequent byte-aligned three-byte sequence equal to 0x000001, //– The end of the byte stream, as determined by unspecified means. //4. NumBytesInNALunit bytes are removed from the bitstream and the current position in the byte stream is //advanced by NumBytesInNALunit bytes. This sequence of bytes is nal_unit( NumBytesInNALunit ) and is //decoded using the NAL unit decoding process. //5. When the current position in the byte stream is not at the end of the byte stream (as determined by unspecified //means) and the next bytes in the byte stream do not start with a three-byte sequence equal to 0x000001 and the //next bytes in the byte stream do not start with a four byte sequence equal to 0x00000001, the decoder extracts //and discards each trailing_zero_8bits syntax element, moving the current position in the byte stream forward //one byte at a time, until the current position in the byte stream is such that the next bytes in the byte stream form //the four-byte sequence 0x00000001 or the end of the byte stream has been encountered (as determined by //unspecified means). NALUnit cNALUnitAudio = null; NALUnit cNALUnitVideo = new NALUnit(); cNALUnitVideo.nStart = _cStream.Position - 3; bool bNALStart = false; byte nStepVideo = 0, nStepAudio = 0; int nByte = 0; //_cStream.ReadByte(); //int nByteType = _cStream.ReadByte(); //if (0 > nByteType) // throw new Exception(); while (true) { if (_cStream.Position >= nDataSize + nDataStart || 0 > (nByte = _cStream.ReadByte())) throw new Exception(); if (0x00 == nByte) { nStepAudio = 0; nStepVideo++; if (2 < nStepVideo) bNALStart = true; } else if (0x01 == nByte) { nStepAudio = 0; if (1 < nStepVideo) bNALStart = true; else nStepVideo = 0; } else { nStepVideo = 0; if (0xFF == nByte) nStepAudio = 1; else if (0xFB == nByte && 1 == nStepAudio) nStepAudio++; else if (_nAudioMP3ControlByte == nByte && 2 == nStepAudio) nStepAudio++; else if ((0x64 == nByte || 0x44 == nByte) && 3 == nStepAudio) { long nPosition = _cStream.Position; _cStream.Position += _cMP3WaveFormat.BlockSize - 4; nByte = _cStream.ReadByte(); if ((0 == nByte && 0 == _cStream.ReadByte() && 0 == _cStream.ReadByte()) || (0xFF == nByte && 0xFB == _cStream.ReadByte() && _nAudioMP3ControlByte == _cStream.ReadByte())) { if (null != cNALUnitVideo) { cNALUnitVideo.nBytesQty = nPosition - cNALUnitVideo.nStart - 4; cNALUnitVideo.bFrameStart = true; NALUnitVideoAdd(cNALUnitVideo); lock (_cSyncRoot) _aFramesOffsets.Add(new FrameOffset(_aAudioNALs.Count + 1, _aVideoNALs.Count)); cNALUnitVideo = null; } cNALUnitAudio = new NALUnit(); cNALUnitAudio.nStart = nPosition - 4; cNALUnitAudio.nBytesQty = (long)_cMP3WaveFormat.BlockSize; NALUnitAudioAdd(cNALUnitAudio); _cStream.Position -= 3; } else _cStream.Position = nPosition; nStepAudio = 0; } else nStepAudio = 0; } if (bNALStart) { if (null != cNALUnitVideo) { cNALUnitVideo.nBytesQty = _cStream.Position - cNALUnitVideo.nStart - 2; NALUnitVideoAdd(cNALUnitVideo); } return; } } }
private void NALUnitAudioAdd(NALUnit cNALUnit) { if (cNALUnit.nStart + cNALUnit.nBytesQty > _cStream.nBytesBuffered) _cStream.Cache(cNALUnit.nStart + cNALUnit.nBytesQty - _cStream.nBytesBuffered); lock (_cSyncRoot) _aAudioNALs.Add(cNALUnit); }
private void NALUnitVideoAdd(NALUnit cNALUnit) { if (cNALUnit.nStart + cNALUnit.nBytesQty > _cStream.nBytesBuffered) _cStream.Cache(cNALUnit.nStart + cNALUnit.nBytesQty - _cStream.nBytesBuffered); double nProgress = 0; lock (_cSyncRoot) { _aVideoNALs.Add(cNALUnit); if (cNALUnit.bFrameStart) { _nFramesBuffered++; if (_bAudioFrameNeeded) { if (1 < (nProgress = (double)(_nFramesBuffered - _nCurrentFrameVideo) / (_nBufferSeconds * 25))) nProgress = 1; ReportGetSampleProgress(nProgress); } } } if (0.9 < nProgress) { if (_bAudioFrameNeeded) GetSampleAsync(MediaStreamType.Audio); if (_bVideoFrameNeeded) GetSampleAsync(MediaStreamType.Video); } }
public override MediaFrame Depacketize(RtpPacket pkt) { MediaFrame frame = null; if (pkt.Sequence != (lastSequence + 1)) { logger.Warn("Bad sequence " + lastSequence + " != " + pkt.Sequence); // TODO: // Добавляем в буфер и пытаемся упорядочить } lastSequence = pkt.Sequence; bool newTimestampSequence = false; var timestamp = pkt.Timestamp; if (timestamp != RtpConst.NoTimestamp && timestamp != lastTimestamp) { if (lastTimestamp != RtpConst.NoTimestamp) { var diff = (timestamp - lastTimestamp); if (diff < 0) { // TODO: } unwrappedTimestamp += diff; } lastTimestamp = timestamp; newTimestampSequence = true; // logger.Warn(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> "); } if (newTimestampSequence) { // группируем по временным меткам // т.е сервер должен передавать данные с правильным вр.метками иначе работать не будет... if (naluBuffer.Count > 0) { var firstNalu = naluBuffer[0]; // все вр.метки должны быть одинаковые... var naluTimestamp = firstNalu.timestamp; var dataBuffer = new List <byte[]>(); int totalLength = 0; foreach (var nal in naluBuffer) { if (naluTimestamp != nal.timestamp) { logger.Warn("naluTimstamp != nal.timestamp"); } var data = nal.data; dataBuffer.Add(data); totalLength += data.Length; } //var dataBuffer = naluBuffer.Select(n => n.data); //var totalLength = dataBuffer.Sum(n => n.Length); var nalUnitData = new byte[totalLength]; int offset = 0; foreach (var data in dataBuffer) { Array.Copy(data, 0, nalUnitData, offset, data.Length); offset += data.Length; } var naluTime = (naluTimestamp / (double)ClockRate); frame = new MediaFrame { Data = nalUnitData, Time = naluTime, }; naluBuffer.Clear(); } } if (naluBuffer.Count > 256) { if (timestamp == RtpConst.NoTimestamp || lastTimestamp == RtpConst.NoTimestamp || timestamp == 0 || lastTimestamp == 0) {// если не удается получить правильные вр.метки - сбрасываем буфер // т.к декодировать не сгруппированные данные не все декодеры могу... logger.Error("No valid timestamp received; drop buffer"); naluBuffer.Clear(); } } NALUnit nalUnit = null; byte[] payload = pkt.Payload.ToArray(); if (payload != null && payload.Length > 0) { nalUnit = ParseH264Payload(payload, unwrappedTimestamp); } if (nalUnit != null) { naluBuffer.Add(nalUnit); } return(frame); //if (data != null) //{ // frame = new MediaFrame // { // Data = data, // Time = monotonicTime, // }; //} //return frame; }
protected override void GetSampleAsync(MediaStreamType eMediaStreamType) { Dictionary <MediaSampleAttributeKeys, string> emptyDict = new Dictionary <MediaSampleAttributeKeys, string>(); MediaStreamSample cMediaStreamSample = null; NALUnit cNALUnit = null; lock (_cSyncRoot) { if (!_bCached && _nCurrentFrameVideo >= _nFramesBuffered) { this.ReportGetSampleProgress(0); if (MediaStreamType.Audio == eMediaStreamType) { _bAudioFrameNeeded = true; } else { _bVideoFrameNeeded = true; } return; } if (MediaStreamType.Audio == eMediaStreamType) { _bAudioFrameNeeded = false; } else { _bVideoFrameNeeded = false; } } if (eMediaStreamType == MediaStreamType.Audio) { try { lock (_cSyncRoot) { if (0 < _aAudioNALs.Count && _aAudioNALs.Count > _nFrameOffsetAudio) { cNALUnit = _aAudioNALs[_nFrameOffsetAudio++]; } } if (null != cNALUnit) { cMediaStreamSample = new MediaStreamSample(_cMediaStreamAudioDescription, new MemoryStream(_cStream.Read(cNALUnit.nStart, cNALUnit.nBytesQty)), 0, cNALUnit.nBytesQty, (long)(_nCurrentFrameAudio * _nSampleDuration), emptyDict); //(long)(_nCurrentFrameAudio * _nSampleDuration) _nCurrentFrameAudio++; } } catch { } if (null == cMediaStreamSample) { cMediaStreamSample = new MediaStreamSample(_cMediaStreamAudioDescription, null, 0, 0, 0, emptyDict); } } else { try { lock (_cSyncRoot) if (0 < _aVideoNALs.Count && _aVideoNALs.Count > _nFrameOffsetVideo) { cNALUnit = _aVideoNALs[_nFrameOffsetVideo++]; } if (null != cNALUnit) { cMediaStreamSample = new MediaStreamSample(_cMediaStreamVideoDescription, new MemoryStream(_cStream.Read(cNALUnit.nStart, cNALUnit.nBytesQty)), 0, cNALUnit.nBytesQty, (long)(_nCurrentFrameVideo * _nFrameDuration), emptyDict); //(long)(_nCurrentFrameVideo * _nFrameDuration) } if (null == cNALUnit || cNALUnit.bFrameStart) { _nCurrentFrameVideo++; } } catch { } if (null == cMediaStreamSample) { cMediaStreamSample = new MediaStreamSample(_cMediaStreamVideoDescription, null, 0, 0, 0, emptyDict); } } this.ReportGetSampleCompleted(cMediaStreamSample); }
private void NALUnitParse() { //3. NumBytesInNALunit is set equal to the number of bytes starting with the byte at the current position in the byte //stream up to and including the last byte that precedes the location of any of the following: //– A subsequent byte-aligned three-byte sequence equal to 0x000000, //– A subsequent byte-aligned three-byte sequence equal to 0x000001, //– The end of the byte stream, as determined by unspecified means. //4. NumBytesInNALunit bytes are removed from the bitstream and the current position in the byte stream is //advanced by NumBytesInNALunit bytes. This sequence of bytes is nal_unit( NumBytesInNALunit ) and is //decoded using the NAL unit decoding process. //5. When the current position in the byte stream is not at the end of the byte stream (as determined by unspecified //means) and the next bytes in the byte stream do not start with a three-byte sequence equal to 0x000001 and the //next bytes in the byte stream do not start with a four byte sequence equal to 0x00000001, the decoder extracts //and discards each trailing_zero_8bits syntax element, moving the current position in the byte stream forward //one byte at a time, until the current position in the byte stream is such that the next bytes in the byte stream form //the four-byte sequence 0x00000001 or the end of the byte stream has been encountered (as determined by //unspecified means). NALUnit cNALUnitAudio = null; NALUnit cNALUnitVideo = new NALUnit(); cNALUnitVideo.nStart = _cStream.Position - 3; bool bNALStart = false; byte nStepVideo = 0, nStepAudio = 0; int nByte = 0; //_cStream.ReadByte(); //int nByteType = _cStream.ReadByte(); //if (0 > nByteType) // throw new Exception(); while (true) { if (_cStream.Position >= nDataSize + nDataStart || 0 > (nByte = _cStream.ReadByte())) { throw new Exception(); } if (0x00 == nByte) { nStepAudio = 0; nStepVideo++; if (2 < nStepVideo) { bNALStart = true; } } else if (0x01 == nByte) { nStepAudio = 0; if (1 < nStepVideo) { bNALStart = true; } else { nStepVideo = 0; } } else { nStepVideo = 0; if (0xFF == nByte) { nStepAudio = 1; } else if (0xFB == nByte && 1 == nStepAudio) { nStepAudio++; } else if (_nAudioMP3ControlByte == nByte && 2 == nStepAudio) { nStepAudio++; } else if ((0x64 == nByte || 0x44 == nByte) && 3 == nStepAudio) { long nPosition = _cStream.Position; _cStream.Position += _cMP3WaveFormat.BlockSize - 4; nByte = _cStream.ReadByte(); if ((0 == nByte && 0 == _cStream.ReadByte() && 0 == _cStream.ReadByte()) || (0xFF == nByte && 0xFB == _cStream.ReadByte() && _nAudioMP3ControlByte == _cStream.ReadByte())) { if (null != cNALUnitVideo) { cNALUnitVideo.nBytesQty = nPosition - cNALUnitVideo.nStart - 4; cNALUnitVideo.bFrameStart = true; NALUnitVideoAdd(cNALUnitVideo); lock (_cSyncRoot) _aFramesOffsets.Add(new FrameOffset(_aAudioNALs.Count + 1, _aVideoNALs.Count)); cNALUnitVideo = null; } cNALUnitAudio = new NALUnit(); cNALUnitAudio.nStart = nPosition - 4; cNALUnitAudio.nBytesQty = (long)_cMP3WaveFormat.BlockSize; NALUnitAudioAdd(cNALUnitAudio); _cStream.Position -= 3; } else { _cStream.Position = nPosition; } nStepAudio = 0; } else { nStepAudio = 0; } } if (bNALStart) { if (null != cNALUnitVideo) { cNALUnitVideo.nBytesQty = _cStream.Position - cNALUnitVideo.nStart - 2; NALUnitVideoAdd(cNALUnitVideo); } return; } } }