Transfert d'images via un socket ?

Transfert d'images via un socket ? - C++ - Programmation

Marsh Posté le 02-01-2005 à 22:38:24    

Salut à tous !
 
Voilà, j'ai dans l'idée de développer un prog style VNC "à ma sauce".
 
Pour débuter j'ai déjà réalisé les parties suivantes :
- capture d'image,
- mise en place de la liaison client/serveur via un socket,
- simulé les événements clavier/souris
 
Bref ça avance bien néanmoins un problème persiste et il n'est pas des moindres pour ce type d'appli : je ne parviens pas à trouver l'astuce pour transférer des images via le socket. Pour le texte y'a aucun problème, c'est le B-A-BA, par contre impossible d'envoyer des données de type bitmap par exemple...
 
Quelqu'un aurait-il une suggestion ?
 
Merci d'avance !
 
P.S: je suis sous Borland C++ Builder 5.0 au cas où ça peut aider...


---------------
...m'enfin !!!
Reply

Marsh Posté le 02-01-2005 à 22:38:24   

Reply

Marsh Posté le 02-01-2005 à 22:39:38    

bin point ne voyons le probleme ? un bitmap, c'est un tas d'octets, de meme qu'un texte, donc ou ca coince ?
 
(Par contre pour faire un VNC-stayle rapide, ca va peut etre demander plus qu'un simple envoie d'image via le reseau)
 
 

Reply

Marsh Posté le 02-01-2005 à 22:40:08    

les données que tu envoies sont non-typées. C'est un train d'octets. Rien d'autre. Si t'es capable d'envoyer quelque chose, t'es capable d'envoyer n'importe quoi

Reply

Marsh Posté le 02-01-2005 à 22:50:19    

Dans ce cas c'est peut-etre l'objet socket inclus dans C++ Builder qui est trop "haut niveau" alors, je m'explique :
 
Je dispose de 3 fonctions pour envoyer des données :
SendText(AnsiString texte); pour envoyer des chaines de caractères
SendStream(TStream flux); pour faire du streeaming j'imagine...
SendBuf(*Buf, sizeof(Buf)); je pensais que c'était ce qu'il me fallait...
 
En gros, la fonction SendText je laisse tomber direct, elle ne prend en charge que les AnsiString, un char* à la sauce Borland quoi.
La fonction SendStream j'ai pas trop compris.
La fonction SendBuf me semblait alors adaptée.
 
Lors de mon test avec la 3è fonction donc, j'ai pu passer sans problème la compilation, mais j'ai un "Access Violation" à l'exécution ; à priori je ne parviens donc pas à convertir correctement mon objet bitmap (Graphics::TBitmap) en quelque chose de compatible avec la variable demandée qui est de type *Buf...  
 
Qu'en pensez-vous ?


---------------
...m'enfin !!!
Reply

Marsh Posté le 02-01-2005 à 22:58:01    

fait voir le code de ton envoi ?

Reply

Marsh Posté le 02-01-2005 à 23:14:36    

chrisbk a écrit :

fait voir le code de ton envoi ?


 
Ben c'est relativement simple...du moins en ce qui concerne le texte ;
 
 
Pour ça j'ai utilisé la fonction SendText :
 

Code :
  1. //Pour l'envoi :
  2. void __fastcall TForm1::CliSendClick(TObject *Sender)
  3. {
  4.         if(ClientSocket1->Active) {
  5.             ClientSocket1->Socket->SendText(Edit1->Text);
  6.         }
  7. }
  8. //Pour la réception
  9. void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender, TCustomWinSocket *Socket)
  10. {
  11.         Label8->Caption = ServerSocket1->Socket->Connections[ComboBox1->ItemIndex]->ReceiveText();
  12. }


 
 
L'avantage avec C++ Builder c'est que tout se fait tout seul, malheureusement ce n'est pas toujours un avantage...la preuve avec l'envoi d'images !
Ca c'était juste le code de la fonction qui envoie le message, auparavant il y a bien entendu toute la phase d'initialisation des adresses IP ainsi que des ports etc...
 
La partie texte fonctionne donc parfaitement, par contre pour une image c'est pas la pied ; là j'ai utilisé la fonction SendBuf :
 

Code :
  1. //Pour l'envoi :
  2. void __fastcall TForm1::CliSendClick(TObject *Sender)
  3. {
  4.         if(ClientSocket1->Active) {
  5.             ClientSocket1->Socket->SendBuf(Image1->Picture->Bitmap, sizeof(Image1->Picture->Bitmap));
  6.         }
  7. }
  8. //Pour la réception :
  9. void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender, TCustomWinSocket *Socket)
  10. {
  11.         ServerSocket1->Socket->Connections[0]->ReceiveBuf(Image2->Picture->Bitmap, sizeof(Image2->Picture->Bitmap));
  12. }


 
 
Je me suis dit qu'après tout un buffer c'est juste une zone mémoire, une image est également stockée dans une zone mémoire alors le tout c'est de trouver le moyen de la faire passer dans la fonction en tant que *Buf...
Je sens que c'est vraiment tout simple, que je suis tout proche mais que je suis en train de me compliquer la vie là :whistle: ...


Message édité par mikegyver le 13-01-2005 à 22:18:09

---------------
...m'enfin !!!
Reply

Marsh Posté le 03-01-2005 à 06:25:53    

C'est parce que tu envoies le contenu de ton objet TBitmap plutôt que d'envoyer le contenu de l'image. TBitmap n'est rien d'autre qu'une classe qui contient la largeur, la hauteur, le nombre de bits par pixel du bitmap, et un pointeur vers le contenu (en fait, un tableau de pointeur vers chaque ligne si j'ai bien compris).  
 
Je me hasarderais à dire qu'il te faut un truc genre:

Code :
  1. TBitmap bmp;
  2. for (size_t h=0; h<bmp.Height; ++h)
  3.     socket.SendBuf(bmp.ScanLine[h], bmp.Width*bpp);


 
Sachant que bmp est ton objet TBitmap, et que bpp est le nombre d'octets par pixel, que tu obtiens via bmp.PixelFormat.

Reply

Marsh Posté le 03-01-2005 à 10:44:21    

+1. Quel courage de s'attaquer à un soft type VNC lorsqu'on débute avec Builder. J'ai aussi l'impression que les débuts de la programmation objet ne sont pas très loin, je me trompe ?
Quant à transférer une bitmap, Lam's te donne une condition nécessaire mais pas suffisante ;)
Il faudra aussi transférer le format de la bitmap AVANT les données elles-mêmes, à moins de se limiter à un format fixe codé en dur. Il faudra que tu le transmette "à la mano", n'espère pas faire :

Code :
  1. ClientSocket1->Socket->ReceiveBuf(Image1->Picture->Bitmap,
  2. sizeof( *  Image1->Picture->Bitmap));//'*': Bitmap est un pointeur

Les objets n'apprécient pas trop de voir leurs données membre vulgairement écrasées. Surtout un objet complexe comme un TBitmap... Il faudra donc créer l'objet Bitmap cible, lui donner le bon format (Picture->Graphic->Height/Width) et lui affecter les lignes de pixels.

Reply

Marsh Posté le 03-01-2005 à 10:48:18    

Allez, je suis gentil, tu demandais à quoi servait la méthode "SendStream()" ? Jette un oeil aux méthodes "Bitmap->SaveToStream() / ReceiveFromStream()", tu verras que tout le boulot que je décrivais dans mon post précédent à déjà été fait ! Merci qui ? non pas YoYo mais Borland.

Reply

Marsh Posté le 03-01-2005 à 11:09:43    

Mikegyver a écrit :


Voilà, j'ai dans l'idée de développer un prog style VNC "à ma sauce".


VNC existe déjà en version ultra rapide, ça s'appelle SubSeven [:petrus75]

Reply

Marsh Posté le 03-01-2005 à 11:09:43   

Reply

Marsh Posté le 03-01-2005 à 11:17:15    

Mikegyver a écrit :


void __fastcall TForm1::CliSendClick(TObject *Sender)
{
        if(ClientSocket1->Active) {
            ClientSocket1->Socket->SendBuf(Image1->Picture->Bitmap, sizeof(Image1->Picture->Bitmap));
        }
}

Pour la réception :
void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender, TCustomWinSocket *Socket)
{
        ServerSocket1->Socket->Connections[0]->ReceiveBuf(Image2->Picture->Bitmap, sizeof(Image2->Picture->Bitmap));
}


ah ben cherche pas, tu envoies et tu reçois le TBitmap, pas les données de l'image
 
edit: [:benou_grilled]


Message édité par Harkonnen le 03-01-2005 à 11:17:40
Reply

Marsh Posté le 04-01-2005 à 21:38:34    

D'accord, d'accord ; j'y vois beaucoup plus clair à présent.
 
Le pire, c'est que pendant la nuit suivant le post, ça a bien cogité (en est tous passés par la hein :D ) et je me suis dit "à tous les coups j'envoie le pointeur au lieu de l'image elle-même" !
 
Ben comme quoi j'étais vraiment pas loin de la réalité hein :jap:
 
En tous cas merci à tous pour vos réponses, je creuse un peu tout ça , notamment la fonction "SaveToStream" qui m'a l'air tout à fait adaptée, et je poste dès que ça avance un peu mieux !
 
@+ !


---------------
...m'enfin !!!
Reply

Marsh Posté le 05-01-2005 à 15:20:18    

Re-Bonjour !
 
Bon voilà, j'ai donc refait les tests et je parviens effectivement à transférer le contenu d'un objet TImage dans une variable de type TStream via la fonction SaveToStream(TStream), puis à récupérer cette variable via LoadFromStream(TStream) pour l'afficher dans un autre objet TImage...
 
Bref, il ne me reste plus en théorie qu'à utiliser la fonction SendStream(TStream) pour expédier la variable via mon socket, cependant il n'existe pas de fonction ReceiveStream(TStream) pour la récupérer de l'autre côté !!!
 
Alors voilà, maintenant que tout est sur le point de fonctionner, je parcours toute la doc de Borland pour trouver une fonction adéquate.
Pour l'instant, la seule fonction qui semble "passer à la compilation", c'est ReceiveBuf(void *Buf, int Count).
 
Du coup j'ai aussi utilisé SendBuf(void *Buf, int Count) pour envoyer stream parce que l'utilisation de SendStream(TStream) qui semble ne fonctionner qu'en mode synchrone ne fait pas réagir le côté serveur du socket via l'évenement OnClientRead...  
 
Je sais, c'est un peu confus, alors voilà le code en question qui je l'espère pourra permettre d'y voir un peu plus clair :
 

Code :
  1. //Pour l'envoi :
  2. void __fastcall TForm1::SrvSendClick(TObject *Sender)
  3. {
  4.         if(ServerSocket1->Active) {
  5.             TMemoryStream *stream = new TMemoryStream();
  6.             Image1->Picture->Bitmap->SaveToStream(stream);
  7.             //Image2->Picture->Bitmap->LoadFromStream(stream); Au passage, ça ça fonctionne, ce qui signifie que l'image est effectivement contenue dans stream.
  8.             ClientSocket1->Socket->SendBuf(stream, sizeof(stream));
  9.             stream->Free();
  10.         }
  11. }
  12. //Pour la réception :
  13. void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender, TCustomWinSocket *Socket)
  14. {
  15.         TMemoryStream *stream = new TMemoryStream();
  16.         ServerSocket1->Socket->Connections[0]->ReceiveBuf(stream, sizeof(stream));
  17.         stream->Position = 0; //Pour permettre de lire depuis le début de la variable, ce que j'avais omis au départ et qui m'a valu des heures perdues...
  18.         Image2->Picture->Bitmap->LoadFromStream(stream); //Chargement de stream pour l'afficher ; etlà, rien ne se passe...
  19.         stream->Free();
  20.         ShowMessage("Reçu !" ); //J'affiche une MessageBox pour être sûr que l'événement à bien lieu, ce qui n'est pas le cas si j'utilise SendStream du côté client...
  21. }


 
En bref, je touche au but mais il me manque cette petite fonction qui permet de lire une variable TStream du côté serveur...
 
Merci à tous ceux qui ont suivi  :D


Message édité par mikegyver le 13-01-2005 à 22:15:06

---------------
...m'enfin !!!
Reply

Marsh Posté le 07-01-2005 à 19:17:28    

Petit up après quelques jours de recherche...
 
Bon ben j'en arrive à penser que cette fonction n'a pas du tout l'air de faire ce qu'elle est sensée... Impossible de faire passer quoi que ce soit avec SendStream() ou SendBuf(). Le prog compile bien mais aucune donnée ne transite via mon socket.
 
Quelqu'un aurait-il une suggestion par rapport à une autre fonction qui remplacerait avantageusement SendStream() ou SendBuf() ?
 
Merci !


---------------
...m'enfin !!!
Reply

Marsh Posté le 11-01-2005 à 11:24:33    

Relis mon post précédant. Les méthodes sont "SendToStream()" et "ReceiveFromStream()" avec en intermédiaire l'accès au TStream que tu auras instancié. Il n'y a pas de raison que ça ne fonctionne pas... C'est une idée que d'essayer déjà ces méthodes en local (Affichage d'une bitmap, copie dans un TStream, récupération et réaffichage) avant d'attaquer le transfert par socket.
Sinon, encore une fois, n'essaie pas d'écraser par un accès direct à la ram les données membre d'un objet ! "ReceiveBuf(stream, sizeof(stream));" est une double horreur :
- sizeof( * stream) serait à peine plus correct
- TStream doit lui-même inclure des pointeurs ou des handles qu'on ne peut pas transférer de cette façon. Tu cours droit vers une erreur système de violation d'accès à la ram ou autres bestioles de ce genre.

Reply

Marsh Posté le 13-01-2005 à 14:21:54    

Oui effectivement, "Access Violation" j'y ai eu droit assez souvent pour l'instant...
 
Cependant SendToStream et ReceiveFromStream ne sont pas reconnues par le compilateur, c'est pour ça que j'utilise SendStream et ReceiveBuf.
 
Concernant le contenu de stream, je parviens parfaitement à le récupérer et à l'afficher dans un autre objet TImage en local, pas de problème de ce côté.
 
J'ai fait ça avec SendBuf et ReceiveBuf finalement.
 
Ca marche un peu mieux mais j'ai l'impression qu'il faudrait travailler avec des sockets synchrones plutôt que de les laisser en asynchrone parce que ça ne fonctionne que si je mets des pauses un peu partout dans le programme, ce qui est plutôt gênant si on veut faire un dizaine de rafraîchissements pas seconde minimum :/...
 
Enfin bon, je vais tenter de trouver une astuce pour rester en asynchrone pour éviter de devoir tout reprendre à zéro ; les sockets synchrones sont peut-être plus fiables mais ils sont plus difficiles à mettre en place.
 
En attendant, voilà le bout de code qui fonctionne...quand il veut  ;)  
 
Pour l'envoi :

Code :
  1. void __fastcall TForm1::CliSendClick(TObject *Sender)
  2. {
  3.         if(ClientSocket1->Active) {
  4.                 TMemoryStream *stream = new TMemoryStream();
  5.                 Image1->Picture->Bitmap->SaveToStream(stream);
  6.                 ClientSocket1->Socket->SendBuf(stream->Memory, stream->Size);
  7.                 delete stream;
  8.         }
  9. }


 
Pour la réception :

Code :
  1. void __fastcall TForm1::ServerSocket1ClientRead(TObject *Sender, TCustomWinSocket *Socket)
  2. {
  3.             TMemoryStream *stream = new TMemoryStream();
  4.             char Buffer[5000];
  5.             int Count;
  6.             while (Count = ServerSocket1->Socket->Connections[ComboBox1->ItemIndex]->ReceiveBuf(Buffer, sizeof(Buffer)) != -1) {
  7.                 stream->Write(Buffer, sizeof(Buffer));
  8.                 Sleep(1000);
  9.             }
  10.             stream->Position = 0;
  11.             Image2->Picture->Bitmap->LoadFromStream(stream);
  12.             delete stream;
  13. }


Message édité par mikegyver le 13-01-2005 à 22:19:34

---------------
...m'enfin !!!
Reply

Marsh Posté le 13-01-2005 à 14:30:03    

mettez le code entre les balises prévues à cet effet svp ça sera bien plus lisible

Reply

Marsh Posté le 13-01-2005 à 22:20:06    

Harkonnen a écrit :

mettez le code entre les balises prévues à cet effet svp ça sera bien plus lisible


OK :jap:


---------------
...m'enfin !!!
Reply

Marsh Posté le 13-01-2005 à 23:05:34    

D'ailleurs, dans le 2ème bout de code, j'ai l'impression qu'il faudrait un bon  
   stream->Write(Buffer, Count);

Reply

Marsh Posté le 14-01-2005 à 16:27:47    

Lam's a écrit :

D'ailleurs, dans le 2ème bout de code, j'ai l'impression qu'il faudrait un bon  
   stream->Write(Buffer, Count);


Exact !
 
Le pire c'est que c'est exactement ce qu'il y a dans mon code  :whistle: !
J'ai fait qques copier/coller pour améliorer la mise en page sur le forum et je sais pas pourquoi mais le count s'est transformé en sizeof(buffer)  :ange:  
 


---------------
...m'enfin !!!
Reply

Marsh Posté le 15-01-2005 à 18:25:47    

Juste une petite réflexion :
VNC n'est très certainement pas construit avec un transfert de captures d'écran.
Je verrais bien plus des transfert d'évènements : tu n'envoi au pervers (celui qui regarde) les modifications depuis le dernier état et c'est lui qui modifiera son "image".
Je pense pas qu'il faille passer par une image d'ailleurs mais ma solution est un peu complexe.
 
De toute facon, VNC est clairement bien plus complexe qu'un simple systeme comme ca pour pouvoir prendre la main sur le PC client.
 
Voilou.
Ca peut etre interessant comme projet ;)

Reply

Marsh Posté le 16-01-2005 à 22:41:09    

J'avoue qu'au départ j'ai voulu détecter le "type" de chaque élément (dossier, icône, fichier, etc...) et reconstituer un équivalent de l'autre côté grâce à des images déjà contenues dans une banque de données en envoyant uniquement des "références", ce qui allègerait énormément le flux de données à transporter.
 
J'ai d'ailleurs cru que c'était ce que faisait VNC mais comment peut-il alors afficher des images ou des fonds d'écrans personnalisés par exemple ?
 
Non, je pense que VNC fait bien une capture d'écran et qu'il n'envoie, suite à la première, que les éléments de l'image qui ont changé par rapport à la précedente pour alléger le flux de données.
 
Ceci dit, pour moi la qualité de l'affichage n'est que secondaire alors peu importe, du moment que je puisse prendre le contrôle à distance.  
 
D'ailleurs j'ai pensé à envoyer des captures d'écran en Jpeg ; c'est certes bien moins beau mais ça allège le flux de données...
 
Concernant le reste du prog, à part la partie "configuration" qui permettra d'établir la connexion au client via un password, cryptage, etc, il suffit de simuler les événements liés au clavier et à la souris, ce qui s'avère relativement simple.
 
En tous cas c'est sûr qu'une fois terminé, je serai bien content si ça fonctionne !!!


---------------
...m'enfin !!!
Reply

Marsh Posté le 16-01-2005 à 23:19:40    

pour info, le source de VNC est dispo... suffit d'aller voir ce qu'il fait...enfin bon, je dis ça, je dis rien hein


Message édité par Harkonnen le 16-01-2005 à 23:19:57

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 18-01-2005 à 18:22:05    

Lut all :-)
 
c'est vrai que c'est un projet carrément intéressant!!! mais tu risques d'y passer qq heures et qq neurones...
 
La transmission marche finalement ou pas? pourquoi ne pas utiliser les fonctions standards (socket(), bind(), connect(), recv(),...) et les encapsuler? Avec elles tu es sûr d'envoyer et recevoir qq chose et de tout contrôler!
 
la compression d'images serait aussi la bienvenue en effet. j'ai fait un sreenshot de mon bureau sous windows et enregistrer l'image en BMP. elle faisait 3,51 Mo. bon en sachant qu'il faudrait rafraichir l'image toutes les 0,1 s, au pire (vraiment au pire!) toutes les 0,5s je laisse faire le calcul en 100 Mb/s. donc en liaison RTC ou ADSL bon courage!
 
en tout cas bonne prog' ++

Reply

Marsh Posté le 22-01-2005 à 17:06:15    

Finalement la transmission de données diverses comme du texte ou les commandes "clavier/sousris" (du texte aussi finalement...) foncitonnent à merveille, même avec plusieurs clients, mais la transmission d'images pose toujours problème, le pire c'est que dès qu'une fonciton fonctionne à peu près, elle commence à "délirer" après une dizaine de compilations... "Un pointeur non désalloué" me direz-vous ? Eh bien même en rebootant la machine, cette fameuse fonction ne fonctionne toujours pas alors que je n'y ai absolument pas touché !!!
 
Programmation quand tu nous tiens...

Reply

Marsh Posté le 22-01-2005 à 18:12:15    

pourquoi tu regardes pas le source de VNC ?


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 27-01-2005 à 08:42:35    

Je pense effectivement que c'est ce qu'il y a de mieux a faire étant donné que depuis un mois je bloque sur ce pb :whistle:...
 
Ah mais c'est que je suis têtu moi... euh oui bon et peut-être aussi que ma méthode de prog est a revoir  : un mois sur un truc aussi con, je me fais peur moi là oh :/ !!!
 
edit :
[HORS SUJET ON]
> Harkonnen : J'ad :love: re Nightwish !!!  :hello:  
[HORS SUJET OFF]


Message édité par mikegyver le 27-01-2005 à 08:46:58

---------------
...m'enfin !!!
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed