如何在VR头显端实现低延迟的RTMP或RTMP播放

news2024/11/24 18:31:34

技术背景

VR(虚拟现实技术)给我们带来身临其境的视觉体验,广泛的应用于城市规划、教育培训、工业仿真、房地产、水利电力、室内设计、文旅、军事等众多领域,常用的行业比如:

  • 教育行业:VR头显可以用于教育培训,提供沉浸式的教学体验,例如虚拟实验室、虚拟课堂等,帮助学生更好地理解和掌握知识。
  • 医疗行业:VR头显可以用于医疗训练和治疗,例如手术模拟、康复训练等,提高医疗效果和质量。
  • 文旅行业:VR头显可以用于旅游娱乐,提供沉浸式的旅游体验,例如虚拟旅游、文化遗产展示等。
  • 房地产行业:VR头显可以用于房地产展示,提供更加真实、直观的房屋展示和体验,帮助客户更好地了解和选择房屋。
  • 展览展示行业:VR头显可以用于展览展示,提供沉浸式的展览体验,例如虚拟展厅、虚拟展品等,吸引观众的注意和参与。
  • 军事行业:VR头显可以用于军事训练和作战指挥,提供更加真实、逼真的军事训练环境。

技术实现

如何在VR头显实现RTMP或RTSP播放?

VR头显播放RTMP或RTSP流数据,简单来说,通过jni层打通RTMP或RTSP流传输,解包并解码回调给Unity YUV或RGB数据,Unity场景下,绘制即可,本文以大牛直播SDK的Unity平台RTMP、RTSP播放为例,介绍下具体技术实现:

开始播放:

    public void Play()
    {
        if (is_running)
        {
            Debug.Log("已经在播放。。");   
            return;
        }

        //获取输入框的url
        string url = input_url_.text.Trim();

        OpenPlayer();

        if ( player_handle_ == 0 )
            return;

        NT_U3D_Set_Game_Object(player_handle_, game_object_);

        /* ++ 播放前参数配置可加在此处 ++ */
        int is_using_tcp = 0;        //TCP/UDP模式设置
        NT_U3D_SetRTSPTcpMode(player_handle_, is_using_tcp);

        int is_report = 0;
        int report_interval = 1;
        NT_U3D_SetReportDownloadSpeed(player_handle_, is_report, report_interval);  //下载速度回调

        NT_U3D_SetBuffer(player_handle_, play_buffer_time_);                        //设置buffer time

        NT_U3D_SetPlayerLowLatencyMode(player_handle_, is_low_latency_ ? 1 : 0);    //设置是否启用低延迟模式

        NT_U3D_SetMute(player_handle_, is_mute_ ? 1 : 0);                           //是否启动播放的时候静音

        NT_U3D_SetAudioVolume(player_handle_, cur_audio_volume_);                   //设置播放音量

        NT_U3D_SetVideoDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.264软硬解模式

        NT_U3D_SetVideoHevcDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.265软硬解模式

        int is_fast_startup = 1;
        NT_U3D_SetFastStartup(player_handle_, is_fast_startup);                     //设置快速启动模式

        int rtsp_timeout = 10;
        NT_U3D_SetRTSPTimeout(player_handle_, rtsp_timeout);                        //设置RTSP超时时间

        int is_auto_switch_tcp_udp = 1;
        NT_U3D_SetRTSPAutoSwitchTcpUdp(player_handle_, is_auto_switch_tcp_udp);    //设置TCP/UDP模式自动切换

        int is_audiotrack = 1;
        NT_U3D_SetAudioOutputType(player_handle_, is_audiotrack);                   //设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式

        NT_U3D_SetUrl(player_handle_, videoUrl);
        /* -- 播放前参数配置可加在此处 -- */

        int flag = NT_U3D_StartPlay(player_handle_);

        if (flag  == DANIULIVE_RETURN_OK)
        {
            is_need_get_frame_ = true;
            Debug.Log("播放成功");
        }
        else
        {
            is_need_get_frame_ = false;
            Debug.LogError("播放失败");
        }

        is_running = true;  
    }

Close Player:

    private void ClosePlayer()
    {
        is_need_get_frame_ = false;
        is_need_init_texture_ = false;
 
        int flag = NT_U3D_StopPlay(player_handle_);
        if (flag == DANIULIVE_RETURN_OK)
        {
            Debug.Log("停止成功");
        }
        else
        {
            Debug.LogError("停止失败");
        }

        flag = NT_U3D_Close(player_handle_);
        if (flag == DANIULIVE_RETURN_OK)
        {
            Debug.Log("关闭成功");
        }
        else
        {
            Debug.LogError("关闭失败");
        }

        player_handle_ = 0;

        NT_U3D_UnInit();

        is_running = false;
        video_width_ = 0;
        video_height_ = 0;
    }

Event事件回调处理:

    /// <summary>
    /// android 传递过来 code
    /// </summary>
    /// <param name="event_message"></param>
    public void onNTSmartEvent(string event_message)
    {
        if (null == event_message || event_message.Length < 1)
            return;

        string[] strs = event_message.Split(',');
        if (null == strs || strs.Length < 6)
            return;

       string player_handle =strs[0];
       string code = strs[1];
       string param1 = strs[2];
       string param2 = strs[3];
       string param3 = strs[4];
       string param4 = strs[5];
        
       Debug.Log("[daiusdk] code: 0x" + Convert.ToString(Convert.ToInt32(code), 16));

       String player_event = "";

        switch (Convert.ToInt32(code))
        {
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
                player_event = "开始..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
                player_event = "连接中..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
                player_event = "连接失败..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
                player_event = "连接成功..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
                player_event = "连接断开..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
                player_event = "停止播放..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
                player_event = "分辨率信息: width: " + Convert.ToInt32(param1) + ", height: " + Convert.ToInt32(param2);
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
                player_event = "收不到媒体数据,可能是url错误..";
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
                player_event = "切换播放URL..";
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
                player_event = "快照: " + param1 + " 路径:" + param3;

                if (Convert.ToInt32(param1) == 0)
                {
                    player_event = "截取快照成功..";
                }
                else
                {
                    player_event = "截取快照失败..";
                }
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
                player_event = "[record]开始一个新的录像文件 : " + param3;
                break;
            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
                player_event = "[record]已生成一个录像文件 : " + param3;
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
                player_event = "Start_Buffering..";
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
                player_event = "Buffering: " + Convert.ToInt32(param1);
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
                player_event = "Stop_Buffering..";
                break;

            case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
                player_event = "download_speed:" + param1 + "Byte/s" + ", "
                        + (Convert.ToInt32(param1) * 8 / 1000) + "kbps" + ", " + (Convert.ToInt32(param1) / 1024)
                        + "KB/s";
                break;
        }

        Debug.Log(player_event);

        player_event = null;
        strs = null;
    }

如何封装实现原生jni层交互:

    /// SmartPlayerAndroidMono.cs
    /// Author: daniusdk.com
    ///Created on 2018/05/10

    /// <summary>
    /// Init
    /// </summary>
    public int NT_U3D_Init()
    {
        return player_obj_.Call<int>("Init", java_obj_cur_activity_);
    }

    /// <summary>
    /// 开始
    /// 返回播放句柄
    /// </summary>
    public long NT_U3D_Open()
    {
        return player_obj_.Call<long>("Open");
    }

    /// <summary>
    /// Register Game Object,用于消息传递
    /// </summary>
    public int NT_U3D_Set_Game_Object(long handle, string gameObjectName)
    {
        return player_obj_.Call<int>("SetGameObject", handle, gameObjectName);
    }

    /// <summary>
    /// 设置H.264解码方式 false 软件解码 true 硬件解码 默认为false
    /// </summary>
    /// <param name="isHwDecoder"></param>
    public int NT_U3D_SetVideoDecoderMode(long handle, int isHwDecoder)
    {
        return player_obj_.Call<int>("SetPlayerVideoHWDecoder", handle, isHwDecoder);
    }

    /// <summary>
    /// 设置H.265 解码方式 false 软件解码 true 硬件解码 默认为false
    /// </summary>
    /// <param name="isHevcHwDecoder"></param>
    public int NT_U3D_SetVideoHevcDecoderMode(long handle, int isHevcHwDecoder)
    {
        return player_obj_.Call<int>("SetPlayerVideoHevcHWDecoder", handle, isHevcHwDecoder);
    }

    /// <summary>
    /// 设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式
    /// </summary>
    /// <param name="use_audiotrack"></param>
    public int NT_U3D_SetAudioOutputType(long handle, int use_audiotrack)
    {
        return player_obj_.Call<int>("SetAudioOutputType", handle, use_audiotrack);
    }

    /// <summary>
    /// 设置播放端缓存大小, 默认200毫秒
    /// </summary>
    /// <param name="buffer"></param>
    public int NT_U3D_SetBuffer(long handle, int buffer)
    {
        return player_obj_.Call<int>("SetBuffer", handle, buffer);
    }

    /// <summary>
    /// 接口可实时调用:设置是否实时静音,1:静音; 0: 取消静音
    /// </summary>
    /// <param name="is_mute"></param>
    public int NT_U3D_SetMute(long handle, int is_mute)
    {
        return player_obj_.Call<int>("SetMute", handle, is_mute);
    }

    /// <summary>
    /// 接口可实时调用:设置播放音量,范围是[0, 100], 0是静音,100是最大音量, 默认是100
    /// </summary>
    /// <param name="audio_volume"></param>
    public int NT_U3D_SetAudioVolume(long handle, int audio_volume)
    {
        return player_obj_.Call<int>("SetAudioVolume", handle, audio_volume);
    }

    /// <summary>
    /// 设置RTSP TCP模式, 1: TCP; 0: UDP
    /// </summary>
    /// <param name="is_using_tcp"></param>
    public int NT_U3D_SetRTSPTcpMode(long handle, int is_using_tcp)
    {
        return player_obj_.Call<int>("SetRTSPTcpMode", handle, is_using_tcp);
    }

    /// <summary>
    /// 设置RTSP超时时间, timeout单位为秒,必须大于0
    /// </summary>
    /// <param name="timeout"></param>
    public int NT_U3D_SetRTSPTimeout(long handle, int timeout)
    {
        return player_obj_.Call<int>("SetRTSPTimeout", handle, timeout);
    }

    /// <summary>
    /// 设置RTSP TCP/UDP自动切换
    /// NOTE: 对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式.
    /// 为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp.
    /// </summary>
    /// <param name="timeout"></param>
    /// timeout:如果设置1的话, sdk将在tcp和udp之间尝试切换播放,如果设置为0,则不尝试切换.
    public int NT_U3D_SetRTSPAutoSwitchTcpUdp(long handle, int is_auto_switch_tcp_udp)
    {
        return player_obj_.Call<int>("SetRTSPAutoSwitchTcpUdp", handle, is_auto_switch_tcp_udp);
    }

    /// <summary>
    /// 设置快速启动该模式,
    /// </summary>
    /// <param name="is_fast_startup"></param>
    public int NT_U3D_SetFastStartup(long handle, int is_fast_startup)
    {
        return player_obj_.Call<int>("SetFastStartup", handle, is_fast_startup);
    }

    /// <summary>
    /// 设置超低延迟模式 false不开启 true开启 默认false
    /// </summary>
    /// <param name="mode"></param>
    public int NT_U3D_SetPlayerLowLatencyMode(long handle, int mode)
    {
        return player_obj_.Call<int>("SetPlayerLowLatencyMode", handle, mode);
    }

    /// <summary>
    /// 设置视频垂直反转
    /// is_flip: 0: 不反转, 1: 反转
    /// </summary>
    /// <param name="is_flip"></param>
    public int NT_U3D_SetFlipVertical(long handle, int is_flip)
    {
        return player_obj_.Call<int>("SetFlipVertical", handle, is_flip);
    }

    /// <summary>
    /// 设置视频水平反转
    /// is_flip: 0: 不反转, 1: 反转
    /// </summary>
    /// <param name="is_flip"></param>
    public int NT_U3D_SetFlipHorizontal(long handle, int is_flip)
    {
        return player_obj_.Call<int>("SetFlipHorizontal", handle, is_flip);
    }

    /// <summary>
    /// 设置顺时针旋转, 注意除了0度之外, 其他角度都会额外消耗性能
    /// degress: 当前支持 0度,90度, 180度, 270度 旋转
    /// </summary>
    /// <param name="degress"></param>
    public int NT_U3D_SetRotation(long handle, int degress)
    {
        return player_obj_.Call<int>("SetRotation", handle, degress);
    }

    /// <summary>
    /// 设置是否回调下载速度
    /// is_report: if 1: 上报下载速度, 0: 不上报.
    /// report_interval: 上报间隔,以秒为单位,>0.
    /// </summary>
    /// <param name="is_report"></param>
    /// <param name="report_interval"></param>
    public int NT_U3D_SetReportDownloadSpeed(long handle, int is_report, int report_interval)
    {
        return player_obj_.Call<int>("SetReportDownloadSpeed", handle, is_report, report_interval);
    }

    /// <summary>
    /// 设置是否需要在播放或录像过程中快照
    /// </summary>
    /// <param name="is_save_image"></param>
    public int NT_U3D_SetSaveImageFlag(long handle, int is_save_image)
    {
        return player_obj_.Call<int>("SetSaveImageFlag", handle, is_save_image);
    }

    /// <summary>
    /// 播放或录像过程中快照
    /// </summary>
    /// <param name="imageName"></param>
    public int NT_U3D_SaveCurImage(long handle, string imageName)
    {
        return player_obj_.Call<int>("SaveCurImage", handle, imageName);
    }

    /// <summary>
    /// 播放或录像过程中,快速切换url
    /// </summary>
    /// <param name="uri"></param>
    public int NT_U3D_SwitchPlaybackUrl(long handle, string uri)
    {
        return player_obj_.Call<int>("SwitchPlaybackUrl", handle, uri);
    }

    /// <summary>
    /// 创建录像存储路径
    /// </summary>
    /// <param name="path"></param>
    public int NT_U3D_CreateFileDirectory(string path)
    {
        return player_obj_.Call<int>("CreateFileDirectory", path);
    }

    /// <summary>
    /// 设置录像存储路径
    /// </summary>
    /// <param name="path"></param>
    public int NT_U3D_SetRecorderDirectory(long handle, string path)
    {
        return player_obj_.Call<int>("SetRecorderDirectory", handle, path);
    }

    /// <summary>
    /// 设置单个录像文件大小
    /// </summary>
    /// <param name="size"></param>
    public int NT_U3D_SetRecorderFileMaxSize(long handle, int size)
    {
        return player_obj_.Call<int>("SetRecorderFileMaxSize", handle, size);
    }

    /// <summary>
    /// 设置录像时音频转AAC编码的开关
    /// aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能.
    /// 注意: 转码会增加性能消耗
    /// </summary>
    /// <param name="is_transcode"></param>
    /// is_transcode:设置为1的话,如果音频编码不是aac,则转成aac,如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.
    public int NT_U3D_SetRecorderAudioTranscodeAAC(long handle, int is_transcode)
    {
        return player_obj_.Call<int>("SetRecorderAudioTranscodeAAC", handle, is_transcode);
    }

    /// <summary>
    /// 设置播放路径
    /// </summary>
    public int NT_U3D_SetUrl(long handle, string url)
    {
        return player_obj_.Call<int>("SetUrl", handle, url);
    }

    /// <summary>
    /// 开始播放
    /// </summary>
    public int NT_U3D_StartPlay(long handle)
    {
        return player_obj_.Call<int>("StartPlay", handle);
    }

    /// <summary>
    /// 获取YUV数据
    /// </summary>
    public AndroidJavaObject NT_U3D_GetVideoFrame(long handle)
    {
        return player_obj_.Call<AndroidJavaObject>("GetVideoFrame", handle);
    }

    /// <summary>
    /// 停止播放
    /// </summary>
    public int NT_U3D_StopPlay(long handle)
    {
        return player_obj_.Call<int>("StopPlay", handle);
    }

    /// <summary>
    /// 开始录像
    /// </summary>
    public int NT_U3D_StartRecorder(long handle)
    {
        return player_obj_.Call<int>("StartRecorder", handle);
    }

    /// <summary>
    /// 停止录像
    /// </summary>
    public int NT_U3D_StopRecorder(long handle)
    {
        return player_obj_.Call<int>("StopRecorder", handle);
    }

    /// <summary>
    /// 关闭播放
    /// </summary>
    public int NT_U3D_Close(long handle)
    {
        return player_obj_.Call<int>("Close", handle);
    }

    /// <summary>
    /// UnInit Player
    /// </summary>
    public int NT_U3D_UnInit()
    {
        return DANIULIVE_RETURN_OK;
    }

技术总结

通过实际测试来看,VR头显端,如果设备性能尚可的话,播放RTMP或RTSP,可实现毫秒级的延迟,可满足大多数有交互诉求的技术场景,此外,如果头显端支持硬解码的话,可以优先考虑硬解码。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/941788.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

资产管理系统中的资产档案管理

资产管理系统中的资产档案管理是指对公司所有的固定资产进行详细的记录、分类、归档和保管。其目的是确保公司的固定资产能够被准确地识别、追踪和管理&#xff0c;以便更好地掌握公司的资产状况&#xff0c;提高资产利用率&#xff0c;降低运营成本。 资产档案管理包括以下几…

try代码块中执行了break。finally也会执行

代码展示 public class Test {public static void main(String[] args) {int a 0;while (true) {try {System.out.println("输出值为&#xff1a;" a);if (a 2) {break;} else {a;}} catch (Exception e) {e.printStackTrace();} finally {System.out.println(&q…

【java并发编程的艺术读书笔记】Lock接口简介、Condition类的使用、可重入锁与读写锁

Lock接口简介 Lock 接口是用于实现线程同步的一种机制。它提供了比传统的synchronized关键字更灵活和可扩展的方式来管理多个线程对共享资源的访问。 Lock 接口的主要目标是解决synchronized关键字的一些限制&#xff0c;例如无法中断正在等待锁的线程、不能尝试获取锁而立即…

电商平台需要用到的API接口封装商品详情SKU数据

想要开发电商系统的朋友对于电商平台API肯定不陌生&#xff0c;API接口即应用程序编程接口&#xff0c;电商平台开放部分API接口&#xff0c;供商家和服务商调用&#xff0c;以满足电商业务管理需求。随着电商市场需求的日益增长以及技术手段的不断成熟&#xff0c;电商平台开放…

镜之Json Compare Diff

前言 “镜” 寓意是凡事都有两面性,Json 对比也不例外! 因公司业务功能当中有一个履历的功能,它有多个版本的 JSON 数据需要对比出每个版本的不同差异节点并且将差异放置在一个新的 JSON 当中原有结构不能变动,差异节点使用数组对象的形式存储,前端点击标红即可显示多个版本的节…

Android全面屏下,默认不会全屏显示,屏幕底部会留黑问题

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 公司以前的老项目&#xff0c;便出现了这种情况&#xff0c;网上搜索了各种资料&#xf…

【SpringCloudAlibaba】Sentinel使用

文章目录 概述官网解决的问题主要特性 配置下载可视化控制台POMYML 流控规则直接(默认)关联链路 降级规则降级策略实战RT异常比例异常数 热点key限流示例&#xff1a;高级选项&#xff1a;参数例外项其他 系统规则SentinelResource按资源名称限流后续处理按照Url地址限流后续处…

轻松掌控视频速度,将过快变得慢慢来!

大家好&#xff01;在观看和编辑视频时&#xff0c;有时我们会遇到速度过快的视频&#xff0c;需要进行调整以获得更佳的观赏体验或编辑效果。为了让您轻松掌控视频速度&#xff0c;我们精心研发了一款强大的工具——视频速度调整器&#xff01;让您能够将过快的视频调慢&#…

相机SD卡数据丢失如何恢复?

出门在外&#xff0c;相机是人们记录生活点滴的重要工具&#xff0c;是旅游的最佳玩伴。人们每到一个地方&#xff0c;都喜欢用相机来见证自己来过的痕迹&#xff0c;拍好的照片都会被放到相机卡里&#xff0c;但在使用相机时&#xff0c;有时我们会意外删除了重要的照片或视频…

骨传导耳机对人体有危险吗?骨传导耳机的危害有哪些

根据目前的科学研究和经验&#xff0c;骨传导耳机被认为是相对安全的使用设备&#xff0c;不会对人体造成危险。由于骨传导耳机是通过振动颅骨传递声音&#xff0c;而不是通过直接传递声音到耳朵&#xff0c;因此对耳朵和听力系统的压力较小。 骨传导耳机相反没什么危害&#x…

【已解决】Windows10 pip安装报错:UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0x98

环境&#xff1a;win10, Python3.9 在Pycharm安装YoloV5的依赖包时出现报错&#xff1a;UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0x98 出现 ‘gbk’ codec can’t decode… 的报错一般是因为读取文件出现编码问题导致没法读取文件&#xff0c;因此可以在报错…

Python基础 - 构造函数

目录 基础构造函数 重写 钻石继承 super函数 基础构造函数 命名为_init_&#xff0c;在实例化对象之前会自动执行该函数 构造函数方便我们在实例化对象的时候实现个性化定制 class A():def __init__(self,x,y):self.x xself.y ydef add(self):return self.xself.yclass…

找回宝塔面板登录信息

找回宝塔面板登录信息 找回面板信息 在Xshell控制面板中输入 /etc/init.d/bt default找回登录信息 在Xshell中输入 bt之后根据提示操作 bt 之后根据提示操作![在这里插入图片描述](https://img-blog.csdnimg.cn/e209039ef4684c8a92126d5b8a0580d6.png#pic_center)

软件测试 —— Airtest的iOS实用接口介绍 (建议收藏)

1. 前言 前段时间Airtest更新了1.3.0.1版本&#xff0c;里面涉及非常多的iOS功能新增和改动&#xff0c;今天想详细跟大家聊一下里面的iOS设备接口。 PS&#xff1a;本文示例均使用本地连接的iOS设备&#xff0c;Airtest版本为1.3.0.1 。 2. 安装接口&#xff1a;install、in…

PAT 1130 Infix Expression

个人学习记录&#xff0c;代码难免不尽人意。 Given a syntax tree (binary), you are supposed to output the corresponding infix expression, with parentheses reflecting the precedences of the operators. Input Specification: Each input file contains one test ca…

煤质软化:提升采矿效率的关键技术

煤质软化&#xff1a;提升采矿效率的关键技术 随着煤炭的广泛应用和能源需求的增长&#xff0c;对煤炭采矿和利用的要求也不断提高。传统的煤炭采矿方法往往面临一些困难和挑战&#xff0c;如厚层、高硬度以及强抗压性等问题&#xff0c;导致采掘效率低下、操作困难&#xff0c…

如何搭建接口自动化测试框架?

经过了一年多的接口测试工作&#xff0c;旧的框架也做了一些新的调整&#xff0c;删除了很多冗余的功能&#xff0c;只保留了最基本的接口结构验证、接口回归测试、线上定时巡检功能。 一、框架的演进 界面 UI 做了优化&#xff0c;整个框架的画风突然不一样了&#xff08;人…

1688API技术解析,实现按图搜索1688商品(拍立淘)

一种可能的解决方案是使用图像识别和相似度匹配的算法。您可以通过将输入的图片与1688上的商品图片进行比对&#xff0c;找出最相似的商品。这涉及到图像特征提取、相似度计算以及数据库匹配等技术。您可以使用开源的图像处理库&#xff08;如OpenCV&#xff09;来进行图像处理…

38. 连续签到领金币数

文章目录 题目需求思路一实现一题目来源 题目需求 用户每天签到可以领1金币&#xff0c;并可以累计签到天数&#xff0c;连续签到的第3、7天分别可以额外领2和6金币。 每连续签到7天重新累积签到天数。 从用户登录明细表中求出每个用户金币总数&#xff0c;并按照金币总数倒…

无涯教程-分类算法 - Python实现函数

为了在Python中实现SVM&#xff0c;无涯教程将从标准库导入开始&#xff0c;如下所示- import numpy as np import matplotlib.pyplot as plt from scipy import stats import seaborn as sns; sns.set() 接下来&#xff0c;从sklearn.dataset.sample_generator创建具有线性可…