• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

scrcpy实现手机屏幕和ubuntu共享、推流、实时分割wifi环境下

武飞扬头像
g了。
帮助1

有些时候我们需要将手机上的视频投到ubuntu上并进行处理,scrcpy就是一个非常低延时并且可以wifi共享的工具,本文主要针对其源码进行修改,实现ubuntu上实时分割手机画面,分为安装编译源(大坑)、修改源码、修改分割推理代码三部分。

scrcpy的使用和wifi连接可参考

一、安装编译源码

后面的源码修改有两个重要文件需要修改:stream.c和decoder.c,前者是用来把yuv流推到深度学习那边去的,后者是为了从scrcpy中把流推出来。如果你按照其他博客下载,即使跌跌撞撞编译成功,但发现没有这两个文件,太狗了,我这边找到最近的有这两个文件的是scrcpy1.22版本,千万别下错了!!!。需要下载的文件有两个:scrcpy源码和server

源码链接:https://github.com/Genymobile/scrcpy/tree/v1.22

server链接:Release scrcpy v1.22 · Genymobile/scrcpy · GitHub

下载完成后,解压scrcpy,善后meson build生成build文件夹,build-app里面把scrcpy-sever放进去,此时在build下运行ninja -Cbuild,此时如果会报错,如果报错消息是server没生成,此时去看看build-app里面有没有可执行文件,如果有那就编译结束了,./scrcpy运行即可

学新通

如果此时报错出现什么版本不一致或者其他错,进到/usr/local/share/scrcpy中把server往这里面拷贝一份。

至此,第一步编译运行源码完成。

二、修改源码

源码最重要的就是输出yuv流并通过本地端口广播出去,进入app/src下。修改decoder.c和stream.c以及对应的h文件,需要修改的部分如下:

decoder.c-----输出yuv流

  1.  
    #include "decoder.h"
  2.  
    #include <zmq.h>
  3.  
    #include <libavcodec/avcodec.h>
  4.  
    #include <libavformat/avformat.h>
  5.  
    #include <stdio.h>
  6.  
    #include "events.h"
  7.  
    #include "video_buffer.h"
  8.  
    #include "trait/frame_sink.h"
  9.  
    #include "util/log.h"
  10.  
     
  11.  
     
  12.  
    #######by zengyb
  13.  
     
  14.  
     
  15.  
    static bool
  16.  
    push_frame_to_sinks(struct decoder *decoder, const AVFrame *frame) {
  17.  
     
  18.  
     
  19.  
     
  20.  
     
  21.  
    //声明结构体
  22.  
    myframe yuv_frame;
  23.  
     
  24.  
     
  25.  
    //初始化长宽
  26.  
    yuv_frame.width= decoder->codec_ctx->width;
  27.  
    yuv_frame.height=decoder->codec_ctx->height;
  28.  
    //读取数据
  29.  
    int offest=0;
  30.  
    for(int i=0;i<yuv_frame.height;i )
  31.  
    {
  32.  
    memcpy(yuv_frame.data_Y offest,frame->data[0] frame->linesize[0]*i,yuv_frame.width);
  33.  
    //指向下一行
  34.  
    offest = yuv_frame.width;
  35.  
    }
  36.  
     
  37.  
    offest=0;
  38.  
    for(int i=0;i<yuv_frame.height/2;i )
  39.  
    {
  40.  
    memcpy(yuv_frame.data_U offest,frame->data[1] frame->linesize[1]*i,yuv_frame.width/2);
  41.  
    offest = yuv_frame.width/2;
  42.  
    }
  43.  
     
  44.  
    offest=0;
  45.  
    for(int i=0;i<yuv_frame.height/2;i )
  46.  
    {
  47.  
    memcpy(yuv_frame.data_V offest,frame->data[2] frame->linesize[2]*i,yuv_frame.width/2);
  48.  
    offest = yuv_frame.width/2;
  49.  
    }
  50.  
    // printf("a");
  51.  
     
  52.  
    int aa = zmq_send(response,&yuv_frame,sizeof(yuv_frame),ZMQ_DONTWAIT);
  53.  
    if (aa == -1) {
  54.  
    printf("zmq_send error: %s\n", zmq_strerror(errno));
  55.  
    }
  56.  
     
  57.  
     
  58.  
    // printf("%d",aa);
  59.  
     
  60.  
     
  61.  
    for (unsigned i = 0; i < decoder->sink_count; i) {
  62.  
    struct sc_frame_sink *sink = decoder->sinks[i];
  63.  
    if (!sink->ops->push(sink, frame)) {
  64.  
    LOGE("Could not send frame to sink %d", i);
  65.  
    return false;
  66.  
    }
  67.  
    }
  68.  
     
  69.  
     
  70.  
     
  71.  
    return true;
  72.  
    }
学新通

decoder.h-------声明结构体和scoket全局变量
 

  1.  
    #define yuv_size 448*1024
  2.  
     
  3.  
    typedef struct{
  4.  
    int width;
  5.  
    int height;
  6.  
    uint8_t data_Y[yuv_size];
  7.  
    uint8_t data_U[yuv_size/4];
  8.  
    uint8_t data_V[yuv_size/4];
  9.  
    }myframe;
  10.  
     
  11.  
    extern void *response;

 stream.c----初始化socket端口,for循环前初始,最终end的时候删除

  1.  
    ###先声明变量
  2.  
    void *response = NULL;
  3.  
    ####
  4.  
     
  5.  
     
  6.  
    void *context = zmq_ctx_new ();
  7.  
     
  8.  
     
  9.  
    response = zmq_socket (context, ZMQ_PUB);
  10.  
     
  11.  
    int rc = zmq_bind(response, "tcp://127.0.0.1:5555");
  12.  
    // printf("%d",rc);
  13.  
    // printf("111");
  14.  
    assert (rc == 0);
  15.  
     
  16.  
    for (;;) {
  17.  
     
  18.  
    bool ok = stream_recv_packet(stream, packet);
  19.  
     
  20.  
    if (!ok) {
  21.  
    // end of stream
  22.  
    break;
  23.  
    }
  24.  
     
  25.  
    ok = stream_push_packet(stream, packet);
  26.  
     
  27.  
    av_packet_unref(packet);
  28.  
    if (!ok) {
  29.  
    // cannot process packet (error already logged)
  30.  
    break;
  31.  
     
  32.  
     
  33.  
    }
  34.  
     
  35.  
     
  36.  
    }
  37.  
     
  38.  
    LOGD("End of frames");
  39.  
     
  40.  
    if (stream->pending) {
  41.  
    av_packet_free(&stream->pending);
  42.  
    }
  43.  
     
  44.  
    av_packet_free(&packet);
  45.  
    finally_close_parser:
  46.  
    av_parser_close(stream->parser);
  47.  
    finally_close_sinks:
  48.  
    stream_close_sinks(stream);
  49.  
    finally_free_codec_ctx:
  50.  
    avcodec_free_context(&stream->codec_ctx);
  51.  
    end:
  52.  
    if (response) {
  53.  
    zmq_close(response);
  54.  
    }
  55.  
    if (context) {
  56.  
    zmq_ctx_term(context);
  57.  
    }
  58.  
    stream->cbs->on_eos(stream, stream->cbs_userdata);
  59.  
     
  60.  
    return 0;
  61.  
    }
学新通

至此,源码修改完成,yuv流输出到本地端口,下一步让我们用起来吧!!!

三、订阅与深度学习实时分割

我先给一份实例直接订阅imshow的,如果需要融合到其他深度学习比如检测分割之类的程序基于此进行修改即可

  1.  
    import zmq
  2.  
    import numpy as np
  3.  
    import cv2
  4.  
     
  5.  
    # 创建一个ZeroMQ Context
  6.  
    context = zmq.Context()
  7.  
     
  8.  
    # 创建一个套接字并连接
  9.  
    socket = context.socket(zmq.SUB)
  10.  
    socket.connect("tcp://127.0.0.1:5555")
  11.  
    socket.setsockopt_string(zmq.SUBSCRIBE, '')
  12.  
     
  13.  
    width = 1024# 这里设置你的宽度
  14.  
    height = 448# 这里设置你的高度
  15.  
     
  16.  
    while True:
  17.  
    # 从套接字接收myframe结构体
  18.  
    yuv_frame = socket.recv()
  19.  
     
  20.  
    # 将bytes数据转化为numpy数组
  21.  
    y_data = np.frombuffer(yuv_frame[8:8 width*height], dtype=np.uint8).reshape(height, width)
  22.  
    u_data = np.frombuffer(yuv_frame[8 width*height:8 width*height*5//4], dtype=np.uint8).reshape(height//2, width//2)
  23.  
    v_data = np.frombuffer(yuv_frame[8 width*height*5//4:8 width*height*3//2], dtype=np.uint8).reshape(height//2, width//2)
  24.  
     
  25.  
    # 这里你可以将YUV格式的图像转化为RGB或者其他你需要的格式,然后进行显示或者处理
  26.  
    # 例如,使用OpenCV库的cvtColor函数转化为BGR格式
  27.  
    yuv_image = cv2.merge([y_data, cv2.resize(u_data, (width, height)), cv2.resize(v_data, (width, height))]).astype(np.uint8)
  28.  
    bgr_image = cv2.cvtColor(yuv_image, cv2.COLOR_YUV2BGR)
  29.  
    cv2.imwrite("./1.jpg",bgr_image)
  30.  
    # 实时显示视频
  31.  
    cv2.imshow('Video', bgr_image)
  32.  
     
  33.  
    # 等待1毫秒,如果此时按下“q”键,那么退出循环
  34.  
    if cv2.waitKey(1) & 0xFF == ord('q'):
  35.  
    break
  36.  
     
  37.  
    # 销毁所有窗口
  38.  
    cv2.destroyAllWindows()
学新通

然后给一份完用unet做分割的代码

  1.  
    elif mode == "video":
  2.  
    # 创建一个ZeroMQ Context
  3.  
    context = zmq.Context()
  4.  
     
  5.  
    # 创建一个套接字并连接
  6.  
    socket = context.socket(zmq.SUB)
  7.  
    socket.connect("tcp://127.0.0.1:5555")
  8.  
    socket.setsockopt_string(zmq.SUBSCRIBE, '')
  9.  
     
  10.  
    width = 1024 # 这里设置你的宽度
  11.  
    height = 448 # 这里设置你的高度
  12.  
     
  13.  
     
  14.  
     
  15.  
    frame_counter = 0
  16.  
    fps = 0.0
  17.  
    while True:
  18.  
    t1 = time.time()
  19.  
    # 从套接字接收myframe结构体
  20.  
    yuv_frame = socket.recv()
  21.  
    frame_counter = 1
  22.  
    if frame_counter % 10 != 0: # Only process every 10th frame
  23.  
    continue
  24.  
     
  25.  
    # 将bytes数据转化为numpy数组
  26.  
    y_data = np.frombuffer(yuv_frame[8:8 width*height], dtype=np.uint8).reshape(height, width)
  27.  
    u_data = np.frombuffer(yuv_frame[8 width*height:8 width*height*5//4], dtype=np.uint8).reshape(height//2, width//2)
  28.  
    v_data = np.frombuffer(yuv_frame[8 width*height*5//4:8 width*height*3//2], dtype=np.uint8).reshape(height//2, width//2)
  29.  
     
  30.  
    yuv_image = cv2.merge([y_data, cv2.resize(u_data, (width, height)), cv2.resize(v_data, (width, height))]).astype(np.uint8)
  31.  
    frame = cv2.cvtColor(yuv_image, cv2.COLOR_YUV2BGR)
  32.  
    frame1 = frame
  33.  
     
  34.  
    frame = Image.fromarray(np.uint8(frame))
  35.  
    # # 进行检测
  36.  
    # t2 = time.time()
  37.  
    # print(t2-t1)
  38.  
     
  39.  
    frame = np.array(unet.detect_image(frame))
  40.  
     
  41.  
    cv2.imshow("video", frame)
  42.  
    # 等待1毫秒,如果此时按下“q”键,那么退出循环
  43.  
    if cv2.waitKey(1) & 0xFF == ord('q'):
  44.  
    break
  45.  
    t2 = time.time()
  46.  
    print(t2-t1)
  47.  
     
  48.  
     
  49.  
    print("Video Detection Done!")
  50.  
    # if video_save_path!="":
  51.  
    # print("Save processed video to the path :" video_save_path)
  52.  
    # out.release()
  53.  
    cv2.destroyAllWindows()
学新通

记得一定要搁几帧读一下socket,不然推理无法实时,速度太慢,到时候socket里面堵塞太多东西了会巨卡------这里卡太久了,一步步debug才发现的

学新通

这里跑的是voc数据集的unet分割,可见才0.3s的延时,凑合凑合就够用了!!! 

error1:

sdl2-config found: NO need '>= 2.0.5'
Run-time dependency sdl2 found: NO (tried pkgconfig and config-tool)

app/meson.build:94:4: ERROR: Dependency "sdl2" not found, tried pkgconfig and config-tool
 

solution:sudo apt-get install libsdl2-dev

error2:ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

solution:sudo apt-get install openjdk-8-jdk

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaccfc
系列文章
更多 icon
同类精品
更多 icon
继续加载