在 Indy 中创建视频流服务器

Create video streaming server in indy

我正在尝试使用 Indy Http 服务器创建视频流服务器。我正在使用远程请求发送大文件。一个数据块有 10 Mb 长。如果请求客户端的视频文件小于 10 Mb,则一切正常并播放 vido。但是,如果文件大小超过 10 Mb,我会 return 第一个数据块。然后客户要求我从文件末尾获取另一块数据,然后我的客户说这是无法识别的视频格式。谁能告诉我我的代码哪里有问题。

我的服务器代码

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := 'Running';
  FServer := TIdHTTPServer.Create(Self);
  FServer.DefaultPort := 7070;
  FServer.OnCommandGet:=@External_Get;
  FServer.Active := True;
end;

procedure TForm1.External_Get(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  FS: TFileStream;
  Ranges: TIdEntityRanges;
  Range: TIdEntityRange;
begin
  Ranges := ARequestInfo.Ranges;
  Range := Ranges.Ranges[0];

  FS := TFileStream.Create('/home/user/Desktop/large_file.mp4', fmOpenRead or fmShareDenyWrite);
  AResponseInfo.ContentType := 'video/mp4';
  AResponseInfo.AcceptRanges := 'bytes';
  AResponseInfo.ContentStream := TIdHTTPRangeStream.Create(
    FS,
    Range.StartPos,
    Range.StartPos + 1024*1024*10,
    True
  );
  AResponseInfo.FreeContentStream := True;

  AResponseInfo.ContentRangeStart := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeStart;
  AResponseInfo.ContentRangeEnd := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeEnd;
  AResponseInfo.ContentRangeInstanceLength := AResponseInfo.ContentRangeEnd - Range.StartPos + 1;
  AResponseInfo.ContentLength := FS.Size;  
  AResponseInfo.ResponseNo := 206;
end; 

这是我的客户端代码(我使用 firefox):

<!DOCTYPE html> 
<html> 
<head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
</head>
<body> 

<video width="400" controls>
  <source src="http://localhost:7070/test38.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

</body> 
</html>

您的服务器代码中有几个错误。

您没有验证实际请求的范围,甚至没有考虑结束范围(如果存在)。

您正在将 AResponseInfo.ContentLength 属性 设置为文件的完整大小,即使您没有一次发送完整文件也是如此。当发送范围响应时,该值属于 AResponseInfo.ContentRangeInstanceLength 属性。您必须将 ContentLength 设置为响应中实际发送的数据的大小,在本例中为您当前的范围块。 ContentLength最好不要设置,可以让服务器根据分配的ContentStream.

给你计算

您正在无条件地将 AResponseInfo.ResponseNo 属性 设置为 206,即使根本没有请求范围,或者请求的范围无法满足。 TIdHTTPRangeStream 在其构造函数中执行验证并相应地设置其 ResponseCode 属性。这是您应该分配给 ResponseNo.

的值

尝试更像这样的东西:

procedure TForm1.External_Get(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  FS: TFileStream;
  Range: TIdEntityRange;
  StartPos, EndPos: Int64;
begin
  if not FileExists('/home/user/Desktop/large_file.mp4') then
  begin
    AResponseInfo.ResponseNo := 404;
    Exit;
  end;

  try
    FS := TFileStream.Create('/home/user/Desktop/large_file.mp4', fmOpenRead or fmShareDenyWrite);
  except
    AResponseInfo.ResponseNo := 500;
    Exit;
  end;

  AResponseInfo.ContentType := 'video/mp4';
  AResponseInfo.AcceptRanges := 'bytes';

  if ARequestInfo.Ranges.Count = 1 then
  begin
    Range := ARequestInfo.Ranges.Ranges[0];

    StartPos := Range.StartPos;
    EndPos := Range.EndPos;

    if StartPos >= 0 then
    begin
      // requesting prefix range from BOF
      if EndPos >= 0 then
        EndPos := IndyMin(EndPos, StartPos + (1024*1024*10) - 1)
      else
        EndPos := StartPos + (1024*1024*10) - 1;
    end else
    begin
      // requesting suffix range from EOF
      if EndPos >= 0 then
        EndPos := IndyMin(EndPos, 1024*1024*10)
      else
        EndPos := (1024*1024*10);
    end;

    AResponseInfo.ContentStream := TIdHTTPRangeStream.Create(FS, StartPos, EndPos);
    AResponseInfo.ResponseNo := TIdHTTPRangeStream(AResponseInfo.ContentStream).ResponseCode;

    if AResponseInfo.ResponseNo = 206 then
    begin
      AResponseInfo.ContentRangeStart := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeStart;
      AResponseInfo.ContentRangeEnd := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeEnd;
      AResponseInfo.ContentRangeInstanceLength := FS.Size;
    end;
  end else
  begin
    AResponseInfo.ContentStream := FS;
    AResponseInfo.ResponseNo := 200;
  end;
end;