VideoModel 是火山引擎播放器 SDK 针对多清晰度场景推出的面向极致体验的自研协议。通过 VideoModel,您可以将包含多清晰度、多格式视频流的复杂信息整合为单个对象,并直接将这些信息传递给播放器 SDK 进行播放。使用 VideoModel 具有以下优势:
说明
如需实现此功能,请集成 iOS 播放器 SDK 1.36.2.10 或以上版本。
本文将详细介绍在 iOS 客户端构造 VideoModel 播放源的两种方法。
视频数据可以来自第三方服务或火山引擎视频点播服务。典型的端到端数据流转路径如下:
TTVideoEngineVideoModelSource,并传递给播放器进行播放。在 iOS 客户端,您需要将视频数据(无论来自火山引擎视频点播服务还是第三方服务)转换为播放器可识别的 TTVideoEngineVideoModelSource 对象。SDK 支持以下两种方式实现这一转换,核心都是将数据映射为播放器内部所需的 TTVideoEngineModel 格式。
SDK 提供 BareVideoInfo 和 BareVideoModel 两个工具类,帮助您快速将各路视频流的属性聚合,并转换为 SDK 内部所需的数据类型。BareVideoInfo.h :
// // BareVideoInfo.h // #import <Foundation/Foundation.h> #import <TTSDK/TTVideoEngineHeader.h> NS_ASSUME_NONNULL_BEGIN /// 单个清晰度的播放信息 @interface BareVideoInfo : NSObject /// 可选值:video,audio @property (nonatomic, copy) NSString *fileType; /// 封装格式 @property (nonatomic, copy) NSString *format; /// 编码类型: h264, h265 @property (nonatomic, copy) NSString *codec; /// @required 清晰度 540p, 480p, 720p, 1080p, 2k, 4k... @property (nonatomic, copy) NSString *definition; /// 文件ID,可以与 md5 相同或是其他的唯一 ID nullable @property (nonatomic, copy) NSString *fileId; /// MD5 @property (nonatomic, copy) NSString *md5; /// @required 播放地址 @property (nonatomic, copy) NSString *mainPlayUrl; /// @optional 播放地址的过期时间 @property (nonatomic, copy) NSString *mainUrlExpireTime; /// 码率,单位 bps @property (nonatomic, assign) NSUInteger bitrate; /// 视频画面宽度 @property (nonatomic, assign) int width; /// 视频画面高度 @property (nonatomic, assign) int height; @end /// 多清晰度播放源数据模型 @interface BareVideoModel : NSObject @property (nonatomic, copy) NSString *vid; @property (nonatomic, assign) int version; @property (nonatomic, assign, readonly) int status; @property (nonatomic, assign) double duration; @property (nonatomic, assign) bool enableAdaptive; @property (nonatomic, copy) NSArray<BareVideoInfo *> *playInfoList; - (TTVideoEngineModel *)engineVideoModel; @end NS_ASSUME_NONNULL_END
BareVideoInfo.m:
// // BareVideoInfo.m // #import "BareVideoInfo.h" @implementation BareVideoInfo - (NSDictionary *)dictionaryRepresentation { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setValue:self.fileType forKey:@"FileType"]; [dict setValue:self.format forKey:@"Format"]; [dict setValue:self.codec forKey:@"Codec"]; [dict setValue:self.fileId forKey:@"FileId"]; [dict setValue:self.md5 forKey:@"Md5"]; [dict setValue:self.definition forKey:@"Definition"]; [dict setValue:self.mainPlayUrl forKey:@"MainPlayUrl"]; [dict setValue:self.mainUrlExpireTime forKey:@"MainUrlExpire"]; [dict setValue:@(self.bitrate) forKey:@"Bitrate"]; [dict setValue:@(self.width) forKey:@"Width"]; [dict setValue:@(self.height) forKey:@"Height"]; return [dict copy]; } @end @implementation BareVideoModel - (instancetype)init { self = [super init]; if (self) { _version = 4; _status = 10; } return self; } - (NSDictionary *)dictionaryRepresentation { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setValue:self.vid forKey:@"Vid"]; [dict setValue:@(self.status) forKey:@"Status"]; [dict setValue:@(self.version) forKey:@"Version"]; [dict setValue:@(self.duration) forKey:@"Duration"]; [dict setValue:@(self.enableAdaptive) forKey:@"EnableAdaptive"]; NSMutableArray *playInfoList = [NSMutableArray array]; for (BareVideoInfo *info in self.playInfoList) { NSDictionary *infoDict = [info dictionaryRepresentation]; [playInfoList addObject:infoDict]; } [dict setValue:playInfoList forKey:@"PlayInfoList"]; return dict.copy; } - (TTVideoEngineModel *)engineVideoModel { return [TTVideoEngineModel videoModelWithDict:[self dictionaryRepresentation]]; } @end
以下示例代码展示了如何将两路不同清晰度的视频流构造成一个 TTVideoEngineVideoModelSource。
#import "BareVideoInfo.h" - (TTVideoEngineVideoModelSource *)constructSource { // 1. 配置第一路视频流信息 (例如: 480p) BareVideoInfo *info1 = [[BareVideoInfo alloc] init]; info1.mainPlayUrl = @"http://example.com/sample1.mp4"; // **必须**。主 url info1.definition = @"480p"; // **必须**。视频清晰度标识 info1.fileType = @"video"; // **必须**。媒体类型 info1.codec = @"h264"; // **必须**。编码类型 (h264/h265) info1.md5 = @"your_md5_string"; // **必须**。文件 hash,作为唯一标识和缓存 key // --- 平滑切换和 ABR 所需信息 --- info1.bitrate = 1024; // 视频码率 info1.format = @"mp4"; // 封装格式 (如 "mp4", "hls") // 2. 配置第二路视频流信息 (例如: 720p) BareVideoInfo *info2 = [[BareVideoInfo alloc] init]; info2.mainPlayUrl = @"http://example.com/sample2.mp4"; info2.definition = @"720p"; info2.fileType = @"video"; info2.codec = @"h264"; info2.md5 = @"your_md5_string_2"; // --- 平滑切换和 ABR 所需信息 --- info2.bitrate = 2048; info2.format = @"mp4"; // 3. 将多路流信息聚合到一个 BareVideoModel 中 BareVideoModel *videoModel = [[BareVideoModel alloc] init]; videoModel.vid = @"your_video_id"; // **必须**。视频唯一 ID videoModel.playInfoList = @[info1, info2]; // 包含所有清晰度流的列表 // 如果需要支持平滑切换,必须设置为 YES。这表示所有流的视频帧是对齐的。 videoModel.enableAdaptive = YES; // 4. 利用工具类方法,生成 Engine 内部使用的 VideoModel 对象 TTVideoEngineModel *innerVideoModel = [videoModel engineVideoModel]; // 5. 构造最终的统一播放源对象 // 在此指定 Vid 和一个默认的起播清晰度 (例如 TTVideoEngineResolutionTypeHD) TTVideoEngineVideoModelSource *source = [[TTVideoEngineVideoModelSource alloc] initWithVid:videoModel.vid resolution:TTVideoEngineResolutionTypeHD]; // 6. 将生成的内部 Model 赋值给播放源 source.videoModel = innerVideoModel; return source; }
您的业务服务端获取到视频点播 GetPlayInfo 接口返回的 JSON 字符串时,可以使用以下方法构造 TTVideoEngineVideoModelSource 播放源:
/** * 从 JSON 字符串创建 TTVideoEngineVideoModelSource 实例的辅助方法。 * @param jsonString 来自火山引擎视频点播 GetPlayInfo 接口的 JSON 字符串。 * @return 一个 TTVideoEngineVideoModelSource 实例;如果解析失败则返回 nil。 */ + (TTVideoEngineVideoModelSource *)createFromJsonString:(NSString *)jsonString { // 1. 解析 JSON 字符串为 NSDictionary NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSError *error = nil; NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; if (error) { NSLog(@"JSON Parse Error: %@", error.localizedDescription); return nil; } // 2. 通过jsonDict创建TTVideoEngineModel TTVideoEngineModel *videoModel = [TTVideoEngineModel videoModelWithDict:jsonDict]; if (!videoModel) { NSLog(@"Create TTVideoEngineModel Error"); return nil; } // 3. 构造最终的统一播放源对象 // 在此指定 Vid 和一个默认的起播清晰度 (例如 TTVideoEngineResolutionTypeHD) TTVideoEngineVideoModelSource *source = [[TTVideoEngineVideoModelSource alloc] initWithVid:[videoModel.videoInfo getValueStr:VALUE_VIDEO_ID] resolution:TTVideoEngineResolutionTypeHD]; source.videoModel = videoModel; return source; }
传入的 JSON 字符串应该是 GetPlayInfo 接口响应体中 Result 字段对应的 JSON 对象,而非整个响应的字符串。Result 对象结构示例如下:
{ "Vid": "v029c1g10003civ2i5mqib*******", "Status": 1, "PosterUrl": "https://img.***.com/1234/8511abe****.jpeg", "Duration": 0.1, "FileType": "video", "EnableAdaptive": true, "TotalCount": 1 // ... 更多字段 }