在印地中创建视频流服务器



我正在尝试使用Indy Http服务器创建视频流服务器。我正在使用范围请求来发送大文件。一个数据块的长度为 10 Mb。如果请求客户端的视频文件小于10 Mb,则一切正常,可以播放视频。但是,如果文件大小超过 10 Mb,我会返回第一个数据块。然后客户要求我从文件末尾提供另一块数据,然后我的客户说它是无法识别的视频格式。有人可以告诉我我的代码中哪里有问题吗?

我的服务器代码

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; 

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

<!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; 

最新更新