You need to enable JavaScript to run this app.
导航
构造 VideoModel 播放源
最近更新时间:2025.11.06 16:16:21首次发布时间:2025.10.21 17:14:22
复制全文
我的收藏
有用
有用
无用
无用

VideoModel 是火山引擎播放器 SDK 针对多清晰度场景推出的面向极致体验的自研协议。通过 VideoModel,您可以将包含多清晰度、多格式视频流的复杂信息整合为单个对象,并直接将这些信息传递给播放器 SDK 进行播放。使用 VideoModel 具有以下优势:

  • 降低首帧耗时:与 Vid 模式(播放器 SDK 通过 Vid 请求视频点播服务端获取播放信息)相比,通过 VideoModel 播放,可以减少一次客户端到视频点播服务端的网络请求。您的业务服务端可以直接生成或者客户端组装生成 VideoModel 播放源,将其传给播放器 SDK 即可开始加载,从而有效缩短视频首帧的加载时间。
  • 功能完整性:VideoModel 完整地承载了视频点播的所有能力,包括 ABR、平滑切换、私有加密等。
  • 灵活性高:您可以完全掌控 VideoModel 的生成和下发时机,更好地与您的业务逻辑集成。

说明

如需实现此功能,请集成 iOS 播放器 SDK 1.36.2.10 或以上版本。

本文将详细介绍在 iOS 客户端构造 VideoModel 播放源的两种方法。

数据流转路径

视频数据可以来自第三方服务或火山引擎视频点播服务。典型的端到端数据流转路径如下:

  1. 数据获取:您的业务服务端获取视频的元数据。
    • 来源一:调用火山引擎视频点播的 GetPlayInfo 接口,获取一个结构化的 JSON 对象。
    • 来源二:从第三方服务或内部媒资系统获取,数据格式由您的业务自定义。
  2. 数据下发:业务服务端通过 API 将获取到的视频数据下发给您的 iOS 客户端。
  3. 数据映射:iOS 客户端接收到数据后,执行关键的映射操作,最终将填充好数据的工具类对象转换为 TTVideoEngineVideoModelSource,并传递给播放器进行播放。

构造方法

在 iOS 客户端,您需要将视频数据(无论来自火山引擎视频点播服务还是第三方服务)转换为播放器可识别的 TTVideoEngineVideoModelSource 对象。SDK 支持以下两种方式实现这一转换,核心都是将数据映射为播放器内部所需的 TTVideoEngineModel 格式。

方式 1:借助 BareVideoInfo/BareVideoModel 工具类构造(推荐)

SDK 提供 BareVideoInfoBareVideoModel 两个工具类,帮助您快速将各路视频流的属性聚合,并转换为 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;
}

方式 2:通过 JSON 直接构造(适用于视频数据来自火山引擎视频点播服务)

您的业务服务端获取到视频点播 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
    // ... 更多字段
}