当前位置:   article > 正文

[Ubuntu]Scrcpy+Zeromq实现手机屏幕yuv数据传输,并通过OpenCV实现连续播放——(二)(思路+代码解析)_scrcpy zmq

scrcpy zmq

        Scrcpy在上一篇博客中有所介绍,并且使用Scrcpy实现了手机屏幕yuv数据的提取([Ubuntu]Scrcpy获取手机屏幕yuv数据_又是谁在卷的博客-CSDN博客)。本文将介绍一个当下较为好用的消息中间件—Zeromq。通过Zeromq中间件对数据进行传输,我们最终通过opencv进行内存的数据读取,并实现连续播放的效果。

        往下阅读之前,记得看我的往期博客了解如何提取yuv数据呀([Ubuntu]Scrcpy获取手机屏幕yuv数据_又是谁在卷的博客-CSDN博客),这里就不再过多介绍yuv提取的知识了。接下里就开始实现Scrcpy+Zeromq实现手机屏幕yuv数据传输,并通过OpenCV实现连续播放。

目录

1. Zeromq简单介绍,以及如何与Scrcpy项目源码进行对接(具体思路+代码解析)

1.1Zeromq简单介绍:Zeromq官网(ZeroMQ)

1.2.接下来让我们思考和Scrcpy对接时需要思考的的问题和流程框架。

 1.3.代码部分,针对以上结论进行实践

 1.3.1将yuv数据通过结构体进行封装

1.3.2将对应数据放入结构体

1.3.3在Scrcpy中创建Zeromq的PUB(发布者)


1. Zeromq简单介绍,以及如何与Scrcpy项目源码进行对接(具体思路+代码解析)

1.1Zeromq简单介绍:Zeromq官网(ZeroMQ

        简单来说,ZeroMQ嵌入式网络编程库的形式实现了一个并行开发框架。能够提供进程内(inproc)、进程间(IPC)、网络(TCP)和广播方式的消息信道,并支持扇出(fan-out)、发布-订阅(pub-sub)、任务分发(task distribution)、请求/响应(request-reply)等通信模式。与传统的消息中间件,Zeromq大大简化了消息传输的中间爱你过程,具有简介易操作的特点。(这里简单介绍,具体细节请参考官方文档。)

1.2.接下来让我们思考和Scrcpy对接时需要思考的的问题和流程框架。

        如果看过我的上一篇博客,我们知道yuv数据提取的过程是有顺序的。那我们就迎来了第一个需要思考的问题,

         1.我们如何将正确的顺序通过怎样的载体进行运输呢?

        这之中可能会有很多有趣的想法(比如yuv分开发送、yuv作为编码为字符串一起直接传输等等)。但是较好的方式是创建一个结构体作为yuv数据的载体,将y、u、v分别作为结构体单独的属性。通过结构体作为载体还有一个好处就是,我们每台设备的分辨率是不同的,分辨率在opencv的脚本中也需要用到(用于还原yuv数据),所以可以放在结构体里一起发送。

        载体选择完之后,yuv数据包装完毕之后,在发送之前。我们遇到了第二个问题。

        2.通过了解我们知道Zeromq有四个模型,我们需要选取最合适的模型。

        Zeromq中最常见的三种种基础模型

                1. REQ/REP 请求响应模型

                2. PUB/SUB发布订阅模型

                3. Pipeline pattern 管道模式

        我们要根据需求对模型进行选择。手机屏幕yuv数据的传输是从Scrcpy运行开始之后就源源不断的。也就是说,不管接收者是否受到数据,我们的发送端都不会停止发送。所以显而易见,在这里 2. 发布订阅者更合适。发布者(PUB)设置在Scrcpy的项目端,而订阅者(SUB)则设置在与opencv对接的python脚本中。

        选定模型之后,通过Zeromq的传输,在另一端和python脚本对接之后通过opencv呈现即可(后面会有代码解析)

 1.3.代码部分,针对以上结论进行实践

 1.3.1将yuv数据通过结构体进行封装

        因为yuv数据在scrcpy-master/app/src/decode.c文件中,所以我们将结构体创建在它的头文件中(decode.h)。代码如下图所示:

  1. # 在decode.h添加如下代码
  2. # 这里的576*1152是我手机分辨率除以1.875,这里算是压缩的操作
  3. #define yuv_buf_size 576*1152
  4. typedef struct{
  5. int cxt_width;
  6. int cxt_height;
  7. uint8_t data_y[yuv_buf_size];
  8. uint8_t data_u[yuv_buf_size/4];
  9. uint8_t data_v[yuv_buf_size/4];
  10. }cxt_frame;

1.3.2将对应数据放入结构体

        以下代码和提取yuv数据时的操作如出一辙,也是在存有AVFrame的函数中(再次友情提醒:以下代码如果有疑惑,请参考我的上一篇博客)。我们通过memcpy函数将yuv数据分别拷贝到结构体对应的属性中。

  1. # 创建结构体
  2. cxt_frame yuv_frame;
  3. # 定义长宽
  4. int yuv_width = decoder->codec_ctx->width;
  5. int yuv_height = decoder->codec_ctx->height;
  6. yuv_frame.cxt_width = yuv_width;
  7. yuv_frame.cxt_height = yuv_height;
  8. # buf_size_aline用于对齐,以下存储yuv的操作和上一篇博客中提取yuv的操作如出一辙
  9. int buf_size_aline = 0;
  10. for(int i = 0;i<yuv_height;i++)
  11. {
  12. memcpy(yuv_frame.data_y + buf_size_aline,frame->data[0]+frame->linesize[0]*i,yuv_width);
  13. buf_size_aline += yuv_width;
  14. }
  15. buf_size_aline = 0;
  16. for(int i = 0;i<yuv_height/2;i++)
  17. {
  18. memcpy(yuv_frame.data_u + buf_size_aline,frame->data[1]+frame->linesize[1]*i,yuv_width/2);
  19. buf_size_aline += yuv_width/2;
  20. }
  21. buf_size_aline = 0;
  22. for(int i = 0;i<yuv_height/2;i++)
  23. {
  24. memcpy(yuv_frame.data_v + buf_size_aline,frame->data[2]+frame->linesize[2]*i,yuv_width/2);
  25. buf_size_aline += yuv_width/2;
  26. }
  27. # 这里是通过Zeromq进行传输结构体数据的操作,这里暂时先不解释,到后面讲解zeromq时会回到此处进行解析
  28. zmq_send(responder,&yuv_frame,sizeof(yuv_frame),ZMQ_DONTWAIT);

1.3.3在Scrcpy中创建Zeromq的PUB(发布者)

        我们在这之前需要清楚,Scrcpy是多线程的。发送消息的指令可以写在和yuv提取相同的位置。但是创建Zeromq发布者对象的时候不行,因为我们只要创建一次即可。写在函数中会反复创建(无法多次创建,会冲突)。所以我们必须找到这个线程进行初始化操作的地方创建Zeromq对象。在ubuntu中,我们可以在终端输入以下命令通过查找调用函数的位置,一步步向上层寻找。直到找到此线程的初始化位置,查找命令如下:

  1. # 在此目录下的.c文件中查找名为“push_frame”的位置
  2. $ find. -name "*.c" | xargs grep -n "push_frame"

 接下来在Zeromq中使用的函数可以在这篇博客里找到解析(ZeroMQ教程中文版_神马_逗_浮云的博客-CSDN博客_zeromq中文

经过查找,我们发现此线程的源头在名为stream.c的文件中,我们将在开始无条件for循环之前创建zeromq对象。代码和输入位置如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-I5piv6LCB5Zyo5Y23,size_20,color_FFFFFF,t_70,g_se,x_16

  1. #创建新的zeromq环境
  2. void *context = zmq_ctx_new ();
  3. # 这里有一个responder对象,我是将它初始化在stream.h的文件中了
  4. # 选择ZMO_PUB发布订阅模型
  5. responder = zmq_socket (context, ZMQ_PUB);
  6. # 端口号,主机的随便一个可用的端口都行
  7. int rc = zmq_bind (responder, "tcp://127.0.0.1:5565");
  8. # 如果连接端口失败,则rc返回值为0
  9. assert (rc == 0);
  10. #最后别忘了在无限for循环之后关闭端口
  11. zmq_close(responder);

 到这里再回去看在存储yuv数据那里的最后一行有发送yuv数据的代码就明白了。

至此我们发布者(PUB)端就设置完毕啦。成功完成Scrcpy和Zeromq的对接,剩下的就是创建python脚本(其他语言也行,我以python为例)接收yuv数据再使用opencv进行播放啦。

剩下内容请关注我的下一篇博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/266513
推荐阅读
相关标签
  

闽ICP备14008679号