本文介绍了在indy中创建视频流服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create video streaming server using Indy Http server. I am using ranged requests to send large files. One chunk of data is 10 Mb long. If video file which requests client is smaller than 10 Mb then it is all ok and vido is played. But if file size is longer than 10 Mb I return first chunk of data. Then client asks me for another chunk of data from the end of file and then my client says that it is unrecognizable video format. Can someone tell me where is problem in my code.

我的服务器代码

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):

And here is my client code (I use 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>


推荐答案

您的服务器代码中存在多个错误。

There are several errors in your server code.

您没有确认实际上正在请求范围,或者即使存在范围,也不会考虑结束范围。

You are not validating that a range is actually being requested, or even respecting an end range if one is present.

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

You are setting the AResponseInfo.ContentLength property to the full size of the file, even when you are not sending the full file at one time. That value belongs in the AResponseInfo.ContentRangeInstanceLength property instead when sending a ranged response. You must set ContentLength to the size of the data actually being sent in the response, which in this case is your current range chunk. It is best not to set the ContentLength at all, you can let the server calculate it for you based on the assigned ContentStream.

您无条件地将 AResponseInfo.ResponseNo 属性设置为206,即使未请求范围也是如此全部,或者如果不能满足要求的范围。 TIdHTTPRangeStream 在其构造函数中执行验证,并相应地设置其 ResponseCode 属性。这是您应该分配给 ResponseNo 的值。

You are setting the AResponseInfo.ResponseNo property to 206 unconditionally, even if a range is not requested at all, or if the requested range cannot be satisfied. TIdHTTPRangeStream performs validations in its constructor and sets its ResponseCode property accordingly. That is the value you should be assigning to 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;

这篇关于在indy中创建视频流服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 19:17