Search code examples
delphihttpcameraindy

Delphi indy streaming Http Server


A camera captures an image.
I want this picture to be available via HTTP.
Can I somehow use the TIdHTTPServer.OnCommandGet event to display it?
I just want to display the image live in a TImage.
If so, how?


Solution

  • If you just need to display the latest image when a client asks for it, you can do something like this:

    type
      TGetImageStream = class(TIdSync)
      protected
        FStream: TStream;
        procedure DoSynchronize; override;
      public
        class procedure GetImage(Stream: TStream);
      end;
    
    procedure TGetImageStream.DoSynchronize;
    begin
      Form1.Image1.Bitmap.SaveToStream(FStream);
    end;
    
    class procedure TGetImageStream.GetImage(Stream: TStream);
    begin
      with Create do
      try
        FStream := Stream;
        Synchronize;
      finally
        Free;
      end;
    end;
    

    procedure TForm1.HTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      Strm: TMemoryStream;
    begin
      Strm := TMemoryStream.Create;
      try
        TGetImageStream.GetImage(Strm);
        Strm.Position := 0;
      except
        Strm.Free;
        raise;
      end;
      AResponseInfo.ResponseNo := 200;
      AResponseInfo.ContentType := 'image/bmp';
      AResponseInfo.ContentStream := Strm;
    end;
    

    But if you need to do live streaming of the camera images in real time, that gets a bit trickier. You can do it a few different ways. For instance, using a client pull:

    procedure TForm1.HTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      Strm: TMemoryStream;
    begin
      if ARequestInfo.Document = '' then
      begin
        AResponseInfo.Redirect('/');
      end
      else if ARequestInfo.Document = '/' then
      begin
        AResponseInfo.ResponseNo := 200;
        AResponseInfo.ContentType := 'text/html';
        AResponseIno.ContentText := '<html>'+EOL+
                                    '<head>'+EOL+
                                    '<title>Camera Image</title>'+EOL+
                                    '<meta http-equiv="Refresh" content=5>'+EOL+
                                    '</head>'+EOL+
                                    '<body>'+EOL+
                                    '<img src="/image">'+EOL+
                                    '</body>'+EOL+
                                    '</html>'+EOL;
      end
      else if ARequestInfo.Document = '/image' then
      begin
        Strm := TMemoryStream.Create;
        try
          TGetImageStream.GetImage(Strm);
          Strm.Position := 0;
        except
          Strm.Free;
          raise;
        end;
        AResponseInfo.ResponseNo := 200;
        AResponseInfo.ContentType := 'image/bmp';
        AResponseInfo.ContentStream := Strm;
      end else begin
        AResponseInfo.ResponseNo := 404;
      end;
    end;
    

    Using a server push instead:

    procedure TForm1.HTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      Strm: TMemoryStream;
    begin
      Strm := TMemoryStream.Create;
      try
        AResponseInfo.ResponseNo := 200;
        AResponseInfo.ContentType := 'multipart/x-mixed-replace; boundary=imgboundary';
        AResponseInfo.CloseConnection := False;
        AResponseInfo.WriteHeader;
    
        AContext.Connection.IOHandler.WriteLn('--imgboundary');
        repeat
          Strm.Clear;
          TGetImageStream.GetImage(Strm);
    
          AContext.Connection.IOHandler.WriteLn('Content-type: image/bmp');
          AContext.Connection.IOHandler.WriteLn;
          AContext.Connection.IOHandler.Write(Strm);
          AContext.Connection.IOHandler.WriteLn;
          AContext.Connection.IOHandler.WriteLn('--imgboundary');
    
          Sleep(5000);
       until False;
      finally
        Strm.Free;
      end;
    end;