2016-04-07 2 views
1

Ich versuche, Live-View einer SONY FDR-X1000V Kamera auf dem Desktop zu streamen. Ich benutze Python, um die API aufzurufen und das Paket herunterzuladen und opencv zu verwenden, um jpeg zu dekodieren. Wenn ich es starte, kann es kaum ein Bild pro Sekunde fangen. Später fand ich heraus, dass die Nutzlastgröße eines JPEGs 8MB betragen kann. FDR-X1000V unterstützt jedoch keine Änderung der Größe der Live-Ansicht. Aber wenn ich die App auf dem iPhone verwende, um den Live-View zu erstellen, streamt es reibungslos. Also hier ist meine Frage: 1. Ist es normal, dass eine JPEG-Payload so groß wie 8 MB sein kann? 2. Wenn ja, wie kann ich die Live-Ansicht flüssig übertragen?Sony Camera Remote API, Live-View-Streaming langsam

Hier ist mein Code:

try: 
    result = api.do('startLiveview') 
    url = result['result'][0] 
except KeyError: 
    print result 
f = urllib2.urlopen(url) 
buff = '' 
chunk_size = 32768 
for i in xrange(3000): 
    if len(buff) < chunk_size: 
     time_s = time.time() 
     buff = buff + f.read(chunk_size) 
     print "Download Speed %f KB/s"%(chunk_size/1000/(time.time() - time_s)) 
    time_s = time.time() 
    start_code = ''.join(buff).find('$5hy') 
    # print "LCS time cost", time.time() - time_s 

    if start_code < 0: 
     buff = buff[-12:] 
     print "skip", len(buff)-12 
    elif start_code < 8: 
     buff = buff[8:] 
    else: 
     if start_code > len(buff) - 129: 
      buff = buff + f.read(chunk_size) 
     payload_type = ord(buff[start_code-7]) 
     payload_size, = struct.unpack('<I', buff[start_code+4:start_code+8].ljust(4,'\0')) 
     padding_size = ord(buff[start_code+8]) 
     print "Type:%d\tPayload:%d\tPadding:%d\t"%(payload_type,payload_size,padding_size) 

     buff = buff[start_code+128:] 
     if payload_type == 1: 
      if payload_size + padding_size > len(buff): 
       time_s = time.time() 
       download_size = payload_size+padding_size-len(buff) 
       buff = buff + f.read(download_size) 
       print "Download Speed %f KB/s"%(download_size/1000/(time.time() - time_s)) 
      img_data = buff[:payload_size] 
      buff = buff[payload_size:] 

      time_s = time.time() 
      d = np.asarray(bytearray(img_data), dtype='uint8') 
      img = cv2.imdecode(d,cv2.IMREAD_COLOR) 
      cv2.imshow('postview',img) 
      cv2.waitKey(30) 
      # print "Decode time cost", time.time() - time_s 

Einige Ausgang:

Type:1 Payload:8410624 Padding:0 
Download Speed 679.626326 KB/s 

Antwort

0

Die Antwort von @Simon Wood und @Robert - Sony sind alle sehr nützlich! Ich habe zwei Fehler in meinem Code gemacht.

Einer ist die Nutzlast Größe ist 3 Bytes statt 4 Bytes.

Eine andere ist, dass ich angenommen habe, dass die Byte-Reihenfolge Little-Endian statt Big-Endian ist.

Das von @Simon Wood erwähnte Projekt ist ein gutes Projekt. Es implementiert zwar keine Resynchronisierung für den 4-Byte-Startcode.

Um dieses Recht zu machen, ich meine eigenen Codes in zwei Methoden schreiben, es zu überprüfen und neu synchronisieren:

try: 
    result = api.do('startLiveview') 
    url = result['result'][0] 
except KeyError: 
    print result 
f = urllib2.urlopen(url) 

#method 1 
buff = '' 
chunk_size = 8192 
for i in xrange(300): 
    if len(buff) < chunk_size: 
     time_s = time.time() 
     buff = buff + f.read(chunk_size) 
     # print "Download Speed %f KB/s"%(chunk_size/1000/(time.time() - time_s)) 
    time_s = time.time() 
    start_code = ''.join(buff).find('$5hy') 
    # print "LCS time cost", time.time() - time_s 

    if start_code < 0: 
     buff = buff[-12:] 
     print "skip", len(buff)-12 
    elif start_code < 8: 
     buff = buff[8:] 
     print "skip a header" 
    else: 
     if start_code > len(buff) - 129: 
      buff = buff + f.read(chunk_size) 
     start_byte = ord(buff[start_code - 8]) 
     payload_type = ord(buff[start_code - 7]) 
     sequence_num, = struct.unpack('>I', buff[start_code - 6:start_code - 4].rjust(4,'\0')) 
     time_stamp, = struct.unpack('>I', buff[start_code - 4:start_code].rjust(4,'\0')) 
     payload_size, = struct.unpack('>I', buff[start_code+4:start_code+7].rjust(4,'\0')) 
     padding_size = ord(buff[start_code+8]) 
     print "StartByte:%d\t sequenceNum:%d\t timeStamp:%d\t Type:%d\t Payload:%d\t Padding:%d\t"%(
      start_byte,sequence_num,time_stamp,payload_type,payload_size,padding_size) 

     buff = buff[start_code+128:] 
     if payload_type == 1: 
      if payload_size + padding_size > len(buff): 
       time_s = time.time() 
       download_size = payload_size+padding_size-len(buff) 
       buff = buff + f.read(download_size) 
       # print "Download Speed %f KB/s"%(download_size/1000/(time.time() - time_s)) 
      img_data = buff[:payload_size] 
      buff = buff[payload_size:] 

      time_s = time.time() 
      d = np.asarray(bytearray(img_data), dtype='uint8') 
      img = cv2.imdecode(d,cv2.IMREAD_COLOR) 
      cv2.imshow('postview',img) 
      cv2.waitKey(10) 
      # print "Decode time cost", time.time() - time_s 

#method 2 
def checkbyte(f): 
    if f.read(4) == '$5hy': 
     return 
    state = 0 
    i = 1 
    while 1: 
     i+=1 
     if state == 0 : 
      if f.read(1) == '$': 
       state = 1 
      else: 
       state = 0 
     if state == 1 : 
      if f.read(1) == '5': 
       state = 2 
      else: 
       state = 0 
     if state == 2 : 
      if f.read(1) == 'h': 
       state = 3 
      else: 
       state = 0 
     if state == 3 : 
      if f.read(1) == 'y': 
       state = 4 
      else: 
       state = 0 
     if state == 4 : 
      print 'skip', i 
      return 
for i in xrange(300): 
    buff = f.read(8) 
    start_byte ord(buff[0]) 
    payload_type, = struct.unpack('>I',buff[1].rjust(4,'\0')) 
    sequence_num, = struct.unpack('>I',buff[2:4].rjust(4,'\0')) 
    time_stamp, = struct.unpack('>I',buff[4:8]) 

    #payload header 
    checkbyte(f) 
    buff = f.read(124) 
    payload_size, = struct.unpack('>I',buff[0:3].rjust(4,'\0')) 
    padding_size= ord(buff[3]) 

    print "StartByte:%d\t sequenceNum:%d\t timeStamp:%d\t Type:%d\t Payload:%d\t Padding:%d\t"%(
      start_byte,sequence_num,time_stamp,payload_type,payload_size,padding_size) 
    d = f.read(payload_size) 
    if padding_size > 0: 
     f.read(padding_size) 

    if payload_type == 1: 
     # Type = 0x01 
     d = np.asarray(bytearray(d), dtype='uint8') 
     img = cv2.imdecode(d,cv2.IMREAD_COLOR) 
     cv2.imshow('postview',img) 
     cv2.waitKey(1) 

print api.do('stopLiveview') 
0

Ein bisschen schamlose Förderung - aber es könnte Ihnen helfen. Hast du es mit Pysony-Code versucht?

Sie müssen manuell die QX_ADDR gesetzt, aber dies sollte Ihnen ein ‚schneller‘ Live View-Stream ... https://github.com/Bloodevil/sony_camera_api/issues/22

Das pygameLiveView Beispiel die Bausteine ​​benötigt hat, dann würden Sie nur die Notwendigkeit zu zwicken jpeg loading in openCV anstelle von pygame zu ziehen

+0

Ich habe versucht, dass 'pysony' jedoch nicht implementiert Resynchronisation auf dem 4-Byte-Start Code. Dies kann zu Fehlern in der Praxis führen. – Jason

0

Ich glaube, das Problem möglicherweise, dass Sie analysieren "Payload Datengröße ohne Padding Größe" in "LiveView Payload Header" als 4 Bytes Daten anstelle von 3 Byte Daten. In diesem Fall sieht die Nutzlast größer aus als erwartet. Dies wird in der Dokumentation unter "Format der Liveview-Daten JPEG-Container" erläutert. Versuchen Sie, dies zu ändern und lassen Sie mich wissen, ob es immer noch nicht für Sie funktioniert.

+0

Ja, Sie haben Recht, aber das Wichtigste, was in dem Dokument nicht erwähnt wird, ist über Little-Endian und Big-Endian. Hier in meinem Code nahm ich an, dass es Little-Endian ist, was dazu führte, dass die meisten Dinge nicht funktionieren. Eine interessante Tatsache ist, dass, obwohl ich die Nutzlastgröße (die immer größer ist als die reale Größe) vermisse, OpenCV immer das Bild entschlüsseln kann, was mich glauben lässt, dass die Nutzlastgröße richtig ist. – Jason