qt下ffmpeg录制mp4经验分享,支持音视频(h264、h265,AAC,G711 aLaw, G711muLaw)

news2025/8/2 23:19:33

前言

    MP4,是最常见的国际通用格式,在常见的播放软件中都可以使用和播放,磁盘空间占地小,画质一般清晰,它本身是支持h264、AAC的编码格式,对于其他编码的话,需要进行额外处理。本文提供了ffmpeg录制mp4的封装代码,经测试视频上它支持h264、h265编码,音频支持了AAC、G711的aLaw、muLaw编码。对于以上编码的支持,部分是需要修改ffmpeg的源码,本文也有提供已编译好的ffmpeg以及说明源码上需要修改的地方。

一、时间戳处理

    在mp4录制中,有碰到一个问题,即在录制实时流后,用播放器进行播放,播放时间没有从0秒开始。windows自带的media play播放时,一开始都是静止的画面,从第n秒后,才开始正式播放,用VLC可以直接跳到n秒进行播放。这个问题的原因是时间戳没有处理好,需要记录下首帧,指定首帧时间戳为0,然后后续视频帧的时间戳等于当前帧的时间戳减去首帧时间戳。代码如下:
在这里插入图片描述

二、添加h264、h265、AAC解码头信息

    解码头信息是保存在解码器上下文(AVCodecContext)的extradata中,这些信息包含h264的SPS、PPS头信息,AAC的adts头信息,h265的VPS、SPS、PPS,我们需要使用比特流过滤器(AVBitStreamFilter)来为每一种格式添加相应的头部信息,这样才能在解码器中正常进行解码。以下为添加解码头信息的相关代码:
    初始化时视频:
在这里插入图片描述
    循环读帧中,视频:
在这里插入图片描述
    初始化时音频:
在这里插入图片描述
    循环读帧中,音频:
在这里插入图片描述

三、ffmpeg支持g711 aLaw muLaw

在ffmpeg源码movenc.c文件中,找到mov_write_audio_tag函数,修改以下:
在这里插入图片描述
和在该文件中增加以下:
在这里插入图片描述
muLaw修改类似,它的MKTAG为 ‘u’,‘l’, ‘a’,‘w’。

四、代码分享

mp4recorder.h

#ifndef MP4RECORDER_H
#define MP4RECORDER_H

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavfilter/avfilter.h"
    #include "libswscale/swscale.h"
    #include "libavutil/frame.h"
    #include "libavutil/imgutils.h"
    #include "libavcodec/bsf.h"
}

#include <QObject>
#include <QMutex>

class mp4Recorder : public QObject
{
    Q_OBJECT
public:
    explicit mp4Recorder(QObject *parent = nullptr);
    virtual ~mp4Recorder();

    bool Init(AVFormatContext *pIfmtCtx, int nCodecType, int nAudioCodecType, QString& sFile);
    bool DeInit();
    bool isInit() {return m_bInit;}
    bool saveOneFrame(AVPacket& pkt, int nCodecType, int nAudioCodecType);

private:
    uint64_t         m_nCounts;
    bool             m_bFirstGoP;
    bool             m_bInit;
    QString          m_sRecordFile;
    AVFormatContext *m_pIfmtCtx;
    AVFormatContext *m_pOfmtCtx; // output stream format. copy from instream format.
    const AVOutputFormat  *m_pOfmt; // save file format.
    QMutex           m_lock;
    int64_t          m_nVideoTimeStamp;
    int              m_nVideoDuration;
    int              m_nVideoIndex = -1;
    int              m_nAudioIndex = -1;
    int				 m_nSpsPpsSize = 0;
    AVBSFContext    *m_pBsfc = nullptr;
    AVBSFContext    *m_pBsfcAAC = nullptr;
    AVPacket        *m_pktFilter = nullptr;
    AVPacket        *m_pktFilterAudio = nullptr;
    int64_t         m_nFirstVideoPts = 0;
    int64_t         m_nFirstAudioPts = 0;
    bool            m_bTransCode = false;

    // stream map.
    int  *m_pStreamMapping;
    int   m_nMappingSize;

};

#endif // MP4RECORDER_H

mp4recorder.cpp

#include "mp4recorder.h"
#include "commondef.h"
#include "cteasyaacencoder.h"

#define TRANSCODE 0

mp4Recorder::mp4Recorder(QObject *parent) : QObject(parent)
{
    QMutexLocker guard(&m_lock);
    m_sRecordFile.clear();
    m_pIfmtCtx = nullptr;
    m_pOfmtCtx = nullptr;
    m_pOfmt = nullptr;
    m_pStreamMapping = nullptr;
    m_nMappingSize = 0;
    m_nCounts = 0;
    m_bFirstGoP = false;
    m_bInit = false;
}

mp4Recorder::~mp4Recorder()
{
    DeInit();
}

bool mp4Recorder::Init(AVFormatContext *pIfmtCtx, int nCodecType, int nAudioCodecType, QString &sFile)
{
    QMutexLocker guard(&m_lock);
    if(!pIfmtCtx || sFile.isEmpty())
    {
        MY_DEBUG << "sFile.isEmpty().";
        return false;
    }

    m_sRecordFile = sFile;
    m_pIfmtCtx = pIfmtCtx;

    QByteArray ba = m_sRecordFile.toLatin1();
    const char* pOutFile = ba.data();

    qDebug() << "pOutFile:" << pOutFile;

    unsigned i = 0;
    int ret = 0;
    int stream_index = 0;

    // 1. create output context
    avformat_alloc_output_context2(&m_pOfmtCtx, nullptr, nullptr, pOutFile);
    if (!m_pOfmtCtx)
    {
        MY_DEBUG << "Could not create output context.";
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    // 2. get memory.
    m_nMappingSize = pIfmtCtx->nb_streams;
    m_pStreamMapping = (int*)av_mallocz_array(m_nMappingSize, sizeof(*m_pStreamMapping));
    if (!m_pStreamMapping)
    {
        MY_DEBUG << "av_mallocz_array fail.";
        ret = AVERROR(ENOMEM);
        goto end;
    }

    // 3. copy steam information.
    m_pOfmt = m_pOfmtCtx->oformat;
    for (i = 0; i < pIfmtCtx->nb_streams; i++)
    {
        AVStream *pOutStream;
        AVStream *pInStream = pIfmtCtx->streams[i];
        AVCodecParameters *pInCodecpar = pInStream->codecpar;
        if (pInCodecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            pInCodecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            pInCodecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
        {
            m_pStreamMapping[i] = -1;
            continue;
        }

        if(pInCodecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            m_nVideoIndex = i;

            //1.找到相应解码器的过滤器
            if(nCodecType == AV_CODEC_ID_HEVC)
            {
                const AVBitStreamFilter *bsf = av_bsf_get_by_name("hevc_mp4toannexb");
                if (!bsf)
                {
                    MY_DEBUG << "av_bsf_get_by_name() video failed";
                    return false;
                }
                //2.过滤器分配内存
                av_bsf_alloc(bsf, &m_pBsfc);
            }
            else
            {
                const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb");
                if (!bsf)
                {
                    MY_DEBUG << "av_bsf_get_by_name() video failed";
                    return false;
                }
                //2.过滤器分配内存
                av_bsf_alloc(bsf, &m_pBsfc);
            }

            //3.添加解码器属性
            avcodec_parameters_copy(m_pBsfc->par_in, pInCodecpar);

            //4. 初始化过滤器上下文
            av_bsf_init(m_pBsfc);
        }
        else if(pInCodecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            m_nAudioIndex = i;

#if TRANSCODE
            if(nAudioCodecType == AV_CODEC_ID_PCM_ALAW || nAudioCodecType == AV_CODEC_ID_PCM_MULAW)
            {
                MY_DEBUG << "ctEasyAACEncoder Init";
                if(nAudioCodecType == AV_CODEC_ID_PCM_ALAW)
                    ctEasyAACEncoder::getInstance().Init(Law_ALaw);
                else
                    ctEasyAACEncoder::getInstance().Init(Law_ULaw);
                m_bTransCode = true;
            }
            else
                m_bTransCode = false;

#endif
            if(m_bTransCode || nAudioCodecType == AV_CODEC_ID_AAC)
            {
                //1. 找到相应解码器的过滤器
                const AVBitStreamFilter *bsf = av_bsf_get_by_name("aac_adtstoasc");
                if (!bsf)
                {
                    MY_DEBUG << "av_bsf_get_by_name() audio failed";
                    return false;
                }

                //2.过滤器分配内存
                av_bsf_alloc(bsf, &m_pBsfcAAC);

                //3.添加解码器属性
                avcodec_parameters_copy(m_pBsfcAAC->par_in, pInCodecpar);

                //4. 初始化过滤器上下文
                av_bsf_init(m_pBsfcAAC);
            }

#if TRANSCODE
            if(m_bTransCode)
                m_pBsfcAAC->par_in->codec_id = AV_CODEC_ID_AAC;
#endif

        }

        // fill the stream index.
        m_pStreamMapping[i] = stream_index++;

        // copy the new codec prameters.
        pOutStream = avformat_new_stream(m_pOfmtCtx, nullptr);
        if (!pOutStream)
        {
            MY_DEBUG << "Failed allocating output stream";
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        ret = avcodec_parameters_copy(pOutStream->codecpar, pInCodecpar);
        if (ret < 0)
        {
            MY_DEBUG << "Failed to copy codec parameters";
            goto end;
        }
#if TRANSCODE
        if(m_bTransCode && pInCodecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            pOutStream->codecpar->codec_id = AV_CODEC_ID_AAC;
#endif

        //pOutStream->codecpar->bit_rate = 2000000;
        //pOutStream->codecpar->codec_tag = 0;
    }

    // 4. create MP4 header.
    if (!(m_pOfmt->flags & AVFMT_NOFILE)) // network stream
    {
        ret = avio_open(&m_pOfmtCtx->pb, pOutFile, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            MY_DEBUG << "Could not open output file " << m_sRecordFile;
            goto end;
        }
    }
    // 5. write file header.
    ret = avformat_write_header(m_pOfmtCtx, nullptr);
    if (ret < 0)
    {
        MY_DEBUG << "Error occurred when opening output file ret:" << ret;
        goto end;
    }

    m_pktFilter = new AVPacket;
    av_init_packet(m_pktFilter);
    m_pktFilter->data = NULL;
    m_pktFilter->size = 0;

    m_pktFilterAudio = new AVPacket;
    av_init_packet(m_pktFilterAudio);
    m_pktFilterAudio->data = NULL;
    m_pktFilterAudio->size = 0;

    m_nFirstVideoPts = 0;
    m_nFirstAudioPts = 0;


    m_bFirstGoP = false;
    m_bInit = true;
    m_nCounts = 0;

    return true;

end:
    DeInit();
    if (ret < 0 && ret != AVERROR_EOF)
    {
        MY_DEBUG << "Error occurred.";
    }
    return false;
}

bool mp4Recorder::DeInit()
{
    // 1. save tail.
    if(m_bInit && m_pOfmtCtx)
    {
        av_write_trailer(m_pOfmtCtx);
    }
    m_bInit = false;

    // 2. close output
    if (m_pOfmtCtx && !(m_pOfmt->flags & AVFMT_NOFILE))
    {
        avio_closep(&m_pOfmtCtx->pb);
    }

    // 3. free contex.
    if(m_pOfmtCtx)
    {
        avformat_free_context(m_pOfmtCtx);
        m_pOfmtCtx = nullptr;
    }
    av_freep(&m_pStreamMapping);

    if(m_pBsfc)
    {
        av_bsf_free(&m_pBsfc);
        m_pBsfc = nullptr;
    }

    if(m_pBsfcAAC)
    {
        av_bsf_free(&m_pBsfcAAC);
        m_pBsfcAAC = nullptr;
    }

#if TRANSCODE
    if(m_bTransCode)
    {
        ctEasyAACEncoder::getInstance().DeInit();
        m_bTransCode = false;
    }
#endif

    return true;
}

bool mp4Recorder::saveOneFrame(AVPacket &pkt, int nCodecType, int nAudioCodecType)
{
    int ret = 0;
    if(!m_bInit)
    {
        return false;
    }
    AVStream *pInStream, *pOutStream;

    if(nCodecType == AV_CODEC_ID_H264)
    {
        if(m_bFirstGoP == false)
        {
            if(pkt.flags != AV_PKT_FLAG_KEY)
            {
                av_packet_unref(&pkt);
                return false; // first frame must be Iframe.
            }
            else
            {
                m_bFirstGoP = true;
            }
        }
    }

    pInStream  = m_pIfmtCtx->streams[pkt.stream_index];
    if (pkt.stream_index >= m_nMappingSize ||
        m_pStreamMapping[pkt.stream_index] < 0)
    {
        av_packet_unref(&pkt);
        return true;
    }

    pkt.stream_index = m_pStreamMapping[pkt.stream_index];
    pOutStream = m_pOfmtCtx->streams[pkt.stream_index];

    if(pInStream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            pInStream->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
    {
        av_packet_unref(&pkt);
        return false;
    }

    if(pInStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        av_bsf_send_packet(m_pBsfc, &pkt);
        av_bsf_receive_packet(m_pBsfc, m_pktFilter);

        m_pktFilter->pts = av_rescale_q_rnd(m_pktFilter->pts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
        m_pktFilter->dts = av_rescale_q_rnd(m_pktFilter->dts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
        m_pktFilter->duration = av_rescale_q_rnd(m_pktFilter->duration, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
        m_pktFilter->stream_index = pOutStream->index;


        //时间戳处理
        if(m_nFirstVideoPts == 0)
        {
            m_nFirstVideoPts = m_pktFilter->pts;
            m_pktFilter->pts = 0;
            m_pktFilter->dts = 0;
        }
        else
        {
            m_pktFilter->pts = m_pktFilter->pts - m_nFirstVideoPts;
            m_pktFilter->dts = m_pktFilter->dts - m_nFirstVideoPts;
        }

        //av_packet_rescale_ts(&pkt, pInStream->time_base, pOutStream->time_base);
        m_pktFilter->pos = -1;
        m_pktFilter->flags |= AV_PKT_FLAG_KEY;

        ret = av_interleaved_write_frame(m_pOfmtCtx, m_pktFilter);
        av_packet_unref(&pkt);
        if (ret < 0)
        {
            qDebug() << "Video Error muxing packet";
        }
    }
    else
    {
#if TRANSCODE

        if(m_bTransCode)
        {
            AVPacket* pAACPkt = av_packet_clone(&pkt);
            if(ctEasyAACEncoder::getInstance().G711ToAAC(pkt.data, pkt.size, pAACPkt->data, pAACPkt->size) == false)
            {
                av_packet_unref(&pkt);
                return false;
            }

            av_bsf_send_packet(m_pBsfcAAC, pAACPkt);
            av_bsf_receive_packet(m_pBsfcAAC, m_pktFilterAudio);
        }
        else
#endif
        if(m_bTransCode || nAudioCodecType == AV_CODEC_ID_AAC)
        {
            av_bsf_send_packet(m_pBsfcAAC, &pkt);
            av_bsf_receive_packet(m_pBsfcAAC, m_pktFilterAudio);

            m_pktFilterAudio->pts = av_rescale_q_rnd(m_pktFilterAudio->pts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            m_pktFilterAudio->dts = av_rescale_q_rnd(m_pktFilterAudio->dts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            m_pktFilterAudio->duration = av_rescale_q_rnd(m_pktFilterAudio->duration, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            m_pktFilterAudio->stream_index = pOutStream->index;

            //用差值作时间戳
            if(m_nFirstAudioPts == 0)
            {
                m_nFirstAudioPts = m_pktFilterAudio->pts;
                m_pktFilterAudio->pts = 0;
                m_pktFilterAudio->dts = 0;
            }
            else
            {
                m_pktFilterAudio->pts = m_pktFilterAudio->pts - m_nFirstAudioPts;
                m_pktFilterAudio->dts = m_pktFilterAudio->dts - m_nFirstAudioPts;
            }

            m_pktFilterAudio->pos = -1;
            m_pktFilterAudio->flags |= AV_PKT_FLAG_KEY;

            ret = av_interleaved_write_frame(m_pOfmtCtx, m_pktFilterAudio);
        }
        else
        {
            pkt.pts = av_rescale_q_rnd(pkt.pts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            pkt.dts = av_rescale_q_rnd(pkt.dts, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            pkt.duration = av_rescale_q_rnd(pkt.duration, pInStream->time_base, pOutStream->time_base, AV_ROUND_NEAR_INF);
            pkt.stream_index = pOutStream->index;

            //用差值作时间戳
            if(m_nFirstAudioPts == 0)
            {
                m_nFirstAudioPts = pkt.pts;
                pkt.pts = 0;
                pkt.dts = 0;
            }
            else
            {
                pkt.pts = pkt.pts - m_nFirstAudioPts;
                pkt.dts = pkt.dts - m_nFirstAudioPts;
            }

            pkt.pos = -1;
            pkt.flags |= AV_PKT_FLAG_KEY;

            ret = av_interleaved_write_frame(m_pOfmtCtx, &pkt);
        }
        av_packet_unref(&pkt);
        if (ret < 0)
        {
            qDebug() << "Audio Error muxing packet";
        }
    }
    return (ret == 0);
}

四、ffmpeg库下载

链接地址:https://download.csdn.net/download/linyibin_123/87542123

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

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

相关文章

2023最全电商API接口 高并发请求 实时数据 支持定制 电商数据 买家卖家数据

电商日常运营很容易理解&#xff0c;就是店铺商品维护&#xff0c;上下架&#xff0c;评价维护&#xff0c;库存数量&#xff0c;协助美工完成制作详情页。店铺DSR&#xff0c;好评率&#xff0c;提升客服服务等等&#xff0c;这些基础而且每天都必须做循环做的工作。借助电商A…

Webpack前端资源加载/打包工具

文章目录一、Webpack1、什么是Webpack2、Webpack安装2.1全局安装2.2安装后查看版本号3、创建项目3.1初始化项目3.2创建src文件夹3.3 src下创建common.js3.4 src下创建utils.js3.5 src下创建main.js4、JS打包4.1创建配置文件4.2执行编译命令4.3创建入口页面4.4测试5、CSS打包5.1…

Spring框架核心功能手写实现

文章目录概要Spring启动以及扫描流程实现基础环境搭建扫描逻辑实现bean创建的简单实现依赖注入实现BeanNameAware回调实现初始化机制模拟实现BeanPostProcessor模拟实现AOP模拟实现概要 手写Spring启动以及扫描流程手写getBean流程手写Bean生命周期流程手写依赖注入流程手写Be…

【项目实战】Nginx配置Https证书

一、背景说明 Nginx配置https证书是常规操作&#xff0c;Nginx支持crtkey或者pem证书格式 二、具体步骤 2.1 上传证书文件 进入Nginx配置文件夹的目录&#xff0c;将这两个证书文件上传至服务器的某个路径中 &#xff08;记住这个路径&#xff09; 2.2 新建配置 然后&am…

Linux 安装npm yarn pnpm 命令

下载安装包 node 下载地址解压压缩包 tar -Jxf node-v19.7.0-linux-x64.tar.xz -C /root/app echo "export PATH$PATH:/app/node-v16.9.0-linux-x64" >> /etc/profile source /etc/profile ln -sf /app/node-v16.9.0-linux-x64/bin/npm /usr/local/bin/ ln -…

SBOM应该是软件供应链中的安全主食

当谈到软件材料清单(SBOM)时&#xff0c;通常的类比是食品包装上的成分列表&#xff0c;它让消费者知道他们将要吃的薯片中有什么。 美国机构有90天时间创建所有软件的清单 同样&#xff0c;SBOM是一个软件中组件的清单&#xff0c;在应用程序是来自多个来源的代码的集合的时…

覆盖5大主流开发平台的报表控件,它值得你一看

为什么大家现在都在使用第三方报表工具呢&#xff1f; 第三方报表工具是数据库存储&#xff0c;数据库程序通常可以存放的数据量是相当大的&#xff0c;可以处理非常复杂的数据结构关系&#xff0c;报表数据交互速度也非常快。不仅能够提高开发效率&#xff0c;还能实现灵活美…

Qt中的QTcpSocket、QWebSocket和QLocalSocket

同时实现了QTcpSocket、QWebSocket和QLocalSocket的简单通讯deamon&#xff0c;支持自动获取本机ip&#xff0c;多个客户端交互。在这个基础上你可以自己加错误检测、心跳发送、包封装解析和客户端自动重连等功能。 获取本机电脑ip&#xff1a; QString Widget::getIp() {QSt…

simulink stateflow 状态机

系列文章目录 文章目录系列文章目录前言一、基操二、stateflow 数据三、chart动作四、chart的执行五、flow chart / junction六、状态机中的函数 Stateflow Functions七、chart层次结构八、案例——吸尘器机器人的驱动模式前言 一、基操 在tooltrip中选择DEBUG&#xff0c;通过…

Tomcat+IDEA+Servlet能显示页面但提交form表单出现404问题

问题&#xff1a; 当我们使用tomcat启动&#xff0c;然后输入对应的url路径时候&#xff0c;能出现该html的页面&#xff0c;但提交表单后&#xff0c;却出现了404的问题&#xff0c;这时候我就很疑惑了....然后开始慢慢分析。 思路&#xff1a; 首先我们得知道404状态码是什…

ISFP型人格的优势和劣势分析(mbti性格测试)

isfp型人格的优势分析ISFP在艺术上具有令人惊叹的天分&#xff0c;他们充沛且敏锐的情感能够轻易捕捉到那些细腻的情感变化。他们具有强大的表现力和感染力&#xff0c;能够通过自己的作品&#xff0c;将情感描绘出来并令观众感同身受&#xff0c;这使得他们在艺术和人文领域能…

为什么会出现植物神经紊乱 总是检查不出来该怎么办

植物神经紊乱是一种很多人都害怕的疾病&#xff0c;你们知道是为什么吗&#xff1f; 植物神经紊乱是一种神经系统失调导致的多种症状的总称&#xff0c;这种疾病是由于社会因素所诱发的脏器功能的失调&#xff0c;是一种非常复杂的疾病。而这种疾病是可能会发生在任何年龄阶段的…

vulnhub靶场实战系列(一)之vulnhub靶场介绍

vulnhub靶场介绍 Vulnhub是一个提供各种漏洞环境的靶场平台&#xff0c;供安全爱好者学习渗透使用&#xff0c;大部分环境是做好的虚拟机镜像文件&#xff0c;镜像预先设计了多种漏洞&#xff0c;需要使用VMware或者VirtualBox运行。每个镜像会有破解的目标&#xff0c;大多是…

关于 interface{} 会有啥注意事项?下

我们一起来回顾一下上一次说到的 interface{} 可以用来做多态 接口类型分为空接口类型和非空接口类型&#xff0c;他们的底层数据结构不太一样 这里顺便说一下&#xff0c;用来作态需要满足这样的条件&#xff1a; 首先得有父类指针指向子类的对象这个接口还必须是非空接口…

【软件逆向】软件破解?病毒木马?游戏外挂?

文章目录课前闲聊认识CTF什么是CTFCTF解题模式什么是逆向定义应用领域CTF中的逆向现状推荐书籍学习要点逆向工程学习基础常规逆向流程阶段一:信息收集阶段二:过保护后静态调试阶段三:结合动态调试阶段四:写解题脚本逆向例题概览1-控制台程序解题过程2-Crackme3-游戏4-移动安全C…

每天学一点之多线程

多线程 一、相关概念 并发与并行 并行&#xff08;parallel&#xff09;&#xff1a;指多个事件任务在同一时刻发生&#xff08;同时发生&#xff09;。 并发&#xff08;concurrency&#xff09;&#xff1a;指两个或多个事件在同一个微小的时间段内发生。程序并发执行可以…

steam/csgo搬砖项目到底真的假的?

搬砖是从国外steam市场置办游戏装备回来&#xff0c;在国内网易buff售卖&#xff0c;低买高卖&#xff0c;产生利润的一个项目。 但我真正上手后&#xff0c;才知道steam是面向全球的游戏平台&#xff0c;用户真的大的夸张&#xff01;&#xff01;市场非常巨大&#xff0c;一…

物联网毕设 -- 智能厨房监测系统(改)

前言 在家庭生活中&#xff0c;厨房是必不可少的&#xff0c;所以厨房的安全问题关乎着我们大家的生命&#xff0c;所以提出智能厨房监测系统&#xff0c;目的就是为我们减少不必要的安全问题 ⚠️⚠️&#xff08;本文章仅提供思路和实现方法&#xff0c;并不包含代码&#x…

雷电模拟器安卓7以上+Charles抓包APP最新教程

一、工具准备&#xff1a; 证书安装工具全局代理工具下载&#xff1a; https://download.csdn.net/download/weixin_51111267/87536481 二、Charles设置 &#xff08;一&#xff09;电脑上证书安装 &#xff08;二&#xff09;安卓模拟器上系统证书安装&#xff08;RooT权限打…

wireshark 抓包使用记录

文章目录前言wireshark 抓包使用记录一、wireshark的基础使用二、wireshark的常用功能1、开始混杂模式2、过滤器操作2.1、抓包过滤器2.2、显示过滤器3、时间格式显示4、统计流量图5、标记显示6、导出数据包7、增加、隐藏、删除显示列前言 如果您觉得有用的话&#xff0c;记得给…