项目作者: Comcast

项目描述 :
Mamba is a Swift iOS, tvOS and macOS framework to parse, validate and write HTTP Live Streaming (HLS) data.
高级语言: Swift
项目地址: git://github.com/Comcast/mamba.git
创建时间: 2017-04-20T14:41:38Z
项目社区:https://github.com/Comcast/mamba

开源协议:Apache License 2.0

下载


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

Mamba

Mamba is a Swift iOS, tvOS and macOS framework to parse, validate and write HTTP Live Streaming (HLS) data.

This framework is used in Comcast applications to parse, validate, edit and write HLS playlists to deliver video to millions of customers. It was written by the Comcast VIPER Player Platform team.

Mamba Project Goals:

  • Simple-to-use parsing, editing and writing of HLS playlists.

  • Maximum performance. We required our parsing library to parse very large HLS playlists (12 hour Video-On-Demand) on low end phones in a few milliseconds. A internal core C library is used for very fast parsing of large playlists.

Requires

  • XCode 10.2+
  • Swift 4+ (written in Swift 5)
  • iOS 9+ or tvOS 9+ or macOS 10.13+

Usage

Parsing a HLS Playlist

Create an PlaylistParser.

  1. let parser = PlaylistParser()

Parse your HLS playlist using the parser. Here’s the asynchronous version:

  1. let myPlaylistData: Data = ... // source of HLS data
  2. let myPlaylistURL: URL = ... // the URL of this playlist resource
  3. parser.parse(playlistData: myPlaylistData,
  4. url: myPlaylistURL,
  5. callback: { result in
  6. switch result {
  7. case .parsedVariant(let variant):
  8. // do something with the parsed VariantPlaylist
  9. myVariantPlaylistHandler(variantPlaylist: variant)
  10. break
  11. case .parsedMaster(let master):
  12. // do something with the parsed MasterPlaylist
  13. myMasterPlaylistHandler(masterPlaylist: master)
  14. break
  15. case .parseError(let error):
  16. // handle the ParserError
  17. myErrorHandler(error: error)
  18. break
  19. }
  20. })

And here’s the synchronous version:

  1. // note: could take several milliseconds for large transcripts!
  2. let result = parser.parse(playlistData: myPlaylistData,
  3. url: myPlaylistURL)
  4. switch result {
  5. case .parsedVariant(let variant):
  6. // do something with the parsed VariantPlaylist object
  7. myVariantPlaylistHandler(variantPlaylist: variant)
  8. break
  9. case .parsedMaster(let master):
  10. // do something with the parsed MasterPlaylist object
  11. myMasterPlaylistHandler(masterPlaylist: master)
  12. break
  13. case .parseError(let error):
  14. // handle the ParserError
  15. myErrorHandler(error: error)
  16. break
  17. }

You now have an HLS playlist object.

MasterPlaylist and VariantPlaylist

These structs are in-memory representations of a HLS playlist.

They include:

  • The URL of the playlist.
  • An array of PlaylistTags that represent each line in the HLS playlist. This array is editable, so you can make edits to the playlist.
  • Utility functions to tell if a variant playlist is a Live, VOD or Event style playlist.
  • Helpful functionality around the structure of a playlist. This structure is kept up to date behind the scenes as the playlist is edited.
    • VariantPlaylist: includes calculated references to the “header”, “footer” and all the video segments and the metadata around them.
    • MasterPlaylist: includes calculated references to the variant streams and their URL’s.

MasterPlaylist and VariantPlaylist objects are highly editable.

Validating a Playlist

Validate your playlist using the PlaylistValidator.

  1. let variantPlaylist: VariantPlaylistInterface = myVariantPlaylistFactoryFunction()
  2. let masterPlaylist: MasterPlaylistInterface = myMasterPlaylistFactoryFunction()
  3. let variantissues = PlaylistValidator.validate(variantPlaylist: variantPlaylist)
  4. let masterissues = PlaylistValidator.validate(masterPlaylist: masterPlaylist)

It returns an array of PlaylistValidationIssues found with the playlist. They each have a description and a severity associated with them.

We currently implement only a subset of the HLS validation rules as described in the HLS specification. Improving our HLS validation coverage would be a most welcome pull request!

Writing a HLS Playlist

Create a PlaylistWriter.

  1. let writer = PlaylistWriter()

Write your HLS playlist to a stream.

  1. let stream: OutputStream = ... // stream to receive the HLS Playlist
  2. do {
  3. try writer.write(playlist: variantPlaylist, toStream: stream)
  4. try writer.write(playlist: masterPlaylist, toStream: stream)
  5. }
  6. catch {
  7. // there was an error severe enough for us to stop writing the data
  8. }

There is also a utility function in the playlist to write out the playlist to a Data object.

  1. do {
  2. let variantData = try variantPlaylist.write()
  3. let masterData = try masterPlaylist.write()
  4. // do something with the resulting data
  5. myDataHandler(data: variantData)
  6. myDataHandler(data: masterData)
  7. }
  8. catch {
  9. // there was an error severe enough for us to stop writing the data
  10. }

Using Custom Tags

Natively, Mamba only understands HLS tags as defined in the Pantos IETF specification. If you’d like to add support for a custom set of tags, you’ll need to create them as a object implementing PlaylistTagDescriptor. Please look at PantosTag or one of the examples in the unit tests for sample code.

If you have any custom PlaylistTagDescriptor collections you’d like to parse alongside the standard Pantos tags, pass them in through this PlaylistParser initializer:

  1. enum MyCustomTagSet: String {
  2. // define your custom tags here
  3. case EXT_MY_CUSTOM_TAG = "EXT-MY-CUSTOM-TAG"
  4. }
  5. extension MyCustomTagSet: PlaylistTagDescriptor {
  6. ... // conform to HLSTagDescriptor here
  7. }
  8. let customParser = PlaylistParser(tagTypes: [MyCustomTagSet.self])

If there is specfic data inside your custom tag that you’d like to access, e.g.

  1. #EXT-MY-CUSTOM-TAG:CUSTOMDATA1="Data1",CUSTOMDATA2="Data1"

you can define that data in an enum that conforms to PlaylistTagValueIdentifier:

  1. enum MyCustomValueIdentifiers: String {
  2. // define your custom value identifiers here
  3. case CUSTOMDATA1 = "CUSTOMDATA1"
  4. case CUSTOMDATA2 = "CUSTOMDATA2"
  5. }
  6. extension MyCustomValueIdentifiers: PlaylistTagValueIdentifier {
  7. ... // conform to PlaylistTagValueIdentifier here
  8. }

You can now look through PlaylistTag objects for your custom tag values just as if it were a valuetype defined in the HLS specification.

Important Note About Memory Safety

In order to achieve our performance goals, the internal C parser for HLS had to minimize the amount of heap memory allocated.

This meant that, for each PlaylistTag object that is included in a MasterPlaylist/VariantPlaylist, instead of using a swift String to represent data, we use a MambaStringRef, which is a object that is a reference into the memory of the original data used to parse the playlist. This greatly speeds parsing, but comes at a cost: these PlaylistTag objects are unsafe to use beyond the lifetime of their parent MasterPlaylist/VariantPlaylist.

In general, this is no problem. Normal usage of a MasterPlaylist/VariantPlaylist would be (1) Parse the playlist, (2) Edit by manipulating PlaylistTags (3) Write the playlist.

If you do, for some reason, need to access PlaylistTag data beyond the lifetime of the parent MasterPlaylist/VariantPlaylist object, you’ll need to make a copy of all MambaStringRef data of interest into a regular swift String. There’s a string conversion function in MambaStringRef to accomplish this.

Note: We have legacy branches for mamba 1.x at our main 1.x branch and our develop 1.x branch. We are maintaining that branch, but may stop updating in the near future. Users are welcome to submit pull requests against the 1.x branches or potentially fork if they do not want to move to 2.0