OPPO面试准备-2024.4.15
OPPO面试准备-2024.4.15
基本信息
方向一:打造多端分布式多媒体框架,归一化媒体流转平台,提供流畅高效媒体流转体验;
方向二:支撑COLOR OS相册主线相关业务,为用户提供个性化、智能化的媒体创作体验
方向三:提升视效整体表现力,形成符合用户需求的真实视效风格
方向四:搭建端侧智慧语音技术平台,提供以手机为中心音频全场景解决方案
方向五:负责相机效果调优:以提升整体相机影像品质为目标,负责相机影像品质调适专案,将3A(Auto Focus, Auto White Balance, Auto Exposure)和ISP(Image signal processing)、曝光、色彩、噪点、清晰度等进行优化,将与晶片厂的团队共同合作,达到发挥镜头、感光元件及影像处理器最大效能;
方向六:负责相机相关的算法优化:运用光学影像原理,维护并提升现有的影像演算法;
方向七:负责Android系统多媒体音视频系统开发与优化:如视频解析解码&流媒体延迟画质问题、音频新方案开发设计与产品化落地;
方向八:负责Android平台显示系统包括SurfaceFlinger/HWC/DRM/input相关模块开发和维护、显示系统性能稳定性类问题、GPU绘制相关问题分析与解决;
方向九:Web多媒体相关开发优化,轻量级Web引擎开发、混合应用框架开发。
任职要求
-
计算机、通信、电子信息、通信工程、软件工程等相关专业;
-
熟悉C/C++/Java/Python等语言,有一定的软件开发经验;
-
熟悉数据结构和算法,了解操作系统原理;
-
有岗位相关方向的开发经验或对应领域知识储备(如音频方向-有声学方面知识优先);
-
逻辑清晰,有系统思维,热爱软件开发工作。
自我介绍
略。
C++11/14/17新特性
C++11新特性
- 自动类型推导(Type Inference):引入了 auto 关键字,可以根据初始化表达式自动推导变量的类型。
- 统一的初始化语法(Uniform Initialization):可以使用花括号 {} 初始化对象,无论是基本类型、数组、类对象还是容器。
- lambda 表达式:可以在代码中定义匿名函数,简化函数对象的创建和使用。
- 范围-based for 循环:用于遍历容器中的元素,提供了一种更简洁、安全的遍历方式。
- nullptr:引入了 nullptr 关键字,代表空指针,替代了传统的 NULL 宏。
- C++11中新增了可变参数模板,能够创建可以接受可变参数的函数模板和类模板。
- C++11中最重要的特性之一就是支持了线程,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念。
C++14新特性
- C++14引入了通用lambda表达式,可以使用auto关键字作为参数类型和返回类型,使得lambda表达式更加灵活。
- C++14引入了二进制字面量,允许程序员使用二进制表示法来表示整数值。二进制字面量以前缀0b或0B开头,后面跟着一串二进制数字。例如,0b101010表示十进制数42。
- 数字分隔符使得大数字的可读性变得更高了。
- C++14中引入了变长参数模板的扩展,可以使用类似于函数参数的语法来定义模板参数列表。这个特性被称为“参数包扩展”或“参数模板扩展”。
C++17新特性
- 自从 C++11 起,C++ 就已经支持以 u8 为前缀的 UTF8 字符串字面量。C++17 进一步支持以 u8 为前缀的 UTF8 字符字面量。比如:char c = u8’6’。
- C++17 之前,在类中定义的非 const 静态变量,需要在类的外面进行初始化。C++17 引入了内联变量的概念,可以直接在类中定义并初始化非 const 静态变量。
- C++17 中,if 和 switch 语句允许我们在条件表达式里声明一个初始化语句。
- C++17 新增类模板参数推导,只要编译器能根据初始值推导出所有模板参数,那么就可以不指明参数。
- C++17 终于将文件系统纳入标准中,提供了关于文件系统的很多功能,基本上应有尽有。比如:创建目录、拷贝文件、判断文件是否存在等等。
struct和class的区别
- 默认访问权限:结构体的成员默认访问权限是公共的(public),类的成员默认访问权限是私有的(private)。
- 成员函数:类可以包含成员函数,这些函数可以操作类的私有成员,并且可以实现类的行为和功能。结构体也可以有成员函数,但是它们的主要目的是为了实现一些操作,而不是定义类似于类的行为。
- 继承:类可以通过继承实现子类与父类之间的关系,可以使用公共、保护或私有继承来控制成员的访问权限。结构体也可以继承,但由于其成员默认是公共的,继承可能导致访问权限问题。
- 构造函数和析构函数:类可以拥有构造函数和析构函数,用于对象的初始化和清理。结构体没有默认的无参构造函数,且只能声明有参的构造函数,没有析构函数,它们的使用场景通常是比较简单的数据封装。
- 默认成员访问标签:在类中,可以使用访问标签(public、private、protected)来指定成员的访问权限。在结构体中,无法使用访问标签来指定成员的访问权限,所有成员都默认是公共的。
- class是引用类型,struct是值类型。作为参数传递时,class变量以按址方式传递,而struct变量是以按值方式传递的。
- class实例由垃圾回收机制来保证内存的回收处理,而struct变量使用完后立即自动解除内存分配。
C++类型转换
C风格强制转换
-
隐式类型转换:编译器在编译阶段自动进行,能转就转,不能转就编译失败。
-
显式类型转换:通过圆括号直接转型。
C++四种类型转换方式
static_cast 是一个C++运算符,功能是把一个表达式转换为某种类型,类似C风格的强制转换,但没有运行时类型检查来保证传唤的安全性。用法:
- 用于类层次结构中基类(父类)和派生类(子类)之间的指针或引用的和转换。
进行向上转换(将派生类的指针或引用转换成基类表示)是安全的;进行向下转换(将基类指针或者引用转换为派生类表示)时,由于没有动态类型的检查,所以是不安全的。 - 用于基本数据类型之间的转换。
如把int转换为char,把int转换为enum。这种转换的安全性也要开发人员来保证。 - 把空指针转换成目标类型的空指针。
- 把任何类型的表达式转换为void类型。
dynamic_cast是将一个基类对象指针(或引用)转换到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。虽然static_cast也具备这样的功能,但是是不安全的,dynamic_cast则会在转型时判断类型,如果类型不符则返回nullptr。由于类型信息是保存在虚函数表中的,因此dynamic_cast操作的基类必须包含virtual函数。用法:
- dynamic_cast在类层次间进行向上转换时,效果和static_cast一样。
- dynamic_cast在类层次间进行向下转换时,具有类型检查功能,更安全。
reinterpret_cast:“重新诠释的转型”。就是不改变指针地址,但是强制将地址处的内存字节解释为某种类型的数据。reinterpret_cast转换的实质是让编译器重新给出对象的比例模型而不进行二值转换。也就是让编译器按照目标转换类型在内存中所占的比特位重新编译原始数据类型。转换前后对象的地址值是不变的,只是对象的读取内存的比例变了而已。
const_cast是一个基于C语言编程开发的运算方法,用于常量指针和非常量指针间的转换,同样适用于引用、函数和模块接口,其主要作用是:修改类型的const或volatile属性。
FFmpeg播放RTSP视频流出现花屏和马赛克,如何排查?
四个可能的原因
- 发送数据包太大,超过了 FFmpeg 的默认最大值。
- 网络情况较差时,因网络状况出现的丢包。
- 解码出错。
- 包乱序。
原因一的解决方法
一种方法是控制播放源的发送数据大小,但这极大浪费了当前的网络带宽,非优选方案。
更好的做法是扩大接收端的接收缓冲区,其修改方法为:
在 FFmpeg 的源码中,找到 udp.c 文件并修改 UDP_MAX_PKT_SIZE 默认值。
这里将 UDP_MAX_PKT_SIZE × 10,将缓冲区扩大了10倍。
原因二的解决方法
排查方法:
- 设全局变量:在丢包时将全局变量置为不同的值,最后在使用的地方根据全局变量的值来判断该帧是否完整,全局变量可在 FFmpeg 任意的头文件中设置(比如 avcodec.h)。
- 修改rtpdec.c文件包含 missed %d package 的地方,这里出现丢包,需修改代码,对帧序号作标记。
在接收端根据RTP包的SeqNumber来判断是否丢包,如果丢包就标记一下。在mark为1或时间戳改变的时候,说明一帧结束了,此时如果标记为丢包了,就扔掉数据,没有丢包就给解码器。如果丢包的帧为I帧,则不仅丢掉当前I帧,此I帧之后的P帧也要丢掉,也就是说在下一个I帧到来之前,所有过来的包都丢掉,然后开始判断收到的RTP包是不是I帧。
判断函数:
static bool isH264iFrame(byte[] paket)
{
int RTPHeaderBytes = 0;
int fragment_type = paket[RTPHeaderBytes + 0] & 0x1F;
int nal_type = paket[RTPHeaderBytes + 1] & 0x1F;
int start_bit = paket[RTPHeaderBytes + 1] & 0x80;
if (((fragment_type == 28 || fragment_type == 29) && nal_type == 5 && start_bit == 128) ||
fragment_type == 5 || fragment_type == 7 || fragment_type == 8)
{
return true;
}
return false;
}
原因三的解决方法
排查方法:
- 设全局变量:在解码出错时将全局变量置为不同的值,最后在使用的地方根据全局变量的值来判断该帧是否完整,全局变量可在 FFmpeg 任意的头文件中设置(比如 avcodec.h)。
- 修改error_resilience.c文件 包含concealing %d DC, %d AC, %d MV errors in %c frame的地方。这里出现解包错误,需标记。
- 修改h264_cavlc.c文件中包含 Invalid level prefix处 这里出错,需标记。
修改h264_cavlc.c文件中包含dquant out of range处,出错,需标记。
修改h264_cavlc.c文件中包含corrupted macroblock处,出错,需标记。
修改h264_cavlc.c文件中包含negative number of zero coeffs at处,出错,需标记。
修改h264_cavlc.c文件中包含mb_type %d in %c slice too large at %d %d处,出错,需标记。
修改h264_cavlc.c文件中包含cbp too large处,出错,需标记。 - 修改error_resilience.c文件中包含Cannot use previous picture in error concealment处,出错,需标记。
修改error_resilience.c文件中包含Cannot use next picture in error concealment处,出错,需标记。 - 修改h264.c文件中包含out of range intra chroma pred mode at处,出错,需标记。
修改h264.c文件中包含top block unavailable for requested intra mode at处,出错,需标记。
修改h264.c文件中包含left block unavailable for requested intra mode at处,出错,需标记。 - 修改h264_slice.c文件中包含error while decoding MB处,出错,需标记。
- 修改svq3.c文件中包含error while decoding MB处,出错,需标记。
当我们从网络中接收到RTP包,去了包头,拿到Payload数据之后一般就会送去解码,但是如果直接送去解码器解码,很可能会出现花屏。这个问题我很早就遇到过,当时查阅过资料,发现送给H264解码器的必须是一个NALU单元,或者是完整的一帧数据(包含H264 StartCode),也就是说我们拿到Payload数据之后,还要将分片的数据组成一个NALU或完整的一帧之后才送给解码器。
原因四的解决方法
打印RTP包的SeqNumber看有没有不连续或乱序的问题,如果是用UDP传输,则RTP包容易发生乱序,需要对包按顺序进行重组再解码。
H.264/H.265的区别
局部变量存储在哪里?
- 不是 new 出来的变量:栈
- new 出来的变量:对象在堆,栈里存放对象的地址