void Dump(bool force, MMalBuffer buffer, int len) { if (force) { _logger.Error("buffer {0}", buffer.ToString()); StringBuilder sb = new StringBuilder(); for (int i = 0; i < buffer.Data.Length && i < len; ++i) { if ((i % 4) == 0) { sb.Append(" "); } sb.AppendFormat("{0:x2}", buffer.Data[i]); if (((i + 1) % 30) == 0) { sb.Append("\r\n"); } } _logger.Error(sb.ToString()); } }
public void Parse(MMalBuffer buffer, MMalBuffer next) { if (_logger.IsDebugEnabled) { _logger.Debug("buffer.Len {0}, presentation time {1}", buffer.Length, buffer.Timestamp); } if (buffer.Data.Length < 4) { return; } bool pictureEndMarker = false; MemoryStream ms = new MemoryStream(buffer.Data); if (NaluIsStartCode(ms) == 0) { Dump(true, buffer, 10); throw new Exception("No Start Code"); } long nalStart = ms.Position; while (ms.Position < ms.Length) { int nalAndTrailingSize; int nalSize; int nalEnd; pictureEndMarker = false; nalAndTrailingSize = nalSize = NaluNextStartCode(ms); if (true) { nalSize = NaluPayloadEnd(ms); } byte[] nalunit = new byte[nalSize]; ms.Read(nalunit, 0, (int)nalSize); ms.Seek(nalStart, SeekOrigin.Begin); byte nalunitType = (byte)(nalunit[0] & 0x1F); // Now that we have found (& copied) a NAL unit, process it if it's of special interest to us: if (IsVPS(nalunitType)) { // Video parameter set // We haven't yet parsed a frame rate from the stream. // So parse this NAL unit to check whether frame rate information is present: // uint num_units_in_tick = 0, time_scale = 0; // AnalyzeVPS(ref num_units_in_tick, ref time_scale); } else if (IsSPS(nalunitType)) { // Sequence parameter set // We haven't yet parsed a frame rate from the stream. // So parse this NAL unit to check whether frame rate information is present: // uint num_units_in_tick = 0, time_scale = 0; //AnalyzeSPS(nalunit, ref num_units_in_tick, ref time_scale); //if (time_scale > 0 && num_units_in_tick > 0) //{ // fFrameRate = fParsedFrameRate = time_scale / (DeltaTfiDivisor * num_units_in_tick); //} } else if (IsPPS(nalunitType)) { } //else if (isSEI(nal_unit_type)) //{ // Supplemental enhancement information (SEI) // analyze_sei_data(nal_unit_type); // // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? ##### //} // Now, check whether this NAL unit ends an 'access unit'. // (RTP streamers need to know this in order to figure out whether or not to set the "M" bit.) bool thisNALUnitEndsAccessUnit = false; if (IsEOF(nalunitType)) { // There is no next NAL unit, so we assume that this one ends the current 'access unit': thisNALUnitEndsAccessUnit = true; } else if (UsuallyBeginsAccessUnit(nalunitType)) { // These NAL units usually *begin* an access unit, so assume that they don't end one here: thisNALUnitEndsAccessUnit = false; } else { // We need to check the *next* NAL unit to figure out whether // the current NAL unit ends an 'access unit': byte nextNalunitType = 0; nalEnd = (int)ms.Position; // gf_bs_get_position(bs); Debug.Assert(nalStart <= nalEnd); Debug.Assert(nalEnd <= nalStart + nalAndTrailingSize); if (nalEnd != nalStart + nalAndTrailingSize) { ms.Seek(nalStart + nalAndTrailingSize, SeekOrigin.Begin); } if (ms.Position == ms.Length) { if (next.Data.Length > 5) { nextNalunitType = (byte)(next.Data[4] & 0x1F); } } else { ms.Seek(nalStart + nalAndTrailingSize, SeekOrigin.Begin); /*consume next start code*/ nalStart = NaluNextStartCode(ms); if (nalStart != 0) { Console.WriteLine("[avc-h264] invalid nal_size ({0})? Skipping {1} bytes to reach next start code\n", nalSize, nalStart); //gf_bs_skip_bytes(bs, nal_start); } nalStart = NaluIsStartCode(ms); if (nalStart == 0) { _logger.Error("avc-h264 error: no start code found"); break; } nalStart = ms.Position; // gf_bs_get_position(bs); nalAndTrailingSize = nalSize = NaluNextStartCode(ms); if (true) { nalSize = NaluPayloadEnd(ms); } byte[] nextNalunit = new byte[nalSize]; ms.Read(nextNalunit, 0, (int)nalSize); nextNalunitType = (byte)(next.Data[4] & 0x1F); ms.Seek(nalStart, SeekOrigin.Begin); } if (IsVCL(nextNalunitType)) { // The high-order bit of the byte after the "nal_unit_header" tells us whether it's // the start of a new 'access unit' (and thus the current NAL unit ends an 'access unit'): byte byteAfter_nal_unit_header = 0x80; if (next.Data.Length >= 5) { byteAfter_nal_unit_header = next.Data[5]; } thisNALUnitEndsAccessUnit = (byteAfter_nal_unit_header & 0x80) != 0; if (_logger.IsDebugEnabled) { _logger.Debug("End of NalUnit {0}", thisNALUnitEndsAccessUnit); } } else if (UsuallyBeginsAccessUnit(nextNalunitType)) { // The next NAL unit's type is one that usually appears at the start of an 'access unit', // so we assume that the current NAL unit ends an 'access unit': thisNALUnitEndsAccessUnit = true; } else { // The next NAL unit definitely doesn't start a new 'access unit', // which means that the current NAL unit doesn't end one: thisNALUnitEndsAccessUnit = false; } } if (thisNALUnitEndsAccessUnit) { if (_logger.IsDebugEnabled) { _logger.Debug(" * ****This NAL unit ends the current access unit * ****\n"); } pictureEndMarker = true; } if (_logger.IsDebugEnabled) { _logger.Debug("EndNalAccessUnit {0}, presentation {1}", thisNALUnitEndsAccessUnit, buffer.Timestamp); } // TODO //byte[] tmp = new byte[nalSize + 4]; //tmp[3] = 1; //Array.Copy(nalunit, 0, tmp, 4, nalunit.Length); //Dump(tmp, numNalunit); //numNalunit++; _fragmenter.OnNewNalUnit(nalunit, thisNALUnitEndsAccessUnit, pictureEndMarker); nalEnd = (int)ms.Position; Debug.Assert(nalStart <= nalEnd); Debug.Assert(nalEnd <= nalStart + nalAndTrailingSize); if (nalEnd != nalStart + nalAndTrailingSize) { ms.Seek(nalStart + nalAndTrailingSize, SeekOrigin.Begin); } if (nalSize == 0) { break; } if (ms.Position == ms.Length) { break; } /*consume next start code*/ nalStart = NaluNextStartCode(ms); if (nalStart != 0) { _logger.Error("[avc-h264] invalid nal_size ({0})? Skipping {1} bytes to reach next start code", nalSize, nalStart); } nalStart = NaluIsStartCode(ms); if (nalStart == 0) { _logger.Error("[avc-h264] error: no start code found"); break; } nalStart = ms.Position; } // while }