iOS开发基础139-视频解码
1.iOS开发基础77-一像素线的几种实现方式2.iOS开发基础78-iOS 国际化3.iOS开发基础82-关于iOS目录4.iOS开发基础81-Runtime实战5.iOS开发基础80-关于Xcode86.iOS开发基础79-强制退出程序7.iOS开发基础90-密码学8.iOS开发基础89-Runloop9.iOS开发基础88-涂鸦效果10.iOS开发基础87-抽屉效果11.iOS开发基础86-FMDB12.iOS开发基础85-线程dispatch apply13.iOS开发基础84-HTTP请求方法详解与增删改查的应用14.iOS开发基础83-线程组15.iOS开发基础106-Instruments16.iOS开发基础105-Xcode收集Crashs的各种方法17.iOS开发基础104-正向代理和反向代理18.iOS开发基础103-APP之间跳转19.iOS开发基础102-后台保活方案20.iOS开发基础101-指纹和面部识别21.iOS开发基础100-MDM证书申请流程22.iOS开发基础99-iOS 内购的防范与优化23.iOS开发基础98-跳转淘宝案例24.iOS开发基础97-应用内购(In-App Purchase)的安全性解析与收据处理流程25.iOS开发基础96-UI类继承关系图26.iOS开发基础95-程序内评价27.iOS开发基础94-xcode1028.iOS开发基础93-GCD死锁29.iOS开发基础92-线程保活30.iOS开发基础91-线程同步技术与资源共享详解31.iOS开发基础138-视频编码32.iOS开发基础137-音视频编解码简介33.iOS开发基础136-防暴力点击34.iOS开发基础135-Core Data35.iOS开发基础134-异步并行上传问题36.iOS开发基础133-崩溃预防37.iOS开发基础132-POSIX线程库38.iOS开发基础131-isa指针39.iOS开发基础130-视频录制上传40.iOS开发基础129-音频录制上传41.iOS开发基础128-应用本地化42.iOS开发基础127-深入探讨KVO43.iOS开发基础126-深入探索设计模式44.iOS开发基础125-深入探索SDWebImage45.iOS开发基础124-RunLoop实现卡顿检测46.iOS开发基础123-自动释放池原理47.iOS开发基础122-RunLoop48.iOS开发基础121-APP启动优化49.iOS开发基础120-通知与线程50.iOS开发基础119-组件化51.iOS开发基础118-Runtime52.iOS开发基础117-Hybrid53.iOS开发基础116-性能监控54.iOS开发基础115-Socket55.iOS开发基础114-YYCache56.iOS开发基础113-Unity3D57.iOS开发基础112-GCD常见场景58.iOS开发基础111-RAC59.iOS开发基础110-Core Graphics应用场景60.iOS开发基础109-网络安全61.iOS开发基础108-常见的编程范式62.iOS开发基础107-iOS直播63.iOS开发基础148-ABM vs MDM64.iOS开发基础147-ABM集中管理Apple设备65.iOS开发基础146-深入解析WKWebView66.iOS开发基础145-Apple Search Ads67.iOS开发基础144-逐字打印效果68.iOS开发基础143-性能优化69.iOS开发基础142-广告归因70.iOS开发基础141-音频解码71.iOS开发基础140-音频编码
72.iOS开发基础139-视频解码
73.iOS开发基础149-由UUIDString引发的思考要进行视频解码,我们同样可以使用VideoToolbox
框架中的API来实现。以下示例会聚焦于解码H.264编码的视频流。解码过程大致分为几个步骤:创建解码会话、设置解码回调、输入编码后的数据,并在回调中接收解码后的图像。
下面是一个简化的视频解码器类实现,展示了如何设置一个解码会话并接收解码的视频帧。
VideoDecoder.h
// 引入必要的库
#import <Foundation/Foundation.h>
#import <VideoToolbox/VideoToolbox.h>
// 声明一个协议,用于处理解码后的视频帧。
@protocol VideoDecoderDelegate <NSObject>
- (void)videoDecoderDidDecodeFrame:(CVImageBufferRef)imageBuffer;
@end
// 定义VideoDecoder类,用于视频解码
@interface VideoDecoder : NSObject
@property (weak, nonatomic) id<VideoDecoderDelegate> delegate; // 委托对象,用于回调处理解码后的视频帧
- (void)decodeVideoData:(NSData *)videoData; // 解码视频数据的方法
@end
VideoDecoder.m
#import "VideoDecoder.h"
@interface VideoDecoder ()
@property (assign, nonatomic) VTDecompressionSessionRef decompressionSession; // 视频解码会话
@property (assign, nonatomic) CMVideoFormatDescriptionRef videoFormatDescription; // 视频格式描述符
@end
@implementation VideoDecoder
- (instancetype)init {
if (self = [super init]) {
_videoFormatDescription = NULL; // 初始化时,将视频格式描述符设置为NULL
_decompressionSession = NULL; // 初始化时,将解码会话设置为NULL
}
return self;
}
- (void)createDecompressionSession {
if (_decompressionSession != NULL) {
// 若解码会话已存在,先使其失效并释放资源
VTDecompressionSessionInvalidate(_decompressionSession);
CFRelease(_decompressionSession);
_decompressionSession = NULL;
}
// 设置解码输出的图像缓存的格式
NSDictionary *destinationImageBufferAttributes = @{
(NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) // 使用YUV格式(420YpCbCr8BiPlanarFullRange)来表示解码后的图像
};
VTDecompressionOutputCallbackRecord callbackRecord; // 解码输出回调记录
callbackRecord.decompressionOutputCallback = decompressionOutputCallback; // 设置解码输出的回调方法
callbackRecord.decompressionOutputRefCon = (__bridge void *)(self); // 将当前对象传递给回调方法,以便在回调中可以访问当前对象的属性或方法
OSStatus status = VTDecompressionSessionCreate(kCFAllocatorDefault, // 使用默认内存分配器
_videoFormatDescription, // 输入的视频格式描述
NULL, // 不使用解码规范
(__bridge CFDictionaryRef)(destinationImageBufferAttributes), // 解码输出图像缓存的设置
&callbackRecord, // 解码输出的回调记录
&_decompressionSession); // 创建的解码会话
if (status != noErr) {
NSLog(@"Error creating decompression session: %d", status); // 若创建失败,打印错误信息
}
}
- (void)decodeVideoData:(NSData *)videoData timestamp:(CMTime)timestamp {
// 要从`NSData`中的视频数据转换为`CMSampleBufferRef`,我们需要创建或获取一个有效的`CMSampleBufferRef`。这个过程相对复杂,因为它涉及到对输入数据的格式有一定的了解。下面是一个简化的示例,展示了如果你已经有了编码的视频帧(这里简化处理为H.264编码),如何构建一个基本的`CMSampleBufferRef`。这个过程涉及到生成或解析SPS(Sequence Parameter Set)和PPS(Picture Parameter Set),这两者用于生成一个视频格式描述来创建`CMSampleBuffer`。
// 以下主要目的是为了演示过程,并不包含所有细节
if (!videoData.length || !_decompressionSession) {
NSLog(@"No video data or decompression session is null.");
return;
}
// 这里假设videoData中已经包含了H.264帧数据。
// 真实情况下,你可能需要从更复杂的数据结构中提取出H.264的NALU单元,并处理它们。
// 1. 创建CMBlockBuffer
CMBlockBufferRef blockBuffer = NULL;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
(void *)videoData.bytes, // video data's pointer
videoData.length, // data length
kCFAllocatorNull,
NULL,
0,
videoData.length,
0,
&blockBuffer);
if (status != kCMBlockBufferNoErr) {
NSLog(@"Error creating CMBlockBuffer: %d", status);
return;
}
// 2. 创建CMSampleBuffer
CMSampleBufferRef sampleBuffer = NULL;
const size_t sampleSizeArray[] = {videoData.length}; // 样本大小数组
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuffer, // 数据块
_videoFormatDescription, // 前面通过SPS和PPS数据创建的视频格式描述
1, // sample count
0,
NULL, // timing info array
1, // sample size array entry count
sampleSizeArray, // sample size array
&sampleBuffer);
// 不再需要blockBuffer,释放资源
CFRelease(blockBuffer);
if (status != kCMBlockBufferNoErr || !sampleBuffer) {
NSLog(@"Error creating CMSampleBuffer: %d", status);
return;
}
// 3. 解码CMSampleBuffer
VTDecodeFrameFlags flags = 0;
VTDecodeInfoFlags flagOut = 0;
status = VTDecompressionSessionDecodeFrame(_decompressionSession,
sampleBuffer,
flags,
NULL, // output callback reference
&flagOut);
if (status != noErr) {
NSLog(@"Decode failed status: %d", status);
}
// 释放sampleBuffer资源
CFRelease(sampleBuffer);
}
static void decompressionOutputCallback(void * CM_NULLABLE decompressionOutputRefCon,
void * CM_NULLABLE sourceFrameRefCon,
OSStatus status,
VTDecodeInfoFlags infoFlags,
CM_NULLABLE CVImageBufferRef imageBuffer,
CMTime presentationTimeStamp,
CMTime presentationDuration) {
if (status != noErr) {
NSLog(@"Error in decompression output callback: %d", status); // 若解码过程中有错误,打印错误信息
return;
}
VideoDecoder *decoder = (__bridge VideoDecoder *)decompressionOutputRefCon; // 从传递到回调的引用中取回VideoDecoder对象
if ([decoder.delegate respondsToSelector:@selector(videoDecoderDidDecodeFrame:)]) {
[decoder.delegate videoDecoderDidDecodeFrame:imageBuffer]; // 将解码后的图像帧通过协议中的方法回调出去
}
}
- (void)dealloc {
if (_decompressionSession) {
VTDecompressionSessionInvalidate(_decompressionSession); // 使解码会话失效
CFRelease(_decompressionSession); // 释放解码会话资源
}
if (_videoFormatDescription) {
CFRelease(_videoFormatDescription); // 释放视频格式描述符资源
}
}
@end
使用方法
VideoDecoder *decoder = [[VideoDecoder alloc] init];
decoder.delegate = self;
[decoder decodeVideoData:frameData];
- (void)videoDecoderDidDecodeFrame:(CVImageBufferRef)imageBuffer {
// 处理解码图像缓冲区(显示、处理等)
}
说明
-
创建解码会话(
VTDecompressionSessionCreate
):在初始化解码器类时或者在第一次接收到视频数据时调用。此方法需要正确的CMVideoFormatDescriptionRef来描述输入视频流的格式,如H.264。对于网络流,这通常可以从流的SPS和PPS单元中解析得到。 -
解码视频数据(
VTDecompressionSessionDecodeFrame
):此函数用于输入编码的视频帧,并异步返回解码后的帧。解码的输出是通过设置的回调函数返回的。 -
回调函数:解码的帧通过设置的解码输出回调(decompressionOutputCallback)返回。回调提供CVImageBufferRef格式的解码视频帧,可以用于显示或进一步处理。
-
注意内存管理和线程安全:确保在不需要解码会话时使用
VTDecompressionSessionInvalidate
来销毁会话,并释放相关资源。解码操作是异步进行的,确保回调中的处理逻辑是线程安全的。
以上是一个基础的视频解码器实现,实际应用中可能需要根据特定需求进行适当的调整。例如,处理解码错误、支持不同的输入格式或优化解码性能等。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2015-07-23 iOS开发基础19-深入理解和实现不等高的 UITableViewCell