Récupérer les images d?une camera ip transmettant un flux mjpeg

Contenu du snippet

Récupérer les images d?une camera IP ( une DLINK DCS-900) transmettant un flux MJPEG (et non MPEG), transformation en image bitmap puis affichage.
Cette base me sert en robotique mobile (simple vision à distance) et pour différents projet (reconnaissance des formes, d?iris etc..) avec traitement.
Elle utilise la librairie PLIB ( plib.net plib.org) pour le transfert entre la camera (donc facilement migrable sous linux), par contre elle utilise les fonctions windows pour la décompression JPEG ( mais on doit pouvoir facilement utiliser la librairie JPEG de ijg.org )

Source / Exemple :


/******************************************************************

  • recuperation d'un flux Mjpeg venant d'une camera IP (DCS-900) *
  • 320x200 *
  • ouvre une connexion tcp client, envoi une requete http, traite *
  • le flux reçu, extrait les images jpeg, les convertit, et *
  • affiche le resultat *
  • Copyright 2006 Grimal sylvain *
  • utilisation libre pour application non commerciale *
  • linker avec: net ul (plib) libsock32 libgdi32 libuser32 *
  • libole32 liboleaut32 libolepro32 libuuid (.a ou .lib selon *
  • compilateur) *
                                                                                                                                    • /
#include <windows.h> #include <olectl.h> #include "include/netsocket.h" // librarie PLIB voir plib.net ou plib.org #define TCP true #define UDP false #define longMaxJpeg 16384 // longueur maximale d'une image jpeg reçue (suffisant pour du 320x200) unsigned char image[longMaxJpeg]; HWND hWmain; netSocket *sockr; // DC d'affichage HDC hdcSortie; // Bitmap après convertion du jpeg HBITMAP hbmp; /*******************************************************
  • convertion d'une image Jpeg en bitmap *
  • utilisation ole de windows cf msdn *
                                                                                                              • /
HBITMAP convertJpegBmp(LPBYTE pmem, DWORD nSize) { HRESULT hr; CoInitialize(0); HBITMAP hbmp_dst = 0; // copie image jpeg dans global HGLOBAL hgbl =(HGLOBAL)GlobalAlloc(GMEM_FIXED, nSize); memcpy(hgbl, pmem, nSize); // création du stream d'échange IStream* stream = 0; // image jpeg dans global dans stream hr = CreateStreamOnHGlobal(hgbl, TRUE, &stream); if(!SUCCEEDED(hr) || !stream) { // si erreur libération des objets déja crées GlobalFree(hgbl); CoUninitialize(); } else { // création d'une 'picture' IPicture* picture = 0; // conversion stream vers picture hr = OleLoadPicture(stream, nSize, 0, IID_IPicture, (void**)&picture); if(!SUCCEEDED(hr) || !picture) { // si erreur libération des objets déja crées stream->Release(); GlobalFree(hgbl); CoUninitialize(); } else { // recuperation du handle de la 'picture' HBITMAP hbmp_src; picture->get_Handle((OLE_HANDLE *)&hbmp_src); // recuperation du handle du bitmap de la 'picture' BITMAP bmp; GetObject(hbmp_src, sizeof bmp, &bmp); // bmp est le bitmap resultant mais son pointeur vers le contenu pointe vers le contenu de 'picture' // comme on va dechargé la 'picture' on copie dans une autre zone memoire hbmp_dst = (HBITMAP)CopyImage(hbmp_src, IMAGE_BITMAP, 0, 0, 0); picture->Release(); stream->Release(); GlobalFree(hgbl); CoUninitialize(); } } return hbmp_dst; } /****************************************************
  • affiche une image jpeg contenue dans le tableau *
  • image[52 ou 53 à longueur-5] *
                                                                                                        • /
int afficheImage(HWND hwnd, DWORD longueur) { HDC hdc; DWORD debut; // recherche debut variable selon texte indication taille image debut=50; // normalement debut=52 si taille<10Ko et 53 si > while (!((image[debut]==0xFF) & (image[debut+1]==0xD8)) & (debut<54)) { debut ++; } // test entete et fin fichier JPEG if ((image[debut]==0xFF) & (image[debut+1]==0xD8) &(image[longueur-6]==0xFF)&(image[longueur-5]==0xD9)) { // conversion de l'image JPEG en Bitmap (ole Windows) hbmp = convertJpegBmp((LPBYTE) &image[debut], longueur-debut-4); // et affichage (GDI windows) hdc = GetDC(hwnd); SelectObject(hdcSortie, hbmp); BitBlt(hdc, 0, 0, 320, 240, hdcSortie, 0, 0, SRCCOPY); ReleaseDC(hwnd,hdc); // et destruction du bitmap DeleteObject(hbmp); } } /***************************************************************
  • Ouverture d'un socket TCP IP Plib port de sortie *
  • geré automatiquement par plib non contrôlable mais avantage *
  • gestion automatique si lancement de plusieurs applications *
                                                                                                                                • /
netSocket* OpenTcpMjpegParser(char* ip,unsigned int port) { netSocket *sockr; netInit(); sockr= new netSocket(); sockr->open(TCP); sockr->connect(ip,port); return sockr; } /***************************************************************
  • Fermeture du socket TCP IP *
                                                                                                                                • /
int CloseTcpMjpegParser(netSocket *sockr) { sockr->close(); } /****************************************************************
  • routine principale: ouvre une connection TCP/IP entre l'hote *
  • (le client) et la camera (le serveur), envoi la demande http *
  • *
                                                                                                                                  • /
DWORD WINAPI TcpMjpegParser( void *param) { char rbuffer[2048]; char limiteImage[19]="--video boundary--"; // limite entre deux images sur dcs-900 int len; DWORD pointeur=0; DWORD i; DWORD n; bool flag; //dc compatible avec mode d'affichage hdcSortie = CreateCompatibleDC(0); // ouverture du socket et envoi de la demande http GET // mettre l'adresse de la camera sockr=OpenTcpMjpegParser("172.16.186.51",80); // requete http à envoyer à la camera pour recevoir le flux MJPEG à adapter à votre camera // utiliser un sniffer lors d'une connexion avex IE par exemple const char* s = netFormat ("GET /VIDEO.CGI HTTP/1.0\r\nUser-Agent: user\r\nAuthorization: Basic YWRtaW46REVVU1Q=\r\n\r\n") ; sockr->send ( s, strlen(s) ) ; /****************************************************************
  • traitement des informations reçues dans le buffer. Attention *
  • un buffer trop petit entraine la perte de TOUTES les infos *
  • trouve la frontiere entre deux images JPEG *
                                                                                                                                  • /
while(true) // a faire fin du thread { len=sockr->recv(rbuffer,2048,0); // si on a reçue quelque chose if (len>=0) { // traitement un à un des octets reçus for (i=0;i<len;i++) { image[pointeur++]=rbuffer[i]; // on ne fait rien tant que l'on à pas reçu au moins 18 octets if (pointeur>18) { // test limite de l'image dans le flux flag=true; for (n=0;n<18;n++) if (image[pointeur-18+n]==limiteImage[n]) flag=flag&true; else flag=false; if (flag) { afficheImage(hWmain, pointeur-18); // 18 pour suppression de la frontiere Sleep(50) ; pointeur=0; } } } // fin de boucle for lecture du buffer } } CloseTcpMjpegParser(sockr); return 0; } /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mssg, WPARAM wParam, LPARAM lParam) { switch(mssg) { case WM_DESTROY: DeleteObject(hbmp); DeleteDC(hdcSortie); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, mssg, wParam, lParam); } /* Make the class name into a global variable */ char szClassName[ ] = "WindowsApp"; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { // classique programmation de gestion windows (generé par dev c++) MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default color as the background of the window */ wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Register the window class, and if it fails quit the program */ if (!RegisterClassEx (&wincl)) return 0; /* The class is registered, let's create the program*/ HWND hWnd; hWnd = CreateWindowEx ( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ "Vision hexapode", /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ CW_USEDEFAULT, /* Windows decides the position */ CW_USEDEFAULT, /* where the window ends up on the screen */ 328, /* The programs width */ 270, /* and height in pixels */ HWND_DESKTOP, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); // pas très propre: handle de la fentre commun à tout le programme hWmain=hWnd; /* Make the window visible on the screen */ ShowWindow (hWmain, nFunsterStil); //création du thread (API Windows) de recuperation et d'affichage DWORD len=0; DWORD total=0; DWORD ThreadId; HANDLE hThread ; DWORD threadID; hThread=CreateThread( NULL, 0, TcpMjpegParser, NULL, NULL , &ThreadId); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage (&messages, NULL, 0, 0)) { /* Translate virtual-key messages into character messages */ TranslateMessage(&messages); /* Send message to WindowProcedure */ DispatchMessage(&messages); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; }

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.