一文搞懂IOS音视频编解码器VideoToolbox

在 IOS 平台上,我们经常需要处理音视频数据,比如播放视频、录制音频等。为了高效处理这些数据,IOS 提供了 VideoToolbox 类,它允许我们对音视频进行编解码操作。

什么是 VideoToolbox?

IOS 8.0之后,苹果开放了硬编解码API,即 VideoToolbox.framework的API。VideoToolbox是一套纯C语言API,可以直接访问硬件编解码器。它提供视频压缩和解压缩以及存储在像素缓存区中的数据转换服务。

image-20240515222131537.png

下面以编码器为例进行说明:

先介绍VideoToolbox相关的几种数据结构

(1)CVPixelBuffer:存储编码前或解码后的视频帧,包含了图像的像素数据以及有关像素格式、大小和颜色空间等信息。
(2)CMBlockBuffer:存储压缩后的视频数据,例如H.264视频流中的NAL单元。
(3)CMSampleBuffer:包含时间戳和持续时间等元数据的样本数据。它可以包含一个CVPixelBuffer或者CMBlockBuffer,相当于存放视频图像的容器数据结构。

H.264编码整体流程:

  • CVPixelBuffer → CMSampleBuffer → H.264

VideoToolbox用法

1. 创建 VideoToolbox 会话

要使用 VideoToolbox,首先需要创建一个 VTCompressionSessionRef 实例作为编码器的句柄。以下是创建编码器实例的基本步骤:

VTCompressionSessionRef compressionSession;
OSStatus status = VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, compressionCallback, NULL, &compressionSession);
if (status != noErr) {
    NSLog(@"Failed to create compression session: %d", (int)status);
    return;
}

其中,widthheight 是要编码的视频帧的宽度和高度,kCMVideoCodecType_H264 是视频编码器类型。compressionCallback 是一个回调函数,用于处理编码后的数据。

2. 配置 VideoToolbox

配置编码器的参数,包括码率、帧率、关键帧间隔等

NSDictionary *compressionProperties = @{
    (__bridge NSString *)kVTCompressionPropertyKey_RealTime: @YES, // 开启实时编码,降低延迟
    (__bridge NSString *)kVTCompressionPropertyKey_ProfileLevel: (__bridge NSString *)kVTProfileLevel_H264_Baseline_AutoLevel, // BaseLine等级
    (__bridge NSString *)kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration: @(10), // 关键帧间隔
    (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate: @(500000), // 平均码率,单位bps
    (__bridge NSString *)kVTCompressionPropertyKey_DataRateLimits: @(600000/8), // 最高码率,单位byte/s
};
VTSessionSetProperties(compressionSession, (__bridge CFDictionaryRef)compressionProperties);

IOS只支持ABR码控模式:

ABR:恒定平均目标码率,简单场景分配较低bit,复杂场景分配足够bit,使得有限的bit数能够在不同场景下合理分配,这类似VBR。同时一定时间内,平均码率又接近设置的目标码率,这样可以控制输出文件的大小,这又类似CBR。可以认为是CBR和VBR的折中方案

3. 处理输入数据

将需要编码的原始视频帧传递给编码器进行处理。首先将原始数据放入 CMSampleBufferRef 中,然后将其传递给 VideoToolbox

CVPixelBufferRef pixelBuffer;   // 存放原始视频帧数据
CFDictionaryRef frameProperties;    // 设置编码帧属性,如是否为关键帧
CMTime presentationTimeStamp = CMTimeMake(value, timescale); // 显示时间戳
VTCompressionSessionEncodeFrame(compressionSession, pixelBuffer, presentationTimeStamp, kCMTimeInvalid, frameProperties, NULL, NULL);

注意事项:
CMTime是IOS专门描述视频时间的一种数据类型
官网注释 /*@field value the value of the CMTime. value/timescale = seconds*/
举个例子说明,假设以下情形:
A视频15帧,播放速度5fps,持续时间=15/5=3s
B视频60帧,播放速度20fps,持续时间60/20=3s
由此可知,timesclae应设置为帧率

4. 处理输出数据

编码器处理完数据后,会通过上述定义的回调函数返回编码后的数据。在回调函数中可以处理编码后的数据,比如将其写入文件或传输到网络上。

void compressionCallback(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer) {
    if (status != noErr) {
        NSLog(@"Failed to encode frame: %d", (int)status);
        return;
    }
    // 从sampleBuffer中取出编码后的数据
    CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
    size_t length, totalLength;
    char *dataPointer;
    // 处理编码后的数据,比如将其写入文件或传输到网络上
    OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);
    if (statusCodeRet == noErr) {
        size_t bufferOffset = 0;
        static const int AVCCHeaderLength = 4; // 返回的NALU数据前四个字节是大端模式的帧长度
        // 循环获取NALU数据
        while (bufferOffset < totalLength - AVCCHeaderLength) {
            uint32_t NALUnitLength = 0;
            // 读取NALU长度的数据
            memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
            // 从大端转系统端
            NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
            NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];
            // 移动到下一个NALU单元
            bufferOffset += AVCCHeaderLength+NALUnitLength;
        }
    }
}

注意事项:
VideoToolbox编码以AVCC格式(头四个字节表示NALU长度)存储数据,且字节顺序是反的,在转换为H.264格式时需取出头四个字节并替换为Annex B格式(头四个字节是0001),并将字节顺序反转得到H.264数据。

5. 销毁编码器

VTCompressionSessionCompleteFrames(compressionSession, kCMTimeInvalid);
VTCompressionSessionInvalidate(compressionSession);

相关系列

  • 38
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 下载阿里云视频播放器SDK,解压后得到两个文件夹:aliyun-sdk-player-android和aliyun-sdk-player-ios。 2. 在React Native项目中安装react-native-aliplayer插件,使用命令:npm install react-native-aliplayer --save。 3. 在android/app/build.gradle文件中添加如下代码: ```gradle dependencies { implementation project(':react-native-aliplayer') ... } ``` 4. 在MainActivity.java中添加如下代码: ```java import com.aliplayer.AliPlayerPackage; public class MainActivity extends ReactActivity { @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new AliPlayerPackage() ); } ... } ``` 5. 在ios项目中,将aliyun-sdk-player-ios文件夹拖入项目中,并在Build Phases的Link Binary With Libraries中添加以下库: - libstdc++.6.tbd - libz.tbd - libresolv.tbd - libbz2.tbd - AVFoundation.framework - AudioToolbox.framework - CoreMedia.framework - MediaPlayer.framework - SystemConfiguration.framework - UIKit.framework - VideoToolbox.framework 6. 在AppDelegate.m中添加如下代码: ```objective-c #import "AliPlayer.h" ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [AliPlayer registerWithAppKey:@"your-app-key"]; ... } ``` 7. 在React Native中使用AliPlayer组件进行视频播放,例如: ```jsx import React from 'react'; import { View } from 'react-native'; import AliPlayer from 'react-native-aliplayer'; export default function App() { return ( <View style={{ flex: 1 }}> <AliPlayer source={{ uri: 'http://player.alicdn.com/video/aliyunmedia.mp4', }} style={{ flex: 1 }} /> </View> ); } ``` 以上就是React Native集成阿里云视频播放器SDK的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值