[C++] UDP-przesyłanie dużych tablic (+openCV)

0

Witam, ostatnio zacząłem bawić się trochę biblioteką OpenCV, która w bardzo prosty sposób umożliwia przejęcie, wyświetlenie, zapisanie itd. np. obrazu z kamerki. Obraz ten jest w podstawie obiektem struktury typu IplImage, która posiada pole ImageData które to jest niczym innym jak tablicą char. Jej wielkość to (przeważnie, z doświadczenia) Wymiary * ilość kanałów (xyn). Skoro tak to pomyślałem, że może by spróbować w prosty sposób realizacji przesyłu video przez sieć. Problem sprowadził się do przesyłania dużych tablic, np. dla obrazu o rozdzielczości 320x240 (3 kanały RGB) rozmiar tablicy to 230400 bajtów! Decydując się na protokół UDP przeczytałem, że efektywny przesył "czystych danych" to 508 bajtów co dawałoby maksymalną tablicę o wielkości lekko ponad 4000 komórek. Postanowiłem więc podzielić tą tablicę na części tak aby pojedynczy datagram nie był większy niż te 508 bajtów.. Gdy testuję program na jednym komputerze działa bardzo dobrze, w momencie gdy łącze się z kompem w mojej sieci lokalnej, po paru sekundach mam zwieche.. Poniżej przedstawiam kod.. Proszę o sugestie, jak efektywnie przesyłać duże porcje danych, czy o wytknięcie jakiś błędów..

Część wysyłająca w wątku klienta:

       #define bits 3840     // liczba bitów jednorazowo przesyłana
	char bufor[bits];
	char buf[2];
	int ret = 0;
	int iSize = sizeof(server_adr);
	static int p = 0;
	
	while(ret != -1)
	{
		img = cvRetrieveFrame(cam);	// przechwycenie obrazu i zapisanie do (IplImage*) img
		cvResize(img, rs_img);		// zmiana rozdzielczosc na 320x240

		for(int i = 0; i < 60; i++)	// funkcja dzielaco-wysylajaca
		{
			for(int j = 0; j < bits; j++, p++)
				bufor[j] = rs_img->imageData[p];
			
			ret = sendto(Socket, bufor, bits, 0, (sockaddr*)&server_adr, sizeof(server_adr));	

			if(!(p < 230400)) 
				p = 0;
			ret = recvfrom(Socket, buf, 2, 0, (sockaddr*)&server_adr, &iSize); //synchronizacja
		}
	}

Część odbierająca w wątku serwera:

       #define bits 3840 // liczba bitow jednorazowo odbierana
	char bufer[bits];
	static int p = 0;

	do
	{
		ret = recvfrom(Server, bufer, bits, 0, (sockaddr*)&local_adr, &iSize); // odbierz dane

		for(int i = 0; i < bits; i++, p++)	// (cząstkowe) kopiowanie do pola imageData
			img->imageData[p] = bufer[i];		
		
		if(!(p < 230400))
		{
			cvShowImage("obraz", img);	// wyswietlenie obrazu
			cvWaitKey(20);
			p = 0;
		}

		sendto(Server, ".", 2, 0, (sockaddr*)&local_adr, sizeof(local_adr)); // synchronizacja

	}while(ret != -1);

Myślę, że problem sprowadza się do przesyłania dużych tablic szybko po sobie.. Zanim ktoś poleci mi od razu odrzucenie tego pomysłu i wskaże na RTP, proszę o jakieś pomysły ;)

0
#define PICTSIZE 230400
#define bytes 508     // liczba BAJTOW jednorazowo przesyłana

void sendpict(){
        char* bufor;
        char buf[2];
	int ret = 0;
	int i;
	
	while(ret != -1)
	{
		img = cvRetrieveFrame(cam);
		cvResize(img, rs_img);

                bufor=(char*)rs_img->imageData[p];
                // kazdy bufor jest dobry, jesli potrzebujesz to sobie zrob 2gi wskaznik do niego
                // ale nie trac czasu na kopiowanie z jednego do drugiego

                i=0;
		while(i<PICTSIZE && ret!=-1) // nie masz wplywu na to ile bajtow wysle sendto
		{
                        int j=PICTSIZE-i; // wysyalja po paczce
                        if(j>bytes)j=bytes; // jesli pozostala czesc do wyslania jest mniejsza od bytes, to wyslij tylko tyle ile masz wyslac
			ret = sendto(Socket, (char*)(bufor+i), j, 0, (sockaddr*)&server_adr, sizeof(server_adr));	
                        // (char*)(bufor+i) , &bufor[i], czy jakkolwiek, adres itego elementu bufora

                        if(ret>0){
                            i+=ret;
                        }else ret=-1; // 0 oznacza zamkniecie polączenia, nie wiem jak przy udp, ale warto sie nad tym zastanowic

		}
                if(ret!=-1){
//                    synchronizacje sam dopisz
                }
	}
}


void recvpict(){
	int ret = 0;
	
	while(ret != -1)
	{
                bufor=(char*)rs_img->imageData[p];

                i=0;
		while(i<PICTSIZE && ret!=-1)
		{

                        int j=PICTSIZE-i;
                        if(j>bytes)j=bytes;
			ret = recvfrom(Socket, (char*)(bufor+i), j, 0, (sockaddr*)&local_adr, sizeof(local_adr));

                        if(ret>0){
                            i+=ret
                        }else ret=-1;

		}
                if(ret!=-1){
//                      i synchronizacja przed czekaniem
                        cvShowImage("obraz", img);        // wyswietlenie obrazu
                        cvWaitKey(20);
                }
	}
}

co to jest cvWaitKey() ? po co czekasz ? przeciez obraz nie pojawi sie zanim go nie odbierzesz z kamerki i nie przeslesz, a na to masz kilka sekund.
tablica charow to nie tablica bitow tylko bajtow.

0

ok, dzięki, przeanalizuję, a cvWaitKey(20) czeka 20 milisekund, jest konieczny po funkcji cvShowImage(), aby obraz się ukazał. A ta funkcja włącza się właśnie dopiero gdy dojdzie już cała ramka

0

Pisze dopiero teraz bo nie miałem ostatnio czasu posiedzieć przy kompie.. Dzięki za przebudowanie funkcji w paru miejscach, niewątpliwie polepszyło to wydajność po stronie osobno serwera i klienta, ale nie zmieniło za bardzo problemu samego przesyłu.. Otóż po kilku sekundach, ramkach, mam zwiechę.. Wygląda to tak, jakby w transferze zrobił się korek..

0

Jeśli przesyłasz obraz wideo przez sieć to użyj kompresji. Rozmiar można nawet kilkadziesiąt razy zmniejszyć. Np. do popularnego DivX'a jest mnóstwo kodeków. Przykładem może być jakiś open source'owy player.

0

tak, tak.. wiem, że takie rozwiązanie (czy też skorzystanie z RTP) byłoby najwłaściwsze dla stricte przesyłu wideo.. Natomiast w moim przypadku nie ma co kompresować, bo problem sprowadził się do przesyłania tablicy bajtów.. Wiem, że to rozwiązanie nie jest profesjonalne, dlatego też problem, wideo zamienia się na problem przesyłu dużych porcji danych.. i coś nie działa.. i to mnie nurtuje..

0

moze twoj problem wynika po prostu z faktu ze UDP moze pogubic pakiety, lub pozamieniac ich kolejnosci.

Nie wiem jak wyglada odbior po stronie klienta, ale jesli odbierasz do jakiejs struktory, ktora ma zoraganizowane pola, to pozamieniane, albo brakujace pakiety moga namieszac w logice,
powodujac ze jakis np. integer bedzie pokazywal na dziwna wartosc, powodujac zwieche.

Ja bym dozucil handshaking i potwierdzenie odebrania kazdej nowej wiadomosci, w wypadku gdy nie dotrze ponowne wyslanie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1