PC - Android: Szybkie wysyłanie obrazu

0

Witam, chciałbym przesyłać obraz z pc do telefonu jak najszybciej (nie wliczając w to predkosci internetu)
ale dane nie chcą dojść

PC:

baos - ByteArrayOutputStream
dos - ObjectOutputStream


   baos.reset();
              ImageIO.write(robot.createScreenCapture(new Rectangle(0, 0, 800, 480)),"bmp",baos); 
              
              
              buff=baos.toByteArray();


              dos.write(buff, 0, buff.length);
              


 

Android:

 

recv=new byte[1152054];
  
int len=0;                                        
while(len<1152054){
len += dis.read(recv);
}
						
Screen.cache=BitmapFactory.decodeByteArray(recv, 0,1152054);
						
						
		
						
screen.refresh();

Pokazuje się czarny ekran. Gdy probuje odczytac do 562000 bajtu to zapełnia się poł obrazu.

Czy wie ktos jak zrobic zeby wysylanie dzialalo do tego najszybciej jak się da ?

0

Czemu wysyłasz tablice bajtów jako obiekt a dodatkowo z tego co rozumiem z kodu, po drugiej stronie zserializowany obiekt odbierasz jako ciąg bajtów...

Wyślij z pc cały bufor tak jak to robisz ( będzie w tym przypadku connection.getOutputSteam().write(buff,0,buff.lenght))
a na kom go po prostu odbierz

InputStream is=connection.getInputStream();
byte[] sBuff=new byte[2048]
ByteArrayOutputStream ba=new ByteArrayOutputStream();
int c=0;
while((c=is.read(sBuff)>0)
{
   ba.write(sBuff,0,c);
}

ba.toArray() // czy tam toByteArray;

na koncu dostaniesz znowu tablice bajtów w takiej samej postaci w jakiej ją wysłałeś. U mnie chyba takie cos działało (znaczy się przesyłanie z koma na pc działa i zdaje mi sie ze w taki sposób je zrobiłem)
Kod pisany palucha bez dokumentacji więc moze zawierać błędy

0

zrobiłem w podobny sposób:


while(true){            //ta petla znajduje sie w watku (Thread)
						
						
int readen = 0;
while(readen<all)      //petla while odczytuje bajty z InputStream;  int all=1152054
{
	 readen += dis.read(recv, readen, all-readen);
}
						
					
Screen.cache=BitmapFactory.decodeByteArray(recv, 0,1152054); //utworzenie obrazka z tablicy bajtow
					
if(Screen.cache==null){System.out.println("NULL!");}else{System.out.println("Draw!");} //sprawdzenie czy bitmapa nie jest pusta(null)
						
screen.refresh(); //odswiezenie View
					

Thread.sleep(33); // ma poczekac 33ms dzieki temu glowna petla jest wykonywana 30razy/sekunde (odswiezanie 30FPS)
dos.write(b,0,2); //jest to informacja ze obrazek zostal odrysowany po jej odebraniu komputer wysyla kolejny obraz
						
}

Wszystko działa tylko program się mozoli, obraz odświeża się 1kl/10sekund. Raczej to nie jest wina prędkości portu USB, bo 30MB powinien spokojnie wycisnąć(1 klatka ok. 1MB).
Co muszę tutaj pozmieniać żeby to szybko działało ? Jeżeli będzie potrzebny kod całej aplikacji, to mogę go udostępnić w następnym poście.

0

Nie przesyłaj danych w tej samej pętli z renderowaniem ich. Komentarz przy sleep jest fałszywy. Przesyłanie powinno iść jak najszybciej, a jest przedzielane pauzami mającymi aż 33 ms. Jeżeli jeden obrazek ma 1MB, to 30 wymaga przepustowości 30MB/s - na granicy możliwości USB 2.0. Tak więc samo przesyłanie bez opóźnień ledwo będzie się wyrabiać. A dochodzi jeszcze dekodowanie oraz sleep.

0

@misiek98919
Ja Tobie pokazuje BestPractices jak czytać dane ze strumieni a Ty dalej swoje.... ;] ale jak tam sobie chcesz
Nie żebym się czepiał Ale są dwie:
Pierwsza Ala mówi, że przy takim zapisie, zastosowanie pętli while jest bezcelowe - na raz próbujesz zassać cały obrazek (a konkretniej 1152054 bajtów). Tak też więc wszelkie dobrodziejstwa Java związane z buforowaniem szlag (thx @airborn) jasny trafia. Więc równie dobrze możesz to zapisać jako pojedynczą instrukcje zamiast pętli.
Druga Ala natomiast o programowani wie niewiele, za to pragnie wręczyć autorowi wątku i powyższego kodu AntyNobla w dziedzinie lingwistyki i pogratulować nowo utworzonego słowa "READEN". Brawo za pomysłowość!

Miałem jeszcze coś do @Olamagato ale nie pamiętam co :)

edit:
i tak się skupiłem na na pierdołach, że nie podsumowałem, iż "zamulanie" może być spowodowane właśnie niebuforowanym odczytem danych - ale nie musi (i w tym przypadku nawet w to za bardzo nie wierze)

BTW. Po stronie odbiorców nie powinno być żadnych sleep'ów. Narzucanie framerate możesz sobie zrobić najwyżej po stronie wysyłającej

edit2:

AA już wiem
@Olamagato
On nie przesyła i renderuje w jednym bloku.

0

pozmieniałem wszystko co się dało. Daje tu kody aplikacji PC i Androida:

ANDROID:


public class Start extends Activity {

	public static int port = 1900;
	
	
	
	Socket s;
	//DataOutputStream dos;
	DataInputStream dis;
	Thread bind,drw;
	byte[] b=new byte[2];
	byte[] recv=new byte[1152054];
	int all=1152054;
	
	Screen screen;
	

	

	
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        

        
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
        setContentView(R.layout.dev);
        
        
        FrameLayout fl =(FrameLayout)findViewById(R.id.czopx);
        
        screen=new Screen(this);
        fl.addView(screen);
        
		try{
			
		ServerSocket ss = new ServerSocket(Start.port);
		System.out.println("Bind!");
		s = ss.accept();
		System.out.println("Connected!");
		dis=new DataInputStream(s.getInputStream());
		//dos=new DataOutputStream(s.getOutputStream());
		
		drw=new Thread(new Runnable(){

			public void run() {
				try{
				
				while(true){
					//System.out.println("START:REF!");
					Screen.cache=BitmapFactory.decodeByteArray(recv, 0,1152054);
					screen.refresh();
					Thread.sleep(33);
					//System.out.println("STOP:REF!");
				}
				}catch(Exception ex){System.out.println("DrawThreadError: "+ex);}
			}
			
			
		});
		
		
		bind=new Thread(new Runnable(){

			public void run() {
				
				try{
					
					while(true){
						
					//System.out.println("START!");
					int readen = 0;
		             while(readen<all)
		              {
		                       readen += dis.read(recv, readen, all-readen);
		                       //System.out.println(readen);
		                }
						
		
						//dos.write(b,0,2);
						
					//	System.out.println("STOP!");
				}
					
					
				}catch(Exception ex){System.out.println("ThreadError: "+ex);}
			}
			
			
		});
		bind.start();
		drw.start();
		System.out.println("Launch Thread!");
	
		}catch(Exception ex){System.out.println("Error: "+ex);}       
        
        
    }
    


    
    
    
    
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_start, menu);
        return true;
    }

    
}

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

class Screen extends View {
	
	public static Bitmap cache;
	Paint pt = new Paint();

	
	public Screen(Context context) {
		super(context);
		
		setBackgroundColor(Color.CYAN);
		
	}
	

	
	public void onDraw(Canvas canvas){
		super.onDraw(canvas);
		
		
		
		if(cache!=null){
			canvas.drawBitmap(cache, 0, 0, pt);
		}
		
		
	}
	public void refresh(){
		this.postInvalidate();
	}



	
	
}







PC:


public class Start {
    
    public static String ip="127.0.0.1";
    public static int port=1900;
    
    public static void main(String[] args){
        Fx fx = new Fx();
    }
    
}
class Fx implements Runnable{
    
    Socket s;
    DataOutputStream dos;
    //DataInputStream dis;
    Thread snapper;
    Robot robot;
    BufferedImage cache;
    
    public Fx(){
        try{
            s=new Socket(Start.ip,Start.port);
           dos = new DataOutputStream(s.getOutputStream());
           //dis = new DataInputStream(s.getInputStream());
           snapper=new Thread(this);

           robot=new Robot();
           
           snapper.start(); 
        }catch(Exception ex){System.out.println("Error: "+ex);}
    }


    
    @Override
    public void run() {
        try{

            
            while(true){
              ImageIO.write(robot.createScreenCapture(new Rectangle(0, 0, 800, 480)),"bmp",dos); 
              Thread.sleep(33);
              //dis.read();
                
                
          }
            
            
        }catch(Exception ex){System.out.println("ThreadError: "+ex);}
    }
    
}

Z tego co sprawdziłem to tylko wątek który odbiera bajty się mozoli. Program na PC wyrabia bez problemu , wątek który tworzy Bitmap z tablicy bajtów i odświeżanie też się wyrabiają.

Wiem że wątek odpowiedzialny za odbieranie danych odbiera z prędkością ok. 1kl/s więc ok. 1MB/s. Nie sądzę żeby to były wszystkie możliwości portu USB :D
Pętla while obiera małe porcje danych więc musi dużo razy się wykonać i to spowalnia cały proces.

Wniosek z tego taki że odbieranie jest skopane i pasowało by je naprawić tylko jakich instrukcji mam użyć ?

@Antoniossss

Słowo readen to nie moja twórczość. Znalazłem tą instrukcje gdzieś na forum bo moja się bardziej mozoliła.

Twierdzisz coś o Bufforowaniu mógłbyś trochę przybliżyć mi to pojęcie bo wiem ze istnieje coś jak BufferemInputStream ?

0

Gdy tamtą funkcję zastąpiłem System.out.println(dis.read(recv,0,1152054)); to zaczęło wywalać w konsoli 4096 a obraz się nie wyświetlał. funkcja nie chce pobrać na raz tych 1152054B ;/

@Edit

Znalazłem gdzieś coś takiego :

 Bitmap bit=BitmapFactory.decodeStream(InputStream in);

próbowałem jej użyć ale zwraca cały czas null ;/

0

Powiedz nam dlaczego używasz BMP? PNG ma taką samą jakość i jest w dodatku dużo mniejsze, a z tym masz problem.

Pisałem parę dni temu b. podobny program - przesyłał przez wifi obraz ekran w czasie rzeczywistym, obraz mógł być przybliżony, a dla rozmiaru 800x480 transfery i FPSy wyglądały tak:
PNG

FPS: 6
252KB/s

dla BMP:

FPS: 1
1348KB/s
0

png trzeba dekodować a to kosztuje obliczenia procesora a poza tym usb spokojnie powinno wyrobić z prędkością. USB 2.0 ma teoretyczna prędkość 60MB/s wiec 30 powinno wyciągnąć. Problem nie tkwi w formacie obrazka tylko odczytywaniem bajtów z InputStream który ma za mały buffor i obciąża procesor.

Jeżeli uda mi się rozwiązać problem z bufforem to wypróbuje format png.

0

W zasadzie jednym miejscem, gdzie nie stosuje się kompresji obrazów jest obręb pamięci danego urządzenia - jej prędkość jest tak duża, że faktycznie łatwiej przesłać obraz bez kompresji. W każdym innym przypadku (no dobra, może jeszcze macierze RAID 0 i dyski SSD) szybciej będzie skompresować plik, przesłać mały i rozpakować.
Mówisz że obciąża procesor - no to masz dane, które wyrzuca mój telefon wymieniający się obrazem z telefonem:

dla PNG:

I/System.out(13066): Started reciving..
I/System.out(13066): Reciving time: 156
I/System.out(13066): Decoding: 73KB bitmap time: 44
I/System.out(13066): Drawing on canvas: 7

BMP:

I/System.out(13066): Started reciving..
I/System.out(13066): Reciving time: 1674
I/System.out(13066): Decoding: 1125KB bitmap time: 108
I/System.out(13066): Drawing on canvas: 8
0

redean
po 1) to jest twór słowopodobny - prawdopodobnie (na pewno) miało oznaczac po angielsku przeczytane (trzecia forma nieregularnego czasownika read- prawidłowo read)
po 2) nie instrukcja tylko w naszym przypadku nazwa zmiennej (którą zwyczajowo raczej można by nazwać c od counter, r od read, lub i - ale to ostatnie raczej do iteracji)

dalej - Jak Ci tłumaczyłem, że źle odczytujesz to nie docierało. Odczytujesz 4 kilobajty prawdopodobnie dlatego, że taki masz bufor strumienia wejściowego. Powyżej podałem Ci kod jak prawidłowo odczytywać dane ze strumienia. Zastosuj mój fragment kodu i wszystko powinno zacząć działać.

Jeżeli mowa o buforowaniu zapraszam do lektury dokumentacji/tutoriali oracle'a. Tam jest wszystko ładnie wyłożone.
http://docs.oracle.com/javase/tutorial/essential/io/buffers.html

0

Ostatecznie idąc na łatwiznę, utworz klasę z jednym polem typu byte[] i przesyłaj ją przez ObjectOutputStream, 100% pewności, dowolny rozmiar danych. Ja u siebie zrobiłem klasę Pakiet z polami int code (1 - daj mi rozdzielczość, 2 - wyswietl obraz itd) oraz Object data (null albo dane w zaleznosci od kodu, moze tez byc byte[]).

0

@Visher

Dzięki. Spróbuję.

EDIT:

Działa ale powoli 1kl/s trzeba popracowac nad bufforem.

@Antoniossss
Użyłem tych instrukcji co napisałeś w swoim pierwszym poście i dalej lipa. Odbiera te 4096B ;/ Próbowałem nawet zwiększyć buffor Socket'owi

s.setReceiveBufferSize(int size);

Program na pc jest na pewno w porządku, ponieważ napisałem PC'towy odpowiednik tego android'owego programu i tam buffor da się bez problemu ustawiać.

PS. mógłbyś zmodyfikować kod programu na "działający" bo albo te instrukcje co podajesz nie działają, albo ja nie umiem ich dobrze użyć w programie ? ;/

Kod:


public class Start extends Activity {

	public static int port = 1900;
	
	
	
	Socket s;
	//DataOutputStream dos;
	BufferedInputStream dis;
	Thread bind,drw;

	byte[] recv=new byte[1152054];
	int all=1152054;
	
	Screen screen;
	ByteArrayOutputStream ba;

	
	byte[] sBuff=new byte[1152054];
	
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        

        
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
        setContentView(R.layout.dev);
        
        
        FrameLayout fl =(FrameLayout)findViewById(R.id.czopx);
        
        screen=new Screen(this);
        fl.addView(screen);
        
		try{
			
		ServerSocket ss = new ServerSocket(Start.port);
		ss.setReceiveBufferSize(1152054);
		System.out.println("Bind!");
		s = ss.accept();
		s.setReceiveBufferSize(1152054);
		s.setSendBufferSize(1152054);
		System.out.println("Connected!");
		dis=new BufferedInputStream(s.getInputStream(),1152054);
		
		//dos=new DataOutputStream(s.getOutputStream());
		
		drw=new Thread(new Runnable(){

			public void run() {
				try{
				
				while(true){
					//System.out.println("START:REF!");
					Screen.cache=BitmapFactory.decodeByteArray(recv, 0,1152054);
					screen.refresh();
					Thread.sleep(33);
					//System.out.println("STOP:REF!");
				}
				}catch(Exception ex){System.out.println("DrawThreadError: "+ex);}
			}
			
			
		});
		
		
		bind=new Thread(new Runnable(){

			public void run() {
				
				try{
					
					while(true){
						
	
						
						
						
						ba=new ByteArrayOutputStream();
						int c=0;
						while((c=dis.read(sBuff))>0)
						{
						   ba.write(sBuff,0,c);
						   System.out.println(c);
						}
						 
						recv=ba.toByteArray(); 
						
						
			
						
						
				}
					
					
				}catch(Exception ex){System.out.println("ThreadError: "+ex);}
			}
			
			
		});
		bind.start();
		//drw.start();
		System.out.println("Launch Thread!");
	
		}catch(Exception ex){System.out.println("Error: "+ex);}       
        
        
    }
    


    
    
    
    
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_start, menu);
        return true;
    }

    
}

0
                       ba=new ByteArrayOutputStream();
                                                int c=0;
                                                while((c=dis.read(sBuff))>0)
                                                {
                                                   ba.write(sBuff,0,c);
                                                   System.out.println(c);
                                                }
 
                                                recv=ba.toByteArray(); 

ten fargment jest ok.
sBuff ma być rozmiaru jakoś 2KB a nie ponad tam 11k. Musisz zrozumieć, że sBuff jest tylko tymczasowym buforem odczytu. Z założenia NIE MAJĄ TAM WCHODZIĆ CAŁE DANE
Ty uparcie jednak robisz swoje - generalnie teraz ten kod działa tak samo jak Twój - jeden przebieg while'a
zadeklaruj sBuff=new byte[2048] albo 4096

Podaj jeszcze kod gdzie wysyłasz - ale pewnie jest taki jak tam wyżej wyżej. A no i jeszcze ważna sprawa - jak odróżniasz 2 transmisje od siebie ? - tylko że to akurat pytanie na później.
PS.
Nie inicjuj recv. To ma być zmienna niezainicjowana - ta tablica którą tworzysz na początku kodu i tak jest ani razu nie wykorzystana i zatracana;
Ciesz się, że java jest z założenia "zarządzana" bo miałbyś totalne wycieki pamięci przy takim działaniu :)

I jak wklejasz kod, to weź go sformatuj żeby go się jakoś ładnie czytało - wywal te wszystkie puste linie, niektóre tabulatory etc.

0

Zrobiłem tak jak kazałeś, do tego dodałem licznik który liczy ilość powtórzeń pętli. sBuff ustawiłem na 2048 i zostawiłem zmienną recv niezainicjowaną.


public void run() {
				
	try{
					
	while(true){
						
	count=0;
	ba=new ByteArrayOutputStream();
        int c=0;
                      
        while((c=dis.read(sBuff))>0)
        {
             ba.write(sBuff,0,c);
                           
             count++;
        }
        System.out.println(count);
        recv=ba.toByteArray(); 
	}
					
					
	}catch(Exception ex){System.out.println("ThreadError: "+ex);}
}

Odczytanie jednego obrazka zajmuje ok. 5s a System.out.println(count); wyrzuca 550 czasami trochę więcej albo mniej.

Kod wysyłający jest taki sam jak wyżej.

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