You need to enable JavaScript to run this app.
视频点播

视频点播

Copy page
Download PDF
进阶功能
构造 VideoModel 播放源
Copy page
Download PDF
构造 VideoModel 播放源

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

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

tip

如需实现此功能,请集成 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
    // ... 更多字段
}
Last updated: 2025.11.06 16:16:21