static void Main(string[] args) { #region udpServer //建立UDPSocket 参数2:udp协议以数据报的方式传输,参数3:UDP协议 Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //为udp服务器绑定ip IPAddress ip = IPAddress.Parse(getLocalIpAddress(3)); EndPoint ipAddress = new IPEndPoint(ip, 18888); udpServer.Bind(ipAddress); //接收数据 本机的所有IP地址,所有可用的端口 EndPoint clientAddress = new IPEndPoint(IPAddress.Any, 0); #endregion #region 缓存区和索引 //单个数据报缓存区 byte[] data = new byte[1450]; //H264缓存区 byte[] H264Buffer = new byte[5 * 1024 * 1024]; //分片NALU缓存区 byte[] fregmentBuffer = new byte[10 * 1024 * 1024]; //SPS+PPS+SEI缓存区 byte[] sps_pps_seiBuffer = new byte[1024 * 1024]; //建立每一个I帧P帧视频序列缓存区 byte[] iFrameAndpFrameSequenceBuffer = new byte[10 * 1024 * 1024]; //获取到的H.264插入视频缓存区中的位置 int index = 0; //SPS+PPS+SEI缓存区中的索引位置 int spsIndex = 0; //每一个I帧P帧视频序列缓存区中索引的位置 int videoSequenceIndex = 0; #endregion //收到的RTP Package的length int length = 0; //需要分片的NALU的原始的长度 int fregmentNaluLength = 0; //IDR帧计算 int iDRFrameCount = 0; //int spsFrameLength = 0; //int ppsFrameLength = 0; //设置分片NALU原始H264数据的起始码 byte[] startCode = new byte[4]; startCode[0] = 0x00; startCode[1] = 0x00; startCode[2] = 0x00; startCode[3] = 0x01; //设置分片NALU原始H264的NALU Header int fuIndicatorTop3Bit = 0; int fuHeaderLast5Bit = 0; //int naluHeaderIndex = 0; //int rtpCount = 0; int fregmentFileCount = 0; Console.WriteLine("服务端已开启,监听端口43999"); //把数据的来源放到第二个参数上 while (true) { //收到每一个数据报 length = udpServer.ReceiveFrom(data, ref clientAddress); byte[] sequenceNumberBytes = new byte[4]; sequenceNumberBytes[0] = data[3]; sequenceNumberBytes[1] = data[2]; int sequenceNumber = BitConverter.ToInt32(sequenceNumberBytes, 0); //Console.SetCursorPosition(0, 1); Console.WriteLine(sequenceNumber); if (length != 1) { if (sequenceNumber == 3) { //首先将SPS+PPS+SEI缓存区中的数据存入每一个H264Buffer最前面 DecodeRTPHelper.CopyToH264(H264Buffer, sps_pps_seiBuffer, spsIndex, ref index); index += spsIndex; } //判断收到的为单一NALU模式 if ((data[12] & 0x1f) != 28) { //首先将SPS_PPS_SEI存入SPS+PPS+SEI缓存区中 if (sequenceNumber <= 2) { var sps_pps_seiBytes = DecodeRTPHelper.DecodeSingleNalu(data, length, ref spsIndex, ref iDRFrameCount); Array.Copy(sps_pps_seiBytes, 0, sps_pps_seiBuffer, spsIndex, sps_pps_seiBytes.Length); spsIndex += sps_pps_seiBytes.Length; continue; } //获取原始的H.264数据,包含了起始字节00 00 00 01 var singleNalu = DecodeRTPHelper.DecodeSingleNalu(data, length, ref videoSequenceIndex, ref iDRFrameCount); //此时到下一个IDR帧了 if (iDRFrameCount == 2) { //加上一个序列之后,长度超出H264Buffer缓存区的长度 if (index + videoSequenceIndex > H264Buffer.Length) { //先将之前的输出到文件 Interlocked.Increment(ref fregmentFileCount); FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + Convert.ToInt32(DateTime.Now.TimeOfDay.TotalMilliseconds) + ".264", FileMode.Create); using (sourceFileStream) { sourceFileStream.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是1:index + videoSequenceIndex > H264Buffer.Length.", fregmentFileCount); } //将之前存好的序列存入H264Buffer中 index = spsIndex; DecodeRTPHelper.CopyToH264(H264Buffer, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, ref index); index += videoSequenceIndex; videoSequenceIndex = 0; //将当前收到的单一NALU存入iFrameAndpFrameSequenceBuffer Array.Copy(singleNalu, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, singleNalu.Length); videoSequenceIndex += singleNalu.Length; iDRFrameCount = 1; } else { //将IFrameBuffer存入H264Buffer中 DecodeRTPHelper.CopyToH264(H264Buffer, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, ref index); index += videoSequenceIndex; videoSequenceIndex = 0; Array.Copy(singleNalu, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, singleNalu.Length); videoSequenceIndex += singleNalu.Length; iDRFrameCount = 1; } } else { //将原始的单个H.264数据存入iFrameAndpFrameSequenceBuffer中 Array.Copy(singleNalu, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, singleNalu.Length); videoSequenceIndex += singleNalu.Length; } } //收到的为分片NALU模式 else { //如果为最后分片 if ((data[13] & 0xc0) == 64) { //将所有分片NALU的片段存入fregmentBuffer中 DecodeRTPHelper.DecodeFregmentNalu(data, startCode, fregmentBuffer, length, ref fregmentNaluLength, ref fuIndicatorTop3Bit, ref fuHeaderLast5Bit, ref iDRFrameCount, ref videoSequenceIndex); //如果检测到收到的分片NALU为IDR帧 if (iDRFrameCount == 2) { //如果之前已存的视频数据+新存入的iFrameAndpFrameSequenceBuffer的总长度超过H264Buffer的总长度 if (index + videoSequenceIndex > H264Buffer.Length) { //将之前的H264Buffer中的数据先存入文件 Interlocked.Increment(ref fregmentFileCount); FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + Convert.ToInt32(DateTime.Now.TimeOfDay.TotalMilliseconds) + ".264", FileMode.Create); using (sourceFileStream) { sourceFileStream.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是2:index + videoSequenceIndex > H264Buffer.Length.", fregmentFileCount); } //将之前存好的iFrameAndpFrameSequenceBuffer存入H264Buffer中 index = spsIndex; DecodeRTPHelper.CopyToH264(H264Buffer, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, ref index); index += videoSequenceIndex; videoSequenceIndex = 0; //将此时收到fregmentBuffer存入iFrameAndpFrameSequenceBuffer中 Array.Copy(fregmentBuffer, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, fregmentNaluLength); videoSequenceIndex += fregmentNaluLength; fregmentNaluLength = 0; iDRFrameCount = 1; } //如果之前的数据加上刚收的分片NALU的长度没超过H264Buffer的长度 else { DecodeRTPHelper.CopyToH264(H264Buffer, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, ref index); index += videoSequenceIndex; videoSequenceIndex = 0; //将此时收到fregmentBuffer存入iFrameAndpFrameSequenceBuffer中 Array.Copy(fregmentBuffer, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, fregmentNaluLength); videoSequenceIndex += fregmentNaluLength; fregmentNaluLength = 0; iDRFrameCount = 1; } } else { //将完整的fregmentBuffer存入iFrameAndpFrameSequenceBuffer中 Array.Copy(fregmentBuffer, 0, iFrameAndpFrameSequenceBuffer, videoSequenceIndex, fregmentNaluLength); videoSequenceIndex += fregmentNaluLength; fregmentNaluLength = 0; } } //如果不为最后分片 else { //将所有分片NALU的片段存入分片NALU缓存区中 DecodeRTPHelper.DecodeFregmentNalu(data, startCode, fregmentBuffer, length, ref fregmentNaluLength, ref fuIndicatorTop3Bit, ref fuHeaderLast5Bit, ref iDRFrameCount, ref videoSequenceIndex); } } } else { Interlocked.Increment(ref fregmentFileCount); FileStream fs = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + Convert.ToInt32(DateTime.Now.TimeOfDay.TotalMilliseconds) + ".264", FileMode.Create); using (fs) { fs.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是3:rap pacakge length ==1.", fregmentFileCount); //fs.Close(); } break; } } Console.WriteLine("视频流写入文件完成."); ////将读取到的视频缓存区中的数据存入文件中 //FileStream fileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + Convert.ToInt32(DateTime.Now.TimeOfDay.TotalMilliseconds) + ".264", // FileMode.Create); //using (fileStream) //{ // fileStream.Write(H264Buffer, 0, index); // Console.WriteLine("获取到的视频流写入文件完成"); // fileStream.Close(); //} }
static void Main(string[] args) { //建立UDPSocket 参数2:udp协议以数据报的方式传输,参数3:UDP协议 Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //为udp服务器绑定ip IPAddress ip = IPAddress.Parse(getLocalIpAddress(3)); EndPoint ipAddress = new IPEndPoint(ip, 43999); udpServer.Bind(ipAddress); //接收数据 本机的所有IP地址,所有可用的端口 EndPoint clientAddress = new IPEndPoint(IPAddress.Any, 0); //单个数据报缓存区 byte[] data = new byte[1450]; //H264缓存区 byte[] H264Buffer = new byte[1 * 1024 * 1024]; //byte[] zeroBuffer = new byte[20 * 1024 * 1024]; int length = 0; //分片NALU缓存区 byte[] fregmentBuffer = new byte[10 * 1024 * 1024]; //需要分片的NALU的原始的长度 int fregmentNaluLength = 0; //获取到的H.264插入视频缓存区中的位置 int index = 0; //设置分片NALU原始H264数据的起始码 byte[] startCode = new byte[4]; startCode[0] = 0x00; startCode[1] = 0x00; startCode[2] = 0x00; startCode[3] = 0x01; //设置分片NALU原始H264的NALU Header int fuIndicatorTop3Bit = 0; int fuHeaderLast5Bit = 0; //int naluHeaderIndex = 0; int rtpCount = 0; int fregmentFileCount = 0; Console.WriteLine("服务端已开启,监听端口43999"); //把数据的来源放到第二个参数上 while (true) { //收到每一个数据报 length = udpServer.ReceiveFrom(data, ref clientAddress); byte[] sequenceNumberBytes = new byte[4]; sequenceNumberBytes[0] = data[3]; sequenceNumberBytes[1] = data[2]; int sequenceNumber = BitConverter.ToInt32(sequenceNumberBytes, 0); //Console.SetCursorPosition(0, 1); Console.WriteLine(sequenceNumber); if (length != 1) { #region 注释 //如果收到的数据帧总长度超过缓存区的长度 //if ((H264Buffer.Length - index) < 1400) //{ // Interlocked.Increment(ref fregmentFileCount); // FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test"+fregmentFileCount+".264", FileMode.Create); // using (sourceFileStream) // { // sourceFileStream.Write(H264Buffer, 0, index); // sourceFileStream.Close(); // } // index = 0; //} //else //{ #endregion //判断收到的为单一NALU模式 if ((data[12] & 0x1f) != 28) { //获取原始的H.264数据,包含了起始字节00 00 00 01 var singleNalu = DecodeRTPHelper.DecodeSingleNalu(data, length, ref index); //将原始的单个H.264数据存入视频缓存区中 Array.Copy(singleNalu, 0, H264Buffer, index, singleNalu.Length); index += singleNalu.Length; //如果收到的数据帧总长度超过缓存区的长度 if ((H264Buffer.Length - index) < 1420) { Interlocked.Increment(ref fregmentFileCount); FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + fregmentFileCount + ".264", FileMode.Create); using (sourceFileStream) { sourceFileStream.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是1:(H264Buffer.Length - index) < 1420.", fregmentFileCount); //sourceFileStream.Close(); } index = 0; } } //收到的为分片NALU模式 else { //如果为最后分片 if ((data[13] & 0xc0) == 64) { //将所有分片NALU的片段存入分片NALU缓存区中 DecodeRTPHelper.DecodeFregmentNalu(data, startCode, fregmentBuffer, length, ref fregmentNaluLength, ref fuIndicatorTop3Bit, ref fuHeaderLast5Bit); //首先判断单一NALU存完之后视频流中的索引位置加上分片NALU的长度是否超过视频流缓存区的长度 if (index + fregmentNaluLength > H264Buffer.Length) { Interlocked.Increment(ref fregmentFileCount); FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + fregmentFileCount + ".264", FileMode.Create); using (sourceFileStream) { sourceFileStream.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是2:index + fregmentNaluLength > H264Buffer.Length.", fregmentFileCount); //sourceFileStream.Close(); } index = 0; Array.Copy(fregmentBuffer, 0, H264Buffer, index, fregmentNaluLength); index += fregmentNaluLength; fregmentNaluLength = 0; } //如果除去之前已经存的H264帧和刚收到的分片NALU之后,视频流缓存区的长度小于1400; else if (H264Buffer.Length - index - fregmentNaluLength < 1420) { Array.Copy(fregmentBuffer, 0, H264Buffer, index, fregmentNaluLength); index += fregmentNaluLength; Interlocked.Increment(ref fregmentFileCount); FileStream sourceFileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + fregmentFileCount + ".264", FileMode.Create); using (sourceFileStream) { sourceFileStream.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是3:H264Buffer.Length - index - fregmentNaluLength < 1420.", fregmentFileCount); //sourceFileStream.Close(); } index = 0; fregmentNaluLength = 0; } //正常情况,分片NALU不会在视频流缓存区的最后一帧出现 else { Array.Copy(fregmentBuffer, 0, H264Buffer, index, fregmentNaluLength); index += fregmentNaluLength; fregmentNaluLength = 0; } //将分片NALU缓存区中的数据存入视频流缓存区中 } //如果不为最后分片 else { //将所有分片NALU的片段存入分片NALU缓存区中 DecodeRTPHelper.DecodeFregmentNalu(data, startCode, fregmentBuffer, length, ref fregmentNaluLength, ref fuIndicatorTop3Bit, ref fuHeaderLast5Bit); } } //} } else { Interlocked.Increment(ref fregmentFileCount); FileStream fs = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + fregmentFileCount + ".264", FileMode.Create); using (fs) { fs.Write(H264Buffer, 0, index); Console.WriteLine("写入文件Test{0}.264,此时结束原因是4:rap pacakge length ==1.", fregmentFileCount); //fs.Close(); } break; } } Console.WriteLine("视频流写入文件完成."); ////将读取到的视频缓存区中的数据存入文件中 //FileStream fileStream = new FileStream(@"D:\vsProject\VS2019Project\FileStorage\Test" + Convert.ToInt32(DateTime.Now.TimeOfDay.TotalMilliseconds) + ".264", // FileMode.Create); //using (fileStream) //{ // fileStream.Write(H264Buffer, 0, index); // Console.WriteLine("获取到的视频流写入文件完成"); // fileStream.Close(); //} }