本地主机TCP通信数据损坏:H264视频消息组装故障排查
使用IcsOverbyte TWSocketServer传输H264视频的消息组装问题
我尝试用IcsOverbyte的TWSocketServer向TWSocket客户端传输原始H264视频数据包,但在消息组装环节遇到问题。
已确认.h264数据本身有效:序列化消息后反序列化,将视频数据包部分保存为文件可通过VLC正常播放;同时验证了序列化/反序列化逻辑正确——服务端与客户端使用相同的类型定义及序列化/反序列化代码。
消息结构定义
type TMessageHeader = packed record MessageID: UInt32; TotalSize: UInt32; {含义:HeaderSize + Payload} end; TMediaDataMessage = record Header: TMessageHeader; Channel: UInt32; Stream: UInt32; MediaType: Byte; {0 - 视频, 1 - 音频} Data: TArray<Byte>; function FromBytes(pBytes: TArray<Byte>): Boolean; function ToBytes: TArray<Byte>; function GetSize: UInt32; end;
客户端消息组装逻辑
procedure TfrmMain.HandleDataAvailable(pSender: TObject; ErrCode: Word); var vRcvdCount: Integer; vBytes: TBytes; vMessageTotalSize: UInt32; vOffset: UInt32; begin {$POINTERMATH ON} if ErrCode <> 0 then Exit; if pSender = nil then Exit; SetLength(vBytes, 65536); vRcvdCount := fTCPClient.Receive(@vBytes[0], 65536); vOffset := 0; try if fRemainingDataSize > 0 then begin if vRcvdCount >= fRemainingDataSize then begin fCurrentBuf.Write(vBytes[vOffset], fRemainingDataSize); Inc(vOffset, fRemainingDataSize); Dec(vRcvdCount, fRemainingDataSize); fRemainingDataSize := 0; SetHandleMessage(fIdx.ToString + 'Sync'); end else begin fCurrentBuf.Write(vBytes[vOffset], vRcvdCount); Dec(fRemainingDataSize, vRcvdCount); end; end; except reLog.Lines.Add('Error sync'); end; try while (vRcvdCount > 0) do begin vMessageTotalSize := PUInt32(@vBytes[vOffset + 4])^; if vRcvdCount >= vMessageTotalSize then begin fCurrentBuf.Write(vBytes[vOffset], vMessageTotalSize); Inc(vOffset, vMessageTotalSize); Dec(vRcvdCount, vMessageTotalSize); SetHandleMessage('Loop'); end else begin fCurrentBuf.Write(vBytes[vOffset], vRcvdCount); fRemainingDataSize := vMessageTotalSize - vRcvdCount; vRcvdCount := 0; end; end; except reLog.Lines.Add('Loop'); end; //fTcpClient.Flush; end;
注:未触发任何异常。
SetHandleMessage实现
procedure TfrmMain.SetHandleMessage(pSort: string); var vMsgBytes: TArray<Byte>; vMessageID: UInt32; begin fCurrentBuf.Position := 0; SetLength(vMsgBytes, fCurrentBuf.Size); fCurrentBuf.Read(vMsgBytes[0], fCurrentBuf.Size); fCurrentBuf.Clear; vMessageID := PUInt32(@vMsgBytes[0])^; HandleMessage(vMessageID, vMsgBytes, pSort); end;
序列化相关函数
FromBytes方法
function TMediaDataMessage.FromBytes(pBytes: TArray<Byte>): Boolean; var vLen: Integer; vOffset: Integer; vArrSize: UInt32; begin vOffset := 0; Move(pBytes[vOffset], PByte(@Header.MessageId)[0], Sizeof(Header.MessageID)); Inc(vOffset, Sizeof(Header.MessageID)); Move(pBytes[vOffset], PByte(@Header.TotalSize)[0], Sizeof(Header.TotalSize)); Inc(vOffset, Sizeof(Header.TotalSize)); Move(pBytes[vOffset], PByte(@Channel)[0], Sizeof(Channel)); Inc(vOffset, Sizeof(Channel)); Move(pBytes[vOffset], PByte(@Stream)[0], Sizeof(Stream)); Inc(vOffset, Sizeof(Stream)); Move(pBytes[vOffset], PByte(@MediaType)[0], Sizeof(MediaType)); Inc(vOffset, Sizeof(MediaType)); Move(pBytes[vOffset], PByte(@vArrSize)[0], Sizeof(vArrSize)); Inc(vOffset, Sizeof(vArrSize)); if vArrSize <> 0 then begin SetLength(Data, vArrSize); Move(pBytes[vOffset], Data[0], vArrSize); end; Exit(True); end;
GetSize方法
function TMediaDataMessage.GetSize: UInt32; begin Result := (Sizeof(uInt32) *2) + Sizeof(Channel) + Sizeof(Stream) + Sizeof(MediaType) + Sizeof(UInt32) + Length(Data); end;
ToBytes方法
function TMediaDataMessage.ToBytes: TArray<Byte>; var vSize: UInt32; vOffset: UInt32; vLen: UInt32; begin vSize := Self.GetSize; SetLength(Result, vSize); vOffset := 0; Move(Self.Header.MessageID, Result[vOffset], Sizeof(Self.Header.MessageID)); Inc(vOffset, Sizeof(Self.Header.MessageID)); Move(Self.Header.TotalSize, Result[vOffset], Sizeof(Self.Header.TotalSize)); Inc(vOffset, Sizeof(Self.Header.TotalSize)); Move(Self.Channel, Result[vOffset], Sizeof(Self.Channel)); Inc(vOffset, Sizeof(Self.Channel)); Move(Self.Stream, Result[vOffset], Sizeof(Self.Stream)); Inc(vOffset, Sizeof(Self.Stream)); Move(Self.MediaType, Result[vOffset], Sizeof(Self.MediaType)); Inc(vOffset, Sizeof(Self.MediaType)); vLen := Length(Self.Data); Move(vLen, Result[vOffset], Sizeof(UInt32)); Inc(vOffset, Sizeof(UInt32)); if vLen > 0 then Move(Self.Data[0], Result[vOffset], vLen); end;
我反复排查仍无法定位问题,直接传输原始.h264数据并保存为文件可行,因此问题可能出在消息组装逻辑中。
内容的提问来源于stack exchange,提问作者Claaaudio
相关产品推荐
相关产品推荐

