FFmpeg常用API与示例(二)—— 解封装与转封装

封装层

封装格式(container format)可以看作是编码流(音频流、视频流等)数据的一层外壳,将编码后的数据存储于此封装格式的文件之内。

封装又称容器,容器的称法更为形象,所谓容器,就是存放内容的器具,饮料是内容,那么装饮料的瓶子就是容器。

  • avformat_open_input()
  • avformat_find_stream_info()
  • av_read_frame()
  • av_write_frame()
  • av_interleaved_write_frame()
  • avio_open()
  • avformat_write_header()
  • av_write_trailer()
1.解封装

1.分配解复用器上下文 avformat_alloc_context

2.根据 url 打开本地文件或网络流 avformat_open_input

3.读取媒体的部分数据包以获取码流信息 avformat_find_stream_info

4.读取码流信息:循环处理

4.1 从文件中读取数据包 av_read_frame

4.2 定位文件 avformat_seek_file 或 av_seek_frame

5.关闭解复用器 avformat_close_input

本例子实现的是将音视频分离,例如将封装格式为 FLV、MKV、MP4、AVI 等封装格式的文件,将音频、视频读取出来并打印。

实现的过程,可以大致用如下图表示:

在这里插入图片描述

#include <stdio.h>
extern "C"
{
    #include <libavformat/avformat.h>
}
/**
* @brief 将一个AVRational类型的分数转换为double类型的浮点数
* @param r:r为一个AVRational类型的结构体变量,成员num表示分子,成员den表示分母,r的值即为(double)r.num / (double)r.den。用这种方法表示可以最大程度地避免精度的损失
* @return 如果变量r的分母den为0,则返回0(为了避免除数为0导致程序死掉);其余情况返回(double)r.num / (double)r.den
*/
static double r2d(AVRational r)
{
    return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}

int main()
{
    //需要读取的本地媒体文件相对路径为video1.mp4,这里由于文件video1.mp4就在工程目录下,所以相对路径为video1.mp4
    const char *path = "ande_10s.mp4";
    //const char *path = "audio1.mp3";
    ///av_register_all(); //初始化所有组件,只有调用了该函数,才能使用复用器和编解码器。否则,调用函数avformat_open_input会失败,无法获取媒体文件的信息
    avformat_network_init(); //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
    AVDictionary *opts = NULL;

    //AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *ic = NULL;

    //媒体打开函数,调用该函数可以获得路径为path的媒体文件的信息,并把这些信息保存到指针ic指向的空间中(调用该函数后会分配一个空间,让指针ic指向该空间)
    int re = avformat_open_input(&ic, path, NULL, &opts);
    if (re != 0)  //如果打开媒体文件失败,打印失败原因。比如,如果上面没有调用函数av_register_all,则会打印“XXX failed!:Invaliddata found when processing input”
    {
        char buf[1024] = { 0 };
        av_strerror(re, buf, sizeof(buf) - 1);
        printf("open %s failed!:%s", path, buf);
    }
    else         //打开媒体文件成功
    {
        printf("打开媒体文件 %s 成功!\n", path);

        //调用该函数可以进一步读取一部分视音频数据并且获得一些相关的信息。
        //调用avformat_open_input之后,我们无法获取到正确和所有的媒体参数,所以还得要调用avformat_find_stream_info进一步的去获取。
        avformat_find_stream_info(ic, NULL);

        //调用avformat_open_input读取到的媒体文件的路径/名字
        printf("媒体文件名称:%s\n", ic->filename);

        //视音频流的个数,如果一个媒体文件既有音频,又有视频,则nb_streams的值为2。如果媒体文件只有音频,则值为1
        printf("视音频流的个数:%d\n", ic->nb_streams);


        //媒体文件的平均码率,单位为bps
        printf("媒体文件的平均码率:%lldbps\n", ic->bit_rate);

        printf("duration:%d\n", ic->duration);
        int tns, thh, tmm, tss;
        tns = (ic->duration) / AV_TIME_BASE;
        thh = tns / 3600;
        tmm = (tns % 3600) / 60;
        tss = (tns % 60);
        printf("媒体文件总时长:%d时%d分%d秒\n", thh, tmm, tss); //通过上述运算,可以得到媒体文件的总时长

        printf("\n");
        //通过遍历的方式读取媒体文件视频和音频的信息,
        //新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果,但这里为了兼容旧版本还是用这种遍历的方式
        for (int i = 0; i < ic->nb_streams; i++)
        {
            AVStream *as = ic->streams[i];
            if (AVMEDIA_TYPE_AUDIO == as->codecpar->codec_type) //如果是音频流,则打印音频的信息
            {
                printf("音频信息:\n");
                printf("index:%d\n", as->index);  //如果一个媒体文件既有音频,又有视频,则音频index的值一般为1。但该值不一定准确,所以还是得通过as->codecpar->codec_type判断是视频还是音频
                printf("音频采样率:%dHz\n", as->codecpar->sample_rate); //音频编解码器的采样率,单位为Hz
                if (AV_SAMPLE_FMT_FLTP == as->codecpar->format)   //音频采样格式
                {
                    printf("音频采样格式:AV_SAMPLE_FMT_FLTP\n");
                }
                else if (AV_SAMPLE_FMT_S16P == as->codecpar->format)
                {
                    printf("音频采样格式:AV_SAMPLE_FMT_S16P\n");
                }
                printf("音频信道数目:%d\n", as->codecpar->channels); //音频信道数目
                if (AV_CODEC_ID_AAC == as->codecpar->codec_id)   //音频压缩编码格式
                {
                    printf("音频压缩编码格式:AAC\n");
                }
                else if (AV_CODEC_ID_MP3 == as->codecpar->codec_id)
                {
                    printf("音频压缩编码格式:MP3\n");
                }
                int DurationAudio = (as->duration) * r2d(as->time_base); //音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
                printf("音频总时长:%d时%d分%d秒\n", DurationAudio / 3600, (DurationAudio % 3600) / 60, (DurationAudio % 60)); //将音频总时长转换为时分秒的格式打印到控制台上
                printf("\n");
            }
            else if (AVMEDIA_TYPE_VIDEO == as->codecpar->codec_type)  //如果是视频流,则打印视频的信息
            {
                printf("视频信息:\n");
                printf("index:%d\n", as->index);   //如果一个媒体文件既有音频,又有视频,则视频index的值一般为0。但该值不一定准确,所以还是得通过as->codecpar->codec_type判断是视频还是音频
                printf("视频帧率:%lffps\n", r2d(as->avg_frame_rate)); //视频帧率,单位为fps,表示每秒出现多少帧
                if (AV_CODEC_ID_MPEG4 == as->codecpar->codec_id) //视频压缩编码格式
                {
                    printf("视频压缩编码格式:MPEG4\n");
                }
                printf("帧宽度:%d 帧高度:%d\n", as->codecpar->width, as->codecpar->height); //视频帧宽度和帧高度
                int DurationVideo = (as->duration) * r2d(as->time_base); //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
                printf("视频总时长:%d时%d分%d秒\n", DurationVideo / 3600, (DurationVideo % 3600) / 60, (DurationVideo % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
                printf("\n");
            }
        }

        //av_dump_format(ic, 0, path, 0);
    }
    if (ic)
    {
        avformat_close_input(&ic); //关闭一个AVFormatContext,和函数avformat_open_input()成对使用
    }
    avformat_network_deinit();
    getchar(); //加上这一句,防止程序打印完信息就马上退出了
    return 0;
}

int mainsdf3324sf(int argc, char **argv)
{

// 1. 打开文件
    const char *ifilename = "ande_10s.mp4";
    printf("in_filename = %s\n", ifilename);
    avformat_network_init();

    // AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *ifmt_ctx = NULL;           // 输入文件的demux
    // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
    int ret = avformat_open_input(&ifmt_ctx, ifilename, NULL, NULL);
    if (ret < 0) {
        char buf[1024] = {0};
        av_strerror(ret, buf, sizeof (buf) - 1);
        printf("open %s failed: %s\n", ifilename, buf);
        return -1;
    }

// 2. 读取码流信息
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)  //如果打开媒体文件失败,打印失败原因
    {
        char buf[1024] = { 0 };
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("avformat_find_stream_info %s failed:%s\n", ifilename, buf);
        avformat_close_input(&ifmt_ctx);
        return -1;
    }


// 3.打印总体信息
    printf_s("\n==== av_dump_format in_filename:%s ===\n", ifilename);
    av_dump_format(ifmt_ctx, 0, ifilename, 0);
    printf_s("\n==== av_dump_format finish =======\n\n");
    printf("media name:%s\n", ifmt_ctx->url);
    printf("stream number:%d\n", ifmt_ctx->nb_streams); //  nb_streams媒体流数量
    printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024)); //  媒体文件的码率,单位为bps/1000=Kbps
    // duration: 媒体文件时长,单位微妙
    int total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒
    printf("audio duration: %02d:%02d:%02d\n",
           total_seconds / 3600, (total_seconds % 3600) / 60, (total_seconds % 60));
    printf("\n");

// 4.读取码流信息
    // 音频
    int audioindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioindex < 0) {
        printf("av_find_best_stream %s eror.", av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
        return -1;
    }
    AVStream *audio_stream = ifmt_ctx->streams[audioindex];
    printf("----- Audio info:\n");
    printf("index: %d\n", audio_stream->index); // 序列号
    printf("samplarate: %d Hz\n", audio_stream->codecpar->sample_rate); // 采样率
    printf("sampleformat: %d\n", audio_stream->codecpar->format); // 采样格式 AV_SAMPLE_FMT_FLTP:8
    printf("audio codec: %d\n", audio_stream->codecpar->codec_id); // 编码格式 AV_CODEC_ID_MP3:86017 AV_CODEC_ID_AAC:86018
    if (audio_stream->duration != AV_NOPTS_VALUE) {
        int audio_duration = audio_stream->duration * av_q2d(audio_stream->time_base);
        printf("audio duration: %02d:%02d:%02d\n",
               audio_duration / 3600, (audio_duration % 3600) / 60, (audio_duration % 60));
    }

    // 视频
    int videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (videoindex < 0) {
        printf("av_find_best_stream %s eror.", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
        return -1;
    }
    AVStream *video_stream = ifmt_ctx->streams[videoindex];
    printf("----- Video info:\n");
    printf("index: %d\n", video_stream->index); // 序列号
    printf("fps: %lf\n", av_q2d(video_stream->avg_frame_rate)); // 帧率
    printf("width: %d, height:%d \n", video_stream->codecpar->width, video_stream->codecpar->height);
    printf("video codec: %d\n", video_stream->codecpar->codec_id); // 编码格式 AV_CODEC_ID_H264: 27
    if (video_stream->duration != AV_NOPTS_VALUE) {
        int video_duration = video_stream->duration * av_q2d(video_stream->time_base);
        printf("audio duration: %02d:%02d:%02d\n",
               video_duration / 3600, (video_duration % 3600) / 60, (video_duration % 60));
    }

// 5.提取码流
    AVPacket *pkt = av_packet_alloc();
    int pkt_count = 0;
    int print_max_count = 100;
    printf("\n-----av_read_frame start\n");
    while (1)
    {
        ret = av_read_frame(ifmt_ctx, pkt);
        if (ret < 0) {
            printf("av_read_frame end\n");
            break;
        }

        if(pkt_count++ < print_max_count)
        {
            if (pkt->stream_index == audioindex)
            {
                printf("audio pts: %lld\n", pkt->pts);
                printf("audio dts: %lld\n", pkt->dts);
                printf("audio size: %d\n", pkt->size);
                printf("audio pos: %lld\n", pkt->pos);
                printf("audio duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
            }
            else if (pkt->stream_index == videoindex)
            {
                printf("video pts: %lld\n", pkt->pts);
                printf("video dts: %lld\n", pkt->dts);
                printf("video size: %d\n", pkt->size);
                printf("video pos: %lld\n", pkt->pos);
                printf("video duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
            }
            else
            {
                printf("unknown stream_index:\n", pkt->stream_index);
            }
        }
        av_packet_unref(pkt);
    }

// 6.结束
    if(pkt)
        av_packet_free(&pkt);
    if(ifmt_ctx)
        avformat_close_input(&ifmt_ctx);


    avformat_network_deinit();
    //getchar(); //加上这一句,防止程序打印完信息马上退出
    return 0;
}
2.转封装

从一种视频容器转成另一种视频容器

所谓的封 装格 式转 换,就是在 AVI,FLV,MKV ,MP4 这些格式 之间转换(对应.avi,.flv,.mkv,.mp4 文件)。需要注意的是,本程序并不进行视音频的编码和解码工作。而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件。

在这里插入图片描述

上图例举了一个举例:FLV(视频:H.264,音频:AAC)转码为 AVI(视频:MPEG2,音频 MP3)的例子。可见视频转码的过程通俗地讲相当于把视频和音频重新“录”了一遍。

在这里插入图片描述

/**
 * @file
 * libavformat/libavcodec demuxing and muxing API example.
 *
 * Remux streams from one container format to another.
 * @example remuxing.c
 */

#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>

static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
           tag,
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
           av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
           pkt->stream_index);
}

int main234ssfdx(int argc, char **argv)
{
    AVOutputFormat *ofmt = NULL;
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
    int ret, i;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;

    if (argc < 3) {
        printf("usage: %s input output\n"
               "API example program to remux a media file with libavformat and libavcodec.\n"
               "The output format is guessed according to the file extension.\n"
               "\n", argv[0]);
        return 1;
    }

    in_filename  = argv[1];
    out_filename = argv[2];

    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        fprintf(stderr, "Could not open input file '%s'", in_filename);
        goto end;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        goto end;
    }

    printf("\n\n-------av_dump_format:ifmt_ctx----------------\n");
    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx) {
        fprintf(stderr, "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    if (!stream_mapping) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ofmt = ofmt_ctx->oformat;

    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *out_stream;
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;

        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }

        stream_mapping[i] = stream_index++;

        out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream) {
            fprintf(stderr, "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0) {
            fprintf(stderr, "Failed to copy codec parameters\n");
            goto end;
        }
        out_stream->codecpar->codec_tag = 0;
    }
    printf("\n\n-------av_dump_format:ofmt_ctx----------------\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);

    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'", out_filename);
            goto end;
        }
    }

    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        goto end;
    }

    while (1) {
        AVStream *in_stream, *out_stream;

        /// 读取音视频压缩包
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;

        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        if (pkt.stream_index >= stream_mapping_size ||
            stream_mapping[pkt.stream_index] < 0) {
            av_packet_unref(&pkt);
            continue;
        }

        pkt.stream_index = stream_mapping[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        log_packet(ifmt_ctx, &pkt, "in");

        /* copy packet */
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        log_packet(ofmt_ctx, &pkt, "out");

        /// 交织写音视频包
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            fprintf(stderr, "Error muxing packet\n");
            break;
        }
        av_packet_unref(&pkt);//包需要解引用
    }

    av_write_trailer(ofmt_ctx);
end:

    avformat_close_input(&ifmt_ctx);

    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);

    av_freep(&stream_mapping);

    if (ret < 0 && ret != AVERROR_EOF) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }

    return 0;
}

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

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

相关文章

【操作系统】处理机调度

处理机调度 处理机调度概念调度概念调度时机 调度原则调度算法实时调度优先级翻转 处理机调度概念 调度概念 进程切换&#xff1a; CPU资源的当前占用者切换 保存当前进程在PCB中的执行上下文(CPU状态)恢复下一个进程的执行上下文 处理机调度: 从就绪队列中挑选下一个占用…

在哪里打印资料比较便宜

在数字时代&#xff0c;我们常常需要在各种文档、资料之间穿梭&#xff0c;然而&#xff0c;有时候我们需要的并不是数字版&#xff0c;而是纸质版。那么&#xff0c;在哪里打印资料比较便宜呢&#xff1f; 琢贝云打印以其超低的价格&#xff0c;优质的打印服务&#xff0c;赢…

html划过盒子出现弹窗

<template><div><div class"content">盒子<div class"topUserInfo">弹窗</div></div></div> </template><script> export default {} </script><style lang"less" scoped> .…

P8802 [蓝桥杯 2022 国 B] 出差

P8802 [蓝桥杯 2022 国 B] 出差 分析 很明显&#xff1a;单源最短路径 没有负权边 dijkstra 1.存图 2.准备两个数组 dis[]&#xff1a;更新源点到各个点的距离 vis[]&#xff1a;标记是否访问 3.从源点开始&#xff0c;更新源点到与其邻接的点的距离&#xff0c;每次选…

01.基本概念

操作系统 为什么要有操作系统&#xff1f; 计算机时一个十分复杂的系统&#xff0c;又cpu、内存、磁盘、IO设备、网络接口等等复杂的硬件组成&#xff0c;人的精力是有限的&#xff0c;不可能了解所有的硬件接口&#xff0c;但是程序可以。 所以我们在计算机上安装了一层软件&…

从零入门激光SLAM(十三)——LeGo-LOAM源码超详细解析4

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

Linux -- 日志

一 日志的重要性 在之前的编程经历中&#xff0c;如果我们的程序运行出现了问题&#xff0c;都是通过 标准输出 或 标准错误 将 错误信息 直接输出到屏幕上&#xff0c;以此来排除程序中的错误。 这在我们以往所写的程序中使用没啥问题&#xff0c;但如果出错的是一个不断在运行…

fb设备驱动框架分析

一、字符设备注册过程&#xff1a; 归根到底&#xff0c;fb设备也是一个字符设备&#xff0c;所以逃不开常规的字符设备驱动框架&#xff1a; Linux内核中编写字符设备驱动通常遵循以下步骤&#xff1a; ①、定义主设备号&#xff1a; 在Linux中&#xff0c;每个字符设备都…

MySQL 通过 systemd 启动时 hang 住了……

mysqld&#xff1a;哥&#xff0c;我起不来了…… 作者&#xff1a;贲绍华&#xff0c;爱可生研发中心工程师&#xff0c;负责项目的需求与维护工作。其他身份&#xff1a;柯基铲屎官。 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编…

如何查看页面对应的Selenium定位参数

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

谷歌外链怎么发?

既要数量也要质量&#xff0c;要保证你的链接广泛分布&#xff0c;在数量上&#xff0c;确实需要你的链接在各种平台上有所展现&#xff0c;这样能提升你网站的知名度和曝光率&#xff0c;但是&#xff0c;光有数量是不够的&#xff0c;如果这些链接的内容不行&#xff0c;那对…

泰迪智能科技企业数据挖掘流程分析及特色服务优势

企业发展会沉淀大量的数据&#xff0c;数据中囊括了企业业务各种维度指标&#xff0c;通过数据挖掘和数据分析 &#xff0c;让企业业务了解过去、现在和未来将要发生什么&#xff0c;从而更好的调整企业发展方向。泰迪智能科技企业数据挖掘平台是面向企业级用户快速处理数据构建…

2024年湖北省专升本C语言程序设计大题真题解析

2024年湖北省的专升本考试已于4月30日举行&#xff0c;考试中&#xff0c;出现了许多不同的考试题目&#xff0c;我在网上找到一所高校专升本的大题&#xff08;好像是湖北师范的&#xff0c;后续会有湖北理工的大题真题解析&#xff0c;敬请期待&#xff09;&#xff0c;那么我…

在新页面中跳转到指定 div容器位置

要在打开新的页面时跳转到指定 div&#xff0c;我们需要结合 HTML、JavaScript 和后端技术来实现。以下是两种常见的方法&#xff1a; 使用 URL 参数传递目标 div 信息 HTML (新页面): 在新页面的链接中&#xff0c;添加参数来指示目标 div 的 id&#xff0c;例如&#xff1a;…

致远M3 Session 敏感信息泄露漏洞复现

0x01 产品简介 M3移动办公是致远互联打造的一站式智能工作平台,提供全方位的企业移动业务管理,致力于构建以人为中心的智能化移动应用场景,促进人员工作积极性和创造力,提升企业效率和效能,是为企业量身定制的移动智慧协同平台。 0x02 漏洞概述 致远M3 server多个日志文…

Vue3自定义指令封装-按钮权限控制v-permission、hasPermissions

背景&#xff1a;平常所接触到的系统权限控制&#xff0c;大部分都是菜单、路由级别的控制&#xff0c;但后台管理系统中&#xff0c;很多操作都是与职责和角色挂钩的&#xff0c;同样一个列表&#xff0c;不同人的操作列并不都一样&#xff0c;有些页面存在一些含有重要数据的…

万物生长大会 | 创邻科技再登杭州准独角兽榜单

近日&#xff0c;由民建中央、中国科协指导&#xff0c;民建浙江省委会、中国投资发展促进会联合办的第八届万物生长大会在杭州举办。 在这场创新创业领域一年一度的盛会上&#xff0c;杭州市创业投资协会联合微链共同发布《2024杭州独角兽&准独角兽企业榜单》。榜单显示&…

MathType2024官方版数学公式编辑器功能全面介绍

在数字化学习和科研的浪潮中&#xff0c;数学公式的编辑与展示成为了不可或缺的一部分。MathType&#xff0c;作为一款专业的数学公式编辑器&#xff0c;凭借其强大的功能和便捷的操作&#xff0c;为科研人员、教师、学生等广大用户提供了极大的便利。下面&#xff0c;我们将对…

基于.NET WinForms 数据CURD功能的实现

使用开发工具 VS 2022 C#&#xff0c;数据库MS SQL SERVER 2019 &#xff0c;基于NET WinForms&#xff0c;实现数据记录的创建(Create)、更新(Update)、读取(Read)和删除(Delete)等功能。主要控件包括&#xff1a;DataGridView&#xff0c;SqlDataApater &#xff0c; DataTab…

字符以及字符串函数

字符以及字符串函数 求字符串长度strlen 长度不受限制的字符串函数strcpystrcatstrcmp 长度受限制的字符串函数strncpystrncatstrncmp 字符串查找strstrstrtok 错误信息报告strerror 字符分类函数字符转换函数tolowertoupper 内存操作函数memcpymemmovememcmpmemset 这篇文章注…
最新文章