如何使用Indy HTTP Server实现以下功能。客户端访问http:// server_name:端口,服务器向其返回视频流,该视频流存储在http:// server_name_video:port/video1.mpgDelphi视频流Http服务器
回答
TIdHTTPServer
本身不支持流媒体。你必须手动实现它。在您的OnCommandGet
事件处理程序,根据需要指定您所需的值给AResponseInfo
参数,如ContentType
和TransferEncoding
,并留下ContentText
和ContentStream
性质未分配,然后调用AResponseInfo.WriteHeader()
只是响应头发送到客户端,然后进入一个循环书写您的视频媒体数据块(根据RFC 2616 Section 3.6.1 Chunked Transfer Coding 中描述的格式),直到客户端断开连接或达到媒体结束。例如:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
FS: TFileStream;
Buf: TIdBytes;
BufLen: Integer;
begin
if ARequestInfo.Document <> '/' then
begin
AResponseInfo.ResponseNo := 404;
Exit;
end;
FS := TFileStream.Create('video1.mpg', fmOpenRead or fmShareDenyWrite);
try
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentType := 'video/mpeg';
AResponseInfo.TransferEncoding := 'chunked';
AResponseInfo.WriteHeader;
SetLength(Buf, 1024);
repeat
BufLen := FS.Read(Buf[0], 1024);
if BufLen < 1 then Break;
AContext.Connection.IOHandler.WriteLn(IntToHex(BufLen, 1));
AContext.Connection.IOHandler.Write(Buf, BufLen);
AContext.Connection.IOHandler.WriteLn;
until False;
AContext.Connection.IOHandler.WriteLn('0');
AContext.Connection.IOHandler.WriteLn;
finally
FS.Free;
end;
end;
另一方面,如果您尝试从其他服务器流式传输媒体,则会变得更加复杂。您必须向其他服务器发送请求,接收响应,然后将数据转发给客户端。但TIdHTTP
不支持流式媒体,因此难以将其用于此目的。你可能最终不得不使用TIdTCPClient
,而不是直接和实现HTTP协议自己,例如需要部分:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Client: TIdTCPClient;
Headers: TIdHeaderList;
S, ResponseCode, ResponseText: string;
Size: Int64;
Strm: TIdTCPStream;
begin
if ARequestInfo.Document <> '/' then
begin
AResponseInfo.ResponseNo := 404;
Exit;
end;
Client := TIdTCPClient.Create;
try
Client.Host := 'server_name_video';
Client.Port := port;
Client.Connect;
try
Client.IOHandler.WriteLn('GET /video1.mpg HTTP/1.0');
Client.IOHandler.WriteLn('Host: server_name_video');
Client.IOHandler.WriteLn;
ResponseText := Client.IOHandler.ReadLn;
Fetch(ResponseText);
ResponseText := TrimLeft(ResponseText);
ResponseCode := Fetch(ResponseText, ' ', False);
ResponseCode := Fetch(ResponseCode, '.', False);
if ResponseCode <> '200' then
begin
AResponseInfo.ResponseNo := StrToInt(ResponseCode);
AResponseInfo.ResponseText := ResponseText;
Exit;
end;
Headers := TIdHeaderList.Create(QuoteHTTP);
try
Headers.FoldLength := MaxInt;
repeat
s := Client.IOHandler.ReadLn;
if s = '' then Break;
Headers.Add(s);
until False;
Strm := TIdTCPStream.Create(AContext.Connection);
try
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentType := Headers.Values['Content-Type'];
if Pos('chunked', Headers.Values['Transfer-Encoding']) <> 0 then
begin
AResponse.TransferEncoding := 'chunked';
AResponseInfo.WriteHeader;
repeat
s := Client.IOHandler.ReadLn;
AContext.Connection.IOHandler.WriteLn(s);
Size := StrToInt64('$'+Fetch(s, ';'));
if Size = 0 then Break;
Client.IOHandler.ReadStream(Strm, Size, False);
s := Client.IOHandler.ReadLn;
AContext.Connection.IOHandler.WriteLn(s);
until false;
repeat
s := Client.IOHandler.ReadLn;
AContext.Connection.IOHandler.WriteLn(s);
until s = '';
end
else if Headers.IndexOfName('Content-Length') <> -1 then
begin
Size := StrToInt64(Headers.Values['Content-Length']);
AResponseInfo.ContentLength := Size;
AResponseInfo.WriteHeader;
if Size > 0 then
Client.IOHandler.ReadStream(Strm, Size, False);
end else
begin
AResponseInfo.CloseConnection := true;
AResponseInfo.WriteHeader;
try
Client.IOHandler.ReadStream(Strm, -1, True);
except
on E: EIdSocketError do begin
if not (E.LastError in [10053, 10054, 10058]) then
raise;
end;
end;
end;
finally
Strm.Free;
end;
finally
Headers.Free;
end;
finally
Client.Disconnect;
end;
finally
Client.Free;
end;
end;
当然,如果需要的话,你必须还实现了的东西,如HTTP认证的请求字节范围等
更新:或者,而不是使用TIdTCPClient
直接,你可以使用毕竟TIdHTTP
,只要给它,因为它被写入到写回原来的客户端的输出TStream
。你可以使用TIdEventStream
用于该目的,或者自己写TStream
类,如:
type
TMyStream = class(TIdBaseStream)
protected
FHTTP: TIdHTTP;
FClient: TIdIOHandler;
FResponse: TIdHTTPResponseInfo;
function IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; override;
function IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; override;
function IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override;
procedure IdSetSize(ASize: Int64); override;
public
constructor Create(AHTTP: TIdHTTP; AClient: TIdIOHandler; AResponse: TIdHTTPResponseInfo);
destructor Destroy; override;
end;
constructor TMyStream.Create(AHTTP: TIdHTTP; AClient: TIdIOHandler; AResponse: TIdHTTPResponseInfo);
begin
inherited Create;
FHTTP := AHTTP;
FClient := AClient;
FResponse := AResponse;
end;
destructor TMyStream.Destroy;
begin
if FResponse.HeaderHasBeenWritten then
begin
FClient.WriteLn('0');
FClient.WriteLn('');
end;
end;
function TMyStream.IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint;
begin
Result := 0;
end;
function TMyStream.IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint;
begin
if not FResponse.HeaderHasBeenWritten then
begin
FResponse.ResponseNo := 200;
FResponseInfo.ContentType := FHTTP.Response.ContentType;
FResponse.TransferEncoding := 'chunked';
FResponse.WriteHeader;
end;
FClient.WriteLn(IntToHex(IndyLength(ABuffer, ACount, AOffset)));
FClient.Write(ABuffer, ACount, AOffset);
FClient.WriteLn;
end;
function TMyStream.IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64;
begin
Result := 0;
end;
procedure TMyStream.IdSetSize(ASize: Int64);
begin
end;
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
HTTP: TIdHTTP;
Strm: TMyStream;
begin
if ARequestInfo.Document <> '/' then
begin
AResponseInfo.ResponseNo := 404;
Exit;
end;
HTTP := TIdHTTP.Create;
try
HTTP.HTTPOptions := HTTP.HTTPOptions + [hoNoProtocolErrorException];
Strm := TMyStream.Create(HTTP, AContext.Connection.IOHandler, AResponseInfo);
try
HTTP.Get('http://server_name_video:'+IntToStr(port)+'/video1.mpg', Strm);
finally
Strm.Free;
end;
if not AResponseInfo.HeaderHasBeenWritten then
begin
AResponseInfo.ResponseNo := HTTP.ResponseCode;
AResponseInfo.ResponseText := HTTP.ResponseText;
end;
finally
HTTP.Free;
end;
end;
另外,如果其他服务器支持chunked
响应,您可以:
使用新
TIdHTTP.OnChunkReceived
事件将每个接收到的块写入客户端,类似于以上,而不使用自定义TStream
(您仍然必须提供TStream
至TIdHTTP.Get()
。您可以使用TIdEventStream
,并且不要将任何事件处理程序分配给它,因此数据被丢弃。这可能会在未来发生变化)。使
TIdHTTP
的新hoNoReadChunked
标志,以及从直接TIdHTTP.IOHandler
到客户端然后只是隧道的原始数据,例如通过使用一个与TIdTCPStream
AContext.Connection.IOHandler.WriteStream()
。
我不禁赞赏你的承诺,雷米! –
Remy,当我尝试下载文件Headers.Values ['Content-Type'] ='application/octet-stream'。并在线Client.IOHandler.ReadStream(Strm,-1,True);引发异常10053.你能帮我解决这个错误吗? –
第二个示例充当代理来转发来自其他服务器的数据。如果Content-Type是application/octet-stream,那么这就是其他服务器实际报告的内容。至于10053错误,这是正常的行为。 'AReadUntilDisconnect'参数设置为True,并且10053是'WSAECONNABORTED',它是断开连接期间几个可能的错误代码之一。 'ReadStream()'当前不处理10053作为断开连接(这是一个TODO项目),所以你将不得不手动处理它。我更新了我的示例以表明这一点。 –
- 1. HTTP流媒体服务器的实时视频流播放器
- 2. 流视频到服务器
- 3. Delphi indy streaming Http服务器
- 4. 支持Http的视频服务器
- 5. Swift上传视频到Http服务器?
- 6. DIY:视频流媒体服务器
- 7. 网络服务器到Android视频流
- 8. 从ftp服务器流视频到iphone?
- 9. 通过服务器的WebRTC视频流
- 10. 视频流媒体服务器软件
- 11. 从服务器流视频。 {iPhone SDK}
- 12. Android - 视频流到服务器
- 13. Python视频流到C++服务器
- 14. 流式视频,云服务器和videojs
- 15. 从iPhone中的服务器流视频?
- 16. 流视频到gstreamer rtsp服务器
- 17. VLC HTTP视频流
- 18. C HTTP流媒体服务器流音频分贝水平
- 19. netty音频流服务器
- 20. 无法编译HTTP实时视频流分段器和分发服务器
- 21. HTTP代理服务器中的Delphi DataSap
- 22. 发送视频和音频流到服务器
- 23. 流媒体视频达尔文流媒体服务器问题
- 24. HTTP视频流逐帧
- 25. 支持HTTP视频流的Xamarin视频播放器
- 26. 通过http服务HTML5视频
- 27. 视频服务器上
- 28. 如何设置实时音频流http服务器?
- 29. 从外部服务流视频
- 30. Java,服务HLS实时视频流
你为什么不使用Tembeddedwb,而不是印来实现这一目的 – DelphiStudent
TEmbeddedWB是一个客户端浏览器。 OP想要创建一个服务器。 –