美文网首页我爱编程HEXA机器人嵌入式 Linux C ARM
HEXA娱乐开发日志技术点006——日拱一卒拱歪了

HEXA娱乐开发日志技术点006——日拱一卒拱歪了

作者: 阿棍儿_Leon | 来源:发表于2018-07-15 13:17 被阅读7次

    HEXA开发日志目录
    上一篇 HEXA娱乐开发日志技术点005——死而复生之Gstreamer推流


    前言

    更新

    虽然没人看,但是还是要给自己一个说法。好久没有更新了,但是我并没有停止这个项目,只是从大步跑变成了小步慢跑,不信你看下图(L和G都是我,名字不同是因为不同主机的签名不同,后面我可能会改一下),我还是每周改了一点东西的,图中这个项目在码云上,只是用来装暂时代码的,就不公开了。
    这次虽然更新了,但是没有实际进展,就是把最近的失败尝试总结一下。


    困难

    进展缓慢并不是遇到了什么大难题,只是一些很繁琐的麻烦。

    1. 交叉编译好烦啊
      上回说到我用Gstreamer推流成功,下面要整合到skill内部,但是skill的编译在上位机进行,用Gstreamer需要用到Gstreamer的库和头文件,这个在上位机编译环境中没有。
      正面刚这个问题有两个办法,一是学习docker,在docker容器(上位机)里面交叉编译出Gstreamer库之后,保存这个容器为镜像,用这个镜像替代编译skill的镜像;二是直接把板子上的Gstreamer库和头文件放到docker容器里,更新镜像。
      当然还有其它方法,例如研究skill的编译过程,自己登陆到docker容器里把它搞定等。
      总之是没有姿势漂亮又学习成本低的方法。
    2. 进程通信好烦啊
      正面刚不行就迂回解决。我把程序分为了2部分,一部分控制Gstreamer,独立成一个程序,暂且叫它G server吧,另一部分在skill中,暂且叫它G client吧。server只管控制Gstreamer,直接在HEXA体内编译,client只管发送命令,不用到Gstreamer的东西,就可以直接在现在的上位机编译了。
      思路很简单,就是实现有点繁琐,包括信令制定和调试,当然最大的问题还在于我对这类程序的不熟悉产生的抗拒。唉~想想就头大。

    日拱一卒

    linux进程通信

    要两个程序相互配合,自然要进程通信,在linux环境下进程通信的方法百度都有,我不展开。我选的是使用套接字的方法,示例代码网上也一大堆。

    协议和信令制定

    我的协议和信令都是随便制定的,并没有参考优秀代码,因为平时接触不多,也不知道要参考什么,跟着感觉做吧。

    协议

    协议就是大家对话的语言规则,机器不会说人话,只认字节,协议给这些字节一个解释方法和发言规则,大家才能沟通。
    我的协议制定如下面代码,只有协议版本和信令两部分,发言规则就是client一句命令,server会有一个ACK回应。

    struct protocol {
        unsigned short version;
        //must be last member
        struct signal signal;
    };
    
    信令

    信令这个词是我从一起拿接触到的东西里拿来用的,它也是给字节流一个解释方法,相对于协议,信令像是一个字典,只可以用来对照理解意思,而协议还要规范大家的说话顺序。

    信令类型(enum eSignalType) 参数
    播放 播放地址url
    停止
    ACK 错误码enum result
    写字(未实现) 文字+位置+大小

    信令就是下面这个结构体

    struct signal {
        unsigned long length;//data长度
        unsigned long type;//信令类型
        unsigned long cmdId;//信令/命令编号,client每次发送+1,用来区分不同的发送
        unsigned char data[0];//柔性数组装参数
    };
    

    拱出的代码

    因为是我暂存代码库的代码,都很糙,仅供参考。

    client端

    主要就4个函数,open_clientclose_client分别是和套接字接力和解除绑定,start_clientstop_client分别发送播放和停止信令,最后main对它们进行测试。

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    #define UNIX_DOMAIN "/tmp/DDM.domain"
    #define PROTOCOL_VERSION 0
    #define RECEIVE_MAX 1024
    enum result {
        RESULT_OK,
        RESULT_ERR_FAIL,
        RESULT_ERR_VERSION,
        RESULT_ERR_LENGTH,
    };
    
    enum eSignalType{
        E_SIGNAL_START,
        E_SIGNAL_STOP,
        E_SIGNAL_ACK,
    };
    
    struct signal {
        unsigned long length;
        unsigned long type;
        unsigned long cmdId;
        unsigned char data[0];
    };
    
    struct protocol {
        unsigned short version;
        //must be last member
        struct signal signal;
    };
    static int connect_fd = -1;
    static struct sockaddr_un srv_addr = {
        .sun_family = AF_UNIX,
        .sun_path = UNIX_DOMAIN
    };
    
    static void dump(struct protocol *p){
        int i;
        printf("++++\n");
        printf("version=%u\n",p->version);
        printf("signal.length=%lu\n",p->signal.length);
        printf("signal.type=%lu\n",p->signal.type);
        printf("signal.cmdId=%lu\n",p->signal.cmdId);
        for (i = 0; i < p->signal.length; ++i){
            printf("%u ", p->signal.data[i]);
        }
        printf("\n");
        printf("----\n");
    }
    
    int open_client(void)
    {
        int ret = 0;
        printf("opening client.\n");
        if (connect_fd < 0)
        {
            connect_fd = socket(PF_UNIX,SOCK_STREAM,0);
            if(connect_fd < 0){
                printf("%s\n", strerror(errno));
                printf("creat socket error.\n");
                return connect_fd;
            }
    
            ret = connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
            if (ret < 0){
                printf("%s\n", strerror(errno));
                printf("connect server error.\n");
                close(connect_fd);
                connect_fd = -1;
                return ret;
            }
            printf("open client success.\n");
        }
        return ret;
    }
    
    void close_client(void)
    {
        printf("closing client.\n");
        close(connect_fd);
        connect_fd = -1;
        printf("close client success.\n");
    }
    int i;
    static unsigned int cmdId = 0;
    int start_client(char *url)
    {
        int ret = -1;
        struct protocol *pdata, data = {
            .version = PROTOCOL_VERSION,
            .signal = {
                .type = E_SIGNAL_START,
            },
        };
        int length = strlen(url);
        if (length <= 0 || length > 512){
            printf("url error");
            printf("%s\n", url);
            return -1;
        }
        data.signal.cmdId = cmdId++;
        data.signal.length = length + 1;
        pdata = (struct protocol *)malloc(sizeof(struct protocol) + data.signal.length);
        *pdata = data;
        memcpy(pdata->signal.data, url, length + 1);
        printf("starting client.\n");
        //dump(pdata);
        if (write(connect_fd, pdata, sizeof(struct protocol) + data.signal.length) == sizeof(struct protocol) + data.signal.length){
            if (read(connect_fd, pdata, sizeof(struct protocol) + data.signal.length) > 0){
                //printf("[%s %d]%d\n", __FUNCTION__, __LINE__);
                //dump(pdata);
                if (pdata->signal.cmdId == data.signal.cmdId && pdata->signal.type == E_SIGNAL_ACK){
                    ret = 0;
                    printf("got ACK error=%d\n", pdata->signal.data[0]);
                }
            }
            else{
                printf("[%s %d]read error\n", __FUNCTION__, __LINE__);
            }
        }
        free(pdata);
        if (ret){
            printf("fail to start client:");
        }
        else{
            printf("started client:");
        }
        printf("%s\n", url);
        return ret;
    }
    
    int stop_client(void)
    {
        int ret = -1;
        struct protocol *pdata, data = {
            .version = PROTOCOL_VERSION,
            .signal = {
                .type = E_SIGNAL_STOP,
            },
        };
        data.signal.cmdId = cmdId++;
        data.signal.length = 0;
        pdata = (struct protocol *)malloc(sizeof(struct protocol) + data.signal.length);
        *pdata = data;
        printf("stopping client.\n");
        //dump(pdata);
        if (write(connect_fd, pdata, sizeof(struct protocol) + data.signal.length) == sizeof(struct protocol) + data.signal.length){
            if (read(connect_fd, pdata, sizeof(struct protocol) + data.signal.length) > 0){
                //printf("[%s %d]%d\n", __FUNCTION__, __LINE__);
                //dump(pdata);
                if (pdata->signal.cmdId == data.signal.cmdId && pdata->signal.type == E_SIGNAL_ACK){
                    ret = 0;
                    printf("got ACK error=%d\n", pdata->signal.data[0]);
                }
            }
            else{
                printf("[%s %d]read error\n", __FUNCTION__, __LINE__);
            }
        }
        free(pdata);
        if (ret){
            printf("fail to stop client\n");
        }
        else{
            printf("stopped client\n");
        }
        return ret;
    }
    #include <unistd.h>
    
    int main(){
        int ret;
        pid_t pid;
        pid = fork();
        if (pid == 0){
            ret = system("./simple_server");
            printf("[%s %d] %d\n", __FUNCTION__, __LINE__, ret);
        }
        else{
            sleep(1);
            ret = open_client();
            printf("[%s %d] %d\n", __FUNCTION__, __LINE__, ret);
            if (!ret){
                ret = start_client("please start!");
                printf("[%s %d] %d\n", __FUNCTION__, __LINE__, ret);
                ret = stop_client();
                printf("[%s %d] %d\n", __FUNCTION__, __LINE__, ret);
                close_client();
            }
        }
    }
    
    server端

    NOT_IN_HEXA中包的代码是和Gstreamer相关的,把这部分注释起来是为了在任意类UNIX平台上调试。

    #include<stdio.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<sys/un.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    #include<string.h>
    
    #define NOT_IN_HEXA
    #ifndef NOT_IN_HEXA
    #include <gst/gst.h>
    #include <glib.h>
    #endif
    
    #define UNIX_DOMAIN "/tmp/DDM.domain"
    #define PROTOCOL_VERSION 0
    #define RECEIVE_MAX 1024
    
    enum result {
        RESULT_OK,
        RESULT_ERR_FAIL,
        RESULT_ERR_VERSION,
        RESULT_ERR_LENGTH,
    };
    
    enum eSignalType{
        E_SIGNAL_START,
        E_SIGNAL_STOP,
        E_SIGNAL_ACK,
    };
    
    struct signal {
        unsigned long length;
        unsigned long type;
        unsigned long cmdId;
        unsigned char data[0];
    };
    
    struct protocol {
        unsigned short version;
        //must be last member
        struct signal signal;
    };
    
    static struct sockaddr_un srv_addr = {
        .sun_family = AF_UNIX,
        .sun_path = UNIX_DOMAIN
    };
    
    static pthread_mutex_t mutex;
    static int running = 0;
    #ifndef NOT_IN_HEXA
    static GMainLoop *loop = NULL;
    static GstElement *pipeline, *videosrc, *text, *videoenc, *videoconvert, *muxer, *sink;
    static GstBus *bus;
    static void *start_loop(){
        /* Iterate */
        g_print ("Running...\n");
        running = 1;
        g_main_loop_run (loop);
        running = 0;
        return NULL;
    }
    #endif
    static void dump(struct protocol *p){
        int i;
        printf(">>>>\n");
        printf("version=%u\n",p->version);
        printf("signal.length=%lu\n",p->signal.length);
        printf("signal.type=%lu\n",p->signal.type);
        printf("signal.cmdId=%lu\n",p->signal.cmdId);
        for (i = 0; i < p->signal.length; ++i){
            printf("%u ", p->signal.data[i]);
        }
        printf("\n");
        printf("<<<<\n");
    }
    static enum result Start(int argc, char *argv[]){
        int ret = 0;
    #ifndef NOT_IN_HEXA
        pthread_t id;
        /* Initialisation */
        pthread_mutex_lock(&mutex);
        {
            if (loop != NULL){
                pthread_mutex_unlock(&mutex);
            return RESULT_ERR_FAIL;
            }
            gst_init (&argc, &argv);
            loop = g_main_loop_new (NULL, FALSE);
        }
        pthread_mutex_unlock(&mutex);
        if (argc < 3)
            return RESULT_ERR_FAIL;
        /* Create gstreamer elements */
        pipeline = gst_pipeline_new ("media-player");
        videosrc = gst_element_factory_make ("v4l2src",         "video-camrta-source");
        text     = gst_element_factory_make ("textoverlay",         "text");
        videoenc = gst_element_factory_make ("imxvpuenc_h264",  "video-h264-byte-stream");
        videoconvert = gst_element_factory_make ("h264parse",       "video-convert");
        muxer    = gst_element_factory_make ("flvmux",          "flv-muxer");
        sink     = gst_element_factory_make ("rtmpsink",      "sink");
    
        if (!pipeline || !videosrc || !text || !videoenc || !videoconvert || !muxer || !sink) {
            g_printerr ("One element could not be created. Exiting.\n");
            loop = NULL;
            return RESULT_OK;
        }
    
        /* Set up the pipeline */
        /* we set the input filename to the source element */
        g_object_set (G_OBJECT (sink), "location", argv[1], NULL);
        g_object_set (G_OBJECT (text), "text", argv[2], NULL);
        /* we add a message handler */
    
        /* we add all elements into the pipeline */
        gst_bin_add_many (GST_BIN (pipeline), videosrc, text, videoenc, videoconvert, muxer, sink, NULL);
        /* we link the elements together */
        if (gst_element_link (videosrc, text)){
            g_print ("link success %d\n", __LINE__);
        }
        else{
            return -1;
        }
        if (gst_element_link (text, videoenc)){
            g_print ("link success %d\n", __LINE__);
        }
        else{
            return -1;
        }
        if (gst_element_link (videoenc, videoconvert)){
            g_print ("link success %d\n", __LINE__);
        }
        else{
            return -1;
        }
        if (gst_element_link (videoconvert, muxer)){
            g_print ("link success %d\n", __LINE__);
        }
        else{
            return -1;
        }
        if (gst_element_link (muxer, sink)){
            g_print ("link success %d\n", __LINE__);
        }
        else{
            return -1;
        }
    
        /* Set the pipeline to "playing" state*/
        //g_print ("Now playing: %s\n", argv[1]);
        gst_element_set_state (pipeline, GST_STATE_PLAYING);
        ret = pthread_create(&id, NULL, (void*)start_loop, NULL);
    #endif
        if (ret){
            printf("create thread fail %d\n", __LINE__);
            return RESULT_ERR_FAIL;
        }
        return RESULT_OK;
    }
    
    static enum result Stop(){
    #ifndef NOT_IN_HEXA
        pthread_mutex_lock(&mutex);
        {
            int count = 0;
            if (loop == NULL){
                pthread_mutex_unlock(&mutex);
                return RESULT_ERR_FAIL;
            }
            g_main_loop_quit(loop);
            while (running && count < 10000) count++;
            g_print ("Returned, stopping playback\n");
            gst_element_set_state (pipeline, GST_STATE_NULL);
    
            g_print ("Deleting pipeline\n");
            gst_object_unref (GST_OBJECT (pipeline));
            g_main_loop_unref (loop);
            loop = NULL;
        }
        pthread_mutex_unlock(&mutex);
    #endif
        return RESULT_OK;
    }
    int i;
    
    void process_rcv(int client_fd, char *rcv_buff, ssize_t len) {
        ssize_t num;
        //parse
        struct protocol *data = (struct protocol*)rcv_buff;
        struct protocol *pfeedback, feedbackData = {
            .version = PROTOCOL_VERSION,
            .signal = {
                .type = E_SIGNAL_ACK,
            },
        };
        enum result error = RESULT_OK;
        if (!len) return;
        if (!data) {
            error = RESULT_ERR_FAIL;
        }
        if (data->version != PROTOCOL_VERSION) {
            error = RESULT_ERR_VERSION;
        }
        if (error == RESULT_OK
          && ((data->signal.length + sizeof(struct protocol)) > RECEIVE_MAX
            || data->signal.length + sizeof(struct protocol) != len
            || len > RECEIVE_MAX)) {
            printf("[%s %d] %zd\n", __FUNCTION__, __LINE__, len);
            error = RESULT_ERR_LENGTH;
        }
        //printf("[%s %d] %d\n", __FUNCTION__, __LINE__, error);
        //dump(data);
        //deal with
        if (error == RESULT_OK) {
            switch ((enum eSignalType)data->signal.type)
            {
                case E_SIGNAL_START:
                    error = Start(1, NULL);
                break;
                case E_SIGNAL_STOP:
                    error = Stop();
                break;
                default:
                    error = RESULT_ERR_FAIL;
                break;
            }
        }
        //feedback
        feedbackData.signal.cmdId = data->signal.cmdId;
        feedbackData.signal.length = sizeof(unsigned long);
        pfeedback = (struct protocol *)malloc(sizeof(struct protocol) + feedbackData.signal.length);
        if (pfeedback)
        {
            memcpy(pfeedback, &feedbackData, sizeof(feedbackData));
            ((unsigned long *)pfeedback->signal.data)[0] = (unsigned long)error;
            //printf("[%s %d]]\n", __FUNCTION__, __LINE__);
            //dump(pfeedback);
            num = write(client_fd, pfeedback, sizeof(struct protocol) + feedbackData.signal.length);
            printf("sent ACK for cmd %lu with result %u\n", data->signal.cmdId, error);
            free(pfeedback);
        }
    }
    int main() {
        int server_fd, client_fd;
        int ret = 0;
        ssize_t num = 1;
        int i;
        struct sockaddr_un client_addr;
        socklen_t addrlen;
        server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
        if(server_fd < 0){
            perror("connect creat communication socket");
            return server_fd;
        }
        unlink(UNIX_DOMAIN);
    
        ret = bind(server_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
        if (ret < 0) {
            perror("cannot bind server socket");
            goto exit;
        }
    
        ret = listen(server_fd, 1);
        if (ret < 0) {
            perror("cannot listen sockfd");
            goto exit;
        }
    
        pthread_mutex_init(&mutex, NULL);
        printf("server started\n");
        client_fd = accept(server_fd, NULL, &addrlen);
        if (client_fd < 0){
            perror("cannot accept requst");
            goto exit;
        }
    
        while (num){
            char rcv_buff[1024];
            memset(rcv_buff, 0, sizeof(rcv_buff));
            num = read(client_fd, rcv_buff, sizeof(rcv_buff));
            process_rcv(client_fd, rcv_buff, num);
        }
        close(client_fd);
    exit:
        close(server_fd);
        unlink(UNIX_DOMAIN);
        return ret;
    }
    

    拱歪了

    其实这点东西都很经典了,没什么难度的,拱歪的地方在于一句permission denied,只要用skill跑这些代码,server的bind函数和client的connect都会报这个错,server我可以用root手动跑起来,但是client我就没办法了。
    因为我的调试都是在HEXA的root下运行的,不会有权限问题,而且在MAC电脑上调试也没有需要这个权限。HEXA关于权限的控制在这里,能加的都加了还是不行。
    好在linux的进程通信还有其它方法,这个思路应该是可以的。


    下一篇 未出炉

    相关文章

      网友评论

        本文标题:HEXA娱乐开发日志技术点006——日拱一卒拱歪了

        本文链接:https://www.haomeiwen.com/subject/mpffyftx.html