mamba 2.2.1

mamba 2.2.1

David Coufal 维护。



mamba 2.2.1

  • Comcast

Build Status Code Coverage from codecov Carthage compatible Cocoapod status GitHub release License Platform

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的引用。

MasterPlaylistVariantPlaylist对象可高度编辑。

验证播放列表

使用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。

--