Python canlı video gönderimi

Selamun Aleykum, Canlı video akışı için bir şeyler buldum (kusra bakmayın linki bulamadım)
lakin bazı yerleri anlamadım.Socket ile veri alış veriş yerleri tamam lakin struct ve pickle araya girince kafam karışıyor mantığını anlayamıyorum

server:

import socket
import sys
import cv2
import pickle
import numpy as np
import struct ## new

HOST='localhost'
PORT=8089

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print 'Socket created'

s.bind((HOST,PORT))
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'

conn,addr=s.accept()

### new
data = ""
payload_size = struct.calcsize("L") 
while True:
    while len(data) < payload_size:
        data += conn.recv(4096)
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("L", packed_msg_size)[0]
    while len(data) < msg_size:
        data += conn.recv(4096)
    frame_data = data[:msg_size]
    data = data[msg_size:]
    ###

    frame=pickle.loads(frame_data)
    print frame
    cv2.imshow('frame',frame)
    cv2.waitKey(0)

client.py:


import cv2
import numpy as np
import socket
import sys
import pickle
import struct ### new code
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))
while True:
    ret,frame=cap.read()
    data = pickle.dumps(frame)
    print data ### new code
    clientsocket.sendall(struct.pack("L", len(data))+data) ### new code

burada struct ve pickle ile olan yerleri anlamadım.Struct nedir araştırdım yine anlamadım yardımcı olur musunuz ?

NOT:(python 2 de yazılı ve çalışıyor evet yavaş ama tam mantığı anlamadığım için udp olarak daha ayarlayamadım )

3 Beğeni

Kodu anlamak için öncelikle bu iki kütüphanenin ne iş yaptığını anlamak lazım. Öncelikle struct kütüphanesine bakalım. Bu kütüphane, pack fonksiyonu ile, kendisine verilen parametrelere göre verileri belli formatlarda sıkıştırır. Mesela birkaç örnek yapalım

>>> struct.pack("i",15)
b'\x0f\x00\x00\x00'
>>> struct.pack("ii",15,12)
b'\x0f\x00\x00\x00\x0c\x00\x00\x00'
>>> struct.pack("iic",15,15,b'a')
b'\x0f\x00\x00\x00\x0c\x00\x00\x00a'

Birinci parametredeki “ii” gibi ifadeler, verilerin hangi tipte sıkıştırılacağını belirtmek için kullanılıyor. Mesela şimdi biz 15 sayısını sıkıştırırken “i” kullandık. 2 tane sayıyı sıkıştırmak için 2 tane i kullandık. Yani sıra ile gelen iki sayıyı. Şu alternatifi de kullanabiliriz.

>>> struct.pack("2i",15,12)
b'\x0f\x00\x00\x00\x0c\x00\x00\x00'

Geri dönüştürmek için de unpack() kullanılır.

>>> veri = struct.pack("2i",15,25)
>>> struct.unpack("2i",veri)
(15, 25)

Yalnız nasıl sıkıştırıldıysa, o şekilde tekrar geri dönüştürülmelidir. Hangi formatlarda sıkıştırma yapılabilir, hangi veri tipleri desteklenir görmek için

https://docs.python.org/2/library/struct.html#format-characters

pickle kütüphanesi de şu şekilde kullanılıyor.

>>> pickle.dumps("merhaba dünya")
b'\x80\x03X\x0e\x00\x00\x00merhaba d\xc3\xbcnyaq\x00.'

Bu kütüphane direkt paketliyor, veri tipine falan bakmıyor.

Karışık oldu farkındayım ama bunu anlatmam için bit,binary,endian gibi konuları da anlatmam gerekiyor. Onu da burada anlatacak vaktim yok malesef.

Peki bu kadar yazdın, sözün özü nedir diyeceksiniz. Şunu diyeyim, bu iki kütüphane verileri binary olarak bir bilgisayardan başka bir bilgisayara belli bir kalıp içinde göndermek için kullanılmıştır. Gönderme işlemini yapmıyorlar, sadece elinizdeki verileri bir kalıba koyuyorlar ve size veriyorlar. Mesela bu programda ekran görüntüsü alındığı zaman, görüntü binary yani ikili kod halinde elimize geçiyor. Bunu pickle ile paketleyip karşı tarafa gönderiyoruz, karşı tarafta da, gelen bu paketi çözüyoruz. Ha şimdi bunun gereği var mı? Hayır tabi ki, bu kütüphaneler kullanılmadan da gönderim yapılabilir. Ama kullanılmasında da yarar vardır.

Umarım anlaşılır olmuştur. Bu konuları pek anlatmayı beceremiyorum…

2 Beğeni

Nasıl yapılabilir ki sadece bir taraftan alınan videoyu canlı olarak aktarmak istiyorum. Ayrıca Server kısmında gelen veri sürekli parçalara ayrılıyor orası nasıl oluyor anlamadım. Endian nedir araştırdım ama anlamadım daha doğrusu işlemcilere göre değişiyor falan vs. Gene aklımda soru kalıyor niye çevriliyor? Neden o şeklide de başka şekilde değil?

1 Beğeni

Rica etsem, kodun ilgili kısımlarını parçar parça göstererek sorunu tekrar sorabilir misin? Yanlış anlama, direkt ilgili satırdan sana cevap vermek isterim.

1 Beğeni


while len(data) < payload_size:
        data += conn.recv(4096)
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("L", packed_msg_size)[0]
    while len(data) < msg_size:
        data += conn.recv(4096)
    frame_data = data[:msg_size]
    data = data[msg_size:]


Evet en basitinden anladım sıkıştırıyor ve gönderiyor ama ben progtamlarımda bunu ya da buna benzer bir şey kullanmak için ezber yapmam lazım onu da programlama da yapmam. Buradaki kodda neler yapılıyor?


clientsocket.sendall(struct.pack("L", len(data))+data) ### new code


Ve burada neden verinin uzunluğu ile veri gidiyor?

1 Beğeni

İlk kodda veri gönderilmiyor, alınıyor. Sanırım yanlış olmuş. Ben ikinci kodu anlatayım önce, sonra da birincisini anlatmaya çalışayım.

İlk olarak şu kodda

clientsocket.sendall(struct.pack("L", len(data))+data) ### new code

Neden veri uzunluğu ve veri gidiyor demişsin. Şunun için. Karşı taraf önce paketin boyutunu şu şekilde öğreniyor.

    while len(data) < payload_size:
        data += conn.recv(4096)

Karşı taraftan alacağı resmin boyutunu elde etti buraya kadar. Nereye kadar?

payload_size = struct.calcsize("L") 

İşaretsiz long bir sayının boyutu kadar. Sonra ne yapıyor? Hani paketlenmiş olarak geldi ya, işte onu çözüyor

msg_size = struct.unpack("L", packed_msg_size)[0]

Güzel. Şimdi karşıdaki resmin boyutunu biliyoruz. Alalım bakalım.

    while len(data) < msg_size:
        data += conn.recv(4096)

Şimdi bu veriyi çözelim

frame=pickle.loads(frame_data)

Gösterelim ekranda

cv2.imshow('frame',frame)

Peki neden boyutunu göndermesi gerekiyor? Çünkü programın aldığı her ekran görüntüsünün boyutu aynı olmuyor. Bu yüzden gönderileceği zaman karşı taraf önce boyutu öğreniyor, sonra da bu boyut kadar while döngüsüne girip veriyi tam olarak alabiliyor, yani resmin tam halini.

1 Beğeni
struct.calcsize("L")

4 sonucu veriyor o zaman 
while len(data) < 4:
        data += conn.recv(4096)


Burada zaten ilk aldığında 4096 lık yer almıyor mu ? Neden while döngüsü var ki tek aşamada gelemez mi ? Ayrıca serverda 2 kez döngüye girilmiş nasıl oluyor da 2 kez veri alınıyor? Halbuki client tarafı tek bir defada gönderiyor verinin uzunluğu ve veriyi.

1 Beğeni

Evet doğru. Ancak veriyi 1024’lük parçalar halinde de alabilirdi, yada veri uzunluğunu sonradan “long long” yani “q” olarak da değiştirebilirdi. Bu veri tipi 8 bayt. Yani 8 defa döngü ihtiyacı doğacak. While döngüsünü oraya sonradan ekleme zahmetini de kaldırmış oluyor.

2 kez veri alma olayı şöyle. Karşı tarafın gönderdiği verinin tamamını okuyana kadar, veri okumada bekler. Sen 2 baytlık verinin 1 baytını bir döngüde, 1 baytını da başka bir döngüde alabilirsin.

1 Beğeni

S1) Neden veriden once veri boyu gonderiliyor?
C1) TCP stream-oriented tabir edilen bir protokol (yukaridaki koddaki SOCK_STREAM'e de dikkat ediniz). Bu, bir taraftan gonderilen byte’larin diger taraftan ayni sirada alinmasi demek. Gonderilen bir seyin boyutunun alici tarafindan bilinebilmesi icin bu bilginin bir sekilde TCP disi bir kanaldan ulastirilmasi lazim. (Sabit bir boyutta anlasilabilir, gonderilen seylerin basi/sonu isaretlenebilir, veya her gonderinin onunden boyutu gonderilebilir.)

S2) Neden while ...: recv(4096) kullanilmis?
C2) Aslinda okunmak istenen verinin boyutu belli, ilk seferde payload_size, ikincide msg_size. Burada dogru noktada yaratilip duzgun isimlendirilen fonksiyonlarin onemini vurgulamak isterim: Bu dongulerin aslinda soyle bir kodla degistirilmesi lazim:
data = su_kadar_byte_veri_al(msg_size) # Duzgun isimlendirmeye ornek olmayabilir

Kullanilan Python socket.recv() fonksiyonu, yeterli soyutlamayi saglayamamis. 1983’teki 4.2BSD’den gelen (ve hala POSIX ve Winsock implementasyonlarinda kullanilan) recv(2) fonksiyonuna gidecek parametreyi Python programcisindan bekliyor. Fonksiyonun varligi sorun degil, fakat pickle ve cv2 gibi high-level kutuphanelerin yaninda abes kaciyor.

2 Beğeni