6/18/2011

JPEG/HTTP PUSH how-to

JPEG/HTTP server side push is done by adopting "multipart/x-mixed-replace" MIME type. More detail information could be found on Wikipedia:

HTTP server push
HTTP server push (also known as HTTP streaming) is a mechanism for sending data from a web server to a web browser. HTTP server push can be achieved through several mechanisms.
Generally the web server does not terminate a connection after response data has been served to a client. The web server leaves the connection open such that if an event is received, it can immediately be sent to one or multiple clients. Otherwise the data would have to be queued until the client's next request is received. Most web servers offer this functionality via CGI (e.g. Non-Parsed Headers scripts on Apache).
Another mechanism is related to a special MIME type called multipart/x-mixed-replace, which was introduced by Netscape in 1995. Web browsers would interpret this as a document changing whenever the server felt like pushing a new version to the client.[1] It is still supported by FirefoxOpera and Safari today, but ignored by Internet Explorer.[2] It can be applied to HTML documents, but also for streaming images in webcam applications.
The WHATWG Web Applications 1.0 proposal[3] included a mechanism to push content to the client. On September 1, 2006, the Opera web browser implemented this new experimental technology in a feature called "Server-Sent Events."[4][5] It is now being standardized as part of HTML5.[6] Another related part of HTML5 is the WebSockets API, which allows a web server and client to communicate over a full-duplex TCP connection. 


Many examples of this kind of "server push" could be found by Google. :)
I summarize some pages here.

From 偶素Johnny啦~: Mjpeg On The Http: "第二種方式則是利用了類似Push的,讓Server傳送一個連續的資料給Client端,MIME的Content-Type中有一個特別的定義multipart/x-mixed-replace,當瀏覽器讀到這個類型時不會立即斷線,而會不斷的讀取資料,並且將新的Jpeg貼在原來的位置。但是每張Jpeg之間必須有間隔字串讓瀏覽器可以辨識,boundary就是定義分隔字串,只要是資料內不會出現的文字就可以了。

motionjpeg.html
<img src="myjpeg.cgi" />
myjpeg.cgi輸出的內容
Content-type: multipart/x-mixed-replace; boundary=xxx分隔字串xxx

xxx分隔字串xxx\r\n
Content-Type: image/jpeg\r\n
\r\n
Jpeg檔案內容
\r\n
xxx分隔字串xxx\r\n
Content-Type: image/jpeg\r\n
\r\n
Jpeg檔案內容
...
xxx分隔字串xxx\r\n


Another post from stackoverflow. It got more clear explain and sample code.

use strict;
# turn off io buffering
$|=1;
print "Content-type: multipart/x-mixed-replace;";
print "boundary=magicalboundarystring\n\n";
print "--magicalboundarystring\n";
#list the jpg images
my(@file_list) = glob "*.jpg";
my($file) = "";
foreach $file(@file_list ) {
     open FILE,">", $file or die "Cannot open file $file: $!";
     print "Content-type: image/jpeg\n\n";

    while ( <FILE> )
    { 
        print "$_";
    }

    close FILE;
     print "\n--magicalboundarystring\n";
     sleep 3;
    next;
}
Another code snippet from Server Push using ISAPI

unit uMain;
interface
uses
  Windows, Messages, SysUtils, Classes, HTTPApp;
type
  TWebModule1 = class(TWebModule)
    procedure WebModuleCreate(Sender: TObject);
    procedure WebModule1WebActionItem1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    procedure WebModule1waImageAction(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  private
    Boundary : string;
    MultipartContentType : string;
    { Private declarations }
  public
    { Public declarations }
  end;
var
  WebModule1: TWebModule1;
implementation
{$R *.DFM}
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
  Boundary := 'erERT45GSqJtasftnd31Afghdrte6';
  MultipartContentType := 'Content-type: multipart/x-mixed-replace';
end;
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Request.WriteString('HTTP/1.1 200'   #13#10);
  Request.WriteString(Format('%s;boundary="%s"'#13#10#13#10, [MultipartContentType, Boundary]));
  repeat
    Request.WriteString(#13#10   '--'   Boundary   #13#10);
    Request.WriteString('Content-type: text/html'   #13#10#13#10);
    Request.WriteString('<title>Server Push Example</title>'   #13#10);
    Request.WriteString('<body>'   #13#10);
    Request.WriteString('<h3>'   FormatDateTime('dddd, dd mmm yyyy hh:nn:ss', Now)   '</h3>'   #13#10);
    Request.WriteString('</body>'   #13#10);
    Request.WriteString('--'   Boundary   #13#10);
    Sleep(1000);
  until (False);
end;
procedure TWebModule1.WebModule1waImageAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  MemStrm : TMemoryStream;
  Images  : array[0..9] of string;
  ImageNo : Integer;
begin
  Images[0] := 'C:\Inetpub\wwwroot\images\thumb_1.jpg';
  Images[1] := 'C:\Inetpub\wwwroot\images\thumb_2.jpg';
  Images[2] := 'C:\Inetpub\wwwroot\images\thumb_3.jpg';
  Images[3] := 'C:\Inetpub\wwwroot\images\thumb_4.jpg';
  Images[4] := 'C:\Inetpub\wwwroot\images\thumb_5.jpg';
  Images[5] := 'C:\Inetpub\wwwroot\images\thumb_6.jpg';
  Images[6] := 'C:\Inetpub\wwwroot\images\thumb_7.jpg';
  Images[7] := 'C:\Inetpub\wwwroot\images\thumb_8.jpg';
  Images[8] := 'C:\Inetpub\wwwroot\images\thumb_9.jpg';
  Images[9] := 'C:\Inetpub\wwwroot\images\thumb_10.jpg';
  ImageNo := 0;
  MemStrm := TMemoryStream.Create;
  try
    Request.WriteString('HTTP/1.1 200'   #13#10);
    Request.WriteString(Format('%s;boundary="%s"'#13#10#13#10, [MultipartContentType, Boundary]));
    repeat
      MemStrm.LoadFromFile(Images[ImageNo]);
      MemStrm.Position := 0;
      Request.WriteString(#13#10   '--'   Boundary   #13#10);
      Request.WriteString('Content-type: image/jpeg'   #13#10#13#10);
      Response.SendStream(MemStrm);
      Request.WriteString('--'   Boundary   #13#10);
      if ImageNo = High(Images) then
        ImageNo := -1;
      Inc(ImageNo);
      Sleep(1000);
    until (False);
  finally
    MemStrm.Free; {The Web server normally takes care of this }
  end;
end;
end.

No comments:

Post a Comment