Bild von IP Kamera holen

Alle Fragen zur Netzwerkkommunikation
Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Bild von IP Kamera holen

Beitrag von Timm Thaler »

Ich habe zwei IP-Kameras, von denen ich Bilder mit dem Raspberry abhole. Bisher verwende ich dazu curl:

Code: Alles auswählen

curl --user user:passwort http://192.168.1.99/mjpeg/snap.cgi?chn=0 --digest -o /home/pi/virtual/webcam1.jpg >/dev/null 2>&1 >/dev/null 2>&1 >/dev/null 2>&1 >/dev/null 2>&1 >/dev/null 2>&1 >/dev/null 2>&1


Das wird mit cron jede Minute aufgerufen und funktioniert bis auf ein paar Kleinigkeiten, so wird zum Beispiel bei fehlender Kamera statt des jpgs eine Textdatei geschrieben. Nun möchte ich die Bilder mit einem Freepascal-Programm abholen, um damit etwas flexibler zu sein: Zeitstempel zufügen, als Bildserie abspeichern, bei fehlender Verbindung Fehler loggen...

Deswegen habe ich mir was mit fphttpclient zusammengebastelt. Ich habe die Prozedur zum Senden von Bildern per POST versucht herumzudrehen, um die Bilder zu empfangen, aber ich komme da nicht weiter:

Code: Alles auswählen

procedure Tcam.ReadCamera();
var
  httpClient : TFPHTTPClient;
  param : TStringList;
  respo : TStringStream;
  fname : string;
  url : string;
  txt : string = '';
begin
    url := 'http://192.168.1.99/mjpeg/snap.cgi';
    fname := '/home/pi/virtual/webcam1.jpg';
    try
      httpClient := TFPHTTPClient.Create(nil);
      try
        param := TStringList.Create();
        param.Values['user'] := 'user';
        param.Values['pwd'] := 'passwort';
        param.Values['chn'] := '0';
        respo := TStringStream.Create('');
==> wie hier weiter?
        httpClient.FileFormPost(url, param, 'photo', fname, respo);
        respo.Destroy;
        param.Free;
      except
        log.Message('Fehler beim Senden');
      end;
    finally
      httpClient.Free;
    end;
  end;
end;


Ich denke, ich bräuchte was, um an der markierten Stelle die Anfrage rauszuschicken, auf ein Response zu warten, wenn dabei eine Fehlermeldung kommt darauf zu reagieren und wenn ein Bild empfangen wird, sollte das in respo liegen und ich könnte das als Datei abspeichern.

Nur was wäre hiervon geeignet: https://svn.freepascal.org/svn/fpc/tags/release_3_0_0/packages/fcl-web/src/base/fphttpclient.pp? FileFormPost, FormPost, HTTPMethod?

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Bild von IP Kamera holen

Beitrag von Warf »

Wie wärs mit sowas:

Code: Alles auswählen

resp := TMemoryStream.Create;
try
  httpClient.FileFormPost(url, param, 'photo', fname, resp);
  resp.Seek(0, 0);
  Pic.LoadFromStream(resp); // Pic: TPicture irgendwo definieren, Createn und natürlich auch wieder destroyen
finally
  resp.Free;
end;

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Bild von IP Kamera holen

Beitrag von Timm Thaler »

Ich denke, das Streaming der Daten habe ich gelöst, aber ich scheitere gerade an der Authentifizierung.

Die Kamera will beim Login Username und Password, was ich mit dem FormPost übergebe. Dennoch erhalte ich nur ein Error 401 Unauthorized zurück. Ich finde aber auch nichts dazu, wie man eine Authentifizierung mit fphttpclient macht.

Code: Alles auswählen

procedure Tcam.ReadCamera();
var
  httpClient : TFPHTTPClient;
  param : TStringList;
  respo : TMemoryStream;
  ifile : TFileStream;
  url : string;
begin
    url := 'http://192.168.1.99/mjpeg/snap.cgi';
    fname := '/home/pi/virtual/webcam1.jpg';
    try
      httpClient := TFPHTTPClient.Create(nil);
      try
        param := TStringList.Create();
//        param.Values['username'] := webcam.user;
//        param.Values['password'] := webcam.pwd;
        param.Values['chn'] := webcam.chn;
        WriteLn(webcam.url);
        respo := TMemoryStream.Create();
        httpClient.AllowRedirect := true;
        httpClient.UserName := 'user';
        httpClient.Password := 'passwort';
        httpClient.FormPost(url, param, respo);
        WriteLn('StatCode: ', httpClient.ResponseStatusCode);
        WriteLn('StatText: ', httpClient.ResponseStatusText);
        respo.Position := 0;
        ifile := TFileStream.Create(fname, fmCreate);
        ifile.CopyFrom(respo, respo.Size);
        respo.Free;
        ifile.Free;
        param.Free;
      except
        log.Message('Fehler beim Senden');
      end;
    finally
      httpClient.Free;
    end;
  end;
end;


Der Stream wird gespeichert, aber natürlich wird kein Bild zurückgeliefert, sondern die Fehlermeldung:

Code: Alles auswählen

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>401 - Unauthorized</title>
 </head>
 <body>
  <h1>401 - Unauthorized</h1>
 </body>
</html>


Wenn ich mich im Browser auf die Seite einlogge, kommt ein Fenster mit der Abfrage von Benutzername und Passwort, und wenn ich die eingebe wird das Bild geliefert. Daten sind also richtig.

Wie mache ich so eine Authentifizierung mit fpc?

sstvmaster
Beiträge: 575
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Bild von IP Kamera holen

Beitrag von sstvmaster »

Kannst du Benutzname und Passwort über die URL im Browser mitgeben? So in der Art: http://192.168.1.99/?user=user&pass=password

Was steht in der Adresszeile wenn du auf die Kamera zugreifst? Was ist das für eine Kamera (Hersteller/Modell)?
LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

sstvmaster
Beiträge: 575
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Bild von IP Kamera holen

Beitrag von sstvmaster »

LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Bild von IP Kamera holen

Beitrag von Timm Thaler »

Nein, mit user:passwort in der Adresse geht es leider nicht, weder im Browser noch im curl noch in meinem Programm. user:passwort funktioniert aber im VLC-Player und im Omxplayer für den rtsp-Stream.

Es ist eine Wansview W3 Outdoor Kamera, die über Wlan angebunden ist. TCP und UDP Ports ins Internet sind blockiert, so dass die Kamera nur im Intranet rumfunken kann. User und Passwort habe ich geändert, ich verwende hier mal beispielshalber das Standardpasswort der Kameras.

Der rtsp-Stream kann abgerufen werden mit rtsp://admin:123456@192.168.2.10/live/ch0 Das geht im VLC-Player und im Omxplayer, auch die App auf dem Tablet nutzt diesen Stream. Im Firefox geht rtsp nicht.

Ein mjpeg-Stream kann abgerufen werden mit http://192.168.2.10/mjpeg/stream.cgi?chn=0 Das geht im Firefox, allerdings kommt eine Passwortabfrage des Firefox, gebe ich User und Passwort ein, kommt ein Bild mit dem Stream. Gebe ich kein Passwort ein, kommt eine 401 Fehlermeldung.

Ich habe mich da täuschen lassen: Einmal eingeloggt, fragt der Firefox nicht wieder nach User und Passwort, solange die IP nicht gewechselt wird. Daher erschien es mir, dass sowohl http://admin:p123456@192.168.2.10/mjpeg/stream.cgi?chn=0 als auch http://192.168.2.10/mjpeg/stream.cgi?user=admin&pwd=123456&chn=0 funktionieren. Das ging aber nur, weil der Firefox sich einmal angemeldet hatte. Ändert sich die IP, bringt der Firefox erneut die Passwortabfrage. Gleiches Verhalten im Chromium auf dem Raspi.

Ein Einzelbild kann ebenso abgerufen werden mit http://192.168.2.10/mjpeg/snap.cgi?chn=0. Einzelbilder kann ich mit curl abrufen und ablegen curl --user admin:123456 http://192.168.2.10/mjpeg/snap.cgi?chn=0 --digest -o /home/pi/homectrl/virtual/webcam1.jpg >/dev/null 2>&1. Das funktioniert, curl muss also irgendwie die Passwortabfrage verwalten können.

Momentan speichere ich Bilder mit cron + curl jede Minute ab. Das hat allerdings einige Nachteile: Ich hätte gern mehrmals in der Minute, gut das könnte ich mit einem script und delays machen. Aber wenn jemand per Browser auf den mjpeg-Stream zugreift, wird der Zugriff per curl blockiert, die Kamera kann auf diesem Weg nur einen Clienten bedienen. (Per rtsp gehen mehrere Streams gleichzeitig). Und dann bleiben die cron-Aufrufe von sh + curl stehen, bis der Port wieder frei ist. Und wenn keine Kamera da ist, speichert curl statt des Bildes die Fehlermeldung als Text in die Bilddatei.

Benutzeravatar
six1
Beiträge: 782
Registriert: Do 1. Jul 2010, 19:01

Re: Bild von IP Kamera holen

Beitrag von six1 »

Hi,
die Authentifizierung muss vorher geschehen.
Schau mal hier: https://de.wikipedia.org/wiki/HTTP-Authentifizierung

schau mal hier: http://forum.lazarus.freepascal.org/ind ... ic=15747.0
Gruß, Michael

sstvmaster
Beiträge: 575
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Bild von IP Kamera holen

Beitrag von sstvmaster »

LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Bild von IP Kamera holen

Beitrag von Warf »

Schonmal mit Wireshark geschaut wie das Webpaket aussieht? Ist der Authorization Header korrekt gesetzt (also Authorization: Basic ENCDATA mit Encdata die Base64 Kodierte Version von User:Passwort)? Wenn nicht setz den einfach selbst (httpClient.RequestHeaders oder so)

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Bild von IP Kamera holen

Beitrag von Timm Thaler »

Ahh, es ist noch bekloppter: Im Chromium unter Windows geht es mit user:password in der url, ohne vorherige Anmeldung. Im Chromium auf dem Raspberry geht es mit der gleichen url nicht. Auf zweien versucht. Im Firefox geht es unter Windows und auf dem Raspberry nicht.

six1 hat geschrieben:die Authentifizierung muss vorher geschehen.


Also auch wenn ich das jetzt nicht verstanden habe: Ich frage vorher mit FormMethod bei der Kamera an, und wenn die mir mit 401 antwortet, schicke ich eine Authentifizierung und bekomme den Bilderstream. Und die Authentifizierung muss ich Base64 kodieren. Aber macht das nicht fphttpclient für mich, wenn ich
httpClient.UserName := user;
httpClient.Password := pwd;
setze?

Es ist aber auch zum Heulen, dass es da keine Beispiele für fpc gibt. Braucht sowas keiner? Machen die das alles mit Python?

sstvmaster hat geschrieben:probier nochmal folgendes: http://admin:password@192.168.1.20/snap ... d=password


Geht nicht. Nene, die urls sind schon richtig, aber offenbar geht das nur bei einigen Browsern.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Bild von IP Kamera holen

Beitrag von Warf »

Timm Thaler hat geschrieben:Also auch wenn ich das jetzt nicht verstanden habe: Ich frage vorher mit FormMethod bei der Kamera an, und wenn die mir mit 401 antwortet, schicke ich eine Authentifizierung und bekomme den Bilderstream. Und die Authentifizierung muss ich Base64 kodieren. Aber macht das nicht fphttpclient für mich, wenn ich
httpClient.UserName := user;
httpClient.Password := pwd;
setze?

Es ist aber auch zum Heulen, dass es da keine Beispiele für fpc gibt. Braucht sowas keiner? Machen die das alles mit Python?


Auf die gefahr hin mich zu wiederholen, benutz Wireshark (oder ähnliches) und schau einfach mal rein was da abgesendet wird. Die FPC HTTP Client klasse würde ich nicht unbedingt als verlässlich bezeichnen (beispiel: Übergib mal ein FormPost mit mindestens einem Parameter = leerer string). Der Vorteil ist, da man sehr einfachen Header Zugriff hat kann man ganz einfach selbst dran rum basteln bis es funktioniert (HTTP ist nicht sonderlich kompliziert, du könntest wahrscheinlich deinen eigenen HTTP client innerhalb von weniger als 1er woche schreiben).

Schau dir aber wirklich mal an was sendet der Browser, und was sendet dein Programm. Da solltest du dann sehen können was fehlt/falsch übertragen wird

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Bild von IP Kamera holen

Beitrag von Timm Thaler »

Warf hat geschrieben:... benutz Wireshark (oder ähnliches) und schau einfach mal rein was da abgesendet wird...


Habs mir gerade runtergeladen.

Ich finds halt etwas eigenartig, dass man so wenig Infos dazu findet.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Bild von IP Kamera holen

Beitrag von Timm Thaler »

Hab jetzt eine neue Anfrage mit Chromium gestartet und mit Wireshark angesehen. Diesmal klappt das Einloggen mit user:password in der Url nicht, ich muss die Logindaten extra eingeben.

Code: Alles auswählen

Chromium > Kamera:
    GET /mjpeg/snap.cgi?chn=0 HTTP/1.1\r\n
Kamera > Chromium:
    HTTP/1.1 401 Unauthorized\r\n
    WWW-Authenticate: Digest realm="IPCamera Login", nonce="c5fc6ce1cebb007107e57d4dbc336b54", qop="auth"\r\n
    Content-Type: text/html\r\n
    Content-Length: 351\r\n
    Connection: close\r\n
Chromium > Kamera:
    GET /mjpeg/snap.cgi?chn=0 HTTP/1.1\r\n
    Host: 192.168.2.61\r\n
    Connection: keep-alive\r\n
    Authorization: Digest username="watch", realm="IPCamera Login", nonce="c5fc6ce1cebb007107e57d4dbc336b54", uri="/mjpeg/snap.cgi?chn=0", response="ad8adbc87c73bac8f7c250d03d256a82", qop=auth, nc=00000001, cnonce="ac94f424f951aedd"\r\n
    Upgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3564.0 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate\r\n
    Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\r\n
Kamera > Chromium:
    HTTP/1.1 200 OK\r\n
    Cache-Control: public,max-age=0\r\n
    Content-Type: image/jpeg\r\n
    Content-Length: 63704\r\n
    Connection: close\r\n
    Date: Thu, 01 Jan 1970 07:33:32 GMT\r\n
    Server: lighttpd/1.4.35\r\n
    File Data: 63704 bytes


Das sollte alles sein, was ich benötige. Die Auth läuft also mit Digest, ich muss aus der ersten Antwort das nonce speichern und in der zweiten Anfrage zurücksenden, den realm am Besten auch gleich. User ist klar, aber wo ist mein Passwort? In response oder in cnonce, und wie verschlüsselt?

NoCee
Beiträge: 170
Registriert: Do 3. Mär 2011, 21:34
OS, Lazarus, FPC: WinXp/7/10 Opensuse13.2/Leap15.3 (L 2.2.0 FPC 3.2.2 )
CPU-Target: Intel 32/64Bit, ARM9
Wohnort: Ulm

Re: Bild von IP Kamera holen

Beitrag von NoCee »

Hallo,

Ich finds halt etwas eigenartig, dass man so wenig Infos dazu findet.

Genau so gings mir auch.
Ich wollte mal vor ein paar Jahren im Prinzip das Gleiche machen.
Da hab ich mir tagelang einen Wolf gegockelt aber nix gescheites bzw. ausführliches gefunden
so daß ich irgend wann aufgegeben und das per Hardware gelöst habe.
Kamera mit Hardwareinput. Die schickt mir ein Bild wenn sie getriggert wird.
Den gleichen Eingang benutze ich dann für ein kleines Lazarusprogramm bei dem ich nur noch Datum und Uhrzeit mitschreibe.
Vielleicht taucht hier ja noch was brauchbares auf.

Grüße
NoCee

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Bild von IP Kamera holen

Beitrag von Warf »

Timm Thaler hat geschrieben:Hab jetzt eine neue Anfrage mit Chromium gestartet und mit Wireshark angesehen. Diesmal klappt das Einloggen mit user:password in der Url nicht, ich muss die Logindaten extra eingeben.


Das sollte alles sein, was ich benötige. Die Auth läuft also mit Digest, ich muss aus der ersten Antwort das nonce speichern und in der zweiten Anfrage zurücksenden, den realm am Besten auch gleich. User ist klar, aber wo ist mein Passwort? In response oder in cnonce, und wie verschlüsselt?


Der Server verwendet Digest Access authentification. Das kann der FPHTTPClient nicht. Entweder selbst implementieren (siehe hier) oder du schaust dich nach einer externen Komponente um die das kann. Soweit ich weiß kann Indy das, bei Synapse hab ich grad bei google nur gefunden das man es da auch selbst implementieren muss.

Antworten