【x265 源码分析系列】:概述

news2024/11/25 19:29:53

介绍

  1. x265 也属于 VLC 的 project。

  2. 版本: x265-3.5(TAG-208)

  3. git: https://bitbucket.org/multicoreware/x265_git.git

  4. 编码特点:
    在这里插入图片描述

  5. 研究了一段时间的 HEVC 编码标准,最近开始研究符合 HEVC 标准的开源编码器 x265;本文对 x265 进行简单梳理代码结构。

  6. x265 使用的是 C++语言标准,而 x264 使用的是 C 语言标准。

  7. HEVC 标准介绍可以参考HEVC编码标准介绍。

函数调用关系图

x265 的从 main 函数到 API 的调用关系如下:
在这里插入图片描述

x265命令行程序

x265 命令行程序通过调用 libx265 库将视频YUV编码成视频流 H265。

入口函数是 main()函数,编码功能主要就是通过结构体CLIOptions、类AbrEncoder来完成;其中CLIOptions主要用来解析命令行以及编码参数,AbrEncoder主要完成了具体编码工作。

AbrEncoder通过线程激活控制核心的编码类PassEncoder;在创建(new 过程)AbrEncoder时其构造函数就创建(new 过程)了PassEncoder类、初始化 init()同时开启了PassEncoder工作线程startThreads();最后销毁 destroy()释放delete资源。

PassEncoder类的初始化 init()函数主要调用了 API 函数encoder_open(m_param)打开编码器。

PassEncoder 类的startThreads()通过控制变量 m_threadActivetruefalse来完成激活线程主函数threadMain()

PassEncoder类的线程主函数threadMain()将结构体里CLIOptions的结构体api拷贝,通过结构体api里 API 函数encoder_headers()picture_init()encoder_encode()encoder_get_stats()encoder_log()encoder_close()param_free()完成核心的视频编码工作。

destroy()主要就调用了 PassEncoder 类的destroy()函数停止工作线程的。

主函数 main()

将外部命令行与内部编码器结合的可执行程序的主体。

/* CLI return codes:
 *
 * 0 - encode successful
 * 1 - unable to parse command line
 * 2 - unable to open encoder
 * 3 - unable to generate stream headers
 * 4 - encoder abort */

int main(int argc, char **argv)
{
#if HAVE_VLD
    // This uses Microsoft's proprietary WCHAR type, but this only builds on Windows to start with
    VLDSetReportOptions(VLD_OPT_REPORT_TO_DEBUGGER | VLD_OPT_REPORT_TO_FILE, L"x265_leaks.txt");
#endif
    PROFILE_INIT();
    THREAD_NAME("API", 0);

    GetConsoleTitle(orgConsoleTitle, CONSOLE_TITLE_SIZE);
    SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED);
#if _WIN32
    char** orgArgv = argv;
    get_argv_utf8(&argc, &argv);
#endif

    uint8_t numEncodes = 1;
    FILE *abrConfig = NULL;
    bool isAbrLadder = checkAbrLadder(argc, argv, &abrConfig);

    if (isAbrLadder)
        numEncodes = getNumAbrEncodes(abrConfig);

    CLIOptions* cliopt = new CLIOptions[numEncodes];

    if (isAbrLadder)
    {
        if (!parseAbrConfig(abrConfig, cliopt, numEncodes))
            exit(1);
        if (!setRefContext(cliopt, numEncodes))
            exit(1);
    }
    else if (cliopt[0].parse(argc, argv))
    {
        cliopt[0].destroy();
        if (cliopt[0].api)
            cliopt[0].api->param_free(cliopt[0].param);
        exit(1);
    }

    int ret = 0;

    if (cliopt[0].scenecutAwareQpConfig)
    {
        if (!cliopt[0].parseScenecutAwareQpConfig())
        {
            x265_log(NULL, X265_LOG_ERROR, "Unable to parse scenecut aware qp config file \n");
            fclose(cliopt[0].scenecutAwareQpConfig);
            cliopt[0].scenecutAwareQpConfig = NULL;
        }
    }

    AbrEncoder* abrEnc = new AbrEncoder(cliopt, numEncodes, ret);
    int threadsActive = abrEnc->m_numActiveEncodes.get();
    while (threadsActive)
    {
        threadsActive = abrEnc->m_numActiveEncodes.waitForChange(threadsActive);
        for (uint8_t idx = 0; idx < numEncodes; idx++)
        {
            if (abrEnc->m_passEnc[idx]->m_ret)
            {
                if (isAbrLadder)
                    x265_log(NULL, X265_LOG_INFO, "Error generating ABR-ladder \n");
                ret = abrEnc->m_passEnc[idx]->m_ret;
                threadsActive = 0;
                break;
            }
        }
    }

    abrEnc->destroy();
    delete abrEnc;

    for (uint8_t idx = 0; idx < numEncodes; idx++)
        cliopt[idx].destroy();

    delete[] cliopt;

    SetConsoleTitle(orgConsoleTitle);
    SetThreadExecutionState(ES_CONTINUOUS);

#if _WIN32
    if (argv != orgArgv)
    {
        free(argv);
        argv = orgArgv;
    }
#endif

#if HAVE_VLD
    assert(VLDReportLeaks() == 0);
#endif

    return ret;
}

AbrEncoder的构造函数

    AbrEncoder::AbrEncoder(CLIOptions cliopt[], uint8_t numEncodes, int &ret)
    {
        m_numEncodes = numEncodes;
        m_numActiveEncodes.set(numEncodes);
        m_queueSize = (numEncodes > 1) ? X265_INPUT_QUEUE_SIZE : 1;
        m_passEnc = X265_MALLOC(PassEncoder*, m_numEncodes);

        for (uint8_t i = 0; i < m_numEncodes; i++)
        {
            m_passEnc[i] = new PassEncoder(i, cliopt[i], this);
            if (!m_passEnc[i])
            {
                x265_log(NULL, X265_LOG_ERROR, "Unable to allocate memory for passEncoder\n");
                ret = 4;
            }
            m_passEnc[i]->init(ret);
        }

        if (!allocBuffers())
        {
            x265_log(NULL, X265_LOG_ERROR, "Unable to allocate memory for buffers\n");
            ret = 4;
        }

        /* start passEncoder worker threads */
        for (uint8_t pass = 0; pass < m_numEncodes; pass++)
            m_passEnc[pass]->startThreads();
    }

PassEncoder类的 init()

   int PassEncoder::init(int &result)
    {
        if (m_parent->m_numEncodes > 1)
            setReuseLevel();
                
        if (!(m_cliopt.enableScaler && m_id))
            m_reader = new Reader(m_id, this);
        else
        {
            VideoDesc *src = NULL, *dst = NULL;
            dst = new VideoDesc(m_param->sourceWidth, m_param->sourceHeight, m_param->internalCsp, m_param->internalBitDepth);
            int dstW = m_parent->m_passEnc[m_id - 1]->m_param->sourceWidth;
            int dstH = m_parent->m_passEnc[m_id - 1]->m_param->sourceHeight;
            src = new VideoDesc(dstW, dstH, m_param->internalCsp, m_param->internalBitDepth);
            if (src != NULL && dst != NULL)
            {
                m_scaler = new Scaler(0, 1, m_id, src, dst, this);
                if (!m_scaler)
                {
                    x265_log(m_param, X265_LOG_ERROR, "\n MALLOC failure in Scaler");
                    result = 4;
                }
            }
        }

        if (m_cliopt.zoneFile)
        {
            if (!m_cliopt.parseZoneFile())
            {
                x265_log(NULL, X265_LOG_ERROR, "Unable to parse zonefile in %s\n");
                fclose(m_cliopt.zoneFile);
                m_cliopt.zoneFile = NULL;
            }
        }

        /* note: we could try to acquire a different libx265 API here based on
        * the profile found during option parsing, but it must be done before
        * opening an encoder */

        if (m_param)
            m_encoder = m_cliopt.api->encoder_open(m_param);
        if (!m_encoder)
        {
            x265_log(NULL, X265_LOG_ERROR, "x265_encoder_open() failed for Enc, \n");
            m_ret = 2;
            return -1;
        }

        /* get the encoder parameters post-initialization */
        m_cliopt.api->encoder_parameters(m_encoder, m_param);

        return 1;
    }

PassEncoder类的 threadmian()

   void PassEncoder::threadMain()
    {
        THREAD_NAME("PassEncoder", m_id);

        while (m_threadActive)
        {

#if ENABLE_LIBVMAF
            x265_vmaf_data* vmafdata = m_cliopt.vmafData;
#endif
            /* This allows muxers to modify bitstream format */
            m_cliopt.output->setParam(m_param);
            const x265_api* api = m_cliopt.api;
            ReconPlay* reconPlay = NULL;
            if (m_cliopt.reconPlayCmd)
                reconPlay = new ReconPlay(m_cliopt.reconPlayCmd, *m_param);
            char* profileName = m_cliopt.encName ? m_cliopt.encName : (char *)"x265";

            if (signal(SIGINT, sigint_handler) == SIG_ERR)
                x265_log(m_param, X265_LOG_ERROR, "Unable to register CTRL+C handler: %s in %s\n",
                    strerror(errno), profileName);

            x265_picture pic_orig, pic_out;
            x265_picture *pic_in = &pic_orig;
            /* Allocate recon picture if analysis save/load is enabled */
            std::priority_queue<int64_t>* pts_queue = m_cliopt.output->needPTS() ? new std::priority_queue<int64_t>() : NULL;
            x265_picture *pic_recon = (m_cliopt.recon || m_param->analysisSave || m_param->analysisLoad || pts_queue || reconPlay || m_param->csvLogLevel) ? &pic_out : NULL;
            uint32_t inFrameCount = 0;
            uint32_t outFrameCount = 0;
            x265_nal *p_nal;
            x265_stats stats;
            uint32_t nal;
            int16_t *errorBuf = NULL;
            bool bDolbyVisionRPU = false;
            uint8_t *rpuPayload = NULL;
            int inputPicNum = 1;
            x265_picture picField1, picField2;
            x265_analysis_data* analysisInfo = (x265_analysis_data*)(&pic_out.analysisData);
            bool isAbrSave = m_cliopt.saveLevel && (m_parent->m_numEncodes > 1);

            if (!m_param->bRepeatHeaders && !m_param->bEnableSvtHevc)
            {
                if (api->encoder_headers(m_encoder, &p_nal, &nal) < 0)
                {
                    x265_log(m_param, X265_LOG_ERROR, "Failure generating stream headers in %s\n", profileName);
                    m_ret = 3;
                    goto fail;
                }
                else
                    m_cliopt.totalbytes += m_cliopt.output->writeHeaders(p_nal, nal);
            }

            if (m_param->bField && m_param->interlaceMode)
            {
                api->picture_init(m_param, &picField1);
                api->picture_init(m_param, &picField2);
                // return back the original height of input
                m_param->sourceHeight *= 2;
                api->picture_init(m_param, &pic_orig);
            }
            else
                api->picture_init(m_param, &pic_orig);

            if (m_param->dolbyProfile && m_cliopt.dolbyVisionRpu)
            {
                rpuPayload = X265_MALLOC(uint8_t, 1024);
                pic_in->rpu.payload = rpuPayload;
                if (pic_in->rpu.payload)
                    bDolbyVisionRPU = true;
            }

            if (m_cliopt.bDither)
            {
                errorBuf = X265_MALLOC(int16_t, m_param->sourceWidth + 1);
                if (errorBuf)
                    memset(errorBuf, 0, (m_param->sourceWidth + 1) * sizeof(int16_t));
                else
                    m_cliopt.bDither = false;
            }

            // main encoder loop
            while (pic_in && !b_ctrl_c)
            {
                pic_orig.poc = (m_param->bField && m_param->interlaceMode) ? inFrameCount * 2 : inFrameCount;
                if (m_cliopt.qpfile)
                {
                    if (!m_cliopt.parseQPFile(pic_orig))
                    {
                        x265_log(NULL, X265_LOG_ERROR, "can't parse qpfile for frame %d in %s\n",
                            pic_in->poc, profileName);
                        fclose(m_cliopt.qpfile);
                        m_cliopt.qpfile = NULL;
                    }
                }

                if (m_cliopt.framesToBeEncoded && inFrameCount >= m_cliopt.framesToBeEncoded)
                    pic_in = NULL;
                else if (readPicture(pic_in))
                    inFrameCount++;
                else
                    pic_in = NULL;

                if (pic_in)
                {
                    if (pic_in->bitDepth > m_param->internalBitDepth && m_cliopt.bDither)
                    {
                        x265_dither_image(pic_in, m_cliopt.input->getWidth(), m_cliopt.input->getHeight(), errorBuf, m_param->internalBitDepth);
                        pic_in->bitDepth = m_param->internalBitDepth;
                    }
                    /* Overwrite PTS */
                    pic_in->pts = pic_in->poc;

                    // convert to field
                    if (m_param->bField && m_param->interlaceMode)
                    {
                        int height = pic_in->height >> 1;

                        int static bCreated = 0;
                        if (bCreated == 0)
                        {
                            bCreated = 1;
                            inputPicNum = 2;
                            picField1.fieldNum = 1;
                            picField2.fieldNum = 2;

                            picField1.bitDepth = picField2.bitDepth = pic_in->bitDepth;
                            picField1.colorSpace = picField2.colorSpace = pic_in->colorSpace;
                            picField1.height = picField2.height = pic_in->height >> 1;
                            picField1.framesize = picField2.framesize = pic_in->framesize >> 1;

                            size_t fieldFrameSize = (size_t)pic_in->framesize >> 1;
                            char* field1Buf = X265_MALLOC(char, fieldFrameSize);
                            char* field2Buf = X265_MALLOC(char, fieldFrameSize);

                            int stride = picField1.stride[0] = picField2.stride[0] = pic_in->stride[0];
                            uint64_t framesize = stride * (height >> x265_cli_csps[pic_in->colorSpace].height[0]);
                            picField1.planes[0] = field1Buf;
                            picField2.planes[0] = field2Buf;
                            for (int i = 1; i < x265_cli_csps[pic_in->colorSpace].planes; i++)
                            {
                                picField1.planes[i] = field1Buf + framesize;
                                picField2.planes[i] = field2Buf + framesize;

                                stride = picField1.stride[i] = picField2.stride[i] = pic_in->stride[i];
                                framesize += (stride * (height >> x265_cli_csps[pic_in->colorSpace].height[i]));
                            }
                            assert(framesize == picField1.framesize);
                        }

                        picField1.pts = picField1.poc = pic_in->poc;
                        picField2.pts = picField2.poc = pic_in->poc + 1;

                        picField1.userSEI = picField2.userSEI = pic_in->userSEI;

                        //if (pic_in->userData)
                        //{
                        //    // Have to handle userData here
                        //}

                        if (pic_in->framesize)
                        {
                            for (int i = 0; i < x265_cli_csps[pic_in->colorSpace].planes; i++)
                            {
                                char* srcP1 = (char*)pic_in->planes[i];
                                char* srcP2 = (char*)pic_in->planes[i] + pic_in->stride[i];
                                char* p1 = (char*)picField1.planes[i];
                                char* p2 = (char*)picField2.planes[i];

                                int stride = picField1.stride[i];

                                for (int y = 0; y < (height >> x265_cli_csps[pic_in->colorSpace].height[i]); y++)
                                {
                                    memcpy(p1, srcP1, stride);
                                    memcpy(p2, srcP2, stride);
                                    srcP1 += 2 * stride;
                                    srcP2 += 2 * stride;
                                    p1 += stride;
                                    p2 += stride;
                                }
                            }
                        }
                    }

                    if (bDolbyVisionRPU)
                    {
                        if (m_param->bField && m_param->interlaceMode)
                        {
                            if (m_cliopt.rpuParser(&picField1) > 0)
                                goto fail;
                            if (m_cliopt.rpuParser(&picField2) > 0)
                                goto fail;
                        }
                        else
                        {
                            if (m_cliopt.rpuParser(pic_in) > 0)
                                goto fail;
                        }
                    }
                }

                for (int inputNum = 0; inputNum < inputPicNum; inputNum++)
                {
                    x265_picture *picInput = NULL;
                    if (inputPicNum == 2)
                        picInput = pic_in ? (inputNum ? &picField2 : &picField1) : NULL;
                    else
                        picInput = pic_in;

                    int numEncoded = api->encoder_encode(m_encoder, &p_nal, &nal, picInput, pic_recon);

                    int idx = (inFrameCount - 1) % m_parent->m_queueSize;
                    m_parent->m_picIdxReadCnt[m_id][idx].incr();
                    m_parent->m_picReadCnt[m_id].incr();
                    if (m_cliopt.loadLevel && picInput)
                    {
                        m_parent->m_analysisReadCnt[m_cliopt.refId].incr();
                        m_parent->m_analysisRead[m_cliopt.refId][m_lastIdx].incr();
                    }

                    if (numEncoded < 0)
                    {
                        b_ctrl_c = 1;
                        m_ret = 4;
                        break;
                    }

                    if (reconPlay && numEncoded)
                        reconPlay->writePicture(*pic_recon);

                    outFrameCount += numEncoded;

                    if (isAbrSave && numEncoded)
                    {
                        copyInfo(analysisInfo);
                    }

                    if (numEncoded && pic_recon && m_cliopt.recon)
                        m_cliopt.recon->writePicture(pic_out);
                    if (nal)
                    {
                        m_cliopt.totalbytes += m_cliopt.output->writeFrame(p_nal, nal, pic_out);
                        if (pts_queue)
                        {
                            pts_queue->push(-pic_out.pts);
                            if (pts_queue->size() > 2)
                                pts_queue->pop();
                        }
                    }
                    m_cliopt.printStatus(outFrameCount);
                }
            }

            /* Flush the encoder */
            while (!b_ctrl_c)
            {
                int numEncoded = api->encoder_encode(m_encoder, &p_nal, &nal, NULL, pic_recon);
                if (numEncoded < 0)
                {
                    m_ret = 4;
                    break;
                }

                if (reconPlay && numEncoded)
                    reconPlay->writePicture(*pic_recon);

                outFrameCount += numEncoded;
                if (isAbrSave && numEncoded)
                {
                    copyInfo(analysisInfo);
                }

                if (numEncoded && pic_recon && m_cliopt.recon)
                    m_cliopt.recon->writePicture(pic_out);
                if (nal)
                {
                    m_cliopt.totalbytes += m_cliopt.output->writeFrame(p_nal, nal, pic_out);
                    if (pts_queue)
                    {
                        pts_queue->push(-pic_out.pts);
                        if (pts_queue->size() > 2)
                            pts_queue->pop();
                    }
                }

                m_cliopt.printStatus(outFrameCount);

                if (!numEncoded)
                    break;
            }

            if (bDolbyVisionRPU)
            {
                if (fgetc(m_cliopt.dolbyVisionRpu) != EOF)
                    x265_log(NULL, X265_LOG_WARNING, "Dolby Vision RPU count is greater than frame count in %s\n",
                        profileName);
                x265_log(NULL, X265_LOG_INFO, "VES muxing with Dolby Vision RPU file successful in %s\n",
                    profileName);
            }

            /* clear progress report */
            if (m_cliopt.bProgress)
                fprintf(stderr, "%*s\r", 80, " ");

        fail:

            delete reconPlay;

            api->encoder_get_stats(m_encoder, &stats, sizeof(stats));
            if (m_param->csvfn && !b_ctrl_c)
#if ENABLE_LIBVMAF
                api->vmaf_encoder_log(m_encoder, m_cliopt.argCnt, m_cliopt.argString, m_cliopt.param, vmafdata);
#else
                api->encoder_log(m_encoder, m_cliopt.argCnt, m_cliopt.argString);
#endif
            api->encoder_close(m_encoder);

            int64_t second_largest_pts = 0;
            int64_t largest_pts = 0;
            if (pts_queue && pts_queue->size() >= 2)
            {
                second_largest_pts = -pts_queue->top();
                pts_queue->pop();
                largest_pts = -pts_queue->top();
                pts_queue->pop();
                delete pts_queue;
                pts_queue = NULL;
            }
            m_cliopt.output->closeFile(largest_pts, second_largest_pts);

            if (b_ctrl_c)
                general_log(m_param, NULL, X265_LOG_INFO, "aborted at input frame %d, output frame %d in %s\n",
                    m_cliopt.seek + inFrameCount, stats.encodedPictureCount, profileName);

            api->param_free(m_param);

            X265_FREE(errorBuf);
            X265_FREE(rpuPayload);

            m_threadActive = false;
            m_parent->m_numActiveEncodes.decr();
        }
    }

后续

通过 x265 的 API 函数进一步分析内部源码结构和算法逻辑。

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

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

相关文章

STM32G070RBT6-MCU温度测量(ADC)

1、借助STM32CubeMX生成系统及外设相关初始化代码。 在以上配置后就可以生成相关初始化代码了。 /* ADC1 init function */ void MX_ADC1_Init(void) {/* USER CODE BEGIN ADC1_Init 0 *//* USER CODE END ADC1_Init 0 */ADC_ChannelConfTypeDef sConfig {0};/* USER COD…

MyBatis-Plus通用Service快速实现赠三改查[MyBatis-Plus系列] - 第489篇

历史文章&#xff08;文章累计480&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 M…

综述 | 关于点云配准的全面综述(二)

原创 | 文 BFT机器人 05 基于优化的配准方法 基于优化的方法的关键思想是开发复杂的优化策略来实现方程&#xff08;1&#xff09;中非线性问题的最优解。 由于同源挑战的影响&#xff0c;这个非线性问题变得具有挑战性。图&#xff08;2a&#xff09;总结了该类别的主要过程。…

从零开始之了解电机及其控制(11)实现空间矢量调制

广泛地说&#xff0c;空间矢量调制只是将电压矢量以及磁场矢量在空间中调制到任意角度&#xff0c;通常同时最大限度地利用整个电压范围。 其他空间矢量调制模式确实存在&#xff0c;并且根据您最关心的内容&#xff0c;它们可能值得研究。 如何实际执行这种所谓的交替反向序列…

看看属猴人性格及近几年的运势怎么样?

属猴的人五行主金&#xff0c;乃是申金之所在&#xff0c;金旺之人&#xff0c;外显懒散&#xff0c;内心富有主见&#xff0c;行事坚定&#xff0c; 有贯彻始终之斗志&#xff0c;与他人合作融洽&#xff0c;且得以财运颇多&#xff1b; 主金&#xff0c;杀伐果决、精明干练&a…

PHP各种老版本下载方式

最近因工作需要&#xff0c;要下载PHP7.3的最新版本版本。 PHP官网上提供了各种老版本下载地址&#xff1a; https://windows.php.net/downloads/releases/archives/ 下载速度不稳定&#xff0c;时快时慢。 使用前&#xff0c;给下载留足时间。 貌似晚上速度快一些。

gif怎么转换成视频MP4?

gif怎么转换成视频MP4&#xff1f;GIF动图已成为一种风靡网络的流行的特殊图片文件&#xff0c;其循环播放和逐帧呈现的特点使其在社交媒体、聊天应用等场合广泛应用&#xff0c;平时我们进行群聊是&#xff0c;大家总会一些gif动态表情的出现而感觉非常的开行&#xff0c;gif动…

Android12之容器类SortedVector、KeyedVector、Vector、VectorImpl总结(一百六十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

极致增长 | NetMarvel 程序化广告最大化广告变现收益

程序化广告已彻底改变广告主触达目标受众的方式。 从早期传统人工售卖流量&#xff0c;到流量平台推出广告联盟&#xff0c;从程序化交易到利用算法和机器学习实时计算买卖广告空间&#xff0c;通过逐渐精微的数据来测评不同渠道、不同受众的广告效果&#xff0c;提高广告主的…

datart:Invalid database configuration. Datart is running in demo mode

datart在IDEA配置好数据库连接之后&#xff0c;启动&#xff0c;报错&#xff1a; 【********* Invalid database configuration. Datart is running in demo mode *********】 原因是缺少一个变量 config 增加即可&#xff1a; 再次启动&#xff0c;就不会报无效数据库配置了…

百华鞋业董事长牛兴华应邀出席德国前总统武尔夫欢迎宴会

2023年9月25日&#xff0c;德国前总统克里斯蒂安-武尔夫&#xff08;Christian Wullff&#xff09;一行来华访问期间&#xff0c;于上海新华联索菲特举办2023中德交流领袖论坛暨武尔夫总统欢迎晚宴。百华鞋业董事长牛兴华先生受邀出席&#xff0c;并受到武尔夫的亲自接见。 山东…

关于NVIC 中断控制器的中断配置。

以下图片均来自NVIC控制器内容。 M3处理器仅实现了每个81个中断&#xff0c;每个中断的优先级由高4位控制。 这里的组优先级我认为是抢占式优先级。

多线程批量下载ERA5逐日数据

介绍 这篇博文主要是整了和ERA5官方参考文档和网上现有的代码&#xff0c;从而实现ERA5逐日数据的批量下载**&#xff08;可指定时区&#xff09;**。 先前准备 在使用python批量下载ERA5逐日数据前我们需要手动配置一下cdsapi 1.访问&#xff1a;CDS官网并注册账号 2.注册好…

掌动智能:UI自动化测试工具的重要性和应用

在软件开发过程中&#xff0c;测试是至关重要的环节。而UI自动化测试工具则成为了测试团队提高效率、降低成本、保证软件质量的重要利器。本文将介绍UI自动化测试工具的概念和重要性&#xff0c;并探讨其在软件开发中的应用和好处。 一、UI自动化测试工具的概念 UI自动化测试工…

获奖作品展示 | 2023嵌入式大赛AidLux系列作品精彩纷呈

第六届&#xff08;2023&#xff09;全国大学生嵌入式芯片与系统设计竞赛应用赛道全国总决赛已于8月下旬圆满结束。 本届赛事中&#xff0c;AidLux是广和通5G智能物联网赛题的唯一软件支持&#xff0c;阿加犀为该赛题学生们提供了全程线上辅导、技术答疑&#xff0c;以及大赛专…

VR庆中秋丨奇幻月景邀您共赏!

中秋佳节&#xff0c; 如何来一场别开生面的云游月景体验&#xff1f; 3DVR技术开启中秋过节新姿势&#xff0c; 嫦娥奔月伴玉兔、 太白花间饮美酒、 吴刚月下伐桂树…… 立体化还原璀璨的传统中秋文化&#xff0c; 还有趣味猜灯谜活动&#xff0c; 丰富豪礼等你来拿&a…

Coovally模型探索:高效下载并使用Hugging Face Transformers预训练模型

Hugging Face Transformers 是一个用于自然语言处理&#xff08;NLP&#xff09;的开源库&#xff0c;提供了各种预训练模型。这些模型被广泛应用于各种任务&#xff0c;如文本分类、命名实体识别、问答、文本生成等。Transformers库易于使用&#xff0c;可方便地集成到现有的深…

归并(merge)排序

归并&#xff08;merge&#xff09;排序也是采用分而治之的思想&#xff0c;其采用二分法将待排列数组分成若干个子数组。然后将相邻的子数组进行归并成新的有序子数组&#xff0c;然后在新的子数组的基础上在进行归并成新的有序数组&#xff0c;直至归并成一个整体有序的数组。…

源码编译postgresql

没什么好研究的了&#xff0c;就试试编译Postgresql源码&#xff0c;按照网站查的资料一步步测试的&#xff0c;方便后期定制数据库时候用&#xff0c;也算是开源的大优势了&#xff0c;只要你愿意折腾&#xff0c;可以自己定制或改进一个数据库来满足特殊业务。后面研究一下他…

Python 练习100实例(1-20)

Python 练习实例1 题目&#xff1a;有四个数字&#xff1a;1、2、3、4&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;各是多少&#xff1f; 程序分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。 …