RK3568上如何使用MPP进行硬解码

news2024/9/23 13:16:02

目录

  • 前言
  • 正文
    • 一、FFmpeg 拉流处理
    • 二、RK3568 mpp硬解码
      • 1、简介
      • 2、普通mpp解码流程
      • 3、核心代码
    • END、总结的知识与问题
      • 1、一直出现`jitter buffer full` 这样的问题
      • 2、如何打印帧率?
      • 3、分析av_packet_alloc、av_init_packet、av_packet_unref、av_packet_free、av_frame_move_ref、av_packet_clone
  • 参考

前言

需求:我这边遇到的需求是需要在RK3568上进行拉流处理,然后使用MPP进行硬解码,但目前解码解的还是比较繁琐一点,不过,没关系把。先记录下,自己后面如果有对这个东西进行重写,再进行详细描述即可。
主要内容:
1、FFmpeg 拉流处理。
2、RK3568 MPP处理。

正文

一、FFmpeg 拉流处理

code

bool CVideoDecodeThd::_AnalysisFile(const QString& _sFilePath)
{
#ifdef ARM
    LOG_INFO << "play video file : " << _sFilePath.toStdString();

    // 打开文件流读取文件头解析出视频信息如轨道信息、时长等
    // m_pFormatCtx初始化为NULL,如果打开成功,它会被设置成非NULL的值,在不需要的时候可以通过avcodec_free_context释放。
    // 这个方法实际可以打开多种来源的数据,url可以是本地路径、rtmp地址等
    // 在不需要的时候通过avformat_close_input关闭文件流
    if (avformat_open_input(&m_pFormatCtx, _sFilePath.toLatin1().data(), nullptr, nullptr) != 0)
    {
        LOG_ERROR << "can't open the file.";
        return false;
    }

    // 读取媒体文件的数据包以获取流信息
    if (avformat_find_stream_info(m_pFormatCtx, nullptr) < 0)
    {
        LOG_ERROR << "can't find stream infomation.";
        return false;
    }

    //查找视频轨道
    int iVideoStream = av_find_best_stream(m_pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (iVideoStream < 0)
    {
        LOG_ERROR << "can't find video stream";
        return false;
    }

    // 查找解码器
    AVCodecParameters *pCodecParame = m_pFormatCtx->streams[iVideoStream]->codecpar;
    if (!pCodecParame)
    {
        LOG_ERROR << "can't find AVCodecParameters";
        return false;
    }

    m_pCodec = (AVCodec *)avcodec_find_decoder(pCodecParame->codec_id);
    if (!m_pCodec)
    {
        LOG_ERROR << "Codec cant found, code id:" << pCodecParame->codec_id;
        return false;
    }

    m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
    if (!m_pCodecCtx)
    {
        LOG_ERROR << "can't alloc codec context";
        return false;
    }

    if (avcodec_parameters_to_context(m_pCodecCtx, (const AVCodecParameters *)pCodecParame) < 0)
    {
        LOG_ERROR << "can't set codec params";
        avcodec_close(m_pCodecCtx);
        return false;
    }

    // 打开解码器
    if (avcodec_open2(m_pCodecCtx, m_pCodec, nullptr) < 0)
    {
        LOG_ERROR << "can't open codec.";
        avcodec_close(m_pCodecCtx);
        return false;
    }

    LOG_INFO << "the file video format is : " << m_pCodec->name;
    float fFrameRate = 0.0;
    if (m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den != 0 && m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num != 0)
    {
        fFrameRate = m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num * 1.0 /m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den;//每秒多少帧
        LOG_INFO << "Frame Rate : " << fFrameRate;
    }

    m_pFrame = av_frame_alloc();
    m_pFrameRGB = av_frame_alloc();

    m_pImageConvertCtx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height,
                                        m_pCodecCtx->pix_fmt, m_pCodecCtx->width,
                                        m_pCodecCtx->height, AV_PIX_FMT_RGB32,
                                        SWS_BICUBIC, nullptr, nullptr, nullptr);
    int iNumBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1);
    uint8_t *pOutBuffer = (uint8_t*)av_malloc(iNumBytes * sizeof(uint8_t));
    av_image_fill_arrays(m_pFrameRGB->data, m_pFrameRGB->linesize, pOutBuffer, AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1);
    int iYsize = m_pCodecCtx->width * m_pCodecCtx->height;
    AVPacket * pPacket = av_packet_alloc();  ///< 分配一个packet内存
    av_new_packet(pPacket, iYsize);     ///< 分配一个packet的数据
    av_dump_format(m_pFormatCtx, 0, _sFilePath.toLatin1().data(), 0);   ///< 输出视频信息

    qint64 iStartTime = 0;
    while (m_bRunning)
    {
        if (0 == iStartTime)
        {
            iStartTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
        }
        if (av_read_frame(m_pFormatCtx, pPacket) < 0)
        {
            emit SIGNAL_PlayEnd();
            break;      // 文件读取完成
        }
        while (!m_bPlay)
        {
            QThread::msleep(20);
            if (!m_bRunning)
            {
                break;
            }
        }

        if (pPacket->stream_index == iVideoStream)
        {
#ifdef MPP
        	int iRet = _DecodeAVPacket(pPacket);
#elif
            int iRet =avcodec_send_packet(m_pCodecCtx, pPacket);
            if (0 == iRet)
            {
                while (avcodec_receive_frame(m_pCodecCtx, m_pFrame) >= 0)
                {
                    if (!m_bRunning || !m_bPlay)
                    {
                        break;
                    }
                    sws_scale(m_pImageConvertCtx, (uint8_t const* const*)m_pFrame->data,
                              m_pFrame->linesize, 0, m_pCodecCtx->height,
                              m_pFrameRGB->data, m_pFrameRGB->linesize);
                    QImage oImg ((uchar*) pOutBuffer, m_pCodecCtx->width, m_pCodecCtx->height, QImage::Format_RGB32);
                    QImage oTempImg = oImg.copy();
                    emit SIGNAL_NewFrame(oTempImg);
                    int iInternal = QDateTime::currentDateTime().toMSecsSinceEpoch() - iStartTime;
                    iStartTime = 0;
                    int iSleepMs = 1000 / fFrameRate - iInternal;
                    if (iSleepMs > 0)
                    {
                        msleep(iSleepMs);
                    }
                }
            }
            else
            {
                LOG_ERROR << "send packet fail : " << iRet;
            }
#endif
        }
        av_packet_unref(pPacket);
        //QThread::msleep(10);
    }

    QThread::msleep(500);   ///< 等待外界渲染完毕
    av_free(pOutBuffer);

    //回收数据包
    av_packet_free(&pPacket);

    //销毁帧
    av_frame_free(&m_pFrame);
    av_frame_free(&m_pFrameRGB);

    //销毁SwsContext
    sws_freeContext(m_pImageConvertCtx);

    //释放解码器
    avcodec_close(m_pCodecCtx);

    // 销毁AVFormatContext
    avformat_close_input(&m_pFormatCtx);

    LOG_INFO << "thread is finished";
    return true;
#else
    return true;
#endif
}

这个函数是关于拉流的函数,具体的拉流的比较详细的内容,在另一篇文章进行详细叙述。

二、RK3568 mpp硬解码

1、简介

瑞芯微提供的媒体处理软件平台(Media Process Platform,简称 MPP)是适用于瑞芯微芯片系列的
通用媒体处理软件平台。该平台对应用软件屏蔽了芯片相关的复杂底层处理,其目的是为了屏蔽不
同芯片的差异,为使用者提供统一的视频媒体处理接口(Media Process Interface,缩写 MPI)。MPP
提供的功能包括:
视频解码:
H.265 / H.264 / H.263 / VP9 / VP8 / MPEG-4 / MPEG-2 / MPEG-1 / VC1 / MJPEG
视频编码:
H.264 / VP8 / MJPEG
视频处理:
视频拷贝,缩放,色彩空间转换,场视频解交织(Deinterlace)

2、普通mpp解码流程

在这里插入图片描述

3、核心代码

上面mpp使用的地方在这里:
_DecodeAVPacket(pPacket);
基本就是拉流获取到AVPacket 包传入Mpp进行解码。
_InitMpp():

int CVideoDecodeThd::_InitMpp()
{
    LOG_INFO << "--> CVideoDecodeThd::_InitMpp Start";
    MPP_RET ret = mpp_create(&m_Ctx, &m_pMpi);

    if (MPP_OK != ret)
    {
        LOG_INFO << ("mpp_create error\n");
        return -1;
    }

    /**
     * 4. 配置解器
     *      - 解码文件需要 split 模式
     *      - 设置非阻塞模式,0非阻塞(默认),-1阻塞,+val 超时(ms)
     */
    RK_U32 need_split = -1;
    ret = m_pMpi->control(m_Ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, (MppParam*)&need_split);

    if (MPP_OK != ret)
    {
        LOG_INFO << ("mpi->control error MPP_DEC_SET_PARSER_SPLIT_MODE\n");
        return -1;
    }

    ret = mpp_init(m_Ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC);  // 固定为H264

    if (MPP_OK != ret)
    {
        LOG_INFO << ("mpp_init error\n");
        return -1;
    }
	//这个是为了后面测试使用的。真实使用不用。
    m_pOut_fp = fopen("/ics/test.yuv", "wb+");
    if (!m_pOut_fp)
    {
        LOG_INFO << ("fopen error\n");
        return -1;
    }
    LOG_INFO << "--> CVideoDecodeThd::_InitMpp End";
    return 0;
}

_DecodeAVPacket:

int CVideoDecodeThd::_DecodeAVPacket(AVPacket *_pPacket)
{
    LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket Start";
    RK_U32 pkt_done = 0;
    MPP_RET ret = MPP_OK;
    MppPacket packet = NULL;
    MppFrame  frame  = NULL;
    LOG_INFO << "--->z CVideoDecodeThd::_DecodeAVPacket _pPacket size:"<<_pPacket->size;
    if (_pPacket->size <= 0)
    {
        LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket _pPacket->size is:"<<_pPacket->size;
        return -1;
    }

    ret = mpp_packet_init(&packet, _pPacket->data, _pPacket->size);

    if(ret < 0)
    {
        LOG_INFO << "mpp_packet_init fail ret:"<<ret;
        return -1;
    }
    mpp_packet_set_pts(packet, _pPacket->pts);

    do{
        RK_S32 times = 5;
        // send the packet first if packet is not done
        if (!pkt_done)
        {
            LOG_INFO << "pkt remain:" << mpp_packet_get_length(packet);
            ret = m_pMpi->decode_put_packet(m_Ctx, packet);

            if (MPP_OK == ret)
            {
                LOG_INFO << "pkt send success remain:" << mpp_packet_get_length(packet);
                pkt_done = 1;
            }
        }

        // then get all available frame and release
        do {
try_again:
            ret = m_pMpi->decode_get_frame(m_Ctx, &frame);
            if (MPP_ERR_TIMEOUT == ret)
            {
                if (times > 0) {
                    times--;
                    msleep(2);
                    goto try_again;
                }
                qDebug() << "decode_get_frame failed too much time:" <<ret;
            }

            if (frame == NULL)
            {
                qDebug() << "get frame:"<<frame;
                return -1;
            }

            qDebug() << "get MPP_OK:" <<MPP_OK;
            qDebug() << "get ret:"<<ret;
            LOG_INFO << ("decode_get_frame success\n") <<"||"<<frame;
            dump_frame_to_file(m_Ctx, m_pMpi, frame, m_pOut_fp);

            if (mpp_frame_get_eos(frame))
            {
                LOG_INFO << ("mpp_frame_get_eos\n");
                mpp_frame_deinit(&frame);
                //                over = 1;
                continue;
            }
            mpp_frame_deinit(&frame);
            break;
        }while(1);
        msleep(3);
        break;
    }while(1);
    LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket End";
    mpp_packet_deinit(&packet);
    return 1;
}

dump_frame_to_file:

void CVideoDecodeThd::dump_frame_to_file(MppCtx ctx, MppApi *mpi, MppFrame frame, FILE *out_fp)
{
    LOG_INFO << "decode_and_dump_to_file:" << frame;

    MPP_RET ret;

    if (mpp_frame_get_info_change(frame)) {
        LOG_INFO << ("mpp_frame_get_info_change\n");
        /**
             * 第一次解码会到这个分支,需要为解码器设置缓冲区.
             * 解码器缓冲区支持3种模式。参考【图像内存分配以及交互模式】Rockchip_Developer_Guide_MPP_CN.pdf
             * 这里使用纯内部模式。
             */
        ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
        if (ret)
        {
            LOG_INFO << "mpp_frame_get_info_change mpi->control error"
                        "MPP_DEC_SET_INFO_CHANGE_READY %d\n" << ret;
        }
        return;
    }

    RK_U32 err_info = mpp_frame_get_errinfo(frame);
    RK_U32 discard = mpp_frame_get_discard(frame);
    LOG_INFO << "err_info: discard:\n"<< err_info<<"||"<<discard;

    if (err_info)
    {
        LOG_INFO << "--> CVideoDecodeThd::dump_frame_to_file err_info:"<<err_info;
        return;
    }

    // save
    dump_frame(frame, out_fp);
    return;
}

dump_frame:

void CVideoDecodeThd::dump_frame(MppFrame frame, FILE *out_fp)
{
    LOG_INFO << ("dump_frame_to_file\n");

    RK_U32 width    = 0;
    RK_U32 height   = 0;
    RK_U32 h_stride = 0;
    RK_U32 v_stride = 0;
    MppFrameFormat fmt  = MPP_FMT_YUV420SP;
    MppBuffer buffer    = NULL;
    RK_U8 *base = NULL;

    width    = mpp_frame_get_width(frame);
    height   = mpp_frame_get_height(frame);
    h_stride = mpp_frame_get_hor_stride(frame);
    v_stride = mpp_frame_get_ver_stride(frame);
    fmt      = mpp_frame_get_fmt(frame);
    buffer   = mpp_frame_get_buffer(frame);
    RK_U32 buf_size = mpp_frame_get_buf_size(frame);
    LOG_INFO << "w x h: %dx%d hor_stride:%d ver_stride:%d buf_size:%d\n"<<
                width<<"||"<< height<<"||"<<h_stride<<"||"<<v_stride<<"||"<< buf_size;

    if (NULL == buffer)
    {
        LOG_INFO << ("buffer is null\n");
        return ;
    }

    base = (RK_U8 *)mpp_buffer_get_ptr(buffer);

    // MPP_FMT_YUV420SP
    if (fmt != MPP_FMT_YUV420SP)
    {
        LOG_INFO << ("fmt %d not supported\n", fmt);
        return;
    }

    char *pOutput = NULL;
    int iLen = static_cast<int>(buf_size);
    LOG_INFO << "--->z iLen:"<<iLen;
    pOutput = (char*)base;

    if (iLen > 0 && pOutput)
    {
    	//用信号把QImage 发送出去
        emit SIGNAL_Image(QByteArray(pOutput, iLen), width, height);
    }
}

END、总结的知识与问题

1、一直出现jitter buffer full 这样的问题

原先开始跑,并不会出现这个问题,就是跑久了,直到抓了2000多个帧之后,慢慢就会报这个错,然后就不再拉流了,就很奇葩,这可是浪费了我很长的时间进行排查。
错误:

[rtsp @ 0x292ee690] jitter buffer full
[rtsp @ 0x292ee690] RTP: missed 84 packets
[rtsp @ 0x292ee690] jitter buffer full
[rtsp @ 0x292ee690] RTP: missed 8 packets

2、如何打印帧率?

    LOG_INFO << "the file video format is : " << m_pCodec->name;
    float fFrameRate = 0.0;
    if (m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den != 0 && m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num != 0)
    {
        fFrameRate = m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num * 1.0 /m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den;//每秒多少帧
        LOG_INFO << "Frame Rate : " << fFrameRate;
    }

3、分析av_packet_alloc、av_init_packet、av_packet_unref、av_packet_free、av_frame_move_ref、av_packet_clone

参考:从源码的层面理解ffmpeg这几个API

参考

1、从源码的层面理解ffmpeg这几个API
2、GitHub - MUZLATAN/ffmpeg_rtsp_mpp: ffmpeg 拉取rtsp h264流, 使用mpp解码, 目前在firefly 板子上跑通了
3、RK3568 MPP编码
4、Rockchip MPP(Media Process Platform)解码H264
5、mpi_dec_test 解码失败

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

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

相关文章

尤雨溪:框架挖坑靠文档来补,这算 PUA 用户吗?丨 RTE 开发者日报 Vol.122

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

经典八股文之RocketMQ

核心概念 NameServer nameserver是整个rocketmq的大脑&#xff0c;是rocketmq的注册中心。broker在启动时向所有nameserver注册。生产者在发送消息之前先从 NameServer 获取 Broker 服务器地址列表(消费者一 样)&#xff0c;然后根据负载均衡算法从列表中选择一台服务器进行消…

ADKEY多按键制作阻值选择2(回答网友问题)

回答网友的问题 网友原来的电路图 adc组合按键电阻阻值参考_ad 检测四个开关 阻值-CSDN博客https://blog.csdn.net/weixin_43833645/article/details/128615455?spm1001.2014.3001.5501截图如下 现在对齐进行简化&#xff08;少了一个按键&#xff09; 其采样值列表如下图 …

Web3.0与虚拟现实:改变前端开发的新机遇

Hello大家好&#xff01;我是咕噜的铁蛋&#xff01;。近年来&#xff0c;Web3.0和虚拟现实技术的兴起引起了广泛的关注和讨论。它们不仅在互联网领域带来了革命性的变化&#xff0c;同时也给前端开发者带来了全新的机遇和挑战。今天铁蛋讲和大家一起探讨Web3.0与虚拟现实如何改…

游戏、设计选什么内存条?光威龙武系列DDR5量大管饱

如果你是一位PC玩家或者创作者&#xff0c;日常工作娱乐中&#xff0c;确实少不了大容量高频内存的支持&#xff0c;这样可以获得更高的工作效率&#xff0c;光威龙武系列DDR5内存条无疑是理想之选。它可以为计算机提供强劲的性能表现和稳定的运行体验&#xff0c;让我们畅玩游…

Python猜数游戏

文章目录 1 Game Rule2 Code3 Result 1 Game Rule 猜数字游戏目的是猜测出程序想出的数字&#xff0c;基本逻辑&#xff1a; 程序随机选择1到100之间的一个数字或任何其他数字组合&#xff1b; 然后它会要求玩家输入它的建议&#xff1b; 然后它会检查这个数字是否与计算机随…

React 入门 - 05(响应式与事件绑定)

本章内容 目录 一、响应式设计思想二、React 中的事件绑定 继上一节我们简单实现一个 TodoList来更加了解编写组件的一些细节。本节继续这个案例功能的完成。 一、响应式设计思想 1、在原生的 JS中&#xff0c;如果要实现点击”提交“按钮就将输入框的内容添加至页面列表中&…

mysql忘记root密码后怎么重置

mysql忘记root密码后重置方法【windows版本】 重置密码步骤停掉mysql服务跳过密码进入数据库在user表中重置密码使用新密码登录mysql到此&#xff0c;密码就成功修改了&#xff0c;完结&#xff0c;撒花~ 重置密码步骤 当我们忘记mysql的密码时&#xff0c;连接mysql会报这样的…

虾皮商品标题:如何创建有效的虾皮商品标题

虾皮&#xff08;Shopee&#xff09;平台是一个非常受欢迎的电商平台&#xff0c;为卖家提供了一个广阔的销售渠道。在虾皮上&#xff0c;一个有效的商品标题是吸引潜在买家注意力的关键元素之一。一个好的商品标题能够吸引更多的点击和浏览量&#xff0c;从而提高销售机会。下…

vue设置height:100vh导致页面超出屏幕可以上下滑动

刚开始设置的height:100vh&#xff0c;就会出现如图的效果&#xff0c;会出现上下滚动 <template><view class"container">......</view> </template><style lang"scss">.container {height: 100vh;} </style> 解决方…

99%的人还不知道的私域流量管理工具

一、多个微信可以聚合管理 简单来讲这是一款一个窗口对多个个人微信号的聊天转化工具&#xff0c;一款集成众多功能的网页版聊天工具&#xff0c;支持多开N个微信号在同一个窗口&#xff0c;实现了集中会话&#xff0c;不用来回切换账号。 二、常用语快捷回复 可对常用的问题…

代码随想录算法训练营Day08|344.反转字符串、541. 反转字符串II、卡码网:替换数字、151.翻转字符串里的单词、卡码网:右旋字符串

文章目录 一、344.反转字符串1. 双指针法 二、541. 反转字符串II1. 字符串解法 三、卡码网&#xff1a;替换数字四、151.翻转字符串里的单词1.使用库函数2.自行编写函数3.创建字符数组填充3.双反转移位 五、卡码网&#xff1a;右旋字符串1. 自行编写函数 总结 一、344.反转字符…

专利:发明和实用新型的区别

上一篇给大家分享了专利&#xff0c;今天说一下发明和实用新型得区别。 什么是发明专利&#xff1f; 发明&#xff0c;是指对产品、方法或者其改进所提出的新的技术方案。发明与实用新型和外观设计一起&#xff0c;构成我国专利法所保护的对象。发明专利作为知识产权的一种&am…

面向设计师的11个必备AI工具

在当今快速发展的设计领域&#xff0c;人工智能&#xff08;AI&#xff09;工具已成为不可或缺的创新催化剂。这些工具专门用于提高效率和创造力&#xff0c;从而重新定义传统的设计方法。AI正在彻底改变设计师的工作方式&#xff0c;从自动处理任务到发掘新的创造力机会&#…

【微信支付】【java】Springboot对接开发微信支付

本文章是介绍java对接微信支付&#xff0c;包括微信预下单、支付、退款等等。 一、微信配置申请 1、微信支付配置申请 详细操作流程参考官方文档&#xff1a;https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml#part-1 配置完成需要以下信息&#xff1…

银河麒麟Kylin-Server-V10-SP3使用ISO镜像搭建本地内网YUM/DNF源cdrom/http

机房服务器安装一般是内网环境&#xff0c;需要配置本地的YUM/DNF源。本文介绍通过ISO镜像搭建内网环境的UM/DNF源 准备工作&#xff1a; 提前准备好Kylin-Server-V10-SP3的ISO镜像文件。 本机IP地址&#xff1a;192.168.40.201 镜像存放目录/data/iso/Kylin-Server-V10-SP3-Ge…

以unity技术开发视角对android权限的讲解

目录 前言 Android权限分类 普通权限 普通权限定义 普通权限有哪些 危险权限 危险权限的定义 危险权限有哪些 动态申请权限实例 申请单个权限实例 第一步&#xff1a;在清单文件中声明权限 第二步&#xff1a;在代码中进行动态申请权限 申请多个权限实例 第一步&am…

[C#]使用PaddleInference图片旋转四种角度检测

官方框架地址】 https://github.com/PaddlePaddle/PaddleDetection.git 【算法介绍】 PaddleDetection 是一个基于 PaddlePaddle&#xff08;飞桨&#xff09;深度学习框架的开源目标检测工具库。它提供了一系列先进的目标检测算法&#xff0c;包括但不限于 Faster R-CNN, Ma…

OV5640 摄像头的图像平滑处理

如图所示&#xff0c;这是整个视频采集系统的原理框图。 上电初始&#xff0c;FPGA 需要通过 IIC 接口对 CMOS Sensor 进行寄存器初始化配置。这些初始化的基本参数&#xff0c;即初始化地址对应的初始化数据都存储在一个预先配置好的 FPGA 片内 ROM中。在初始化配置完成后&…

视频监控系统EasyCVR如何通过调用API接口查询和下载设备录像?

智慧安防平台EasyCVR是基于各种IP流媒体协议传输的视频汇聚和融合管理平台。视频流媒体服务器EasyCVR采用了开放式的网络结构&#xff0c;支持高清视频的接入和传输、分发&#xff0c;平台提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联…