You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地主机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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:25:26