FFmpeg将编码后数据保存成mp4

news2024/11/28 0:44:13

      以下测试代码实现的功能是:持续从内存块中获取原始数据,然后依次进行解码、编码、最后保存成mp4视频文件。

      可保存成单个视频文件,也可指定每个视频文件的总帧数,保存多个视频文件。

      为了便于查看和修改,这里将可独立的程序段存放在单个函数中:

      1.线程函数set_packet:独立线程,用于用户向指定的内存块中持续写入数据,这里通过调用队列类PacketScaleQueue,可参考:https://blog.csdn.net/fengbingchun/article/details/132128885 中说明。这里填充的数据参考FFmpeg源码中的doc/examples/encode_video.c:每次向队列中写入一帧数据

void set_packet(PacketScaleQueue& packet_encode)
{
    while (packet_encode_flag) {
        Buffer buffer;
        packet_encode.popPacket(buffer);
        static int i = 0;
    
        // refrence: ffmpeg/doc/examples/encode_video.c
        // prepare a dummy image: Y
        unsigned char* p1 = buffer.data;
        for (auto y = 0; y < height; ++y) {
            for (auto x = 0; x < width; ++x) {
                p1[y * width + x] = x + y + i * 3;
            }
        }

        // Cb and Cr
        unsigned char* p2 = buffer.data + width * height;
        unsigned char* p3 = buffer.data + width * height + width * height / 4;
        for (auto y = 0; y < height / 2; ++y) {
            for (auto x = 0; x < width / 2; ++x) {
                p2[y * width / 2 + x] = 128 + y + i * 2;
                p3[y * width / 2 + x] = 64 + x + i * 5;
            }
        }

        packet_encode.pushScale(buffer);

        if (++i > 25) i = 0;
        std::this_thread::sleep_for(std::chrono::milliseconds(40));
    }
}

      2.回调函数read_packet:avio_alloc_context中使用,不断从队列中获取数据,av_read_frame中也会从这里获取数据:每次从队列中获取一帧数据

int read_packet(void* opaque, uint8_t* buf, int buf_size)
{
    PacketScaleQueue* packet_encode = static_cast<PacketScaleQueue*>(opaque);
    Buffer buffer;
    packet_encode->popScale(buffer);
    memcpy(buf, buffer.data, buf_size);
    packet_encode->pushPacket(buffer);

    return buf_size;
}

      3.函数get_input_format_context:创建输入AVFormatContext,需要调用av_dict_set设置video_size和pixel_format

AVFormatContext* get_input_format_context(AVIOContext* avio_ctx)
{
    AVFormatContext* ifmt_ctx = avformat_alloc_context();
    if (!ifmt_ctx) {
        print_error_string(AVERROR(ENOMEM));
        return nullptr;
    }
    ifmt_ctx->pb = avio_ctx;

    AVDictionary* dict = nullptr;
    av_dict_set(&dict, "video_size", video_size, 0);
    av_dict_set(&dict, "pixel_format", pixel_format, 0);

    auto ret = avformat_open_input(&ifmt_ctx, nullptr, av_find_input_format("rawvideo"), &dict);
    if (ret < 0) {
        fprintf(stderr, "Could not open input\n");
        print_error_string(ret);
        return nullptr;
    }
    av_dict_free(&dict);

    ret = avformat_find_stream_info(ifmt_ctx, nullptr);
    if (ret < 0) {
        fprintf(stderr, "Could not find stream information\n");
        print_error_string(ret);
        return nullptr;
    }

    for (unsigned int i = 0; i < ifmt_ctx->nb_streams; ++i) {
        const AVStream* stream = ifmt_ctx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            fprintf(stdout, "type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",
                stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);
        }
    }

    if (video_stream_index == -1) {
        fprintf(stderr, "error: no video stream\n");
        return nullptr;
    }

    if (ifmt_ctx->streams[video_stream_index]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {
        fprintf(stderr, "error: this test code only support rawvideo encode: %d\n", ifmt_ctx->streams[video_stream_index]->codecpar->codec_id);
        return nullptr;
    }

    av_dump_format(ifmt_ctx, 0, "nothing", 0);
    return ifmt_ctx;
}

      4.函数get_decode_context:创建解码AVCodecContext

AVCodecContext* get_decode_context(AVFormatContext* ifmt_ctx)
{
    AVCodec* decoder = nullptr;
    auto ret = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
    if (ret < 0) {
        fprintf(stderr, "fail to av_find_best_stream: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    AVCodecContext* dec_ctx = avcodec_alloc_context3(decoder);
    if (!dec_ctx) {
        fprintf(stderr, "fail to avcodec_alloc_context3\n");
        return nullptr;
    }

    ret = avcodec_parameters_to_context(dec_ctx, ifmt_ctx->streams[video_stream_index]->codecpar);
    if (ret < 0) {
        fprintf(stderr, "fail to avcodec_parameters_to_context: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    dec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[video_stream_index], nullptr);
    ret = avcodec_open2(dec_ctx, decoder, nullptr);
    if (ret != 0) {
        fprintf(stderr, "fail to avcodec_open2: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    return dec_ctx;
}

      5.函数get_encode_context:创建编码AVCodecContext,注意对enc_ctx的相关成员的赋值

AVCodecContext* get_encode_context(AVFormatContext* ifmt_ctx)
{
    AVCodec* encodec = avcodec_find_encoder_by_name("mpeg1video"); // ffmpeg.exe -encoders
    if (!encodec) {
        fprintf(stderr, "fail to avcodec_find_encoder_by_name\n");
        return nullptr;
    }

    AVCodecContext* enc_ctx = avcodec_alloc_context3(encodec);
    if (!enc_ctx) {
        fprintf(stderr, "fail to avcodec_alloc_context3\n");
        return nullptr;
    }

    enc_ctx->bit_rate = 400000;
    enc_ctx->framerate = ifmt_ctx->streams[video_stream_index]->r_frame_rate;
    enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    enc_ctx->height = ifmt_ctx->streams[video_stream_index]->codecpar->height;
    enc_ctx->width = ifmt_ctx->streams[video_stream_index]->codecpar->width;
    enc_ctx->time_base = av_inv_q(av_d2q(frame_rate, 4096));
    enc_ctx->max_b_frames = 1;

    //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) // true
    enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    auto ret = avcodec_open2(enc_ctx, encodec, nullptr);
    if (ret != 0) {
        fprintf(stderr, "fail to avcodec_open2: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    return enc_ctx;
}

      6.函数get_output_format_context:创建输出AVFormatContext

AVFormatContext* get_output_format_context(const AVCodecContext* enc_ctx, const char* filename)
{
    AVFormatContext* ofmt_ctx = nullptr;
    auto ret = avformat_alloc_output_context2(&ofmt_ctx, nullptr, nullptr, filename);
    if (ret < 0 || !ofmt_ctx) {
        fprintf(stderr, "fail to avformat_alloc_output_context2: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
    if (!out_stream) {
        fprintf(stderr, "fail to avformat_new_stream\n");
        return nullptr;
    }

    ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
    if (ret < 0) {
        fprintf(stderr, "fail to avcodec_parameters_from_context: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    out_stream->time_base = enc_ctx->time_base;

    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { // true
        ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "fail to avio_open: %d\n", ret);
            print_error_string(ret);
            return nullptr;
        }
    }

    ret = avformat_write_header(ofmt_ctx, nullptr);
    if (ret < 0) {
        fprintf(stderr, "fail to avformat_write_header: %d\n", ret);
        print_error_string(ret);
        return nullptr;
    }

    av_dump_format(ofmt_ctx, 0, filename, 1);
    return ofmt_ctx;
}

      7.函数decode: 注意对packet的判断,它有可能为nullptr;调用一次avcodec_send_packet,可能需调用多次avcodec_receive_frame,因此需要将avcodec_receive_frame放在while中;需要对dec_ctx->time_base进行设置

int decode(AVPacket* packet, AVFormatContext* ifmt_ctx, AVCodecContext* dec_ctx, AVFrame* frame)
{
    if (packet)
        av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, dec_ctx->time_base);

    auto ret = avcodec_send_packet(dec_ctx, packet);
    if (ret < 0) {
        fprintf(stderr, "fail to avcodec_send_packet: %d\n", ret);
        print_error_string(ret);
        return -1;
    }

    while (1) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): decode is not yet complete
            //fprintf(stderr, "Warning: avcodec_receive_frame: %d\n", ret);
            //print_error_string(ret);
            break;
        }
        else if (ret < 0) {
            fprintf(stderr, "fail to avcodec_receive_frame: %d\n", ret);
            print_error_string(ret);
            return ret;
        }

        frame->pts = frame->best_effort_timestamp;
        break;
    }

    if (packet)
        av_packet_unref(packet);

    return 0;
}

      8.函数encode:注意:调用一次avcodec_send_frame,可能需要调用多次avcodec_receive_packet,因此需要将avcodec_receive_packet放在while中;需要对ofmt_ctx->streams[0]->time_base进行设置,否则生成的mp4中无法获取帧速率;也可在调用avformat_write_header时设置video_track_timescale指定

int encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* packet, AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx)
{
    auto ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0) {
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            fprintf(stderr, "Warning: avcodec_send_frame: %d\n", ret);
            print_error_string(ret);
            ret = 0;
        }
        else {
            fprintf(stderr, "fail to avcodec_send_frame: %d\n", ret);
            print_error_string(ret);
            return ret;
        }
    }

    while (1) {
        ret = avcodec_receive_packet(enc_ctx, packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): encode is not yet complete
            //fprintf(stderr, "Warning: avcodec_receive_packet: %d\n", ret);
            //print_error_string(ret);
            break;
        }

        if (ret < 0) {
            fprintf(stderr, "fail to avcodec_receive_packet: %d\n", ret);
            print_error_string(ret);
            return ret;
        }

        packet->stream_index = 0;
        av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, ofmt_ctx->streams[0]->time_base);
        //packet2->pts = packet2->dts = frame->pts *
        //    ofmt_ctx->streams[0]->time_base.den / ofmt_ctx->streams[0]->time_base.num / 
        //    (enc_ctx->framerate.num / enc_ctx->framerate.den);

        ret = av_interleaved_write_frame(ofmt_ctx, packet);
        if (ret < 0) {
            print_error_string(ret);
            return ret;
        }

        av_packet_unref(packet);
    }

    return 0;
}

      9.主函数test_ffmpeg_save_video_slice:注意:在退出前需要flush decoder和encoder;写入视频文件需先调用avformat_write_header,然后持续调用av_interleaved_write_frame,退出前再需调用av_write_trailer;每次新文件的写入,需重新调用get_decode_context、get_encode_context、get_output_format_context并在之前作相应free

int test_ffmpeg_save_video_slice()
{
    PacketScaleQueue packet_encode;
    packet_encode.init(queue_size, block_size);

    std::thread thread_packet(set_packet, std::ref(packet_encode));

    uint8_t* avio_ctx_buffer = static_cast<uint8_t*>(av_malloc(block_size));
    if (!avio_ctx_buffer) {
        print_error_string(AVERROR(ENOMEM));
        return -1;
    }

    AVIOContext* avio_ctx = avio_alloc_context(avio_ctx_buffer, block_size, 0, &packet_encode, &read_packet, nullptr, nullptr);
    if (!avio_ctx) {
        print_error_string(AVERROR(ENOMEM));
        return -1;
    }

    AVFormatContext* ifmt_ctx = get_input_format_context(avio_ctx);
    if (!ifmt_ctx) {
        fprintf(stderr, "fail to get_input_format_context\n");
        return -1;
    }

    AVPacket *packet = av_packet_alloc(), *packet2 = av_packet_alloc();
    if (!packet || !packet2) {
        fprintf(stderr, "fail to av_packet_alloc\n");
        return -1;
    }

    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "fail to av_frame_alloc\n");
        return -1;
    }

    int count = 0, name = 1;
    AVCodecContext* enc_ctx = nullptr;
    AVFormatContext* ofmt_ctx = nullptr;
    AVCodecContext* dec_ctx = nullptr;

    while (1) {
        auto ret = av_read_frame(ifmt_ctx, packet);
        if (ret < 0) {
            break;
        }

        if (packet->stream_index != video_stream_index) {
            av_packet_unref(packet);
            continue;
        }

        if (count % slice_size == 0) {
            enc_ctx = get_encode_context(ifmt_ctx);
            if (!enc_ctx) {
                fprintf(stderr, "fail to avcodec_alloc_context3\n");
                return -1;
            }

            std::string filename = std::to_string(name) + ".mp4";
            filename = std::string(path) + filename;
            ofmt_ctx = get_output_format_context(enc_ctx, filename.c_str());
            if (!ofmt_ctx) {
                fprintf(stderr, "fail to get_output_format_context\n");
                return -1;
            }

            dec_ctx = get_decode_context(ifmt_ctx);
            if (!dec_ctx) {
                fprintf(stderr, "fail to get_decode_context\n");
                return -1;
            }

            ++name;
        }

        if (decode(packet, ifmt_ctx, dec_ctx, frame) != 0) return -1;

        if (encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx) != 0) return -1;

        //fprintf(stdout, "count: %d\n", count);
        if (count + 1 == total_frames) { // terminate loop
            packet_encode_flag = false;

            // flush the decoder
            decode(nullptr, ifmt_ctx, dec_ctx, frame);
            if (frame->data[0])
                encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);

            // flush the encoder
            encode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);

            av_write_trailer(ofmt_ctx);
            break;
        }

        ++count;

        if (count > 1 && count % slice_size == 0) {
            // flush the decoder
            decode(nullptr, ifmt_ctx, dec_ctx, frame);
            if (frame->data[0])
                encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);

            // flush the encoder
            encode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);

            av_write_trailer(ofmt_ctx);

            avcodec_free_context(&dec_ctx);
            avcodec_free_context(&enc_ctx);
            avio_closep(&ofmt_ctx->pb);
            avformat_free_context(ofmt_ctx);
        }
    }

    av_packet_free(&packet);
    av_packet_free(&packet2);
    av_frame_free(&frame);

    avcodec_free_context(&dec_ctx);
    avcodec_free_context(&enc_ctx);
    avio_closep(&ofmt_ctx->pb);
    avformat_close_input(&ofmt_ctx);
    avformat_close_input(&ifmt_ctx);

    // note: the internal buffer could have changed, and be != avio_ctx_buffer
    if (avio_ctx) {
        av_freep(&avio_ctx->buffer);
        av_freep(&avio_ctx);
    }

    thread_packet.join();
    fprintf(stdout, "test finish\n");

    return 0;
}

      执行结果如下图所示:

      可调用ffprobe.exe验证每个生成的视频文件的总帧数,如1.mp4,执行如下命令:

ffprobe.exe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 1.mp4

      GitHub:https://github.com/fengbingchun/OpenCV_Test

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

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

相关文章

Redis追本溯源(四)集群:主从模式、哨兵模式、cluster模式

文章目录 一、主从模式1.主从复制——全量复制2.主从复制——增量复制 二、哨兵模式1.实时监控与故障转移2.Sentinel选举领导者 三、cluster模式1.三种分片方案2.cluster模式 Redis 有多种集群搭建方式&#xff0c;比如&#xff0c;主从模式、哨兵模式、Cluster 模式。 一、主…

一些不错的VSCode设置和插件

设置 同步设置 我们做的各项设置&#xff0c;不希望再到其他机器的时候还得再重新配置一次。VSCode中我们可以登陆微软账号或者GitHub账号&#xff0c;登陆后我们可以开启同步设置。开启设置同步&#xff0c;根据提示登陆即可。 允许侧边栏水平滑动 在目录层次较深或者文件…

Leetcode-每日一题【剑指 Offer 07. 重建二叉树】

题目 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 示例 1: Input: preorder [3,9,20,15,7], inorder [9,3,15,20,7]Output: [3,9,20,null,null,15,7] 示例 2: Input: preo…

【Python小demo】JOJO替身面板生成

文章目录 1 前言2 代码实现3 总结 1 前言 最近暑假在数模研赛备赛&#xff0c;情理之中地接触了一些图表。也是第一次才知道雷达图这么个图表形式&#xff0c;用于直观地评价多个变量或指标。看到雷达图&#xff0c;我不禁联想到了JOJO中的替身面板&#xff0c;于是兴起想用Py…

.NET 应用程序 部署

**硬件支持型号 点击 查看 硬件支持 详情** DTU701 产品详情 DTU702 产品详情 DTU801 产品详情 DTU802 产品详情 DTU902 产品详情 G5501 产品详情 本文内容 在设备上部署 dotnet应用&#xff0c;与任何其他平台的部署相同&#xff0c;可以2种方式&#xff1a; 依赖于框…

Zebec Protocol ,不止于 Web3 世界的 “Paypal”

Paypal 是传统支付领域的巨头企业&#xff0c;在北美支付市场占有率约为 77% 以上。从具体的业务数据看&#xff0c;在8月初&#xff0c;Paypal 公布的 2023 年第二季度财报显示&#xff0c;PayPal 第二季度净营收为 73 亿美元&#xff0c;净利润为 10.29 亿美元。虽然 Paypal …

【C# 基础精讲】运算符和表达式

在C#编程中&#xff0c;运算符和表达式是构建复杂逻辑的关键元素。运算符用于执行各种数学、逻辑和其他操作&#xff0c;而表达式则由运算符、变量、常量和函数组成&#xff0c;用于生成计算结果。本文将详细介绍C#中常见的运算符和表达式的概念&#xff0c;以及它们在程序中的…

全面讲解最小二乘法

常见的最小二乘法我们就不多说了&#xff0c;下面主要介绍一下最小二乘法的一些先进方法。 正则化的最小二乘法 在使用常见的最小二乘法进行回归分析时&#xff0c;常常会遇到过拟合的问题&#xff0c;也就是在训练数据集上表现的很好&#xff0c;但是在测试数据集上表现的很…

Flutter 让软键盘不再自动弹起

1、问题说明&#xff1a; 在开发中&#xff0c;经常遇到这种事&#xff0c;一个页面有输入框&#xff0c;点击输入框后&#xff0c;会弹起软键盘&#xff0c;同时输入框会聚焦&#xff0c;手动收起软键盘后&#xff0c;点击另一个按钮前往下一个页面或者显示一个弹窗&#xff0…

heima头条项目学习--Day1: 环境搭建、SpringCloud微服务(注册发现、网关)

Nacos注册发现、网关 a. 项目介绍b. app登录1) 需求分析2) 表结构分析3) 手动加密&#xff08;md5随机字符串&#xff09;4) 用户端微服务搭建5) 功能实现6) app网关7) 网关校验jwt8) 前端集成, 配置nginx a. 项目介绍 业务说明 技术栈说明 [外链图片转存失败,源站可能有防盗…

Nginx的优化和防盗链(面试高频!!!)

Nginx的优化和防盗链 全篇高能&#xff01;&#xff01;&#xff01;&#xff01;干货较多&#xff01;&#xff01;&#xff01;&#xff01;本篇含面试高频题&#xff1a; 修改配置文件时&#xff0c;先备份&#xff01;&#xff01;&#xff01;以便回滚&#xff01;&…

API接口:企业信息核验

企业信息核验是现代企业管理中必不可少的一项业务&#xff0c;它可以帮助企业做出正确的决策。在这篇文章里&#xff0c;我们将会介绍如何使用API接口来对企业信息进行核验&#xff0c;并实现快捷、准确的查询。 一、API接口 在这里我们使用的是挖数据提供的企业信息核验API接…

医学影像PACS临床信息系统源码

医学影像临床信息系统&#xff08;Picture Archiving and Communication Systems&#xff09;PACS是指从医疗影像设备中获得数字影像&#xff0c;利用高速网络进行存储、管理、传输的医疗影像信息管理系统。通过该系统&#xff0c;能实现影像数字化、无胶片化管理。 登记系统 …

MyCat水平分表

1.水平拆分案例场景 2.MyCat配置 这个表只是在 schema.xml配置的逻辑表&#xff0c;在具体的数据库里面是没有的 根据id的模确定数据存在哪个节点上&#xff01;&#xff01;

8.7 作业 c高级

1.写一个函数&#xff0c;获取用户的uid和gid并使用变量接收&#xff1a; #!/bin/bash function fun() {uidid -u ubuntugidid -g ubuntu }funecho "UID: $uid" echo "GID: $gid"思维导图&#xff1a;

(树) 剑指 Offer 54. 二叉搜索树的第k大节点 ——【Leetcode每日一题】

❓剑指 Offer 54. 二叉搜索树的第k大节点 难度&#xff1a;简单 给定一棵二叉搜索树&#xff0c;请找出其中第 k 大的节点的值。 示例 1: 输入: root [3,1,4,null,2], k 13/ \1 4\2 输出: 4示例 2: 输入: root [5,3,6,2,4,null,null,1], k 35/ \3 6/ \2 4/1 输出…

【多音音频测试信号】具有指定采样率和样本数的多音信号,生成多音信号的相位降低波峰因数研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Jmeter响应中的乱码问题

文章目录 问题描述解决办法 问题描述 Jmeter在访问接口的时候&#xff0c;响应内容如果有中文可能会显示乱码 响应页面没有做编码处理&#xff0c;JMeter默认按照ISO-8859-1编码格式进行解析 解决办法 在线程组中添加BeanShell PostProcessor后置处理器 prev.setDataEnco…

爬虫009_字符串高级_替换_去空格_分割_取长度_统计字符_间隔插入---python工作笔记028

然后再来看字符串的高级操作 取长度 查找字符串下标位置 判断是否以某个字符,开头结尾 计算字符出现次数 替换

STM32调试过程中的使用技巧及解决办法

文章目录 技巧 1.定时器使用过程中&#xff0c;分频系数越大&#xff0c;定时误差越大&#xff0c;越不准确。问题 2.编译时出现下述警告&#xff1a;解决办法&#xff1a; keil5 编译提示 single-precision operand implicity converted to double-precesion&#xff0c;意思是…