macOS使用ffmpeg与QT进行音视频推拉流

news2024/12/24 19:17:43

1.先启动流服务器  ./mediamtx

2.开始推流: ffmpeg -re -stream_loop -1 -i /Users/hackerx/Desktop/test.mp4 -c copy -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:8554/stream

  3. 安装ffmpeg 4.4

brew install ffmpeg@4

 4.添加ffmpeg头文件目录与库目录

5.链接ffmpeg相关库 

6.设计界面

7.拉流

  

 ffmpegmanager.cpp:

#include "ffmpegmananger.h"
#include <QThread>
//构造
ffmpegMananger::ffmpegMananger(QObject *parent) : QObject{parent}
{
    m_pInFmtCtx = nullptr;//输入流格式上下文
    m_pTsFmtCtx  = nullptr;//输出流格式上下文
    m_strInputStreamUrl = "";//输入流地址
    m_strOutputStreamPath = "";//输出流地址

}

//拆构
ffmpegMananger::~ffmpegMananger()
{
    avformat_free_context(m_pInFmtCtx);//释放输入流格式上下文
    avformat_free_context(m_pTsFmtCtx);//释放输出流格式上下文
}

//取输入流地址
void ffmpegMananger::getRtspAddress(QString url)
{
    this->m_strInputStreamUrl = url;
}

//取输出流地址
void ffmpegMananger::getOutputAddress(QString path)
{
    this->m_strOutputStreamPath = path;
    printf("输出流地址: %s\n",m_strOutputStreamPath.toStdString().c_str());
}

void ffmpegMananger::setOutputCtx(AVCodecContext *encCtx, AVFormatContext **pTsFmtCtx,int &nVideoIdx_out)
{
    avformat_alloc_output_context2(pTsFmtCtx , nullptr, nullptr, m_strOutputStreamPath.toStdString().c_str());
    if (!pTsFmtCtx ) {
        printf("创建输出上下文失败: avformat_alloc_output_context2\n");
        return;
    }
    if (avio_open(&((*pTsFmtCtx)->pb), m_strOutputStreamPath.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0)
    {
        avformat_free_context(*pTsFmtCtx);
        printf("打开输出流失败: avio_open\n");
        return;
    }
    AVStream *out_stream = avformat_new_stream(*pTsFmtCtx, encCtx->codec);
    nVideoIdx_out = out_stream->index;
    avcodec_parameters_from_context(out_stream->codecpar, encCtx);
    printf("输出流信息:\n");
    av_dump_format(*pTsFmtCtx, 0, m_strOutputStreamPath.toStdString().c_str(), 1);
    printf("----------------------------\n");
}

//拉流并播放
int ffmpegMananger::ffmepgInput()
{
    int nRet = 0;
    AVCodecContext *encCtx = nullptr;
    std::string temp = m_strInputStreamUrl.toStdString();
    const char *pUrl = temp.c_str();
    printf("输入流地址: %s\n",pUrl);

    //设置选项
    AVDictionary *dict = nullptr;
    av_dict_set(&dict,"rtsp_transport", "tcp", 0);
    av_dict_set(&dict,"stimeout","10000000",0);
    av_dict_set(&dict, "buffer_size", "1024000", 0);

    //打开输入流
    nRet = avformat_open_input(&m_pInFmtCtx,pUrl,nullptr,&dict);
    if( nRet < 0)
    {
        printf("打开输入流失败\n");
        return nRet;
    }

    avformat_find_stream_info(m_pInFmtCtx, nullptr);
    printf("输入流信息:\n");
    av_dump_format(m_pInFmtCtx, 0, pUrl, 0);
    printf("---------------------------\n");

    //视频流索引
    int nVideo_indx = av_find_best_stream(m_pInFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
    if(nVideo_indx < 0)
    {
        avformat_free_context(m_pInFmtCtx);
        printf("查找视频流索引失败: av_find_best_stream\n");
        return -1;
    }

    //查找解码器
    auto pInCodec = avcodec_find_decoder(m_pInFmtCtx->streams[nVideo_indx]->codecpar->codec_id);
    if(nullptr == pInCodec)
    {
        printf("查找解码器失败: avcodec_find_decoder fail.");
        return -1;
    }

    //解码器上下文
    AVCodecContext* pInCodecCtx = avcodec_alloc_context3(pInCodec);

    //设置解码器参数
    nRet = avcodec_parameters_to_context(pInCodecCtx, m_pInFmtCtx->streams[nVideo_indx]->codecpar);
    if(nRet < 0)
    {

        avcodec_free_context(&pInCodecCtx);
        printf("设置解码器参数失败: avcodec_parameters_to_context");
        return -1;
    }

    //打开解码器
    if(avcodec_open2(pInCodecCtx, pInCodec, nullptr) < 0)
    {
        avcodec_free_context(&pInCodecCtx);
        printf("打开解码器失败: avcodec_open2\n");
        return -1;
    }

    //输出视频分辨率
    printf("视频宽:%d\n", pInCodecCtx->width);
    printf("视频高:%d\n", pInCodecCtx->height);

    int frame_index = 0;//帧索引
    int got_picture = 0;//帧解码结果

    //输出输入流
    AVStream *in_stream =nullptr;
    AVStream *out_stream =nullptr;

    //分配内存
    AVFrame *pFrame= av_frame_alloc();
    AVFrame *pFrameRGB = av_frame_alloc();
    AVPacket *newpkt = av_packet_alloc();
    AVPacket *packet = av_packet_alloc();

    //初始化视频包
    av_init_packet(newpkt);
    av_init_packet(packet);


    //图像色彩空间转换、分辨率缩放、前后图像滤波处理
    SwsContext *m_SwsContext = sws_getContext(
                                    pInCodecCtx->width,
                                    pInCodecCtx->height,
                                    pInCodecCtx->pix_fmt,
                                    pInCodecCtx->width,
                                    pInCodecCtx->height,
                                    AV_PIX_FMT_RGB32,
                                    SWS_BICUBIC,
                                    nullptr, nullptr, nullptr);

    int bytes = av_image_get_buffer_size(
                                    AV_PIX_FMT_RGB32,
                                    pInCodecCtx->width,
                                    pInCodecCtx->height,
                                    4);
    uint8_t *m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));

    //将分配的内存空间给pFrameRGB使用
    avpicture_fill((AVPicture *)pFrameRGB,
                   m_OutBuffer,
                   AV_PIX_FMT_RGB32,
                   pInCodecCtx->width,
                   pInCodecCtx->height);

    if(encCtx == nullptr)
    {
        //打开编码器
        openEncoder(pInCodecCtx->width, pInCodecCtx->height,&encCtx);
    }
    //视频索引
    int videoindex_out = 0;
    //设置输出文件上下文
    setOutputCtx(encCtx,&m_pTsFmtCtx,videoindex_out);
    //写文件头
    if (avformat_write_header(m_pTsFmtCtx, nullptr) < 0)
    {
        avformat_free_context(m_pTsFmtCtx);
        printf("写文件头失败\n");
        return -1;
    }
    printf("写文件头成功.\n");

    int count = 0;//已解码帧数量
    nRet = 0;//读取帧结果
    //从pInFmtCtx读H264数据到packet;
    while(av_read_frame(m_pInFmtCtx, packet) >= 0)
    {
        if(packet->stream_index != nVideo_indx)//仅保留图像
        {
            continue;
        }

        //送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
        if(avcodec_send_packet(pInCodecCtx, packet)<0)
        {
            break;
        }
        //释放已解码帧引用
        av_packet_unref(packet);
        //把解码好的YUV数据放到pFrame中
        got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);

        //解码好一帧数据
        if(0 == got_picture)
        {
            //发送显示图像的信号
            // 对解码视频帧进行缩放、格式转换等操作
            sws_scale(m_SwsContext,
                      (uint8_t const * const *)pFrame->data,
                      pFrame->linesize,
                      0,
                      pInCodecCtx->height,
                      pFrameRGB->data,
                      pFrameRGB->linesize);

            // 转换到QImage
            QImage tmmImage((uchar *)m_OutBuffer, pInCodecCtx->width, pInCodecCtx->height, QImage::Format_RGB32);
            //复制图像
            QImage image = tmmImage.copy();
            //发送图像帧解码完成信息
            emit Sig_GetOneFrame(image);
            //设置解码器PTS
            setDecoderPts(newpkt->stream_index,count, pFrame);
            count++;//已解码计数
            //送原始数据给编码器进行编码
            nRet = avcodec_send_frame(encCtx,pFrame);
            if(nRet < 0)
            {
                continue;
            }
            //从编码器获取编号的数据
            while(nRet >= 0)
            {
                //接收已编码包
                nRet = avcodec_receive_packet(encCtx,newpkt);
                if(nRet < 0)
                {
                    break;
                }
                //设置编码包PTS
                setEncoderPts(nVideo_indx,frame_index,videoindex_out,newpkt);
                int _count = 1;
                printf("写%d包,大小:%5d,PTS:%lld\n", _count,newpkt->size, newpkt->pts);

                if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
                {
                    printf("写帧失败: av_interleaved_write_frame\n");
                    goto end;
                }
                _count++;
                av_packet_unref(newpkt);//释放已编码包
            }
        }
    }
    while(1)//从pInFmtCtx读H264数据到packet;
    {
        if(packet->stream_index != nVideo_indx)//仅保留图像
        {
            continue;
        }

        //送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
        if(avcodec_send_packet(pInCodecCtx, packet)<0)
        {
            continue;
        }

        //释放已解码包
        av_packet_unref(packet);
        //把解码好的YUV数据放到pFrame中
        got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);

        //解码好一帧数据
        if(!got_picture)
        {
            AVRational in_time_base1 = in_stream->time_base;
            in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
            //PTS
            int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
            count++;
            //送原始数据给编码器进行编码
            nRet = avcodec_send_frame(encCtx,pFrame);
            if(nRet < 0)
            {
                break;
            }
            //从编码器获取编号的数据
            while(nRet >= 0)
            {
                nRet = avcodec_receive_packet(encCtx,newpkt);
                if(nRet < 0)
                {
                    continue;
                }
                in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
                out_stream = m_pTsFmtCtx->streams[videoindex_out];
                if (newpkt->stream_index == nVideo_indx)
                {
                    if (newpkt->pts == AV_NOPTS_VALUE)
                    {
                        //写入PTS
                        AVRational time_base1 = in_stream->time_base;
                        int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
                        //设置包参数
                        newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                        newpkt->dts = newpkt->pts;
                        newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                        frame_index++;
                    }
                }
                //转换PTS/DTS
                newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
                newpkt->pos = -1;
                newpkt->stream_index = videoindex_out;
                int count = 1;
                printf("写%d包,大小:%5dPTS:%lld\n", count,newpkt->size, newpkt->pts);

                if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
                {
                    printf("写帧失败: av_interleaved_write_frame\n");
                    goto end;
                }
                count++;
                av_packet_unref(newpkt);
            }
        }
    }
    //Write file trailer
    av_write_trailer(m_pTsFmtCtx);
end:
    av_frame_free(&pFrame);
    av_frame_free(&pFrameRGB);
    av_packet_unref(newpkt);
    av_packet_unref(packet);
    std::cout<<"拉流完成";
    return  0;
}

void ffmpegMananger::setDecoderPts(int idx,int count,AVFrame *pFrame)
{
    AVStream* in_stream = m_pInFmtCtx->streams[idx];
    AVRational in_time_base1 = in_stream->time_base;
    //Duration between 2 frames (us)
    int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
    pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
}

void ffmpegMananger::setEncoderPts(int nVideo_indx,int frame_index,int videoindex_out,AVPacket *newpkt)
{
    AVStream*in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
    AVStream*out_stream = m_pTsFmtCtx->streams[videoindex_out];
    if (newpkt->stream_index == nVideo_indx)
    {
        //FIX:No PTS (Example: Raw H.264)
        //Simple Write PTS
        if (newpkt->pts == AV_NOPTS_VALUE)
        {
            //Write PTS
            AVRational time_base1 = in_stream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            //Parameters
            newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            newpkt->dts = newpkt->pts;
            newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            frame_index++;
        }
    }
    //Convert PTS/DTS
    newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
    newpkt->pos = -1;
    newpkt->stream_index = videoindex_out;
}

void ffmpegMananger::writeTail()
{
    //Write file trailer
    av_write_trailer(m_pTsFmtCtx);
}

void ffmpegMananger::openEncoder(int width, int height, AVCodecContext** enc_ctx)
{
    //使用libx264编码器
    auto pCodec = avcodec_find_encoder_by_name("libx264");
    if(nullptr == pCodec)
    {
        printf("avcodec_find_encoder_by_name fail.\n");
        return;
    }
    //获取编码器上下文
    *enc_ctx = avcodec_alloc_context3(pCodec);
    if(nullptr == enc_ctx)
    {
        printf("avcodec_alloc_context3(pCodec) fail.\n");
        return;
    }
    //sps/pps
    (*enc_ctx)->profile = FF_PROFILE_H264_MAIN;
    (*enc_ctx)->level = 30;//表示level是5.0
    //分辨率
    (*enc_ctx)->width = width;
    (*enc_ctx)->height = height;
    //gop
    (*enc_ctx)->gop_size = 25;//i帧间隔
    (*enc_ctx)->keyint_min = 20;//设置最小自动插入i帧的间隔.OPTION
    //B帧
    (*enc_ctx)->max_b_frames = 0;//不要B帧
    (*enc_ctx)->has_b_frames = 0;//
    //参考帧
    (*enc_ctx)->refs = 3;//OPTION
    //设置输入的yuv格式
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    //设置码率
    (*enc_ctx)->bit_rate = 3000000;
    //设置帧率
    (*enc_ctx)->time_base = (AVRational){1,25};//帧与帧之间的间隔
    (*enc_ctx)->framerate = (AVRational){25,1};//帧率 25帧每秒
    if(avcodec_open2((*enc_ctx),pCodec,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    return;
}


ffmpegmanager.h

#ifndef FFMPEGMANANGER_H
#define FFMPEGMANANGER_H
#pragma execution_character_set("utf-8")
//QT头
#include <QObject>
#include <QTimer>
#include <QImage>
//C标准头
#include <stdio.h>
#include <iostream>
//FFmpeg头
extern "C"
{
    #include "libswscale/swscale.h"
    #include "libavdevice/avdevice.h"
    #include "libavcodec/avcodec.h"
    #include "libavcodec/bsf.h"
    #include "libavformat/avformat.h"
    #include "libavutil/avutil.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/log.h"
    #include "libavutil/time.h"
    #include <libswresample/swresample.h>

}

class ffmpegMananger : public QObject
{
    Q_OBJECT
public:
    //构造
    explicit ffmpegMananger(QObject *parent = nullptr);
    //拆构
    ~ffmpegMananger();
    //取输入流地址
    void getRtspAddress(QString url);
    //取输出流地址
    void getOutputAddress(QString path);
    //ffmpeg拉流播放
    int ffmepgInput();
    //打开解码器
    void openEncoder(int width, int height, AVCodecContext** enc_ctx);
    //设置输出上下文
    void setOutputCtx(AVCodecContext *encCtx, AVFormatContext **pTsFmtCtx,int &nVideoIdx_out);
    //写文件尾
    void writeTail();
    //设置解码的pts
    void setDecoderPts(int idx,int count,AVFrame *pFrame);
    //设置编码的pts
    void setEncoderPts(int nVideo_indx,int frame_index,int videoindex_out,AVPacket *newpkt);
signals:
    //取一帧图像信号
    void Sig_GetOneFrame(QImage img);
private:
    //输入流地址
    QString m_strInputStreamUrl;
    //输出流地址
    QString m_strOutputStreamPath;
    //输入流动格式上下文
    AVFormatContext *m_pInFmtCtx;
    //输出流动格式上下文
    AVFormatContext *m_pTsFmtCtx;
    bool m_ifRec;

};

#endif // FFMPEGMANANGER_H

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

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

相关文章

【Rust】Rust学习 第十一章编写自动化测试

Rust 是一个相当注重正确性的编程语言&#xff0c;不过正确性是一个难以证明的复杂主题。Rust 的类型系统在此问题上下了很大的功夫&#xff0c;不过它不可能捕获所有种类的错误。为此&#xff0c;Rust 也在语言本身包含了编写软件测试的支持。 编写一个叫做 add_two 的将传递…

⑤ Axios网络请求

Axios安装 cnpm install --save axios post请求需要用到的&#xff1a; cnpm install --save querystring(用来转换格式的) 引入 一般是全局引入&#xff0c;在main.js中引入 全局引入后的get和post方式使用 get请求方式 post请求方式 先引入&#xff1a; axios封装…

Android之版本号、版本别名、API等级列表(全)(一百六十二)

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

前端性能优化——包体积压缩,打包速度提升,提升浏览器响应的速率

前端代码优化 –其他的优化可以具体在网上搜索 压缩项目打包后的体积大小、提升打包速度&#xff0c;是前端性能优化中非常重要的环节&#xff0c;结合工作中的实践总结&#xff0c;梳理出一些 常规且有效 的性能优化建议 ue 项目可以通过添加–report命令&#xff1a; "…

Java进阶-Oracle(二十)(1)

&#x1f33b;&#x1f33b; 目录 一、Oracle 数据库介绍1、Oracle 的概述2、Oracle 的结构2、Oracle的功能 二、安装与卸载1、卸载2、安装 三、使用&#xff08;需要关注得只有下面这两个&#xff09;四、PLSQL 的简单使用五、DBeaver 的简单使用 一、Oracle 数据库介绍 1、O…

ORB-SLAM2第一节---地图初始化

单目初始化 1.前提条件&#xff08;640*480&#xff09; 参与初始化的两帧各自的特征点数目都需要大于100.两帧特征点成功匹配的数目需要大于或等于100.两帧特征点三角化成功的三维点数目需要大于50. 2.针对条件三 流程如下 记录当前帧和参考帧&#xff08;第一帧&#xff…

MyBaits动态SQL

MyBaits动态SQL <include>用法 <where>用法 <if>用法 CONCATlike使用${...}使用#{...}使用CONCAT()函数连接参数形式 <choose><when><otherwise>例子 limit 字段变量&#xff0c;内部属性“refid”&#xff0c;后跟自定义的一段内容的名字…

什么是管程?

前言 在并发编程领域&#xff0c;最核心的两个理念就是同步和互斥&#xff0c;并发编程就是围绕这两个核心概念来完成的。 互斥&#xff1a;同一时刻只能有一个线程持有共享资源同步&#xff1a;多个线程之间协调、互作 在最初&#xff0c;人们利用信号量机制来实现互斥和同步…

多元最短路(Floyd)

是一个基于动态规划的全源最短路算法。它可以高效地求出图上任意两点之间的最短路 时间复杂度 O(n^3) 状态转移方程 f[i][j]min(f[i][j],f[i][k]f[k][j]) 核心代码 void floyd(){for(int k1;k<n;k)for(int i1;i<n;i)for(int j1;j<n;j)s[i][j]min(s[i][j],s[i][k…

使用巴特沃兹滤波器的1D零相位频率滤波研究(Matlab代码实现)

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

使用vscode在vue项目中重命名文件选择了更新导入路径仍有部分导入路径没有更新

背景: 将一个js文件重命名&#xff0c;vscode弹出是否更新导入路径&#xff0c;选择更新导入后&#xff0c;发现js文件中导入路径都自动更新&#xff0c;vue文件中路径都没有更新。 解决方案&#xff1a; 在设置中搜索updateimport&#xff0c;将最下面的Vue>Update Imports…

2023/08/13_____JMM JAVA Memory Model JAVA内存模型

JMM JAVA Memory Model java内存模型 作用&#xff1a;缓存一致性协议&#xff0c;用于定义数据读写的规则&#xff08;遵守&#xff0c;找到这个规则&#xff09; JMM定义了线程2工作内存和主内存之间的抽象关系&#xff1a;线程之间的共享变量存储在主内存&#xff08;main …

(贪心) 剑指 Offer 63. 股票的最大利润 ——【Leetcode每日一题】

❓剑指 Offer 63. 股票的最大利润 难度&#xff1a;中等 假设把某股票的价格按照时间先后顺序存储在数组中&#xff0c;请问买卖该股票一次可能获得的最大利润是多少&#xff1f; 示例 1: 输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天&#xff08;股票价格 1&#xff09;的…

在 Windows 中恢复数据的 5 种方法

发生数据丢失的原因有多种。无论是因为文件被意外删除、文件系统或操作系统损坏&#xff0c;还是由于软件或硬件级别的存储故障&#xff0c;数据都会在您最意想不到的时候丢失。今天我们重点介绍五种数据恢复方法&#xff0c;以应对意外情况的发生。 1.从另一台机器启动硬盘 如…

24近3年内蒙古大学自动化考研院校分析

今天给大家带来的是内蒙古大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、内蒙古大学 学校简介 内蒙古大学位于内蒙古自治区首府、历史文化名城呼和浩特市&#xff0c;距北京400余公里&#xff0c;是中华人民共和国成立后党和国家在民族地区创办的第一所综合大…

阿里云轻量应用服务器使用教程(从购买配置、连接到网站上线)

阿里云轻量应用服务器怎么使用&#xff1f;阿里云百科分享轻量应用服务器从购买、配置建站环境、轻量服务器应用服务器远程连接、开端口到网站上线全流程&#xff1a; 目录 阿里云轻量应用服务器使用教程 步骤一&#xff1a;购买一台轻量应用服务器 步骤二&#xff1a;重置…

深入理解 Vue 3 计算属性:优雅地处理响应式数据计算

计算属性的定义 在 Vue3 的 HTML 模板中是支持 JavaScript 表达式的&#xff0c;例如&#xff1a; <h2>买5个共计&#xff1a;{{ price * 5 }} 元</h2>但是如果当表达式过于复杂时&#xff0c;模板代码就会变得非常臃肿并且可读性就会变差&#xff0c;恰巧&#…

【设计模式——学习笔记】23种设计模式——解释器模式Interpreter(原理讲解+应用场景介绍+案例介绍+Java代码实现)

案例引入 通过解释器模式来实现四则运算&#xff0c;如计算ab-c的值&#xff0c;具体要求 先输入表达式的形式&#xff0c;比如abc-de&#xff0c;要求表达式的字母不能重复在分别输入a,b,c,d,e的值最后求出结果 传统方案 编写一个方法&#xff0c;接收表达式的形式&#xf…

vue中有趣的几个功能

vue中有趣的几个功能 老实说&#xff0c;我们大多数人都不太喜欢阅读文档&#xff0c;但是当使用像 Vue 这样不断发展的现代前端框架时&#xff0c;每个新版本都会发生很多变化&#xff0c;我们可能会错过一些后来推出的新的、闪亮的功能。让我们来看看那些有趣但不那么受欢迎…

【Windows 常用工具系列 5 -- Selenium IDE的使用方法 】

文章目录 Selenium 介绍Selenium IDE 介绍 Selenium IDE安装Chrome 浏览器安装Selenium IDE使用 Selenium 介绍 Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。 Selenium家庭成员有三个&#xff0c;分别是S…