FFmpeg的AVcodecParser

文章目录

    • 结构体
    • 操作函数
    • 支持的AVCodecParser

这个模块是AVCodec中的子模块,专门用来提前解析码流的元数据,为后面的解码做准备,这一点对cuda-NVdec非常明显,英伟达解码器的元数据解析是放在CPU上的,所以就非常依赖这个解析模块。
FFmpeg中有很多现成的parser
在这里插入图片描述

结构体

下面是它的上下文


typedef struct AVCodecParserContext {
    void *priv_data;
    const struct AVCodecParser *parser;
    int64_t frame_offset; /* offset of the current frame */
    int64_t cur_offset; /* current offset
                           (incremented by each av_parser_parse()) */
    int64_t next_frame_offset; /* offset of the next frame */
    /* video info */
    int pict_type; /* XXX: Put it back in AVCodecContext. */
    /**
     * This field is used for proper frame duration computation in lavf.
     * It signals, how much longer the frame duration of the current frame
     * is compared to normal frame duration.
     *
     * frame_duration = (1 + repeat_pict) * time_base
     *
     * It is used by codecs like H.264 to display telecined material.
     */
    int repeat_pict; /* XXX: Put it back in AVCodecContext. */
    int64_t pts;     /* pts of the current frame */
    int64_t dts;     /* dts of the current frame */

    /* private data */
    int64_t last_pts;
    int64_t last_dts;
    int fetch_timestamp;

#define AV_PARSER_PTS_NB 4
    int cur_frame_start_index;
    int64_t cur_frame_offset[AV_PARSER_PTS_NB];
    int64_t cur_frame_pts[AV_PARSER_PTS_NB];
    int64_t cur_frame_dts[AV_PARSER_PTS_NB];

    int flags;
#define PARSER_FLAG_COMPLETE_FRAMES           0x0001
#define PARSER_FLAG_ONCE                      0x0002
/// Set if the parser has a valid file offset
#define PARSER_FLAG_FETCHED_OFFSET            0x0004
#define PARSER_FLAG_USE_CODEC_TS              0x1000

    int64_t offset;      ///< byte offset from starting packet start
    int64_t cur_frame_end[AV_PARSER_PTS_NB];

    /**
     * Set by parser to 1 for key frames and 0 for non-key frames.
     * It is initialized to -1, so if the parser doesn't set this flag,
     * old-style fallback using AV_PICTURE_TYPE_I picture type as key frames
     * will be used.
     */
    int key_frame;

    // Timestamp generation support:
    /**
     * Synchronization point for start of timestamp generation.
     *
     * Set to >0 for sync point, 0 for no sync point and <0 for undefined
     * (default).
     *
     * For example, this corresponds to presence of H.264 buffering period
     * SEI message.
     */
    int dts_sync_point;

    /**
     * Offset of the current timestamp against last timestamp sync point in
     * units of AVCodecContext.time_base.
     *
     * Set to INT_MIN when dts_sync_point unused. Otherwise, it must
     * contain a valid timestamp offset.
     *
     * Note that the timestamp of sync point has usually a nonzero
     * dts_ref_dts_delta, which refers to the previous sync point. Offset of
     * the next frame after timestamp sync point will be usually 1.
     *
     * For example, this corresponds to H.264 cpb_removal_delay.
     */
    int dts_ref_dts_delta;

    /**
     * Presentation delay of current frame in units of AVCodecContext.time_base.
     *
     * Set to INT_MIN when dts_sync_point unused. Otherwise, it must
     * contain valid non-negative timestamp delta (presentation time of a frame
     * must not lie in the past).
     *
     * This delay represents the difference between decoding and presentation
     * time of the frame.
     *
     * For example, this corresponds to H.264 dpb_output_delay.
     */
    int pts_dts_delta;

    /**
     * Position of the packet in file.
     *
     * Analogous to cur_frame_pts/dts
     */
    int64_t cur_frame_pos[AV_PARSER_PTS_NB];

    /**
     * Byte position of currently parsed frame in stream.
     */
    int64_t pos;

    /**
     * Previous frame byte position.
     */
    int64_t last_pos;

    /**
     * Duration of the current frame.
     * For audio, this is in units of 1 / AVCodecContext.sample_rate.
     * For all other types, this is in units of AVCodecContext.time_base.
     */
    int duration;

    enum AVFieldOrder field_order;

    /**
     * Indicate whether a picture is coded as a frame, top field or bottom field.
     *
     * For example, H.264 field_pic_flag equal to 0 corresponds to
     * AV_PICTURE_STRUCTURE_FRAME. An H.264 picture with field_pic_flag
     * equal to 1 and bottom_field_flag equal to 0 corresponds to
     * AV_PICTURE_STRUCTURE_TOP_FIELD.
     */
    enum AVPictureStructure picture_structure;

    /**
     * Picture number incremented in presentation or output order.
     * This field may be reinitialized at the first picture of a new sequence.
     *
     * For example, this corresponds to H.264 PicOrderCnt.
     */
    int output_picture_number;

    /**
     * Dimensions of the decoded video intended for presentation.
     */
    int width;
    int height;

    /**
     * Dimensions of the coded video.
     */
    int coded_width;
    int coded_height;

    /**
     * The format of the coded data, corresponds to enum AVPixelFormat for video
     * and for enum AVSampleFormat for audio.
     *
     * Note that a decoder can have considerable freedom in how exactly it
     * decodes the data, so the format reported here might be different from the
     * one returned by a decoder.
     */
    int format;
} AVCodecParserContext;

下面是插件接口的入口。
如果你要实现一个自己的parse,只要简单的重写下面四个函数就可以了。

typedef struct AVCodecParser {
    int codec_ids[7]; /* several codec IDs are permitted */
    int ;
    int (*parser_init)(AVCodecParserContext *s);
    /* This callback never returns an error, a negative value means that
     * the frame start was in a previous packet. */
    int (*parser_parse)(AVCodecParserContext *s,
                        AVCodecContext *avctx,
                        const uint8_t **poutbuf, int *poutbuf_size,
                        const uint8_t *buf, int buf_size);
    void (*parser_close)(AVCodecParserContext *s);
    int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size);
} AVCodecParser;

特别注意上面的priv_data_size是AVCodecParserContext中的priv_data,这里面就是保存各种元信息。比如说对于h264来说,里面保存的就是:

static av_cold int init(AVCodecParserContext *s)
{
    H264ParseContext *p = s->priv_data;
	...
    return 0;
}

其实就是sps,pps,sei的值

typedef struct H264ParseContext {
    ParseContext pc;
    H264ParamSets ps;
    H264DSPContext h264dsp;
    H264POCContext poc;
    H264SEIContext sei;
    int is_avc;
    int nal_length_size;
    int got_first;
    int picture_structure;
    uint8_t parse_history[6];
    int parse_history_count;
    int parse_last_mb;
    int64_t reference_dts;
    int last_frame_num, last_picture_structure;
} H264ParseContext;

比如h264就是重写了下面三个函数接口。

const AVCodecParser ff_h264_parser = {
    .codec_ids      = { AV_CODEC_ID_H264 },
    .priv_data_size = sizeof(H264ParseContext),
    .parser_init    = init,
    .parser_parse   = h264_parse,
    .parser_close   = h264_close,
};

操作函数

下面是操作函数,第一个是列出所有的parser

/**
 * Iterate over all registered codec parsers.
 *
 * @param opaque a pointer where libavcodec will store the iteration state. Must
 *               point to NULL to start the iteration.
 *
 * @return the next registered codec parser or NULL when the iteration is
 *         finished
 */
const AVCodecParser *av_parser_iterate(void **opaque);

第二个是初始化parser

AVCodecParserContext *av_parser_init(int codec_id);

第三个是最重要的,也就是解析pkt

/**
 * Parse a packet.
 *
 * @param s             parser context.
 * @param avctx         codec context.
 * @param poutbuf       set to pointer to parsed buffer or NULL if not yet finished.
 * @param poutbuf_size  set to size of parsed buffer or zero if not yet finished.
 * @param buf           input buffer.
 * @param buf_size      buffer size in bytes without the padding. I.e. the full buffer
                        size is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE.
                        To signal EOF, this should be 0 (so that the last frame
                        can be output).
 * @param pts           input presentation timestamp.
 * @param dts           input decoding timestamp.
 * @param pos           input byte position in stream.
 * @return the number of bytes of the input bitstream used.
 *
 * Example:
 * @code
 *   while(in_len){
 *       len = av_parser_parse2(myparser, AVCodecContext, &data, &size,
 *                                        in_data, in_len,
 *                                        pts, dts, pos);
 *       in_data += len;
 *       in_len  -= len;
 *
 *       if(size)
 *          decode_frame(data, size);
 *   }
 * @endcode
 */
int av_parser_parse2(AVCodecParserContext *s,
                     AVCodecContext *avctx,
                     uint8_t **poutbuf, int *poutbuf_size,
                     const uint8_t *buf, int buf_size,
                     int64_t pts, int64_t dts,
                     int64_t pos);


最后一个是close parser

void av_parser_close(AVCodecParserContext *s);

下面是一个用例


int main(int argc, char **argv)
{
    const char *filename, *outfilename;
    const AVCodec *codec;
    AVCodecParserContext *parser;
    AVCodecContext *c= NULL;
    FILE *f;
    AVFrame *frame;
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data;
    size_t   data_size;
    int ret;
    AVPacket *pkt;

    if (argc <= 2) {
        fprintf(stderr, "Usage: %s <input file> <output file>\n"
                "And check your input file is encoded by mpeg1video please.\n", argv[0]);
        exit(0);
    }
    filename    = argv[1];
    outfilename = argv[2];

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);

    /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    /* find the MPEG-1 video decoder */
    codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */

    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }

    while (!feof(f)) {
        /* read raw data from the input file */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;

        /* use the parser to split the data into frames */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;
            data_size -= ret;

            if (pkt->size)
                decode(c, frame, pkt, outfilename);
        }
    }

    /* flush the decoder */
    decode(c, frame, NULL, outfilename);

    fclose(f);

    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return 0;
}

上面的例子展示了一个从文件中读取码流,然后通过av_parser_parse2将码流解析为一个个分离的NALU单元,然后送去解码,我们可以看到,第二个参数是c,也就是说将来要把sps,pps,sei这些信息保存在c中的extradata中。

下面第一个函数是av_parser_init,看到除了分配空间外,就是对parser->parser_init(s)的封装。


AVCodecParserContext *av_parser_init(int codec_id)
{
    AVCodecParserContext *s = NULL;
    const AVCodecParser *parser;
    void *i = 0;
    int ret;

    if (codec_id == AV_CODEC_ID_NONE)
        return NULL;

    while ((parser = av_parser_iterate(&i))) {
        if (parser->codec_ids[0] == codec_id ||
            parser->codec_ids[1] == codec_id ||
            parser->codec_ids[2] == codec_id ||
            parser->codec_ids[3] == codec_id ||
            parser->codec_ids[4] == codec_id ||
            parser->codec_ids[5] == codec_id ||
            parser->codec_ids[6] == codec_id)
            goto found;
    }
    return NULL;

found:
    s = av_mallocz(sizeof(AVCodecParserContext));
    if (!s)
        goto err_out;
    s->parser = parser;
    s->priv_data = av_mallocz(parser->priv_data_size);
    if (!s->priv_data)
        goto err_out;
    s->fetch_timestamp=1;
    s->pict_type = AV_PICTURE_TYPE_I;
    if (parser->parser_init) {
        ret = parser->parser_init(s);
        if (ret != 0)
            goto err_out;
    }
    s->key_frame            = -1;
    s->dts_sync_point       = INT_MIN;
    s->dts_ref_dts_delta    = INT_MIN;
    s->pts_dts_delta        = INT_MIN;
    s->format               = -1;

    return s;

err_out:
    if (s)
        av_freep(&s->priv_data);
    av_free(s);
    return NULL;
}

在这里插入图片描述
第二个函数也很简单av_parser_parse2,内部其实也是对parser_parse的封装。

int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx,
                     uint8_t **poutbuf, int *poutbuf_size,
                     const uint8_t *buf, int buf_size,
                     int64_t pts, int64_t dts, int64_t pos){
   ...
    index = s->parser->parser_parse(s, avctx, (const uint8_t **) poutbuf,
                                    poutbuf_size, buf, buf_size);
                                   
  ...
  }

第三个函数av_parser_close就更加简单了

void av_parser_close(AVCodecParserContext *s)
{
    if (s) {
        if (s->parser->parser_close)
            s->parser->parser_close(s);
        av_freep(&s->priv_data);
        av_free(s);
    }
}

上面全部是架构代码,如果想要查看详细的解析过程,可以单独看每个插件的内容,也就是那三个函数。

支持的AVCodecParser


extern const AVCodecParser ff_aac_parser;
extern const AVCodecParser ff_aac_latm_parser;
extern const AVCodecParser ff_ac3_parser;
extern const AVCodecParser ff_adx_parser;
extern const AVCodecParser ff_amr_parser;
extern const AVCodecParser ff_av1_parser;
extern const AVCodecParser ff_avs2_parser;
extern const AVCodecParser ff_avs3_parser;
extern const AVCodecParser ff_bmp_parser;
extern const AVCodecParser ff_cavsvideo_parser;
extern const AVCodecParser ff_cook_parser;
extern const AVCodecParser ff_cri_parser;
extern const AVCodecParser ff_dca_parser;
extern const AVCodecParser ff_dirac_parser;
extern const AVCodecParser ff_dnxhd_parser;
extern const AVCodecParser ff_dolby_e_parser;
extern const AVCodecParser ff_dpx_parser;
extern const AVCodecParser ff_dvaudio_parser;
extern const AVCodecParser ff_dvbsub_parser;
extern const AVCodecParser ff_dvdsub_parser;
extern const AVCodecParser ff_dvd_nav_parser;
extern const AVCodecParser ff_flac_parser;
extern const AVCodecParser ff_g723_1_parser;
extern const AVCodecParser ff_g729_parser;
extern const AVCodecParser ff_gif_parser;
extern const AVCodecParser ff_gsm_parser;
extern const AVCodecParser ff_h261_parser;
extern const AVCodecParser ff_h263_parser;
extern const AVCodecParser ff_h264_parser;
extern const AVCodecParser ff_hevc_parser;
extern const AVCodecParser ff_ipu_parser;
extern const AVCodecParser ff_jpeg2000_parser;
extern const AVCodecParser ff_mjpeg_parser;
extern const AVCodecParser ff_mlp_parser;
extern const AVCodecParser ff_mpeg4video_parser;
extern const AVCodecParser ff_mpegaudio_parser;
extern const AVCodecParser ff_mpegvideo_parser;
extern const AVCodecParser ff_opus_parser;
extern const AVCodecParser ff_png_parser;
extern const AVCodecParser ff_pnm_parser;
extern const AVCodecParser ff_rv30_parser;
extern const AVCodecParser ff_rv40_parser;
extern const AVCodecParser ff_sbc_parser;
extern const AVCodecParser ff_sipr_parser;
extern const AVCodecParser ff_tak_parser;
extern const AVCodecParser ff_vc1_parser;
extern const AVCodecParser ff_vorbis_parser;
extern const AVCodecParser ff_vp3_parser;
extern const AVCodecParser ff_vp8_parser;
extern const AVCodecParser ff_vp9_parser;
extern const AVCodecParser ff_webp_parser;
extern const AVCodecParser ff_xbm_parser;
extern const AVCodecParser ff_xma_parser;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

对于初学者来说,从哪些方面开始学习 Java 编程比较好?

对于初学者来说&#xff0c;从哪些方面开始学习 Java 编程比较好&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「Java的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全…

react Hooks(useRef、useMemo、useCallback)实现原理

Fiber 上篇文章fiber简单理解记录了react fiber架构&#xff0c;Hooks是基于fiber链表来实现的。阅读以下内容时建议先了解react fiber。 jsx -> render function -> vdom -> fiber树 -> dom vdom 转 fiber 的过程称为 recocile。diff算法就是在recocile这个过程…

70套大数据可视化大屏模板,总有一款适合你(含演示示例)

分享70款还不错的前端数据可视化大屏源码 其中包含行业&#xff1a;智慧社区、智慧物业、政务系统、智慧交通、智慧工程、智慧医疗、智慧金融银行等&#xff0c;全网最新、最多&#xff0c;最全、最酷、最炫大数据可视化模板。 你可以点击预览获取查看该源码资源的最终展示效果…

思福迪运维安全管理系统 test_qrcode_b RCE漏洞复现

0x01 产品简介 思福迪运维安全管理系统是思福迪开发的一款运维安全管理堡垒机。 0x02 漏洞概述 由于思福迪运维安全管理系统 test_qrcode_b路由存在命令执行漏洞&#xff0c;攻击者可通过该漏洞在服务器端任意执行代码&#xff0c;写入后门&#xff0c;获取服务器权限&#…

05进程间通信-学习笔记

进程间通信&#xff08;IPC&#xff09; 概念 进程信技术简称IPC,可以利用此技木让多个进程相传建消数据&#xff0c;有大量的进程间通信方案 pipe 匿名管道fifo 命名管简单理解&#xff0c;管道文件是一个指向内核管道缓冲区的指针&#xff0c;所有向管道文件读写的操作&am…

人机交互——自然语言理解

人机交互中的自然语言理解是人机交互的核心&#xff0c;它是指用自然语言&#xff08;例如中文、英文等&#xff09;进行交流&#xff0c;使计算机能理解和运用人类社会的自然语言&#xff0c;实现人机之间的自然语言通信。 自然语言理解在人工智能领域中有着非常重要的地位&a…

计算机网络(三)

&#xff08;十一&#xff09;路由算法 A、路由算法分类 动态路由和静态路由 静态路由&#xff1a;人工配制&#xff0c;路由信息更新慢&#xff0c;优先级高。这种在实际网络中要投入成本大&#xff0c;准确但是可行性弱。 动态路由&#xff1a;路由更新快&#xff0c;自动…

【操作系统导论】内存篇——分页

引入 采用 「分段」 的方式&#xff0c;将空间切成 不同长度的分片&#xff0c;会出现 碎片化 问题&#xff0c;随着时间推移&#xff0c;分配内存会越来越困难。 因此&#xff0c;值得考虑「分页」的方法&#xff1a; 将空间分割成 固定长度的分片 &#xff1b; 将物理内存…

中医电子处方管理系统软件,中医配方模板一键生成软件操作教程

一、前言&#xff1a; 在中医开电子处方时&#xff0c;如果能够使用配方模板功能&#xff0c;则可以节省很多时间。使用配方模板一键导入&#xff0c;几秒即可完成开单。 下面就以佳易王电子处方管理系统软件V17.1版本为例说明&#xff0c;其他版本可以参考&#xff0c;软件下…

场景的组织及渲染(一)

在OSG 中存在两棵树&#xff0c;即场景树和渲染树。场景树是一棵由 Node组成的树,这些Node可能是矩阵变换、状态切换或真正的可绘制对象&#xff0c;它反映了场景的空间结构&#xff0c;也反映了对象的状态。本章重点介绍场景树&#xff0c;在第 5章将会对渲染树作详细的介绍。…

GoWin FPGA, GPIO--- startup2

一个Bank只能用一个电压&#xff0c;假如同一个Bank&#xff0c;在引脚里设置不同的电压&#xff0c;编译不过。 解释说明 2. 错误引脚限制 以上编译设置会导致编译错误。

Docker | 发布镜像到镜像仓库

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:Docker系列 ✨特色专栏: MySQL学习 🥭本文内容:Docker | 发布镜像到镜像仓库 📚个人知识库: [Leo知识库]https://gaoziman.gitee.io/bl…

std::vector

这里主要介绍下reserce/resize、push_back/emplace_back、shrink_to_fit/clear等接口&#xff1b; 1. reserve and resize C的vector对象可以通过reserve方法来设置vector对象的容量&#xff0c;通过resize方法来改变vector对象的大小。reserve所设置的容量指的是vector容器中可…

0基础学习VR全景平台篇第127篇:什么是VR全景/720全景漫游?

“全景”作为一种表现宽阔视野的手法&#xff0c;在很久之前就得到了普遍的认同。北宋年间&#xff0c;由张择端绘制的《清明上河图》就是一幅著名的全景画。摄影术出现后&#xff0c;全景摄影也随之而生。 到今天&#xff0c;全景拍摄不再被专业摄影师所独享&#xff0c;广大…

云计算与AI融合:Amazon Connect开创客户服务智能时代

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 在亚马逊云科技 re:Invent 2023 大会上&#xff0c;Amazon Connect…

[AutoSar]状态管理(四)单核BswM(二)流程、配置、 代码

目录 关键词平台说明一、BswM的模式处理流程图二、stand state handling三、配置、代码、状态转移3.1 initial -> wakeup   3.2 WakeUp -> Run3.3 Run -> PostRun &#xff08;first step&#xff09;3.4 Run -> PostRun &#xff08;second step&#xff09;3.5…

Python安装报错: This environment is externally managed

error: externally-managed-environment This environment is externally managed ╰─> To install Python packages system-wide, try apt installpython3-xyz, where xyz is the package you are trying toinstall.这个错误信息表示当前Python环境是由系统外部管理的&…

基于Docker-Compose实现ELK+Kafka搭建分布式日志采集系统

ELKKafka搭建日志采集系统 ELK概述搭建与配置docker-compose.yml配置日志采集规则启动服务 模拟发送日志消息日志发送队列日志切面配置application.yaml发送日志消息 Kibana的使用创建索引模式Discovery搜索数据可视化数据 ELKRabbitMQ发送日志消息配置日志采集规则 ELK概述 E…

Java IO 流详解

Java IO 流详解 1 .Java IO概念 Java IO&#xff1a;即 Java 输入 / 输出系统。 Java 的 IO 模型设计非常优秀&#xff0c;它使用 Decorator (装饰者)模式&#xff0c;按功能划分 Stream &#xff0c;您可以动态装配 这些 Stream &#xff0c;以便获得您需要的功能。 Stream &…

20 Redis进阶 - 运维监控

1、理解Redis监控 Redis运维和监控的意义不言而喻&#xff0c;可以以下三个方面入手 1.首先是Redis自身提供了哪些状态信息&#xff0c;以及有哪些常见的命令可以获取Redis的监控信息; 2.一些常见的UI工具可以可视化的监控Redis; 3.理解Redis的监控体系;2、Redis自身状态及命…