Reputation: 113
My scenario is as follows.
I have three computers A, B, C. Device A has a webcam. I want to stream and save the video from A to device B. At the same time, I want to watch the live video on C (from B).
I successfully managed to do the first part (saving video from A to B). But could not compete with the later part (watching live video on C).
Following are the codes that I used to build the client (on A) and server (on B) respectively.
# Client part - Device A
import cv2
import numpy as np
import socket
import sys
import pickle
import struct
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('your_real_IP_address',8089))
while True:
ret,frame=cap.read()
data = pickle.dumps(frame)
message_size = struct.pack("L", len(data))
clientsocket.sendall(message_size + data)
# Server part - Device B
import pickle
import socket
import struct
import cv2
HOST = ''
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()
data = b''
payload_size = struct.calcsize("Q")
result = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc(*'MJPG'), 5, (640, 480))
while True:
while len(data) < payload_size:
data += conn.recv(8192)
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("Q", packed_msg_size)[0]
while len(data) < msg_size:
data += conn.recv(8192)
frame_data = data[:msg_size]
data = data[msg_size:]
frame = pickle.loads(frame_data)
result.write(frame)
if cv2.waitKey(1) & 0xFF == ord('s'):
break
cv2.destroyAllWindows()
print("The video si successfully saved")
For the later part, I tried to run the following code on device C to fetch the live video from B (while it is getting saved). But I did not succeed. Can anyone please help me to fix this problem?
import socket
import pickle
import struct
import cv2
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host_ip = 'you_real_IP_address' # paste your server ip address here
port = 8090
client_socket.connect((host_ip, port))
data = b""
payload_size = struct.calcsize("L")
while True:
while len(data) < payload_size:
packet = client_socket.recv(8192)
if not packet: break
data += packet # append the data packet got from server into data variable
packed_msg_size = data[:payload_size] #will find the packed message size i.e. 8 byte, we packed on server side.
data = data[payload_size:] # Actual frame data
msg_size = struct.unpack("Q", packed_msg_size)[0] # meassage size
# print(msg_size)
while len(data) < msg_size:
data += client_socket.recv(8192) # will receive all frame data from client socket
frame_data = data[:msg_size] #recover actual frame data
data = data[msg_size:]
frame = pickle.loads(frame_data) # de-serialize bytes into actual frame type
cv2.imshow("RECEIVING VIDEO", frame) # show video frame at client side
key = cv2.waitKey(1) & 0xFF
if key == ord('q'): # press q to exit video
break
client_socket.close()
Upvotes: 1
Views: 2925
Reputation: 1616
You may use gstreamer for streaming. This answer assumes that you have gstreamer installed on A, B and C and opencv built with gstreamer support as well (at least on B).
Also note that streaming in raw mode may not be the most efficient way, but the following follows your description.
For A, the hard part is that you didn't give any details about your camera. Assuming it has a native mode of 640x480@30 fps, A code could be:
#!/usr/bin/env python
import time
import cv2
while True:
while True:
cap = cv2.VideoCapture("v4l2src device=/dev/video0 ! video/x-raw,width=640,height=480,framerate=30/1 ! videoconvert ! video/x-raw,format=BGR ! queue ! appsink drop=1", cv2.CAP_GSTREAMER)
if cap.isOpened():
print('Cam opened')
break
print('Waiting for camera')
time.sleep(1)
writer = cv2.VideoWriter("appsrc ! queue ! multipartmux ! tcpserversink port=8089 ", cv2.CAP_GSTREAMER, 0, 30.0, (640,480))
if not writer.isOpened():
print('Error: failed to open Writer')
exit()
print('Streaming camera')
while True:
ret_val, img = cap.read();
if not ret_val:
print('Failed to read from camera')
break
writer.write(img);
cv2.waitKey(1)
writer.release()
cap.release()
If you don't have opencv build with gstreamer support and don't need to do any processing, you can avoid opencv application and just run from shell:
gst-launch-1.0 v4l2src device=/dev/video0 ! video/x-raw,width=640,height=480,framerate=30/1 ! videoconvert ! video/x-raw,format=BGR ! multipartmux ! tcpserversink port=8089
For B:
#!/usr/bin/env python
import time
import cv2
while True:
while True:
cap = cv2.VideoCapture("tcpclientsrc host=<IP_of_A> port=8089 ! multipartdemux ! video/x-raw,format=BGR, width=640, height=480, framerate=30/1 ! queue ! appsink drop=1", cv2.CAP_GSTREAMER)
if cap.isOpened():
print('Cam stream opened')
break
print('Waiting for camera')
time.sleep(1)
writer = cv2.VideoWriter("appsrc ! video/x-raw,format=BGR,width=640, height=480, framerate=30/1 ! queue ! videoconvert ! video/x-raw,format=I420 ! tee name=t ! queue ! jpegenc ! avimux ! video/x-msvideo ! filesink location=test.avi t. ! queue ! multipartmux ! tcpserversink port=8192 ", cv2.CAP_GSTREAMER, 0, 30.0, (640,480))
if not writer.isOpened():
print('Error: failed to open Writer')
exit()
print('writer opened')
while True:
ret_val, img = cap.read();
if not ret_val:
print('Failed to read from camera')
break
#cv2.imshow('Test', img)
writer.write(img);
cv2.waitKey(1)
writer.release()
cap.release()
time.sleep(1)
where <IP_of_A> is the IP address of A streaming the camera. Note that the avi file will be overwritten after the source has disconnected/reconnected, you would have to manage file names for that.
For C:
#!/usr/bin/env python
import time
import cv2
while True:
while True:
cap = cv2.VideoCapture("tcpclientsrc host=<IP_of_B> port=8192 ! multipartdemux ! video/x-raw,format=I420, width=640, height=480, framerate=30/1 ! videoconvert ! video/x-raw,format=BGR ! queue ! appsink drop=1", cv2.CAP_GSTREAMER)
if cap.isOpened():
print('Stream opened')
break
print('Waiting for server')
time.sleep(1)
while True:
ret_val, img = cap.read();
if not ret_val:
print('Failed to read from server')
break
cv2.imshow('Received', img)
cv2.waitKey(1)
cap.release()
cv2.destroyAllWindows()
time.sleep(1)
where <IP_of_B> is the IP address of B.
If you don't have gstreamer support in opencv on C, you can just use :
gst-launch-1.0 tcpclientsrc host=<IP_of_B> port=8192 ! multipartdemux ! video/x-raw,format=I420, width=640, height=480, framerate=30/1 ! videoconvert ! xvimagesink
EDIT: In case only B has static IP, you can have A code to send to tcpclientsink host=<IP_of_B> instead of tcpserversink. So B would use tcpserversrc instead of tcpclientsrc host=<IP_of_A>. The limitation is that cv writer write() function doesn't return any value that could help to detect a failure such as B server being down... The only way I see would be to use a script launching python script and checking its output to detect the warnings issued by gstreamer backend writer code and in such case kill the program and restart a new one.
Upvotes: 3