Profile 定义了一组解码器能够支持的编码算法。为了与接收方通信,编码后的码流应符合解码器支持的特定 profile。在 Video Toolbox 中,我们支持一系列 profile,例如 baseline profile、main profile 和 high profile。该系列添加了两个新 profile:constrained baseline profile(CBP) 和 constrained high profile(CHP)。CBP 主要用于低码率应用,而 CHP 具有更先进的算法以获得更好的压缩比。
1 2 3 4 5 6 7 8 9 10 11
VTSessionSetProperty( compressionSession, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel // value CBP )
VTSessionSetProperty( compressionSession, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_ConstrainedHigh_AutoLevel // value CHP )
时间可扩展性(temporal scalability)
使用该特性可以提高多方视频通话的效率。 下图为一个简单的三方视频会议场景:在此模型中,接收方 A 的带宽较低为 600 kbps,而接收方 B 的带宽较高为 1,000 kbps。通常,发送方需要对编码输出两路码流,以满足每个接收方的下行带宽(ps:实际现在一般是主播推一路流,cdn 进行转码),这可能不是最佳的方案。
当使用该特性时,编码会更高效,模型如下图:发送方只需要编码输出一路码流,然后根据接收方进行分层。
实现原理
下图为一组视频帧,其中每一帧都使用前一帧作为参考帧。可以将一半的帧放入另一层,更改参考帧,以便只有原始层中的帧用于参考帧。原始层称为 base layer,新构建的层称为 enhancement layer。enhancement layer 可以作为 base layer 的补充,以提高帧率。对于接收方 A,我们可以发送 base layer 帧,因为基础层本身已经是可解码的。更重要的是,由于 base layer 仅包含一半的帧,因此传输的码率将很低。接收方 B 可以享受更流畅的视频,因为它有足够的带宽来接全部视频帧。
// Read a heic image from file let inputURL =URL(fileURLWithPath: "/tmp/image.heic") let source =CGImageSourceCreateWithURL(inputURL asCFURL, nil) let imageProperties =CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [String: Any] let image =CGImageSourceCreateImageAtIndex(source, 0, nil) let options = [kCGImageSourceCreateThumbnailFromImageIfAbsent asString: true, kCGImageSourceThumbnailMaxPixelSize asString: 320] as [String: Any] let thumb =CGImageSourceCreateThumbnailAtIndex(source, 0, options asCFDictionary)
HEIF 文件写入
与写入一张 JPEG 图像也仅有扩展名的区别:
1 2 3 4 5 6 7 8 9 10
// Writing a CGImage to a HEIC file let url =URL(fileURLWithPath: "/tmp/output.heic") guardlet destination =CGImageDestinationCreateWithURL(url asCFURL, AVFileType.heic asCFString, 1, nil) else { fatalError("unable to create CGImageDestination") }
但很快,一些厂商认识到HEVC高昂专利费用带来的弊端,他们决定创立一个开放联盟,推广开放、免费的媒体编码标准。这个联盟就是开放媒体联盟(Alliance for Open Media),创始成员有Amazon、Cisco、Google、Intel、Microsoft、Mozilla和Netflix这些我们熟悉的大公司,而后加入的还有苹果、ARM、三星、NVIDIA、AMD这些同样耳熟能详的公司。
JPG converts from RGB to Y,Cb,Cr color model; Which comprises of Luminance (Y), Chroma Blue (Cb) and Chroma Red (Cr). The reason for this, is that psycho-visual experiments (aka how the brain works with info the eye sees) demonstrate that the human eye is more sensitive to luminance than chrominance, which means that we may neglect larger changes in the chrominance without affecting our perception of the image. As such, we can make aggressive changes to the CbCr channels before the human eye notices. JPG 将 RGB 转换为YCbCr 颜色空间, 包括亮度 (Y)、色度蓝 (Cb) 和色度红 (Cr)。原因是心理视觉实验(也就是大脑如何处理眼睛看到的信息)表明人眼对亮度比对色度更敏感, 这意味着我们可以忽略较大的色度变化并且不影响我们对图像的感知。因此, 我们可以在人眼注意到区别前对 Cb Cr 两个通道进行较大的更改。
JPG converts from RGB to Y,Cb,Cr color model; Which comprises of Luminance (Y), Chroma Blue (Cb) and Chroma Red (Cr). The reason for this, is that psycho-visual experiments (aka how the brain works with info the eye sees) demonstrate that the human eye is more sensitive to luminance than chrominance, which means that we may neglect larger changes in the chrominance without affecting our perception of the image. As such, we can make aggressive changes to the CbCr channels before the human eye notices. JPG 将 RGB 转换为YCbCr 颜色空间, 包括亮度 (Y)、色度蓝 (Cb) 和色度红 (Cr)。原因是心理视觉实验(也就是大脑如何处理眼睛看到的信息)表明人眼对亮度比对色度更敏感, 这意味着我们可以忽略较大的色度变化并且不影响我们对图像的感知。因此, 我们可以在人眼注意到区别前对 Cb Cr 两个通道进行较大的更改。
为了避免这种情况,很自然地会想到将内容的亮度范围通过函数映射到当前显示设备支持的亮度范围,这也是 HDR 中的重要概念:色调映射(tone mapping)。通过这种技术,HDR 内容甚至可以显示在只支持 SDR 的设备上。为了在映射后获得更好的展示效果,发展出了多种色调映射技术,包括 Reinhard Tone Mapping,Filmic Tone Mapping,ACES(Acadamy Color Encoding System)等。
如今苹果尝试将 Gain Map HDR 技术标准化,这种标准化主要反映在两个方面,一是增益图的生产和消费过程中,HDR 和 SDR 信息应满足特定的数值关系,二是元数据的编码格式以及在不同文件格式(例如 HEIF、JPEG)中的存储应满足特定的规范。这种标准化后的技术被苹果成为 Adaptive HDR。从 iOS18 开始,传统的 Gain Map HDR 将全面迁移到符合新标准的 Adaptive HDR,iPhone 15 和 iPhone 15 Pro 将能够拍摄符合新标准的 Adaptive HDR 图片。
除了 Adaptive HDR 外,今年苹果也针对传统的 ISO HDR 渲染进行了优化,色调映射从默认的 ITU Global Tone Mapping,升级为新研发的 Reference White Tone Mapping,这将减少亮部的裁剪并提升色彩还原的质量,这种新的技术将被用于 iOS,macOS,tvOS,watchOS 和 visionOS。
Adaptive HDR 的读取、编辑、保存与展示
读取、编辑与保存
对于 Adaptive HDR,除了获取 SDR 图片和 HDR 图片,开发者还能够读取其增益图:
1 2 3 4 5
let sdrImage =CIImage(contentsOf: url)
let hdrImage =CIImage(contentsOf: url, options: [.expandToHDR : true])
let gain =CIImage:(contentsOf: url, options: [.auxiliaryHDRGainMap : true])
// read and edit let sdr =CIImage(contentsOf: url) var gain =CIImage:(contentsOf: url, options: [.auxiliaryHDRGainMap : true]) let xform =CGAffineTransform(scaleX: sdr.extent.size.width/gain.extent.size.width, y: sdr.extent.size.height/gain.extent.size.height) gain = gain.transformd(by: xform)
let filter =CIFilter.strechCropFilter() filter.size =CGSize(width: 1280, height: 720) filter.cropAccount =1.0
filter.inputImage = sdr let editedSDR = filter.outputImage filter.inputImage = gain let editedGain = filter.outputImage
// save ctx.writeHEIF10Representation(of: editedSDR, to: url, colorSpace: p3Space, options: [.HDRGainMapImage: editedGain])
let image =UIImageReader.default.image(contentsOfURL: url) let imageView =UIImageView(image: image) imageView.preferredImageDynamicRange = .high // .low or .constraindHigh
// display HDR image with current display headroom let headroom = view.window.screen.currentEDRHeadroom let tonemappedImage = editedHDR.applyingFilter("CIToneMapHeadroom", parameters: ["inputTargetHeadroom" : headroom]) cicontent.startTask(toRender: tonemappedImage, ...)
// display HDR image with SDR image, Gain Map and current display headroom let headroom = view.window.screen.currentEDRHeadroom let tonemappedImage = editedSDR.applyingGainMap(editedGain, headroom: headroom) cicontext.startTask(toRender: tonemappedImage, ...)
#00x00007f91396cd207in raise() from /lib64/libc.so.6 #1 0x00007f91396ce8f8 in abort() from /lib64/libc.so.6 #2 0x00007f913970fd27 in __libc_message() from /lib64/libc.so.6 #3 0x00007f91397165d4 in malloc_printerr() from /lib64/libc.so.6 #4 0x00007f91397186cb in _int_free() from /lib64/libc.so.6 #5 0x00007f913ce85fa1 in Posxxx::releasexxx() #6 0x00007f913cdf53be in xxxProvider::~xxxProvider(this=0x151fbc0, __in_chrg=<optimized out>) at /root/workspace/feature/xxxProvider/xxxProvider.cpp:27
valgrind: m_mallocfree.c:305 (get_bszB_as_is): Assertion ‘bszB_lo == bszB_hi’ failed. valgrind: Heap block lo/hi size mismatch: lo = 1360, hi = 3212836864. This is probably caused by your program erroneously writing past the end of a heap block and corrupting heap metadata.
valgrind检查到了堆内存访问越界,并指出了发生非法内存写操作的地方–”Invalid write of size 8”,相关问题出在xxx_define.h:380中的reset函数。
==92== Invalid write of size 8
==92== at 0x50F317C: reset (xxx_define.h:380)
==92== by 0x50F317C: xxxInfo (xxx_define.h:299)
==92== by 0x50F317C: xxx::xxx::xxxData() (xxxDefineBase.cpp:4)
==92== by 0x6089286: ??? (in /root/workspace/test/sdk/xxxResim/libxxxSimulater.so)
==92== by 0x400F8F2: _dl_init (in /usr/lib64/ld-2.17.so)
==92== by 0x4001159: ??? (in /usr/lib64/ld-2.17.so)
[root@4bad734105ec stl_template_demo]# nm A.so |grep vector 000000000000134a W _ZNKSt6vectorIwSaIwEE4sizeEv 00000000000012ec W _ZNSt6vectorIwSaIwEEC1Ev 00000000000012ec W _ZNSt6vectorIwSaIwEEC2Ev 0000000000001306 W _ZNSt6vectorIwSaIwEED1Ev 0000000000001306 W _ZNSt6vectorIwSaIwEED2Ev
Starting program: /root/workspace/test/stl_template_demo/main_A_link_first warning: Error disabling address space randomization: Operation not permitted
Breakpoint 1, main () at main.cpp:6 6 funcA(); Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.3.x86_64 libgcc-4.8.5-36.el7_6.1.x86_64 (gdb) n funcA: size of wchar_t:2 7 funcB(); (gdb) s funcB () at B.cpp:7 7 std::cout << "funcB: size of wchar_t:" << sizeof(wchar_t) << std::endl; (gdb) n funcB: size of wchar_t:4 8 std::vector<wchar_t> words = {}; (gdb) s std::vector<wchar_t, std::allocator<wchar_t> >::vector (this=0x7fff35804dd0) at /usr/include/c++/4.8.2/bits/stl_vector.h:249 249 : _Base() { } (gdb) i register pc pc 0x7f312b8f62f8 0x7f312b8f62f8 <std::vector<wchar_t, std::allocator<wchar_t> >::vector()+12> (gdb) i symbol 0x7f312b8f62f8 std::vector<wchar_t, std::allocator<wchar_t> >::vector() + 12 in section .text of ./A.so
在 C++ 中,std::string和std::wstring之间的转换涉及到字符编码的转换。如果在转换过程中出现乱码,可能是由于字符编码不匹配导致的。要正确地进行std::string 和 std::wstring之间的转换,需要确保源字符串的字符编码和目标字符串的字符编码一致,避免C++中的字符串处理乱码,可以使用Unicode编码(如UTF-8、UTF-16或UTF-32)来存储和处理字符串。
template <> inlinebool _Sp_counted_base<_S_atomic>::_M_add_ref_lock_nothrow() noexcept { // Perform lock-free add-if-not-zero operation. _Atomic_word __count = _M_get_use_count(); do { if (__count == 0) returnfalse; // Replace the current counter value with the old value + 1, as // long as it's not changed meanwhile. } while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)); returntrue; }
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same instance of shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
多线程环境中,对于持有相同裸指针的std::shared_ptr实例,所有成员函数的调用都是线程安全的。 a. 当然,对于不同的裸指针的 std::shared_ptr 实例,更是线程安全的 b. 这里的 “成员函数” 指的是 std::shared_ptr 的成员函数,比如 get ()、reset ()、operrator->()等
如果C++11中引入的新词要评一个”最令人困惑”奖,那么constexprhen很有可能获此殊荣。当它应用于对象时,其实就是一个加强版的const,但应用于函数时,却有着相当不同的意义。在使用 C++ const和consterpx的时候,可能都会犯晕乎,那constexpr和 const都有什么区别,这节简单梳理下。
constexprfloat x = 42.0; constexprfloat y{108}; constexprfloat z = exp(5, 3); constexprint i; // Error! Not initialized int j = 0; constexprint k = j + 1; //Error! j not a constant expression
catch <type> @ExcType This clause means that the landingpad block should be entered if the exception being thrown is of type @ExcType or a subtype of @ExcType. For C++, @ExcType is a pointer to the std::type_info object (an RTTI object) representing the C++ exception type.
A two-phase exception-handling model is not strictly necessary to implement C++ language semantics, but it does provide some benefits. Forexample, the first phase allows an exception-handling mechanism to dismiss an exception before stack unwinding begins, which allows resumptive exception handling (correcting the exceptional condition and resuming execution at the point where it was raised). While C++ does not support resumptive exception handling, other languages do, and the two-phase model allows C++ to coexist with those languages on the stack.
两个阶段的异常处理模型对于 C++ 并不是严格必需的,但是它可以带来一些好处。比如,第一阶段允许异常处理机制在栈帧展开之前消除异常,这样可以进行恢复式异常处理(对异常情况进行修复,然后在抛出异常的地方继续执行),虽然 C++ 不支持恢复式的异常处理,但其它语言支持,两阶段模型允许 C++ 与那些语言在堆栈上共存。
staticbool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT { if (__lhs == __rhs) returntrue; if (__is_type_name_unique(__lhs) || __is_type_name_unique(__rhs)) // Either both are unique and have a different address, or one of them // is unique and the other one isn't. In both cases they are unequal. returnfalse; return __builtin_strcmp(__type_name_to_string(__lhs), __type_name_to_string(__rhs)) == 0; }
// Check if there is already an external RTTI descriptor for this type. if (IsStandardLibraryRTTIDescriptor(Ty) || ShouldUseExternalRTTIDescriptor(CGM, Ty)) returnGetAddrOfExternalRTTIDescriptor(Ty);
条件1: IsStandardLibraryRTTIDescriptor 判断是否是基础类型,比如 int bool float double。
// If RTTI is disabled, assume it might be disabled in the // translation unit that defines any potential key function, too. if (!Context.getLangOpts().RTTI) returnfalse;
禁用 RTTI 后上述两个条件都不满足,会继续执行生成异常类型的 type_info,同时也会生成 base 的 type_info。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
///Record 表示 Structure/Class descriptor case Type::Record: { const CXXRecordDecl *RD = cast<CXXRecordDecl>(cast<RecordType>(Ty)->getDecl()); if (!RD->hasDefinition() || !RD->getNumBases()) { // We don't need to emit any fields. break; }
if (CanUseSingleInheritance(RD)) BuildSIClassTypeInfo(RD); else BuildVMIClassTypeInfo(RD);
首先尝试了 N 条消息 N 个线程的方案:用 GCD 派发 N 个并发任务,然后用 DispatchGroup 等待这些任务执行完成。通过并行预布局,将原本一个线程需要几十毫秒的预布局减少到了十几毫秒。这个方案后来发现了 2 个问题:
并行布局 N 条消息的总耗时还是比串行布局一条消息的耗时要大得多,受限于 CPU 核心数,代码中的锁或其他资源竞争导致 N 条消息的参数准备和布局计算没有能充分的并行。
这N条消息的布局任务分别和 N 个 GCD 任务一对一绑定了,GCD 调度这 N 个任务中有任何一个调度慢都会拉长整个预布局的耗时。
充分利用多核CPU的算力;使用并行计算,布局计算的总耗时减少了约76%。
调整后的方案如上图所示,使用了 M 个执行者来执行N条消息的布局任务(N>=M>0)。当前线程(异步布局主线程)来执行 1 个执行者,然后再由 GCD 额外调度(M-1)个线程来执行(M-1)个执行者。 首先将待计算的消息放入一个队列中,每个执行者都会循环从待计算的消息队列中取出一条消息执行布局计算,直到待计算的消息队列为空。因为消息的布局任务没有和任何一个执行者绑定,即使有执行者较长时间没有被调度也不会导致布局计算迟迟无法完成,大部分情况下这 M 个执行者会被 M 个线程并行执行。
首先尝试了 N 条消息 N 个线程的方案:用 GCD 派发 N 个并发任务,然后用 DispatchGroup 等待这些任务执行完成。通过并行预布局,将原本一个线程需要几十毫秒的预布局减少到了十几毫秒。这个方案后来发现了 2 个问题:
并行布局 N 条消息的总耗时还是比串行布局一条消息的耗时要大得多,受限于 CPU 核心数,代码中的锁或其他资源竞争导致 N 条消息的参数准备和布局计算没有能充分的并行。
这N条消息的布局任务分别和 N 个 GCD 任务一对一绑定了,GCD 调度这 N 个任务中有任何一个调度慢都会拉长整个预布局的耗时。
充分利用多核CPU的算力;使用并行计算,布局计算的总耗时减少了约76%。
调整后的方案如上图所示,使用了 M 个执行者来执行N条消息的布局任务(N>=M>0)。当前线程(异步布局主线程)来执行 1 个执行者,然后再由 GCD 额外调度(M-1)个线程来执行(M-1)个执行者。 首先将待计算的消息放入一个队列中,每个执行者都会循环从待计算的消息队列中取出一条消息执行布局计算,直到待计算的消息队列为空。因为消息的布局任务没有和任何一个执行者绑定,即使有执行者较长时间没有被调度也不会导致布局计算迟迟无法完成,大部分情况下这 M 个执行者会被 M 个线程并行执行。
首先不同于很多人先入为主的认识的是,Java 代码其实也是先解释执行的。Java 的 Class 文件其实就是Java的字节码,在JVM中,class文件会先以解释执行的方式进行执行。然后JVM在发现了一些热点函数之后,会对热点函数进行JIT编译来加速性能。众所周知JVM经常自己去和C++比较性能,然后说自己”甚至有时候比C++性能还好”。刨去其中王婆卖瓜的部分,其实JIT真的在一定条件下可以做到比肩甚至超过静态编译语言的性能的。但这是有一些前提的:
structnano_blk_addr_s { uint64_t nano_offset:NANO_OFFSET_BITS,// locates the block nano_slot:NANO_SLOT_BITS,// bucket of homogenous quanta-multiple blocks nano_band:NANO_BAND_BITS, nano_mag_index:NANO_MAG_BITS,// the core that allocated this block nano_signature:NANO_SIGNATURE_BITS;// the address range devoted to us. }; // 这个是指针,也是该内存对象的信息 typedefunion { uint64_taddr; structnano_blk_addr_sfields; } nano_blk_addr_t;
// For logging VM allocation and deallocation, arg1 here // is the mach_port_name_t of the target task in which the // alloc or dealloc is occurring. For example, for mmap() // that would be mach_task_self(), but for a cross-task-capable // call such as mach_vm_map(), it is the target task.
// We set malloc_logger to NULL to disable logging, if we encounter errors // during file writing typedefvoid(malloc_logger_t)(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t num_hot_frames_to_skip); externmalloc_logger_t *malloc_logger;