Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi selbstgebastelter webcam stream (https://www.delphipraxis.net/147307-selbstgebastelter-webcam-stream.html)

gsh 5. Feb 2010 14:20

Re: selbstgebastelter webcam stream
 
Ich möchte noch etwas kurz einwerfen:
Es ist nicht intelligent Webcam Livebilder über TCP (wegen Handshake, FlowControl, ..) zu übertragen. Besser geeignet ist in diesem Fall UDP.

Astat 5. Feb 2010 14:24

Re: selbstgebastelter webcam stream
 
Zitat:

Zitat von jokerfacehro
..aber schreibt ruhig mehr

Naja!

1. UDP verwenden, ist bedeutend schneller. EDIT: zu spät! :cry:
2. Nur die Pixel übertragen, die sich geändert haben (Different Screening)
3. Andere Kompression verwenden, wenn Bitmap Kompression notwendig,
das wesentlich performantere paszlib anstatt ZLib*.obj Files verwenden.

lg. Astat

jokerfacehro 5. Feb 2010 14:24

Re: selbstgebastelter webcam stream
 
da
Zitat:

Zitat von gsh
Ich möchte noch etwas kurz einwerfen:
Es ist nicht intelligent Webcam Livebilder über TCP (wegen Handshake, FlowControl, ..) zu übertragen. Besser geeignet ist in diesem Fall UDP.

geb ich dir recht :D

jokerfacehro 5. Feb 2010 14:41

Re: selbstgebastelter webcam stream
 
ich hab mir grad ma ne to do list geschrieben :D

grizzly 5. Feb 2010 14:54

Re: selbstgebastelter webcam stream
 
Nu, vielleicht aktiviert Du ja alle Methoden!

Zu den Web-Cam Kompressionen:
In dem Dialog, in dem Du die Größe deiner Videos auswählen kannst, bietet Dir der Treiber in der Regel noch unterschiedliche Kompressionen: RGB 24 beispielsweise ist der unkomprimierte Datenstrom, 3 Bytes pro Pixel, das wären dann bei 640x480 Pixel großen Bilder schon 921600 Bytes. Andere wären YUY2 (2 Bytes pro Pixel, 33% Ersparnis), MJPEG (Jedes Bild als JPG), I420 (eineinhalb Bytes pro Pixel, 50% Ersparnis).
Die so komprimierten Bilder kann man mit DirectX zu normalen Bitmaps auspacken. Das war mir aber etwas zu kompliziert, deshalb habe ich für einige Kompressionen eigene Entpacker in den Code eingebaut (sind natürlich nicht so performant wie die DirectX Dinger). Wenn man ein Bild mit GetBitmap() abholt, entpacke ich den Datenstrom (UnpackFrame). Mein Code sollte YUY2 (=YUYV, YUNV), MJPG, I420 (=YV12, IYUV) unterstützen.

Falls also Deine Webcam einige der Kompressionsformate beherrscht, könntest Du neben GetBitmap() ja noch eine andere Funktion basteln ala GetFrameData(), welche Dir einen Zeiger auf die Originaldaten (samt deren Größe und den FourCC code) zurückliefert. Maximal simpel so:
Delphi-Quellcode:
procedure TVideoImage.GetFrameData(VAR FrameData: pointer; VAR FrameDataSize: integer; VAR FourCC : cardinal);
begin
  FrameData := fImagePtr[fImagePtrIndex];
  FrameDataSize := fImagePtrSize[fImagePtrIndex];
  FourCC := fFourCC;
end;
Die kopierst du dann gleich weg und schickst sie an den anderen Rechner (oder zippst sie vorher, solange es kein MJPEG war, weil da zippt sich nicht mehr viel).
Auf dem Rechner, der die Daten empfängt, baust Du dann eine angepaßte UnpackFrame(..) Routine (siehe VFrames.pas) ein, um an Dein Bitmap zu kommen. Dieser angepaßten Routine müssen halt der FourCC code (der gibt an, welche Kompression verwendet wurde) und das Zielbitmap mit übergeben werden (momentan verwendet die ja irgendwelche Object-internen Variablen)...

Gruß
Michael

jokerfacehro 5. Feb 2010 15:08

Re: selbstgebastelter webcam stream
 
jop

ich hab mir das ma angeschaut.

ich hol mir einfach die daten vom pointer in den stream
und verschicke die.

nebenbei muss ich dem server denn sagen welche kompression etc...
und die dekompressionsfunktionen sind ja alle inner VSamples.pas vorhanden.

die kann ich ja dem server einfach hinzufügen.

die manuelle kompression ist trotzdem interessant, da ich ne funktion habe zur darstellung des desktops

jokerfacehro 11. Feb 2010 12:49

Re: selbstgebastelter webcam stream
 
sooo da bin ich mal wieder

folgendes hat sich getan:

ich hol mir jetz das komprimierte und bild und verschicke es per UDP vom Client an den Server.

Der server hat die nötige prozedur zur dekomprimierung der daten.



hier mal quelltext:

Die Client Methoden:

Holen der Daten vom VideoImage

Delphi-Quellcode:
procedure TVideoImage.GetFrameData(VAR FrameData: pointer; VAR FrameDataSize: integer; VAR FourCC : cardinal; VAR Width,Height:integer);
begin
  FrameData := fImagePtr[fImagePtrIndex];
  FrameDataSize := fImagePtrSize[fImagePtrIndex];
  FourCC := fFourCC; //codec
  Width:=fWidth;
  Height:=fHeight;
end;

Verschicken der Daten

Delphi-Quellcode:
procedure TForm1.sendStream;
var MStream:TMemoryStream;
    Data:string;
    FrPointer:Pointer;
    FrSize,fWidth,fHeight,bSize:integer;
    Codec:Cardinal;
begin

  fVideoImage.GetFrameData(FrPointer,FrSize,Codec,fWidth,fHeight);

  FrSize:=FrSize+3;
  setLength(Data,8192);  //#0#13#0



  try
    Move(FrPointer^,Data[1],8192);
  //  Data:=IntToStr(8192);
    WebcamStream.Client.Send(Data);
  except
    end;

while FrSize>8192 do begin

  try
    Data:='';
    setLength(Data,8192);
    Move(FrPointer^,Data[1],8192);
    WebcamStream.Client.Send(Data);
  except
    end;

  FrSize:=FrSize-8192;
 end;
 
Data:='';
setLength(Data,FrSize);
try
    Move(FrPointer^,Data[1],FrSize);
    Data:=Data+#0#13#0;
    WebcamStream.Client.Send(Data);
  except
    end;
end;



Server methoden:

Empfangen der Daten

Delphi-Quellcode:
procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
  AData: TIdBytes; ABinding: TIdSocketHandle);
var  i:integer;
      buffer,data:string;
      MStream:TMemoryStream;
      bmp:TBitmap;
begin

//MStream:=TMemoryStream.Create;

buffer:=AThread.Server.ReceiveString(-1);
data:=buffer;

while(Pos(#0#13#0,buffer)<1) do begin
buffer:=AThread.Server.ReceiveString(-1);
data:=data+buffer;
end;

//buffer

//MStream.Read(buffer,Length(buffer));
Decoder.DecodeFrame(data);

//Form2.Image1.Picture.Bitmap.LoadFromStream(MStream);

//if Pos(#0#13#0,buffer)>0 then Showmessage('okkk');

//MStream.Free;
end;


dekomprimieren der daten

Delphi-Quellcode:
procedure TDecoder.DecodeFrame(Bitmap:TBitmap;Data:String);
var  fp:^string;
begin
fp:=@Data;
YUY2_to_RGB(fp,Data);

end;

Delphi-Quellcode:
procedure TDecoder.YUY2_to_RGB(pData: pointer;data:string);
// [url]http://msdn.microsoft.com/en-us/library/ms893078.aspx[/url]
// [url]http://en.wikipedia.org/wiki/YCbCr[/url]
type
  TFour = ARRAY[0..3] OF byte;
VAR
  L, X, Y : integer;
  ps     : pbyte;
  pf     : ^TFour;
  FrPointer:Pointer;
      FrSize,fWidth,fHeight,i:integer;
      Codec:Cardinal;
      bmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.PixelFormat:=pf24Bit;

pf:=pData;

//fVideoImage.GetFrameData(FrPointer,FrsIze,Codec,fWidth,fHeight);
fWidth:=640;
fHeight:=480;
bmp.Width:=fWidth;
bmp.Height:=fHeight;

Showmessage(IntToStr(Length(Data)));

  PrepareTables;
  FOR Y := 0 TO bmp.Height-1 DO
    BEGIN
      ps := bmp.ScanLine[Y];
      FOR X := 0 TO (bmp.Width div 2)-1 DO
        begin
          L := ValueY_298[pf^[0]]; // HIER KOMMT DER FEHLER
          ps^ := ValueClip[(L + ValueU_516[pf^[1]]                    ) div 256];
          Inc(ps);
          ps^ := ValueClip[(L + ValueU_100[pf^[1]] + ValueV_208[pf^[3]]) div 256];
          Inc(ps);
          ps^ := ValueClip[(L                     + ValueV_409[pf^[3]]) div 256];
          Inc(ps);
          L := ValueY_298[pf^[2]];
          ps^ := ValueClip[(L + ValueU_516[pf^[1]]                    ) div 256];
          Inc(ps);
          ps^ := ValueClip[(L + ValueU_100[pf^[1]] + ValueV_208[pf^[3]]) div 256];
          Inc(ps);
          ps^ := ValueClip[(L                     + ValueV_409[pf^[3]]) div 256];
          Inc(ps);


          Inc(pf);
        end;
    END;
    Form2.Image1.Picture.Assign(bmp);
    bmp.Free;
end;


so ich hänge jetzt an der letzten funktion.
Diese berechnet das bild zurück

Die Value Arrays enthalten umrechnungszahlen für die einzelnen pixel.

der pointer pf soll auf die Data zeigen.



Das Problem ist jetzt, dass ich bei der letzten funktion ne access violation kriege, siehe Kommentar im quelltext.
Die kommt allerdings erst nach etwa dem 20. durchlauf der For schleife mit Y.

Raised exception class C000005


hat jemand ne Idee ?

grizzly 11. Feb 2010 14:12

Re: selbstgebastelter webcam stream
 
Hallo!

Beim Zugriff auf ValueY_298 kanns ja kaum sein, denn das ist ein array von 0 bis 255. Ergo kein Rangecheck. Sieht also eher so aus, als ob es das byte "pf^[0]" nicht gibt. (access violation). Dazu hätt' ich ein paar Kandidaten:

1. Das Übergeben der Daten als string. Schlägt da nicht irgendwo vielleicht doch eine raffinierte Routine durch, die eine 0 in den Bilddaten findet und dann dort einfach den string abschneidet?

2. Die Datenübertragung läuft bis zu einem #0#13#0. Es ist natürlich schon möglich, daß dieses #0#13#0 schon in den Bilddaten selbst vorkommt! Dann würdest Du mit einem unvollständigen Bild in die Auspack-Routine gehen.

3. Hat das Übergebene Bild überhaupt 640*480 Pixel? Nicht, daß das viel kleiner ist! fWidth und fHeight werden ja explizit auf diese festen Werte gesetzt in YUY2_to_RGB.

Klappt die Übergabe für RGB24 kodierte Bilder? Da hätten alle Frames wenigstens dieselbe Größe: Width*Height*3 und Du könntest in der Routine prüfen, ob der übergebene Speicher tatsächlich so groß ist. Obwohl, YUY2 Bilder haben auch immer eine feste Größe, da könnte man also auch leicht prüfen, ob die Höhe und Breite zur Menge der übergebenen Bytes paßt.


Gruß
Michael

jokerfacehro 11. Feb 2010 14:45

Re: selbstgebastelter webcam stream
 
also
ich hab natürlich bevor ich die dekomprimierung in den server ausgelagert habe, die dekomprimierung direkt beim client getestet und hat wunderbar gefunzt.

das mit der string terminierung ist schon ne idee.

das komische ist nur: mein string ist etwa 3mal so groß wie die erwartete datenmenge und die länge von Data variiert.

die Höhe und Breite sind okay, da ich zur zeit nur meine cam nutze und die ist 100% auf 640*480



Edit:

ich sehe gerade der Pointer ist vom typ:

Delphi-Quellcode:
TFour = ARRAY[0..3] OF byte;
und versuche diesem pointer jetzt einen string zu zuweisen



weiß aber nicht genau weiter ^^

jokerfacehro 15. Feb 2010 13:03

Re: selbstgebastelter webcam stream
 
Nach ewigem rumprobieren bin ich immer noch nicht weiter.

Ich bekomme alle Daten wunderbar in meinen string eingelesen beim server.

Die Stringlänge ist auch aufs Byte genau richtig.

Ich weiß jetzt bloß nicht wie den string ins richtige format kriege damit der pointer ordentlich drauf zugreifen kann.

bzw eine möglichkeit diesen pointer zu umgehen.



Hat jemand nen Plan, bin mit Pointern nicht gerade der geschickteste



Edit: irgendwas haut nicht hin, wenn ich während des aufrufs von UDPServerRead ein Showmessage mache, ändert
sich komischer weise willkürlich die fenstergröße der dialogbox.

mal ist sie normal groß, manchmal breiter als der bildschirm... :gruebel: :wall:


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:24 Uhr.
Seite 2 von 3     12 3      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz