public void Parse_TestData_ReturnsValidFrame() { var testCodecInfo = new AACCodecInfo { ConfigBytes = new byte[] { 20, 8 }, IndexDeltaLength = 3, IndexLength = 3, SizeLength = 13 }; byte[] testBytes = Convert.FromBase64String( "ABAPyAE2Nay0eJ0OEMF4cOFyZeskqICVBWXLiAFDYHBG3GXFf0r/3nn+pRYcsdweO8Pzlxhjm0Q5QrEf7jx31b1PX8VuZOcZZ516jVY6sW/ISf8SQb6fONzpYJLiNRi7SO1o+" + "ByL8qHHiUeF9EoqyQ1VgamBApL8XaznlKoszkm9Td02961s0SznSmtSZcuhaEYCZaPe1k5qM+U3wHzs3CCnWMSNsK9UNCq1dzSm4YZWRkJPIY4oXJXsSCW67r8Xdb4slBDvxqu709z" + "ggyqrTuoOtkR3aHhFs6JHL8QwVgiusj39H8FrZ8Nu3Dt3qu0NQo5KpHN4R1jn2G71VHMfJwGEwZisU2e9Pz3h316dvpt97XbNcxLTgUCz9UpleUNXWAeygFCrPGTpWFv1Qn3JxS4W1xI" + "W5W/dHymYM5ElSMhtF+PFounb/IKbxKJThTUkxx3FWwYt00q7nMOilx2t6odQue/bTaENXKw304l1X5aiEl2S4+C0pIEzkHpbMp+5bmLw1+M8FNRkzlEngnPPATaJQSNOqTHTwVmK+" + "fAVSESminLM1TAOg0EZUw0pLTl3ka+scMp9kmxkFKLr3YRip0Uy0JMSnfefWxGezotf3sbV4afJZrnuVnKyanaK33korbN4LMIhvBgAAAAAAAAAAAAAAAAAAAAAAAAAA70="); byte[] frameBytes = Convert.FromBase64String( "ATY1rLR4nQ4QwXhw4XJl6ySogJUFZcuIAUNgcEbcZcV/Sv/eef6lFhyx3B47w/OXGGObRDlCsR/uPHfVvU9fxW5k5xlnnXqNVjqxb8hJ/xJBvp843OlgkuI1GLtI7Wj4HIvyoce" + "JR4X0SirJDVWBqYECkvxdrOeUqizOSb1N3Tb3rWzRLOdKa1Jly6FoRgJlo97WTmoz5TfAfOzcIKdYxI2wr1Q0KrV3NKbhhlZGQk8hjihclexIJbruvxd1viyUEO/Gq7vT3OCDKqtO6" + "g62RHdoeEWzokcvxDBWCK6yPf0fwWtnw27cO3eq7Q1Cjkqkc3hHWOfYbvVUcx8nAYTBmKxTZ70/PeHfXp2+m33tds1zEtOBQLP1SmV5Q1dYB7KAUKs8ZOlYW/VCfcnFLhbXEhblb90f" + "KZgzkSVIyG0X48Wi6dv8gpvEolOFNSTHHcVbBi3TSrucw6KXHa3qh1C579tNoQ1crDfTiXVflqISXZLj4LSkgTOQelsyn7luYvDX4zwU1GTOUSeCc88BNolBI06pMdPBWYr58BVIRKaK" + "cszVMA6DQRlTDSktOXeRr6xwyn2SbGQUouvdhGKnRTLQkxKd959bEZ7Oi1/extXhp8lmue5WcrJqdorfeSits3gswiG8GAAAAAAAAAAAAAAAAAAAAAAAAAADvQ=="); RawAACFrame frame = null; var parser = new AACAudioPayloadParser(testCodecInfo); parser.FrameGenerated = rawFrame => frame = (RawAACFrame)rawFrame; parser.Parse(TimeSpan.Zero, new ArraySegment <byte>(testBytes), true); Assert.IsNotNull(frame); Assert.AreEqual(FrameType.Audio, frame.Type); Assert.IsTrue(frame.ConfigSegment.SequenceEqual(testCodecInfo.ConfigBytes)); Assert.IsTrue(frame.FrameSegment.SequenceEqual(frameBytes)); }
/// <summary> /// Creates media frame data segments. /// </summary> /// <param name="mediaFrame">Media frame.</param> /// <param name="metadataRequired">Indicates whether to include metadata.</param> /// <returns>An array of data segments.</returns> private static ArraySegment <byte>[] CreateDataSegments(RawFrame mediaFrame, bool metadataRequired) { if (!metadataRequired) { return new[] { new byte[] { 0 }, mediaFrame.FrameSegment } } ; var codecName = mediaFrame switch { RawAACFrame _ => "AAC", RawG711AFrame _ => "G711A", RawG711UFrame _ => "G711U", RawG726Frame _ => "G726", RawPCMFrame _ => "PCM", RawH264IFrame _ => "H264", RawH264PFrame _ => "H264", RawJpegFrame _ => "MJPEG", _ => string.Empty }; var bitsPerCodedUnit = mediaFrame switch { RawG726Frame rawG726Frame => rawG726Frame.BitsPerCodedSample, _ => 0 }; var configSegment = mediaFrame switch { RawAACFrame rawAacFrame => rawAacFrame.ConfigSegment, RawH264IFrame rawH264IFrame => rawH264IFrame.SpsPpsSegment, _ => default }; var codecBytes = Encoding.UTF8.GetBytes(codecName); Array.Resize(ref codecBytes, 10); var metaSegment = new byte[19]; using var stream = new MemoryStream(metaSegment); using var writer = new EndianBinaryWriter(stream); writer.Write((byte)1); writer.Write(codecBytes); writer.Write(bitsPerCodedUnit); writer.Write(configSegment.Count); return(configSegment.Count > 0 ? new[] { metaSegment, configSegment, mediaFrame.FrameSegment } : new[] { metaSegment, mediaFrame.FrameSegment }); }
public override void Parse(TimeSpan timeOffset, ArraySegment <byte> byteSegment, bool markerBit) { int auHeadersBitLength = BigEndianConverter.ReadUInt16(byteSegment.Array, byteSegment.Offset); int auHeadersLengthBytes = (auHeadersBitLength + 7) / 8; int headerBitSize = _codecInfo.SizeLength + _codecInfo.IndexLength; int audioBitsAvail = auHeadersBitLength - headerBitSize; if (audioBitsAvail < 0 || headerBitSize <= 0) { return; } int framesCount = 1 + audioBitsAvail / (_codecInfo.SizeLength + _codecInfo.IndexDeltaLength); _bitStreamReader.ReInitialize(byteSegment.SubSegment(2)); int offset = byteSegment.Offset + auHeadersLengthBytes; for (int i = 0; i < framesCount; ++i) { int frameSize = _bitStreamReader.ReadBits(_codecInfo.SizeLength); if (i == 0) { _bitStreamReader.ReadBits(_codecInfo.IndexLength); } else if (_codecInfo.IndexDeltaLength != 0) { _bitStreamReader.ReadBits(_codecInfo.IndexDeltaLength); } Debug.Assert(byteSegment.Array != null, "byteSegment.Array != null"); var frameBytes = new ArraySegment <byte>(byteSegment.Array, offset, frameSize); DateTime timestamp = GetFrameTimestamp(timeOffset); var aacFrame = new RawAACFrame(timestamp, frameBytes, new ArraySegment <byte>(_codecInfo.ConfigBytes)); OnFrameGenerated(aacFrame); offset += frameSize; } }
public void Parse_TestData_ReturnsValidFrame() { var testCodecInfo = new AACCodecInfo { ConfigBytes = new byte[] { 20, 8 }, IndexDeltaLength = 3, IndexLength = 3, SizeLength = 13 }; byte[] testBytes = Convert.FromBase64String( "ABAPyAE2Nay0eJ0OEMF4cOFyZeskqICVBWXLiAFDYHBG3GXFf0r/3nn+pRYcsdweO8Pzlxhjm0Q5QrEf7jx31b1PX8VuZOcZZ516jVY6sW/ISf8SQb6fONzpYJLiNRi7SO1o+" + "ByL8qHHiUeF9EoqyQ1VgamBApL8XaznlKoszkm9Td02961s0SznSmtSZcuhaEYCZaPe1k5qM+U3wHzs3CCnWMSNsK9UNCq1dzSm4YZWRkJPIY4oXJXsSCW67r8Xdb4slBDvxqu709z" + "ggyqrTuoOtkR3aHhFs6JHL8QwVgiusj39H8FrZ8Nu3Dt3qu0NQo5KpHN4R1jn2G71VHMfJwGEwZisU2e9Pz3h316dvpt97XbNcxLTgUCz9UpleUNXWAeygFCrPGTpWFv1Qn3JxS4W1xI" + "W5W/dHymYM5ElSMhtF+PFounb/IKbxKJThTUkxx3FWwYt00q7nMOilx2t6odQue/bTaENXKw304l1X5aiEl2S4+C0pIEzkHpbMp+5bmLw1+M8FNRkzlEngnPPATaJQSNOqTHTwVmK+" + "fAVSESminLM1TAOg0EZUw0pLTl3ka+scMp9kmxkFKLr3YRip0Uy0JMSnfefWxGezotf3sbV4afJZrnuVnKyanaK33korbN4LMIhvBgAAAAAAAAAAAAAAAAAAAAAAAAAA70="); byte[] frameBytes = Convert.FromBase64String( "D8gBNjWstHidDhDBeHDhcmXrJKiAlQVly4gBQ2BwRtxlxX9K/955/qUWHLHcHjvD85cYY5tEOUKxH+48d9W9T1/FbmTnGWedeo1" + "WOrFvyEn/EkG+nzjc6WCS4jUYu0jtaPgci/Khx4lHhfRKKskNVYGpgQKS/F2s55SqLM5JvU3dNvetbNEs50prUmXLoWhGAmWj3tZO" + "ajPlN8B87Nwgp1jEjbCvVDQqtXc0puGGVkZCTyGOKFyV7Egluu6/F3W+LJQQ78aru9Pc4IMqq07qDrZEd2h4RbOiRy/EMFYIrrI9/R/" + "Ba2fDbtw7d6rtDUKOSqRzeEdY59hu9VRzHycBhMGYrFNnvT894d9enb6bfe12zXMS04FAs/VKZXlDV1gHsoBQqzxk6Vhb9UJ9ycUuFt" + "cSFuVv3R8pmDORJUjIbRfjxaLp2/yCm8SiU4U1JMcdxVsGLdNKu5zDopcdreqHULnv202hDVysN9OJdV+WohJdkuPgtKSBM5B6WzKfuW" + "5i8NfjPBTUZM5RJ4JzzwE2iUEjTqkx08FZivnwFUhEpopyzNUwDoNBGVMNKS05d5GvrHDKfZJsZBSi692EYqdFMtCTEp33n1sRns6LX97" + "G1eGnyWa57lZysmp2it95KK2zeCzCIbwYAAAAAAAAAAAAAAAAAAAAAAAAAA=="); RawAACFrame frame = null; var parser = new AACAudioPayloadParser(testCodecInfo); parser.FrameGenerated = rawFrame => frame = (RawAACFrame)rawFrame; parser.Parse(TimeSpan.Zero, new ArraySegment <byte>(testBytes), true); Assert.IsNotNull(frame); Assert.AreEqual(FrameType.Audio, frame.Type); Assert.IsTrue(frame.ConfigSegment.SequenceEqual(testCodecInfo.ConfigBytes)); Assert.IsTrue(frame.FrameSegment.SequenceEqual(frameBytes)); }