C++音视频开发面试题集锦

news2024/11/17 14:28:29

老规矩,先上面试题目:

  • 1、iOS 中系统 API 提供了哪些视频编码的方式?
  • 2、VideoToolbox 视频帧解码失败以后应该如何重试?
  • 3、如何使用 PSNR 对视频转码质量进行评估?
  • 4、什么是 VAO,什么是 VBO,它们的作用是什么?
  • 5、介绍一下 FFmpeg 中关于 timebase 的基础知识与应用?
  • 6、如何识别一个视频是 HDR 视频?
  • 7、如何通过优化播放器来优化音乐播放体验,比如提升音质或音效?
  • 8、介绍一下 SIMD 以及它在音视频处理中的应用?
  • 9、AVPlayer 中如何实现视频片段加速预览播放?
  • 10、如何高效获取一个视频的关键帧序列?
  • 11、SPS 和 PPS 在 extradata 中的作用是什么?
  • 12、I 帧和 IDR 帧有什么区别?在什么情况下 I 帧不是 IDR 帧?

1、iOS 中系统 API 提供了哪些视频编码的方式?

在 iOS 中,实现视频编码的方式主要包括以下两种:

  • AVFoundation 框架:AVFoundation 是苹果提供的一个用于处理音视频数据的框架,它提供了一系列用于捕获、处理和输出音视频数据的类和方法。通过 AVFoundation 框架,可以使用 AVAssetWriter 和 AVAssetWriterInput 类来实现编码视频。
  • VideoToolbox 框架:VideoToolbox 是苹果提供的一个专门用于处理视频数据的框架,它提供了硬件加速的视频编码和解码功能。使用 VideoToolbox,可以利用 iOS 设备上的硬件编码器来实现高效的视频编码。

相比而言,AVFoundation 框架则提供了更加上层的接口,更简单易用,但因此对于一些特殊需求和高级功能,可能无法满足。VideoToolbox 则提供了更直接的对硬件编码器的访问,允许开发者能更细致的控制编码器的配置和参数,并且可以直接操作编码器的输入和输出数据,灵活性更好。

2、Videotoolbox 视频帧解码失败以后应该如何重试?

  • 1、重新初始化解码器:尝试重新初始化 Videotoolbox 解码器,有时候重新初始化可以解决解码过程中的一些临时问题。
  • 2、检查视频文件:确保视频文件没有损坏或者格式不正确。有时候解码失败是因为视频文件本身的问题,可以尝试使用其他工具或者重新获取视频文件。
  • 3、检查当前内存:在解码过程中如果 CMSampleBuffer 不及时释放,可能会导致内存过高导致解码器报 -11800 通用错误。
  • 4、尝试重新解码当前帧:将当前帧以及当前 gop 内前序帧都重新输入给解码器。

3、如何使用 PSNR 对视频转码质量进行评估?

  • 1、计算图像差异:获得原始视频帧和转码后的未经过任何图像效果处理的视频帧使用同一解码器解码,并将它们的每一帧转换成相同的格式(比如 YUV 格式)。
  • 2、计算 PSNR 值:使用以下公式计算每一帧的 PSNR 值。
  • 3、计算平均 PSNR:将所有帧的 PSNR 值求平均,得到视频的平均 PSNR 值。
  • 4、分析结果:根据平均 PSNR 值来评估转码后视频的质量。较高的 PSNR 值表示转码后的视频质量与原始视频相似度较高,而较低的 PSNR 值则表示质量损失较大。

举例来说两个宽高为 m×n 视频帧 I 和 K, I 为转码前视频帧,K 为转码后的视频帧,那么它们的均方误差(MSE)定义为:

他们的 PSNR 计算公式如下:

其中,MAXI 是表示图像点颜色的最大数值,如果每个采样点用 8 位表示,那么就是 255。

4、什么是 VAO,什么是 VBO,它们的作用是什么?

1、Vertex Buffer Object (VBO)

  • VBO 主要用于存储顶点数据,如顶点坐标、法线、颜色等。
  • 通过将顶点数据存储在 GPU 的显存中,可以提高渲染效率,因为 GPU 能够更快地访问这些数据,而无需反复从 CPU 内存中读取。

2、Vertex Array Object (VAO)

  • VAO(Vertex Array Object)顶点数组对象,主要作用是用于管理 VBO 或 EBO。
  • VBO 保存了一个模型的顶点属性信息,每次绘制模型之前需要绑定顶点的所有信息,当数据量很大时,重复这样的动作变得非常麻烦。VAO 可以把这些所有的配置都存储在一个对象中,每次绘制模型时,只需要绑定这个 VAO 对象就可以了,可以减少 glBindBuffer 、glEnableVertexAttribArray、 glVertexAttribPointer 这些调用操作,高效地实现在顶点数组配置之间切换。

5、介绍一下 FFmpeg 中关于 timebase 的基础知识与应用?

1)timebase 定义

在 FFmpeg 中,time_base 是一个关键概念,它用于表示时间单位。在处理音频或视频流时,time_base 可以根据不同的采样频率或帧率来定义。timebase 在 FFmpeg 的定义是一个 AVRational 结构体:

typedef struct AVRational{
    int num; ///< numerator  
    int den; ///< denominator  
} AVRational;

2)timebase 的使用

在某些情况下,time_base 是根据采样频率来定义的。例如:对于视频采样频率为 90KHz(90000Hz)的情况,time_base 就相当于 1/90000 秒。另一种定义 time_base 的方式是根据帧率。例如:对于视频帧率为 24fps 的情况,time_base 就相当于 1/24 秒。在 FFmpeg 的分层结构中,原始数据层、编解码层和封装层都有对应的 time_base。原始数据层和封装层都通过 AVStream 进行处理,而编解码层则对应 AVCodec。

3)封装层 timebase,视频流/音频流 timebase 和现实时间戳的的关系和转换

封装层 tbn、视频 tbc 和音频 tbc 可以各不相同,相互不影响。现实时间基我们一般选用 1us 即 (1/1000000)s。因为每一层用的时间基不同,在函数参数传递上只会使用时间基前面的倍数值,timebase 是统一的,因此时间在不同的时间基上面需要做一层转换。 例如:现实时间 1s 转换到音频流时间实现为 1000000 * (1/1000000) = 44100 * (1/44100),那么现实时间 1000000 在音频流时间值则为 44100。举一个开发中的实例:如果想 seek 视频到现实时间的 X ms。

int64_t seekTime = (int64_t)(( X / 1000 )  / av_q2d(videoStream->time_base)); 
av_seek_frame(videoFormatCtx_, video_index_, seekTime, AVSEEK_FLAG_BACKWARD);

因为 av_seek_frame 是在视频流层面,时间基与现实时间不同,需要转换并将转换后的值作为参数才能得到正确的结果。

4)转换函数解析

double av_q2d(AVRational a) //将AVRational 对象转换为小数,便于转换
// 将一个时间戳a从时基bq转换到时基cq下
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

例如,将视频流的一帧 pts(a * atbr) 转换到封装层打包成 AVPacket,封装层 timebase 为 tbn,此时需要转换 int64 t = av_rescale_q_rnd(a, atbr, tbn);。

6、如何识别一个视频是 HDR 视频?

iOS 判断一个视频是否是 HDR 视频的方法:判断是否带有 HDR 特征的 track 即可,如下:

NSArray<AVAssetTrack *> *hdrTracks = 
[asset tracksWithMediaCharacteristic:AVMediaCharacteristicContainsHDRVideo];
if (hdrTracks.count > 0){
   return YES;
}

Android 需要我们自己解析出 colortransforfunction和ccolorStandard,如下:

@RequiresApi(api = Build.VERSION_CODES.R)
public static boolean isHDR(MediaMetadataRetriever mediaMetadataRetriever)
        throws NumberFormatException {
    String colorTransferString =
            mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COLOR_TRANSFER);
    Log.e("isHDR", colorTransferString);
    String colorStandardString =
            mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COLOR_STANDARD);
    Log.e("isHDR", colorStandardString);
    int colorTransfer = Integer.parseInt(colorTransferString);
    int colorStandard = Integer.parseInt(colorStandardString);
    // This check needs to match the isHDR check in
    // frameworks/av/media/libstagefright/FrameDecoder.cpp.
    return (colorTransfer == MediaFormat.COLOR_TRANSFER_HLG
            || colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084)
            && colorStandard == MediaFormat.COLOR_STANDARD_BT2020;
}

7、如何通过优化播放器来优化音乐播放体验,比如提升音质或音效?

在播放侧可以使用自动增益控制算法(AGC)来提升音效。AGC 算法通过自动调整音频信号的增益,使其保持在一定的范围内,这种算法可以避免因音频信号的幅度变化而引起的声音过大或过小的问题,保证了音频信号的稳定性和可听性,目前有开源的实现例如 webrtcagc,可以把算法移植到自己的项目中。

8、介绍一下 SIMD 以及它在音视频处理中的应用?

SIMD(Single Instruction Multiple Data)是一种并行计算的技术,它允许在单个指令中同时处理多个数据元素。SIMD 指令集通常由处理器提供,用于加速向量化计算,从而提高程序的性能。

下面是一个 SIMD 的示例:向量化乘法

假设有两个数组 A 和 B,我们想要将它们的对应元素相乘,并将结果存储在另一个数组 C 中,使用 SIMD 指令,可以一次处理多个元素,提高计算效率。

// 使用 SIMD 指令进行向量化乘法
#include <immintrin.h>

void vectorMultiply(float* A, float* B, float* C, int size) {
    for (int i = 0; i < size; i += 8) {
        __m256 a = _mm256_load_ps(A + i); // 加载 8 个单精度浮点数到向量寄存器 A
        __m256 b = _mm256_load_ps(B + i); // 加载 8 个单精度浮点数到向量寄存器 B
        __m256 result = _mm256_mul_ps(a, b); // 执行向量乘法
        _mm256_store_ps(C + i, result); // 存储结果到数组 C
    }
}

在实际应用中,还可以使用 SIMD 指令进行其他操作,如减法、除法、逻辑运算等,以及应用于不同的数据类型,如整数、双精度浮点数等。通过合理地使用 SIMD 优化,可以显著提高程序的性能。

在音视频开发中,SIMD 也有不少的应用场景。比如:

1)在音频处理中,SIMD 可以用于实时音频效果处理,如均衡器、压缩器、混响器等,通过同时处理多个音频样本,可以提高音频处理的效率和实时性。

2)在视频处理中,SIMD 可以用于加速图像处理算法,如图像滤波、边缘检测、图像压缩等,通过同时处理多个像素,可以提高图像处理的速度和质量。

3)在视频编码中,SIMD 可以用于加速压缩和解压算法,如 H.264、H.265 编码器一些实现中,可以通过并行处理视频数据来提高视频编解码的效率和性能。

总之,SIMD 在音视频开发中的合理应用可以提高数据处理速度,降低功耗。

9、AVPlayer 中如何实现视频片段加速预览播放?

在编辑场景用 AVPlayer 来实现预览播放器时,对视频中某一段内容进行加速播放的实现代码如下:

// 创建 AVMutableComposition 对象
AVMutableComposition *composition = [AVMutableComposition composition];
// 将视频文件加载到 AVURLAsset 对象中
NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"your_video" withExtension:@"mp4"];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
// 将视频的前 3 秒进行加速处理
CMTime startTime = kCMTimeZero;
CMTime duration = CMTimeMake(3, 1); // 加速的时间范围为前 3 秒
CMTimeRange timeRange = CMTimeRangeMake(startTime, duration);
[composition scaleTimeRange:timeRange toDuration:CMTimeMake(1, 1)]; 
// 将时间范围加速到 1 秒
// 创建 AVPlayerItem 对象并将组合后的视频添加到其中
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
// 创建 AVPlayer 对象并将 AVPlayerItem 对象添加到其中
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];

10、如何高效获取一个视频的关键帧序列?

获取一个视频的关键帧序列,基于 Android 平台 API 实现:

MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(getVideoPath());

int trackIndex = MediaExtractorUtil.selectVideoTrack(extractor);
extractor.selectTrack(trackIndex);

List<Long> keyframeTimestampsMS = new ArrayList<Long>();

while (extractor.getSampleTime() != -1) {
    long sampleTime = extractor.getSampleTime();

    if ((extractor.getSampleFlags() & MediaExtractor.SAMPLE_FLAG_SYNC) > 0) {
        keyframeTimestampsMS.add(sampleTime / 1000);
    }
    // 此处表示 extractor seek 的间隔为 1000 微妙
    extractor.seekTo(sampleTime + 1000, MediaExtractor.SEEK_TO_NEXT_SYNC);
}

获取一个视频的关键帧序列,基于 FFmpeg 实现:

#include "libavcodec/avcodec.h"  
#include "libavutil/ff_time.h"  
#include "libavformat/avformat.h"  
#include "libavformat/avc.h"

AVFormatContext* formatCtx = avformat_alloc_context();  
avformat_open_input(&formatCtx, path.c_str(), NULL, NULL);  
int videoIndex = -1;  
  
for (int i = 0; i < formatCtx->nb_streams; i++) {  
    AVStream* stream = formatCtx->streams[i];  
    AVMediaType type = stream->codecpar->codec_type;  
    if (type == AVMEDIA_TYPE_VIDEO) {  
        videoIndex = i;  
        break;  
    }
}  
if (videoIndex > 0) {
    AVStream* videoStream = formatCtx->streams[videoIndex];  
    AVInputFormat* iformat = formatCtx->iformat;  
    if (strcmp(iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0) {  
        std::vector<int64_t> keyframe_time_list_tmp;  
        MOVStreamContext* sc = (MOVStreamContext*) videoStream->priv_data;  
        for (int videoIndex = 0; videoIndex < videoStream->nb_index_entries; videoIndex++) {  
            AVIndexEntry indexEntry = videoStream->index_entries[videoIndex];  
            if (indexEntry.flags & AVINDEX_KEYFRAME) {  
                MOVStts cttsData = {0};  
                if (sc && sc->ctts_count == videoStream->nb_index_entries) {  
                    cttsData = sc->ctts_data[videoIndex];  
                } 
                double doublePts = (indexEntry.timestamp + sc->dts_shift + cttsData.duration) * av_q2d(videoStream->time_base) * 1000.0;  
                int64_t ptsTime = ceil(doublePts);  
                keyframe_time_list_tmp.push_back(ptsTime);  
            }    
        }  
    }
}

11、SPS 和 PPS 在 extradata 中的作用是什么?

SPS(Sequence Parameter Set)和 PPS(Picture Parameter Set)是 H.264 视频编码中的两种重要参数集。它们包含了视频序列的特性和参数信息,对于解码器来说非常重要。

SPS 包含了视频序列的全局参数,如分辨率、帧率、颜色空间等。PPS 则包含了与特定图像相关的参数,如切片组的配置、参考帧的使用等。

在 extradata 中,SPS 和 PPS 的作用是为解码器提供视频序列的配置信息,以确保解码器能够正确地解释和处理视频数据。通过提供这些参数集,解码器能够准确地还原视频序列的特性,从而实现高质量的视频解码。

12、I 帧和 IDR 帧有什么区别?在什么情况下 I 帧不是 IDR 帧?

I 帧:I 帧是视频序列中的关键帧,它是一个完整的图像帧,类似于 JPEG 或 BMP 图像文件。I 帧不依赖于其他帧,因此可以独立解码和显示。在视频序列中,I 帧通常用于随机访问点,也作为其他帧解码的参考。

IDR 帧:IDR 帧是一种特殊的 I 帧,它具有刷新解码器缓冲区的功能。当解码器接收到 IDR 帧时,它会清除之前的解码状态,确保从该帧开始解码,从而避免错误传播。IDR 帧通常用于视频序列的随机访问点,以及在视频传输或存储中用于错误恢复。

因此 IDR 帧一定是 I 帧,但是 I 帧则不一定是 IDR 帧。在遇到 OpenGOP 的情况下,就会出现 I 帧为非 IDR 帧的情况。

如上图所示右数第一个 I 帧就是一个非 IDR 的 I 帧,前一个 GOP 中的 B 帧依赖了当前 GOP 的 I 帧。所以右数第一个 I 帧接受时,不能刷新解码器,否则上一个 GOP 中的 B 帧无法被成功解码,可能会出现花屏或者报错。

粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

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

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

相关文章

【图书推荐】《机器学习实战(视频教学版)》

本书用处 快速入门Python机器学习基础算法。 最后3个综合实战项目&#xff08;包括新闻内容分类实战、泰坦尼克号获救预测实战、中药数据分析项目实战&#xff09;可以作为研究可以的素材。 内容简介 本书基于Python语言详细讲解机器学习算法及其应用&#xff0c;用于读者快…

Java 五种内部类演示及底层原理详解

内部类 什么是内部类 在A类的内部定义B类&#xff0c;B类就被称为内部类 发动机类单独存在没有意义 发动机为独立个体 可以在外部其他类里创建内部类的对象去调用方法 类的五大成员 属性 方法 构造方法 代码块 内部类 内部类的访问特点 内部类可以直接访问外部类的成员&a…

Java处理CSV文件示例

Java处理CSV文件示例 1. 导入依赖 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.10.0</version></dependency>文件示例 下面是示例文件文件数据 vscode和idea都有解析…

第二证券股市资讯:连续3天20%涨停!A股这一赛道,牛股批量出现!

今日&#xff0c;A股小幅轰动调整&#xff0c;上证指数下试3100点支撑。 两市成交7453亿元&#xff0c;创近4个月来新低&#xff0c;超4000只个股下跌。盘面上&#xff0c;电力、芯片、煤炭、石油等板块涨幅居前&#xff0c;铜缆高速衔接、房地产、工程机械、网络游戏等板块跌幅…

word-主控文档、文档拆分及标书编写技巧建议

一、主控文档 视图-大纲视图-显示文档-插入子文档 子文档一旦更新&#xff0c;主文档也会更新。更新主文档&#xff0c;子文档也会更新 需要注意&#xff0c;不可修改子文档名字 二、上交文件 显示文档-折叠子文档-只显示一级-取消链接-关闭大纲视图-保存 三、文档拆分 根…

Transformer 从attention到grouped query attention (GQA)

Attention原理和理解 attention原理参考&#xff1a; Attention Is All You Need The Illustrated Transformer – Jay Alammar – Visualizing machine learning one concept at a time. Transformer图解 - 李理的博客 Attention首先对输入x张量乘以WQ, WK, WV得到query,…

本地开发正常 线上CI/CD构建项目过程报错文件未能正确引用

问题快照 原因分析&#xff1a; 一般遇到这样的错误就是 文件路径或者文件名称未能正确匹配 或者文件不存在 会报这样的错误 以为很好解决 但这次 都排查 了 就是 没发现原因 不管怎么说还是要感谢 GPT的能力(分析问题的能力) 先上图 当我看到 第四步的时候 我立马 去仓库里查…

没开玩笑!高速信号不能参考电源网络这条规则,其实很难做到

高速先生成员--黄刚 看到这篇文章的题目&#xff0c;我相信大家心里都呈现出了这么一个场景&#xff1a;高速信号线在L20层&#xff0c;我只要把L19和L21层都铺上完整的地平面&#xff0c;这不就满足了高速信号线不能参考电源平面这条规则了吗&#xff1f;这难道很难做到吗&…

Windows 使用技巧

Windows 使用技巧 ①局域网内共享文件 ②CTRL Y 和 CTRL Z ①局域网内共享文件 第一步&#xff1a; 选择要共享的文件&#xff08;分享方操作&#xff09; 第二步&#xff1a; 右键打开属性&#xff0c;选择共享&#xff08;分享方操作&#xff09; 第三步&#xff1a; …

Spring使用的设计模式

Spring 框架是一个广泛使用的 Java 框架&#xff0c;它内部使用了多种设计模式来简化开发过程、提高代码的可维护性和扩展性。 以下是一些在 Spring 框架中常见的设计模式&#xff0c;以及用代码示例来解释它们&#xff1a; 一、工厂模式&#xff08;Factory Pattern&#xff…

C#开发上位机应用:基础与实践

C#是一种流行的面向对象编程语言&#xff0c;常用于Windows应用程序的开发。上位机应用是一种用于监控和控制设备或系统的应用程序&#xff0c;通常与下位机&#xff08;如传感器、执行器等&#xff09;进行通信。在本文中&#xff0c;我们将介绍C#开发上位机应用的基础知识和实…

Vue3 之 动态组件和KeepAlive组件

一、动态组件 1、简介 ​ 在某些业务场景下&#xff0c;页面的某模块具有多个组件但在同一时间只显示一个&#xff0c;需要在多个组件之间进行频繁的切换&#xff0c;如&#xff1a;tab切换等场景。除了可以使用v-if、v-show根据不同条件显示不同组件之外&#xff0c;还可以通…

Element-Plus中表格及分页功能

导入Element-Plus 具体步骤如下&#xff1a;&#xff08;内容参照官网&#xff1a;安装 | Element Plus&#xff09; # 选择一个你喜欢的包管理器# NPM $ npm install element-plus --save# Yarn $ yarn add element-plus# pnpm $ pnpm install element-plus 在main.js文件的…

【论文阅读笔记】The Google File System

1 简介 Google File System (GFS) 是一个可扩展的分布式文件系统&#xff0c;专为快速增长的Google数据处理需求而设计。这篇论文发表于2003年&#xff0c;此前已在Google内部大规模应用。 GFS不仅追求性能、可伸缩性、可靠性和可用性等传统分布式文件系统的设计目标&#xf…

超市进销存|基于SprinBoot+vue的超市进销存系统(源码+数据库+文档)

超市进销存系统 目录 基于SprinBootvue的超市进销存系统 一、前言 二、系统设计 三、系统功能设计 1 登录注册 2 管理员功能模块 3员工功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#x…

leetcode 1264页面推荐(postgresql)

需求 朋友关系列表&#xff1a; Friendship ---------------------- | Column Name | Type | ---------------------- | user1_id | int | | user2_id | int | ---------------------- 这张表的主键是 (user1_id, user2_id)。 这张表的每一行代表着 user1_id 和 user2_id 之间…

URL跳转

1.URL介绍 开放重定向&#xff08;Open Redirect&#xff09;&#xff0c;也叫URL跳转漏洞&#xff0c;是指服务端未对传入的跳转url变量进行检查和控制&#xff0c;导致诱导用户跳转到恶意网站&#xff0c;由于是从可信的站点跳转出去的&#xff0c;用户会比较信任。 2.URL跳…

【最新区块链论文录用资讯】CCF A—INFOCOM 2024 共17篇

Conference&#xff1a;IEEE International Conference on Computer Communications CCF level&#xff1a;CCF A Categories&#xff1a;计算机网络 Year&#xff1a;2024 Num&#xff1a;17 A Generic Blockchain-based Steganography Framework with High Capacity via …

员工管理和激励怎么做?试试场景化激励解决方案!

截止到2020年底&#xff0c;中国企业主体数量达3858.3万&#xff0c;同比增速达11.1%。如何留住人才、激励人才以强化人才与企业“黏性”&#xff0c;最大化提升员工的忠诚度与敬业度&#xff0c;成为企业未来人才发展战略的主要方向之一。 一、传统激励方式存在哪些不足 传统的…

【加密与解密(第四版)】第十三章笔记

第十三章 HOOK技术 13.1 Hook概述 IAT HOOK&#xff08;改地址&#xff09; BOOL IAT_InstallHook(){BOOL bResult FALSE ;HMODULE hCurExe GetModuleHandle(NULL);PULONG_PTR pt ;ULONG_PTR OrginalAddr;bResult InstallModuleIATHook(hCurExe,"user32.dll",&qu…