FFmpeg零基础学习(二)——视频文件信息获取

目录

  • 前言
  • 正文
    • 一、获取宽高信息
      • 1、核心代码
      • 2、AVFormatContext
      • 3、avformat_alloc_context
      • 4、avformat_open_input
      • 5、avformat_find_stream_info
      • 6、av_dump_format
      • 7、av_find_best_stream
    • End、遇到的问题
      • 1、Qt Debug模式avformat_alloc_context 无法分配对象,而Release模式可以
      • 2、avformat_open_input 返回值为-22
  • 参考

前言

本篇文章的目的在于输出导入的视频文件的宽高信息,或者其他信息。

正文

一、获取宽高信息

1、核心代码

int CFFmpegDemoTest::_GetVideoWidthAndHeight(const QString &_sVideoPath, int &_iWidth, int &_iHeight)
{
    AVFormatContext *fmt_ctx = avformat_alloc_context();//创建对象并初始化

    if (fmt_ctx == nullptr)
    {
        return -1;
    }
    int ret = 0;

    AVStream *videoStream = nullptr;
    const char* cFilePath = _sVideoPath.toStdString().c_str();
    char errbuf[AV_ERROR_MAX_STRING_SIZE];

    do {
        //打开文件

        if ((ret = avformat_open_input(&fmt_ctx, cFilePath, NULL, NULL)) < 0)
        {
            qDebug() << "--> Cannot open Video File";
            av_strerror(ret, errbuf, sizeof(errbuf));
            qDebug() << "错误信息:" << errbuf<<";ret:"<<ret;
            break;
        }

        if ((ret = avformat_find_stream_info (fmt_ctx, NULL)) < 0)
        {
            qDebug() << "--> Cannot Find Stream Information";
            av_strerror(ret, errbuf, sizeof(errbuf));
            qDebug() << "错误信息:" << errbuf;
            break;
        }

        av_dump_format(fmt_ctx, 0, cFilePath, 0);//输出视频信息
        int iVideoStreamIndex = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);

        if (iVideoStreamIndex >= 0)
        {
            videoStream = fmt_ctx->streams[iVideoStreamIndex];
            _iWidth = videoStream->codecpar->width;
            _iHeight = videoStream->codecpar->height;
            qDebug() << "--> CFFmpegDemoTest::_GetVideoWidthAndHeight _iWidth:"<<_iWidth<<";_iHeight:"<<_iHeight;
        }
    } while(0);


    avformat_close_input(&fmt_ctx);
    return ret;
}

获取到的打印信息

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'D:/FFmpeg/FFmpegPlayer/product/video/phone1.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2023-11-22T06:06:55.000000Z
    location        : +24.6182+118.0385/
    location-eng    : +24.6182+118.0385/
    com.android.version: 13
  Duration: 00:01:41.40, start: 0.000000, bitrate: 17998 kb/s
  Stream #0:0[0x1](eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1080x1920, 17845 kb/s, 29.67 fps, 60 tbr, 90k tbn (default)
    Metadata:
      creation_time   : 2023-11-22T06:06:55.000000Z
      handler_name    : VideoHandle
      vendor_id       : [0][0][0][0]
    Side data:
      displaymatrix: rotation of 90.00 degrees
  Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      creation_time   : 2023-11-22T06:06:55.000000Z
      handler_name    : SoundHandle
      vendor_id       : [0][0][0][0]

2、AVFormatContext

AVFormatContext是FFmpeg中的一个重要数据结构,用于表示音视频封装格式的上下文。它包含了音视频封装格式相关的信息,比如输入/输出文件、流信息、封装格式参数等。

AVFormatContext结构体定义在libavformat/avformat.h头文件中,它的定义如下:

typedef struct AVFormatContext {
    const AVClass *av_class;
    AVInputFormat *iformat;
    AVOutputFormat *oformat;
    void *priv_data;
    ...
} AVFormatContext;

下面是对AVFormatContext结构体中常用字段的详细解释:

av_class:指向一个AVClass结构体的指针,用于获取封装格式的类信息,比如名称、类型等。

iformat:指向一个AVInputFormat结构体的指针,表示输入的封装格式。在打开输入文件时,FFmpeg会根据输入文件的扩展名或者其他信息自动选择合适的AVInputFormat。

oformat:指向一个AVOutputFormat结构体的指针,表示输出的封装格式。在写入输出文件时,FFmpeg会根据输出文件的扩展名或者其他信息自动选择合适的AVOutputFormat。

priv_data:指向一个私有数据结构的指针,用于存储封装格式相关的私有数据。具体的结构体类型和内容取决于使用的封装格式。

除了上述字段之外,AVFormatContext还包含了一些其他的字段,用于存储音视频封装格式的相关信息,比如:

nb_streams:表示流的数量,即输入/输出文件中的音视频流的数量。

streams:一个指针数组,用于存储每个音视频流的信息。每个流都是一个AVStream结构体,包含了流的索引、时长、编解码器参数等信息。

duration:表示音视频文件的总时长。

bit_rate:表示音视频文件的比特率。

metadata:一个字典结构,用于存储音视频文件的元数据,比如标题、作者、描述等。

filename:指向输入/输出文件名的指针。

pb:指向一个AVIOContext结构体的指针,用于输入/输出文件的读写操作。

AVFormatContext结构体是FFmpeg中非常重要的一个数据结构,它提供了对音视频封装格式的访问和操作接口。通过操作AVFormatContext,可以实现音视频文件的解封装、封装、转码等功能。
在这里插入图片描述
执行完avformat_alloc_context,主要的一些东西。

3、avformat_alloc_context

avformat_alloc_context是FFmpeg库中的一个函数,用于动态分配并初始化一个AVFormatContext结构体。它的函数原型如下:

/**
 * Allocate an AVFormatContext.
 * avformat_free_context() can be used to free the context and everything
 * allocated by the framework within it.
 */
AVFormatContext *avformat_alloc_context(void);

该函数会分配一块内存,并将其初始化为一个空的AVFormatContext结构体,然后返回指向该结构体的指针。

使用avformat_alloc_context函数可以创建一个空的AVFormatContext对象,然后可以通过设置不同的字段和参数来配置它,以便进行音视频封装或解封装操作。
总结来说,avformat_alloc_context函数用于动态分配和初始化一个空的AVFormatContext对象,为后续的音视频封装和解封装操作做准备。
所以,分配后,可以对AVFormatContext 对象进行判空,防止初始化失败。

4、avformat_open_input

avformat_open_input是FFmpeg库中的一个函数,用于打开音视频输入文件并初始化相关的输入上下文(AVFormatContext)。它的函数原型如下:

/**
 * Open an input stream and read the header. The codecs are not opened.
 * The stream must be closed with avformat_close_input().
 *
 * @param ps       Pointer to user-supplied AVFormatContext (allocated by
 *                 avformat_alloc_context). May be a pointer to NULL, in
 *                 which case an AVFormatContext is allocated by this
 *                 function and written into ps.
 *                 Note that a user-supplied AVFormatContext will be freed
 *                 on failure.
 *> 传入值为avformat_alloc_context 分配的对象
 * @param url      URL of the stream to open.
 *> 要打开的流的地址
 * @param fmt      If non-NULL, this parameter forces a specific input format.
 *                 Otherwise the format is autodetected.
 * >输入的文件的格式,若为NULL,则自动检测
 * @param options  A dictionary filled with AVFormatContext and demuxer-private
 *                 options.
 *                 On return this parameter will be destroyed and replaced with
 *                 a dict containing options that were not found. May be NULL.
 *
 * @return 0 on success, a negative AVERROR on failure.
 *> 返回值0,为正确,其他值为失败
 * @note If you want to use custom IO, preallocate the format context and set its pb field.
 */
int avformat_open_input(AVFormatContext **ps, const char *url,
                        const AVInputFormat *fmt, AVDictionary **options);

该函数的参数说明如下:
ps:指向指针的指针,用于存储分配的AVFormatContext对象。
url:输入文件的URL或文件名。
fmt:指定输入格式,如果为NULL,则由FFmpeg自动检测输入文件的格式。
options:指向包含附加选项的字典。可以在打开输入文件时提供一些特定的选项,比如设置超时时间、设置输入缓冲大小等。

1、强调关闭使用avformat_close_input
2、函数返回一个整数值,表示操作的结果。如果返回值小于0,则表示打开输入文件失败,否则返回0表示操作成功。使用avformat_open_input函数可以打开一个音视频输入文件,并将其与一个AVFormatContext对象关联起来,以便后续的音视频解封装操作。
3、avformat_open_input函数用于打开音视频输入文件,并初始化相关的输入上下文。它是进行音视频解封装操作的起点之一。

源码如下:

int avformat_open_input(AVFormatContext **ps, const char *filename,
                        ff_const59 AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
    int i, ret = 0;
    AVDictionary *tmp = NULL;
    ID3v2ExtraMeta *id3v2_extra_meta = NULL;

    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);
    if (!s->av_class) {
        av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
        return AVERROR(EINVAL);
    }
    if (fmt)
        s->iformat = fmt;

    if (options)
        av_dict_copy(&tmp, *options, 0);

    if (s->pb) // must be before any goto fail
        s->flags |= AVFMT_FLAG_CUSTOM_IO;

    if ((ret = av_opt_set_dict(s, &tmp)) < 0)
        goto fail;

    if (!(s->url = av_strdup(filename ? filename : ""))) {
        ret = AVERROR(ENOMEM);
        goto fail;
    }

#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGS
    av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endif
    if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;
    s->probe_score = ret;

    if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
        s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
        if (!s->protocol_whitelist) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
        s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
        if (!s->protocol_blacklist) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
        av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
        ret = AVERROR(EINVAL);
        goto fail;
    }

    avio_skip(s->pb, s->skip_initial_bytes);

    /* Check filename in case an image number is expected. */
    if (s->iformat->flags & AVFMT_NEEDNUMBER) {
        if (!av_filename_number_test(filename)) {
            ret = AVERROR(EINVAL);
            goto fail;
        }
    }

    s->duration = s->start_time = AV_NOPTS_VALUE;

    /* Allocate private data. */
    if (s->iformat->priv_data_size > 0) {
        if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        if (s->iformat->priv_class) {
            *(const AVClass **) s->priv_data = s->iformat->priv_class;
            av_opt_set_defaults(s->priv_data);
            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                goto fail;
        }
    }

    /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
    if (s->pb)
        ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);

#if FF_API_DEMUXER_OPEN
    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
#else
    if (s->iformat->read_header)
#endif
        if ((ret = s->iformat->read_header(s)) < 0)
            goto fail;

    if (!s->metadata) {
        s->metadata = s->internal->id3v2_meta;
        s->internal->id3v2_meta = NULL;
    } else if (s->internal->id3v2_meta) {
        av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");
        av_dict_free(&s->internal->id3v2_meta);
    }

    if (id3v2_extra_meta) {
        if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
            !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
            if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
                goto close;
            if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
                goto close;
            if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)
                goto close;
        } else
            av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
    }
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);

    if ((ret = avformat_queue_attached_pictures(s)) < 0)
        goto close;

#if FF_API_DEMUXER_OPEN
    if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
#else
    if (s->pb && !s->internal->data_offset)
#endif
        s->internal->data_offset = avio_tell(s->pb);

    s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

    update_stream_avctx(s);

    for (i = 0; i < s->nb_streams; i++)
        s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;

    if (options) {
        av_dict_free(options);
        *options = tmp;
    }
    *ps = s;
    return 0;

close:
    if (s->iformat->read_close)
        s->iformat->read_close(s);
fail:
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);
    av_dict_free(&tmp);
    if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
        avio_closep(&s->pb);
    avformat_free_context(s);
    *ps = NULL;
    return ret;
}

分析可以查看雷神的这篇文章:
FFmpeg源代码简单分析:avformat_open_input()
可以看到,若打开文件失败,或是分配资源失败等等,都会将传入的AVFormatContext的对象置为NULL.

5、avformat_find_stream_info

avformat_find_stream_info是FFmpeg库中的一个函数,用于获取音视频文件的流信息。它会分析输入文件,并将解码器的相关信息填充到AVFormatContext中的streams数组中的每个元素中。
函数原型如下:

/**
 * Read packets of a media file to get stream information. This
 * is useful for file formats with no headers such as MPEG. This
 * function also computes the real framerate in case of MPEG-2 repeat
 * frame mode.
 * The logical file position is not changed by this function;
 * examined packets may be buffered for later processing.
 *
 * @param ic media file handle
 * @param options  If non-NULL, an ic.nb_streams long array of pointers to
 *                 dictionaries, where i-th member contains options for
 *                 codec corresponding to i-th stream.
 *                 On return each dictionary will be filled with options that were not found.
 * @return >=0 if OK, AVERROR_xxx on error
 *
 * @note this function isn't guaranteed to open all the codecs, so
 *       options being non-empty at return is a perfectly normal behavior.
 *
 * @todo Let the user decide somehow what information is needed so that
 *       we do not waste time getting stuff the user does not need.
 */
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

该函数的参数说明如下:
ic:指向AVFormatContext的指针,表示输入文件的上下文。
options:指向包含附加选项的字典。可以在获取流信息时提供一些特定的选项,比如设置解码器参数、设置过滤器等。

函数返回一个整数值,表示操作的结果。如果返回值小于0,则表示获取流信息失败,否则返回0表示操作成功。

使用avformat_find_stream_info函数可以在打开输入文件后调用,以获取输入文件的流信息,包括音频流、视频流、字幕流等。通过分析文件的数据包,该函数将填充AVFormatContext中的streams数组,每个数组元素对应一个流,并包含有关该流的详细信息,如编解码器类型、时间基准、时长、帧率等。
avformat_find_stream_info函数获取文件的流信息,并将其填充到AVFormatContext对象中的streams数组中。最后,可以通过遍历streams数组,处理每个流的相关信息。
总结来说,avformat_find_stream_info函数用于获取音视频文件的流信息,是进行音视频解封装操作的一步。通过分析输入文件的数据包,它将填充AVFormatContext对象中的streams数组。

6、av_dump_format

av_dump_format是FFmpeg库中的一个函数,用于打印音视频文件的详细格式信息。它可以用于调试和分析音视频文件,提供有关文件的元数据、流信息、编码参数等方面的详细信息。
函数原型:

/**
 * Print detailed information about the input or output format, such as
 * duration, bitrate, streams, container, programs, metadata, side data,
 * codec and time base.
 *
 * @param ic        the context to analyze
 * @param index     index of the stream to dump information about
 * @param url       the URL to print, such as source or destination file
 * @param is_output Select whether the specified context is an input(0) or output(1)
 */
void av_dump_format(AVFormatContext *ic,
                    int index,
                    const char *url,
                    int is_output);

该函数的参数说明如下:
ic:指向AVFormatContext的指针,表示输入/输出文件的上下文。
index:要打印信息的流的索引。如果设置为-1,则会打印所有流的信息。
url:指定输入/输出文件的URL或文件名。
is_output:指示是否为输出文件。如果设为非零值(例如1),则会打印输出文件的格式信息。

av_dump_format函数会打印音视频文件的格式信息到标准输出(stdout)或由FFmpeg库设置的日志回调函数。它会包含文件的元数据(例如文件格式、时长、比特率)、流的信息(例如流索引、编解码器、时长、帧率)以及其他相关参数。
总结来说,av_dump_format函数是一个用于打印音视频文件格式信息的便捷函数。它可用于调试和分析音视频文件,提供有关文件的详细信息。

7、av_find_best_stream

av_find_best_stream是FFmpeg库中的一个函数,用于查找最佳的音视频流。它可以根据指定的流类型(音频、视频、字幕等)和其他条件,选择最合适的流进行后续操作,如解码、编码等。
函数原型如下:

/**
 * Find the "best" stream in the file.
 * The best stream is determined according to various heuristics as the most
 * likely to be what the user expects.
 * If the decoder parameter is non-NULL, av_find_best_stream will find the
 * default decoder for the stream's codec; streams for which no decoder can
 * be found are ignored.
 *
 * @param ic                media file handle
 * @param type              stream type: video, audio, subtitles, etc.
 * @param wanted_stream_nb  user-requested stream number,
 *                          or -1 for automatic selection
 * @param related_stream    try to find a stream related (eg. in the same
 *                          program) to this one, or -1 if none
 * @param decoder_ret       if non-NULL, returns the decoder for the
 *                          selected stream
 * @param flags             flags; none are currently defined
 *
 * @return  the non-negative stream number in case of success,
 *          AVERROR_STREAM_NOT_FOUND if no stream with the requested type
 *          could be found,
 *          AVERROR_DECODER_NOT_FOUND if streams were found but no decoder
 *
 * @note  If av_find_best_stream returns successfully and decoder_ret is not
 *        NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec.
 */
int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        const struct AVCodec **decoder_ret,
                        int flags);

该函数的参数说明如下:
ic:指向AVFormatContext的指针,表示输入文件的上下文。
type:要查找的流类型,使用AVMediaType枚举类型,例如AVMEDIA_TYPE_VIDEO表示视频流,AVMEDIA_TYPE_AUDIO表示音频流,AVMEDIA_TYPE_SUBTITLE表示字幕流等。
wanted_stream_nb:期望的流索引。如果设置为大于等于0的值,则表示要查找的具体流索引;如果设置为负值(例如-1),则表示要查找符合条件的第一个流。
related_stream:关联流索引。在某些情况下,需要根据另一个流来选择最佳流,例如根据视频流选择对应的音频流。如果没有关联流,可以设置为-1。
decoder_ret:指向AVCodec指针的指针,用于返回找到的解码器。
flags:查找流的标志位。可以使用一些特定的标志位,如AV_FIND_STREAM_INFO_IGNORED表示忽略流信息等。

End、遇到的问题

1、Qt Debug模式avformat_alloc_context 无法分配对象,而Release模式可以

这个问题会出现在Qt 5.15 MSVC2019 Debug模式中,后面我切换成MinGW就可以了,就暂不深究了,若想深究的,可以看一下这篇文章。
FFmpeg调试环境搭建
在这里插入图片描述
有教在不同的环境中调试的方法,但依我的拙见,我觉得学习一样的东西,一定不断的给自己正反馈, 否则,学习很难进行下去。

2、avformat_open_input 返回值为-22

可能的问题出现在前面avformat_alloc_context 对对象的分配不对。分配的结果可能是空。

参考

1、FFmpeg调试环境搭建

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

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

相关文章

【SQL Server Management】使用手册

目录 ⛳️【SQL Server Management】 ⛳️1. 启动登录 ⛳️2. 忘记密码 ⛳️3. 操作数据库和表 3.1 新建数据库text 3.2 新建表 3.3 编辑表 3.4 编写脚本 ⛳️【SQL Server Management】 ⛳️1. 启动登录 需要开启服务 ⛳️2. 忘记密码 登录windows--> 安全性 -->…

VMware虚拟机安装华为OpenEuler欧拉系统

首先去欧拉官方网站下载openEuler的安装镜像&#xff1a; openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 我下载的是最新的23.03长期维护版本&#xff0c;架构选择x86_64。 创建新虚拟机&#xff1a;选择典型配置&#xff0c;点击下一步&#xff1a;选择下载的镜像文…

如何在手机上打开电脑端本地的网页

目录 一.手机端预览VSCode生成的网页站点二.手机端预览VS2022生成的 WebApi网页站点三.总结 今天遇到了2个小问题&#xff1a;1.想在手机上运行VSCode上写好的网页代码。2.同样在手机上运行VS2022 WebApi生成的网页。查找了一晚上资料&#xff0c;终于动手解决了&#xff0c;记…

虽不想承认,但这就是CSGO游戏搬砖行业的现状

其实整个搬砖市场&#xff0c;现在已经变得乌烟瘴气&#xff0c;散发着“恶臭”。童话个人非常鄙视那些虚有其表&#xff0c;大小通吃的做法&#xff0c;那些甚至连搬砖数据都看不懂的人&#xff0c;也出来吹嘘着“实力强大&#xff0c;经验丰富”。这个世界太浮躁了&#xff0…

ShowWeb-浏览器插件:可视化元素路径查看器

ShowWeb&#x1f47b;&#xff1a;可视化元素路径查看器适配【谷歌】【Edge】 每次写前端最烦的就是一层一层找元素&#xff0c;又臭又长。所以我开发了一个小插件来缓解这个问题&#xff0c;这个插件可以输出整个路径&#xff0c;并把最后元素的内容输出方便查看&#xff0c;…

javascript 运算符

javascript 运算符 目录 javascript 运算符 一、算术运算符 1、自增运算符 2、自减运算符 二、比较运算符 三、赋值运算符 四、逻辑运算符 五、条件运算符 疑难解答&#xff1a; 这一节&#xff0c;我们来介绍JavaScript的运算符。运算符是完成一系列操作的符号&…

vim工具以及如何给用户加上sudo的权限

Linux开发工具之vim以及如何给用户配置sudo的权限文件的操作 1.vim概念的介绍 2.vim的多模式的介绍 3.vim的命令模式与低行模式的相关指令操作 4.vim如何配置 5.如何给普通用户配置sudo的权限 本文开始~~~~ 1. vim概念的介绍 vim是一款多模式的文本编辑器&#xff0c;简单…

CSGO搬砖还能做吗?CSGO饰品未来走势如何?

steam/csgo搬砖项目真能月入过万吗&#xff1f;到底真的假的&#xff1f; 如何看待CSGO饰品市场的整体走向&#xff1f; 从整体来说&#xff0c;CSGO的饰品市场与规模肯定会持续不断的上升&#xff0c;大盘不会发生特别大的波动&#xff0c;目前处于稳定期&#xff01;&…

PT里如何针对某个模块设置false path

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 如题&#xff0c;这个问题实际上讲的是get_cells的用法&#xff0c;我们要抓取某个模块内的全部cell&#xff0c;在ICC2里可以get_flat_cells xx/xx/module_name*&#xff0c;但…

创意二维码案例:意大利艺术家的最新二维码艺术展!

意大利艺术家——米开朗基罗皮斯特莱托&#xff08;Michelangelo Pistoletto&#xff09;的个人艺术展“二维码‘说’”&#xff08;QR CODE POSSESSION&#xff09;正在北京798艺术区的常青艺术画廊展出&#xff0c;这是一次别出心裁的创意艺术展&#xff01; 主要体现在3个方…

final关键字-Java

final关键字 一、使用场景1、当不希望类被继承时&#xff0c;可以用final修饰。2、当不希望父类的某个方法被子类覆盖/重写(override)时&#xff0c;可以用final修饰。3、当不希望类的的某个属性的值被修改&#xff0c;可以用final修饰。4、当不希望某个局部变量被修改&#xf…

防火墙简介

防火墙概念 是指一种将内部网和公众访问网&#xff08;如Internet&#xff09;分开的方法&#xff0c;它实际上是一种建立在现代通信网络技术和信息安全技术基础上的应用性安全技术&#xff0c;隔离技术。 将需要保护的网络和不可信网络进行隔离&#xff0c;隐藏信息并…

振南技术干货集:znFAT 硬刚日本的 FATFS 历险记(5)

注解目录 1、znFAT 的起源 1.1 源于论坛 &#xff08;那是一个论坛文化兴盛的年代。网友 DIY SDMP3 播放器激起了我的兴趣。&#xff09; 1.2 硬盘 MP3 推了我一把 &#xff08;“坤哥”的硬盘 MP3 播放器&#xff0c;让我深陷 FAT 文件系统不能自拔。&#xff09; 1.3 我…

Java开发者的Python快速进修指南:自定义模块及常用模块

自定义模块 我来举一个在Java开发中常用的开发方式作为例子。在我们进行项目开发时&#xff0c;通常会在项目的结构中创建一个util包&#xff0c;用于存放一些工具类。同样&#xff0c;Python也可以采用类似的方式来组织代码结构&#xff0c;让大家更容易理解。 在同目录下 如果…

priority_queue模拟实现

目录 仿函数 模拟实现 结果 大根堆 小根堆 完整代码 priority_queue.h test.c 仿函数 仿函数的通俗定义&#xff1a;仿函数&#xff08;functor&#xff09;又称为函数对象&#xff08;function object&#xff09;是一个能行使函数功能 的类。仿函数的语法几乎和我们…

为啥网络安全那么缺人,但很多人却找不到工作?

文章目录 一、学校的偏向于学术二、学的东西太基础三、不上班行不行 为什么网络安全的人才缺口那么大&#xff0c;但是大学毕业能找到网安工作的人却很少&#xff0c;就连招聘都没有其他岗位多&#xff1f; 明明央视都说了网络安全的人才缺口还有300多万&#xff0c;现在找不到…

Vue框架学习笔记——计算属性

文章目录 前文提要代码需求描述插值语法实现methods实现 计算属性getter执行时间&#xff1a;setter 计算属性简写形式&#xff08;只读不改&#xff0c;才能如此简写&#xff09;slice截取元素&#xff0c;限制输入字符数量 前文提要 本人仅做个人学习记录&#xff0c;如有错…

【一周AI简讯】OpenAI奥特曼王者归来,马斯克AI模型Grok下周开放测试,ChatGPT语音对话功能向所有用户免费开放

OpenAI奥特曼王者归来&#xff0c;董事会改组 终于&#xff0c;经历大约5天的极限拉扯&#xff0c;年底AI界吃瓜大戏落下帷幕&#xff0c;奥特曼确认回归。 ChatGPT语音对话功能向所有用户免费开放 ChatGPT 语音输入最初于 9 月份推出&#xff0c;标题是“ChatGPT 现在可以看…

12、模块化编程

模块化编程 1、传统方式编程&#xff1a;所有的函数均放在main.c里&#xff0c;若使用的模块比较多&#xff0c;则一个文件内会有很多的代码&#xff0c;不利于代码的组织和管理&#xff0c;而且很影响便朝着的思路 2、模块化编程&#xff1a;把各个模块的代码放在不同的.c文件…

线性分类器--图像表示

整个模型 图像表示 二进制图像 灰度图像 彩色图像 大多数分类算法都要求输入向量&#xff01; rbg的图像矩阵转列向量 大小为 32X32 的话&#xff0c;图像矩阵转列向量是多少维&#xff1f; 32x32x3 3072 维列向量