功能 读取视频并播放
注释部分为多线程方案
- 单线程epoll方案每帧都要请求,可以做到帧同步
- 多线程方案可以不用每帧都请求,不能做到帧同步, 各发各的
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <opencv2/opencv.hpp>
#include <pthread.h>
#include <fcntl.h>
#define MAXLINE 1024
#define MAX_EVENTS 50
//epoll描述符
int epollfd;
//事件数组
struct epoll_event eventList[MAX_EVENTS];
ssize_t writen(int fd, const void *vptr, size_t n) //发送完全
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
//线程函数
void * playVideo(void* pargs){
printf("hello thread\n");
int connfd = *((int*)pargs);
printf("connfd_in %d\n",connfd);
//打开相关
cv::VideoCapture cap;
cap.open("/home/wdh/projects/viewEngine/videos/facedemo.mp4");
if(!cap.isOpened()){
return NULL;
}
int totalFrames = cap.get(CV_CAP_PROP_FRAME_COUNT);
int currentFrames = 0;
int H = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
int W = cap.get(cv::CAP_PROP_FRAME_WIDTH);
int C = 3;
unsigned char * databuf = (unsigned char *)malloc(H*W*C*sizeof(unsigned char));
cv::Mat frame;
while(1){
usleep(100);
//取帧
if (currentFrames == totalFrames - 1){
currentFrames = 0;
cap.set(CV_CAP_PROP_POS_FRAMES, 0);
}else{
++currentFrames;
}
cap>>frame;
for(int r=0;r<frame.rows;++r){
memcpy(databuf+r*W*C,frame.ptr<unsigned char>(r),W*C*sizeof(unsigned char));
}
// send(connfd,databuf,H*W*C*sizeof(unsigned char),0);
writen(connfd,databuf,H*W*C*sizeof(unsigned char));
}
free(databuf);
return NULL;
}
int main(int argc, char** argv){
int listenfd;
struct sockaddr_in servaddr;
char buff[1024];
int n;
//打开相关
cv::VideoCapture cap;
cap.open("/home/wdh/projects/viewEngine/videos/facedemo.mp4");
if(!cap.isOpened()){
return 1;
}
int totalFrames = cap.get(CV_CAP_PROP_FRAME_COUNT);
int currentFrames = 0;
int H = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
int W = cap.get(cv::CAP_PROP_FRAME_WIDTH);
int C = 3;
unsigned char * databuf = (unsigned char *)malloc(H*W*C*sizeof(unsigned char));
cv::Mat frame;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
//设置socket为非阻塞模式
int flags = fcntl(listenfd, F_GETFL, 0);
fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
//在服务端bind or listen前,复用端口
unsigned int value = 0x1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(void *)&value,sizeof(value));
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
printf("======waiting for client's request======\n");
// epoll 初始化
epollfd = epoll_create(MAX_EVENTS);
struct epoll_event event;
event.events = EPOLLIN|EPOLLOUT|EPOLLET;
event.data.fd = listenfd;
//添加epoll事件
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event)<0){
printf("epoll add fail : fd = %d\n",listenfd);
return -1;
}
// std::map<int,int> fd_status;
while(1){
printf("epoll wait\n");
//获取返回事件
int ret = epoll_wait(epollfd,eventList,MAX_EVENTS,3000);
printf("epoll wait 2 %d\n",ret);
if(ret < 0){
printf("epoll error\n");
continue;
}else if(ret == 0){
printf("timeout ... \n");
continue;
}
//取帧
if (currentFrames == totalFrames - 1){
currentFrames = 0;
cap.set(CV_CAP_PROP_POS_FRAMES, 0);
}else{
++currentFrames;
}
cap >> frame;
for(int i=0;i<ret;++i){
// if(eventList[i].events & EPOLLERR ||
// eventList[i].events & EPOLLHUP ||
// eventList[i].events & EPOLLIN){
// printf("epoll error\n");
// close(eventList[i].data.fd);
// }
if(eventList[i].data.fd == listenfd){
int connfd;
//添加新连接
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
struct epoll_event event;
event.data.fd = connfd;
event.events = EPOLLIN|EPOLLET;
printf("before_control %d\n",eventList[i].data.fd);
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&event);
printf("connfd_out %d\n",eventList[i].data.fd);
// pthread_t thread_id;
// pthread_create(&thread_id,NULL,playVideo,(void*)&connfd);
}else{
int connfd = eventList[i].data.fd;
if(1){
n = recv(connfd, buff, MAXLINE, 0);
printf("%d,%d\n",currentFrames,n);
printf("recv msg from client:%s\n",buff);
if(buff[0]=='2' || n<=0){
close(connfd);
continue;
}
for(int r=0;r<frame.rows;++r){
memcpy(databuf+r*W*C,frame.ptr<unsigned char>(r),W*C*sizeof(unsigned char));
}
// send(connfd,databuf,H*W*C*sizeof(unsigned char),0);
writen(connfd,databuf,H*W*C*sizeof(unsigned char));
memset(buff,0,MAXLINE);
}
}
}
}
close(epollfd);
close(listenfd);
free(databuf);
return 0;
}
客户端
from socket import *
import numpy as np
import cv2
HOST = '127.0.0.1' # The remote host
PORT = 6666 # The same port as used by the server
H=720
W=1280
C=3
def startClient():
BUFSIZE = H*W*C
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
# data = input('> ')
# if not data:
# break
data='1'
tcpCliSock.send(data.encode())
data_buf = tcpCliSock.recv(BUFSIZE,MSG_WAITALL)
print(BUFSIZE,len(data_buf))
frame = np.frombuffer(data_buf,"uint8",BUFSIZE,0)
frame = frame.reshape((H,W,C))
print(frame)
print(frame.shape)
cv2.imshow("frame",frame)
key = cv2.waitKey(100)
if(key==ord('q')):
data='2'
tcpCliSock.send(data.encode())
tcpCliSock.close()
if __name__ == "__main__":
root = startClient()
root.mainloop()
后台为多线程则不用每次都send