Mamba
Mamba 是一个用于解析、验证和写入 HTTP Live Streaming (HLS) 数据的 Swift iOS、tvOS 和 macOS 框架。
此框架在 Comcast 应用程序中使用,用于解析、验证、编辑和写入 HLS 播单,以便向数百万客户提供视频服务。它是通过 Comcast VIPER 播放器平台团队编写的。
Mamba 项目目标
-
简单易用的 HLS 播单解析、编辑和编写
-
最高性能。我们需要我们的解析库在低端手机上解析非常大的 HLS 播单(12小时的点播视频),并在几毫秒内完成。我们使用了一个内部核心 C 库来解析大型播单。
需求
- XCode 10.2+
- Swift 4+(用 Swift 5 编写)
- iOS 9+ 或 tvOS 9+ 或 macOS 10.13+
使用
解析 HLS 播单
创建一个 PlaylistParser
。
let parser = PlaylistParser()
使用解析器解析你的 HLS 播单。以下是异步版本
let myPlaylistData: Data = ... // source of HLS data
let myPlaylistURL: URL = ... // the URL of this playlist resource
parser.parse(playlistData: myPlaylistData,
url: myPlaylistURL,
callback: { result in
switch result {
case .parsedVariant(let variant):
// do something with the parsed VariantPlaylist
myVariantPlaylistHandler(variantPlaylist: variant)
break
case .parsedMaster(let master):
// do something with the parsed MasterPlaylist
myMasterPlaylistHandler(masterPlaylist: master)
break
case .parseError(let error):
// handle the ParserError
myErrorHandler(error: error)
break
}
})
这是同步版本
// note: could take several milliseconds for large transcripts!
let result = parser.parse(playlistData: myPlaylistData,
url: myPlaylistURL)
switch result {
case .parsedVariant(let variant):
// do something with the parsed VariantPlaylist object
myVariantPlaylistHandler(variantPlaylist: variant)
break
case .parsedMaster(let master):
// do something with the parsed MasterPlaylist object
myMasterPlaylistHandler(masterPlaylist: master)
break
case .parseError(let error):
// handle the ParserError
myErrorHandler(error: error)
break
}
你现在有一个 HLS 播单对象。
母播放列表和变体播放列表
这些结构是HLS播放列表的内存表示。
它们包括
- 播放列表的
URL
。 - 一个代表HLS播放列表中每一行的
PlaylistTag
数组。此数组可编辑,因此您可以修改播放列表。 - 实用函数,用于判断变体播放列表是实时、点播还是事件样式播放列表。
- 有关播放列表结构的实用功能。该结构在幕后随时更新,因为播放列表被编辑。
VariantPlaylist
:包括对“头部”、“尾部”以及围绕它们的视频片段和相关元数据的引用。MasterPlaylist
:包括对变体流及其URL的引用。
MasterPlaylist
和VariantPlaylist
对象可高度编辑。
验证播放列表
使用PlaylistValidator
验证您的播放列表。
let variantPlaylist: VariantPlaylistInterface = myVariantPlaylistFactoryFunction()
let masterPlaylist: MasterPlaylistInterface = myMasterPlaylistFactoryFunction()
let variantissues = PlaylistValidator.validate(variantPlaylist: variantPlaylist)
let masterissues = PlaylistValidator.validate(masterPlaylist: masterPlaylist)
它返回播放列表中找到的PlaylistValidationIssue
数组。每个问题都有相应的描述和严重性。
我们目前只实现了一部分HLS验证规则,这些规则在HLS规范中有描述。 improvements to our HLS validation coverage would be especially welcome as a pull request!
编写HLS播放列表
创建一个PlaylistWriter
。
let writer = PlaylistWriter()
将您的HLS播放列表写入流中。
let stream: OutputStream = ... // stream to receive the HLS Playlist
do {
try writer.write(playlist: variantPlaylist, toStream: stream)
try writer.write(playlist: masterPlaylist, toStream: stream)
}
catch {
// there was an error severe enough for us to stop writing the data
}
播放列表中还包含一个实用函数可以将其写入到Data
对象。
do {
let variantData = try variantPlaylist.write()
let masterData = try masterPlaylist.write()
// do something with the resulting data
myDataHandler(data: variantData)
myDataHandler(data: masterData)
}
catch {
// there was an error severe enough for us to stop writing the data
}
使用自定义标签
默认情况下,Mamba只能理解由Pantos IETF规范定义的HLS标签。如果您想添加对一组自定义标签的支持,您需要创建实现PlaylistTagDescriptor
的对象。请查看PantosTag
或单元测试中的示例以获取示例代码。
如果您希望解析标准Pantos标签外的任何自定义 PlaylistTagDescriptor
集合,请通过此 PlaylistParser
初始化器传入
enum MyCustomTagSet: String {
// define your custom tags here
case EXT_MY_CUSTOM_TAG = "EXT-MY-CUSTOM-TAG"
}
extension MyCustomTagSet: PlaylistTagDescriptor {
... // conform to HLSTagDescriptor here
}
let customParser = PlaylistParser(tagTypes: [MyCustomTagSet.self])
如果您的自定义标签中存在您想要访问的特定数据,例如
#EXT-MY-CUSTOM-TAG:CUSTOMDATA1="Data1",CUSTOMDATA2="Data1"
您可以将这些数据定义在符合 PlaylistTagValueIdentifier
的枚举中
enum MyCustomValueIdentifiers: String {
// define your custom value identifiers here
case CUSTOMDATA1 = "CUSTOMDATA1"
case CUSTOMDATA2 = "CUSTOMDATA2"
}
extension MyCustomValueIdentifiers: PlaylistTagValueIdentifier {
... // conform to PlaylistTagValueIdentifier here
}
现在您可以像使用HLS规范中定义的valuetype一样在 PlaylistTag
对象中搜索您自定义标签的值
关于内存安全的重要说明
为了达到我们的性能目标,HLS的内部C解析器不得不最小化分配的堆内存量。
这意味着,对于包含在 MasterPlaylist/VariantPlaylist
中的每个 PlaylistTag
对象,我们不是使用Swift的 String
来表示数据,而是使用 MambaStringRef
,它是一个指向原始数据内存的引用对象,该原始数据用于解析播放列表。这大大提高了解析速度,但代价是:这些 PlaylistTag
对象在其父 MasterPlaylist/VariantPlaylist
的生命周期结束后是不安全的。
一般来说,这不会有问题。正常使用 MasterPlaylist/VariantPlaylist
的流程通常是(1)解析播放列表,(2)通过操作 PlaylistTag
进行编辑,(3)写入播放列表。
如果您出于某些原因需要超出其父 MasterPlaylist/VariantPlaylist
对象的生命周期来访问 PlaylistTag
数据,您需要将所有感兴趣的 MambaStringRef
数据复制到一个常规Swift的 String
中。在 MambaStringRef
中有一个字符串转换函数可以完成这个任务。
--
注意:我们在 主1.x分支 和 开发1.x分支 上有mamba 1.x的旧版分支。我们正在维护这个分支,但未来可能会停止更新。欢迎用户在1.x分支上提交pull请求或者在不想迁移到2.0的情况下进行fork。
--