epoll + socket

4 篇文章 0 订阅
2 篇文章 0 订阅
本文介绍了一个C++实现的视频播放程序,使用了epoll和多线程技术。程序从服务器端接收视频帧并通过网络发送给客户端。服务器端采用epoll进行事件监听,处理新连接和数据传输。客户端接收到数据后,将其转化为OpenCV的Mat对象进行显示。文章探讨了单线程epoll实现的帧同步与多线程方案的异步播放特点。
摘要由CSDN通过智能技术生成

功能 读取视频并播放
注释部分为多线程方案

  • 单线程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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值