Parse and generate m3u8 playlists for Apple HTTP Live Streaming (HLS) in Ruby.

Gem Version Build Status Coverage Status Code Climate Dependency Status security

m3u8

m3u8 provides easy generation and parsing of m3u8 playlists defined in the HTTP Live Streaming (HLS) Internet Draft published by Apple.

  • The library completely implements version 20 of the HLS Internet Draft.
  • Provides parsing of an m3u8 playlist into an object model from any File, StringIO, or string.
  • Provides ability to write playlist to a File or StringIO or expose as string via to_s.
  • Distinction between a master and media playlist is handled automatically (single Playlist class).
  • Optionally, the library can automatically generate the audio/video codecs string used in the CODEC attribute based on specified H.264, AAC, or MP3 options (such as Profile/Level).

Installation

Add this line to your application's Gemfile:

gem 'm3u8'

And then execute:

$ bundle

Or install it yourself as:

$ gem install m3u8

Usage (creating playlists)

Create a master playlist and add child playlists for adaptive bitrate streaming:

require 'm3u8'
playlist = M3u8::Playlist.new

Create a new playlist item with options:

options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
            audio_codec: 'aac-lc', bandwidth: 540, uri: 'test.url' }
item = M3u8::PlaylistItem.new(options)
playlist.items << item

Add alternate audio, camera angles, closed captions and subtitles by creating MediaItem instances and adding them to the Playlist:

hash = { type: 'AUDIO', group_id: 'audio-lo', language: 'fre',
         assoc_language: 'spoken', name: 'Francais', autoselect: true,
         default: false, forced: true, uri: 'frelo/prog_index.m3u8' }
item = M3u8::MediaItem.new(hash)
playlist.items << item

Create a standard playlist and add MPEG-TS segments via SegmentItem. You can also specify options for this type of playlist, however these options are ignored if playlist becomes a master playlist (anything but segments added):

options = { version: 1, cache: false, target: 12, sequence: 1 }
playlist = M3u8::Playlist.new(options)

item = M3u8::SegmentItem.new(duration: 11, segment: 'test.ts')
playlist.items << item

You can pass an IO object to the write method:

require 'tempfile'
file = Tempfile.new('test')
playlist.write(file)

You can also access the playlist as a string:

playlist.to_s

M3u8::Writer is the class that handles generating the playlist output.

Alternatively you can set codecs rather than having it generated automatically:

options = { width: 1920, height: 1080, codecs: 'avc1.66.30,mp4a.40.2',
            bandwidth: 540, uri: 'test.url' }
item = M3u8::PlaylistItem.new(options)

Usage (parsing playlists)

file = File.open 'spec/fixtures/master.m3u8'
playlist = M3u8::Playlist.read(file)
playlist.master?
# => true

Access items in playlist:

playlist.items.first
#  => #<M3u8::PlaylistItem:0x007fa569bc7698 @program_id="1", @resolution="1920x1080", 
#  @codecs="avc1.640028,mp4a.40.2", @bandwidth="5042000", 
#  @playlist="hls/1080-7mbps/1080-7mbps.m3u8">

Create a new playlist item with options:

options = { width: 1920, height: 1080, profile: 'high', level: 4.1,
            audio_codec: 'aac-lc', bandwidth: 540, uri: 'test.url' }
item = M3u8::PlaylistItem.new(options)
#add it to the top of the playlist
playlist.items.unshift(item)

M3u8::Reader is the class handles parsing if you want more control over the process.

Usage (misc)

Generate the codec string based on audio and video codec options without dealing a playlist instance:

options = { profile: 'baseline', level: 3.0, audio_codec: 'aac-lc' }
codecs = M3u8::Playlist.codecs(options)
# => "avc1.66.30,mp4a.40.2"
  • Values for audio_codec (codec name): aac-lc, he-aac, mp3
  • Values for profile (H.264 Profile): baseline, main, high.
  • Values for level (H.264 Level): 3.0, 3.1, 4.0, 4.1.

Not all Levels and Profiles can be combined and validation is not currently implemented, consult H.264 documentation for further details.

Roadmap

  • Implement validation of all tags, attributes, and values per HLS I-D.
  • Perhaps support for different versions of HLS I-D, defaulting to latest.

Contributing

  1. Fork it ( https://github.com/sethdeckard/m3u8/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Run the specs, make sure they pass and that new features are covered. Code coverage should be 100%.
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create a new Pull Request

License

MIT License - See LICENSE.txt for details.

Comments
  • Defined require order

    Defined require order

    #<NameError: uninitialized constant M3u8::KeyItem::Encryptable>
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8/key_item.rb:4:in `<class:KeyItem>'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8/key_item.rb:3:in `<module:M3u8>'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8/key_item.rb:1:in `<top (required)>'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8.rb:2:in `require'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8.rb:2:in `block in <top (required)>'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8.rb:2:in `each'
    /apps/video-processing/vendor/bundle/ruby/2.2.0/gems/m3u8-0.6.2/lib/m3u8.rb:2:in `<top (required)>'
    
  • Iptv support

    Iptv support

    Hi,

    This is a great M3U8 library, I was looking to create an application for build lists on the fly and this was doing the trick but I needed to add some live channels too and it wasn't possible, so I decided to change the library in order to allows me to do it. Maybe someone else is looking for this in the internet so I decided to share with you, in case you thing is helpful.

    Regards,

    Neftali

  • get segment from TimeItem

    get segment from TimeItem

    Hello, I've tried parsing my file

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-TARGETDURATION:10
    #EXT-X-MEDIA-SEQUENCE:1582061
    #EXTINF:10, no desc
    #EXT-X-PROGRAM-DATE-TIME:2016-01-01T18:22:09Z
    20160321T150304_2_5560256_1582061.ts
    #EXTINF:10, no desc
    #EXT-X-PROGRAM-DATE-TIME:2016-01-01T18:22:19Z
    20160321T150304_2_5560256_1582062.ts
    

    I get a list of TimeItem which makes sens, but how can I get the filename (.ts) ? Does this lib support this format? thanks

  • Feature request: Type conversion from M3u8::Playlist to M3u8::PlaylistItem

    Feature request: Type conversion from M3u8::Playlist to M3u8::PlaylistItem

    Recently I had a use case where I was looking to convert a M3u8::Playlist to M3u8::PlaylistItem. I was a bit surprised it's not supported by this gem, and that PlaylistItem instances need to be built from an options hash. This doesn't seem like a rare use case, especially when working with a master playlists, parsing media playlists they contain, working with them, and building a new master playlist as a result.

    Here's a short example use case I'm imagining:

    my_file = File.open('spec/fixtures/media_playlist-664.m3u8')
    media_playlist = M3u8::Playlist.read(my_file)
    playlist_item = M3u8::PlaylistItem.new(media_playlist)
    

    My temporary workaround for this is to dump all of a Playlist's instance variables into a hash (excluding items, etc.) and building a new PlaylistItem from that. If there's a more intuitive way to do this that I'm missing, please let me know.

    A secondary solution could be to implement a Playlist#to_options method that returns an options hash that allows for easy creation of a PlaylistItem:

    my_file = File.open('spec/fixtures/media_playlist-664.m3u8')
    media_playlist = M3u8::Playlist.read(my_file)
    playlist_item = M3u8::PlaylistItem.new(media_playlist.to_options)
    

    If you are on board with the feature request, I'll be happy to write the specs and open a PR.

    Thanks!

  • #EXT-X-ENDLIST needs to be set when the stream finishes

    #EXT-X-ENDLIST needs to be set when the stream finishes

    If a live stream finishes, the #EXT-X-ENDLIST tag must be included in the end

    See 6.2.1. General Server Responsibilities

    If the Media Playlist contains the final Media Segment of the presentation then the Playlist file MUST contain the EXT-X-ENDLIST tag; this allows clients to minimize unproductive Playlist reloads.

  • Add NAME attribute to playlist item

    Add NAME attribute to playlist item

    Support non-standard Name attribute in playlist item.

    This is used by Wowza and JWPlayer to show a quality selector option in the player (see https://support.jwplayer.com/customer/portal/articles/1430240-hls-adaptive-streaming )

  • added EXT-X-DISCONTINUITY support

    added EXT-X-DISCONTINUITY support

    This patch added "EXT-X-DISCONTINUITY" tag support. "EXT-X-DISCONTINUITY" tag could merge multiple m3u8 playlist to one.

    https://tools.ietf.org/html/draft-pantos-http-live-streaming-13#section-3.4.11

  • Do not require codecs for M3u8::PlaylistItem#to_s. Do not include CODECS in EXT-X-STREAM-INF if codec not recognized.

    Do not require codecs for M3u8::PlaylistItem#to_s. Do not include CODECS in EXT-X-STREAM-INF if codec not recognized.

    When the video profile is not recognized (e.g. "Constrained Baseline"), EXT-X-STREAM-INF will be written with only the audio codec. This causes Android to play only the audio stream and iOS 10 to not play at all (there may be other behaviour on different versions). The HLS spec indicates only that CODECS "SHOULD" be included.

    This PR makes the following changes to M3u8::PlaylistItem:

    • no longer enforces non-nil codecs in to_s
    • does not set CODECS if either video codec string or audio codec string can not be determined from the input (level, profile, audio_codec)
  • M3u8::Playlist.read(

    M3u8::Playlist.read("path/to/file") fails silently

    Had a few minutes of head scratching when I tried calling M3u8::Playlist.read with a path before I realized I needed an IO object. Calling read with a path doesn't throw an error and returns a playlist regardless

    irb(main):002:0> M3u8::Playlist.read("~/Desktop/long_hls")
    => #<M3u8::Playlist:0x007fb77388de20 @version=nil, @sequence=0, @cache=nil, @target=10, @type=nil, @iframes_only=false, @independent_segments=false, @items=[]>
    
  • fix parsing when resolution not set

    fix parsing when resolution not set

    Fix for not correctly parsing playlists with no resolution set

    Also possible to fix by making "initialize options" the last line in PlaylistItem.parse presumably as it used to work but this fix seems clearer.

  • Support for Name attribute in #EXT-X-STREAM-INF

    Support for Name attribute in #EXT-X-STREAM-INF

    Hi,

    would you consider a PR that would add the non-standard Name attribute to #EXT-X-STREAM-INF?

    This is used by Wowza and JWPlayer to show a quality selector option in the player (see https://support.jwplayer.com/customer/portal/articles/1430240-hls-adaptive-streaming )

    Thanks

  • Request/bug: Support iTunes' m3u8 files

    Request/bug: Support iTunes' m3u8 files

    Such as the files that iTunes outputs when exporting playlists.

    It's understandable (and clearly necessary) to have a library that handles HLS m3u8 files, it would just also be nice to import these other standard files.

  • #EXT-X-PROGRAM-DATE-TIME missing?

    #EXT-X-PROGRAM-DATE-TIME missing?

    https://github.com/sethdeckard/m3u8/blob/6e2a54b7f64cf2c1c93b958e31e0aa79e343db09/lib/m3u8/segment_item.rb#L14

    It looks like when this gets rendered it's missing the #EXT-X-PROGRAM-DATE-TIME, only rendering the time. I'm not sure if this was an intentional design decision or a bug? It looks like it's been in there for 5 years if it is a bug, so wanted to make sure before working on a PR.

    Details:

    ...
    playlist.items << M3u8::SegmentItem.new(program_date_time: Time.now, duration: 10, segment: 'http://example.com/segment-1.aac')
    ....
    

    Expected:

    #EXTINF:9.770667,
    #EXT-X-PROGRAM-DATE-TIME: 2020-11-25 14:27:00 -0600
    http://example.com/segment-1.aac
    

    Actual:

    #EXTINF:9.770667,
    2020-11-25 14:27:00 -0600
    http://example.com/segment-1.aac
    

    the latter seems to cause players to try and request "2020-11-25 14:27:00 -0600" as a fragment url.

    I worked around the issue for the timebeing by passing the "#EXT-X-PROGRAM-DATE-TIME: #{Time.now}", as the program_date_time attribute, which feels a little clunky unless I'm missing the reasoning for it being this way. Thoughts?

Library and program to parse and forward HAProxy logs

haminer Library and program to parse and forward HAProxy logs. Supported forwarder, Influxdb Requirements Go for building from source code git for dow

Aug 17, 2022
Changelog management tool, avoid merge conflicts and generate markdown changelogs.

chalog This is chalog, a changelog management tool. With chalog you can manage your project's changelog in a simple markdown format, split across mult

Jul 7, 2022
Goal is to generate logger and tracer wraps around a certain struct
Goal is to generate logger and tracer wraps around a certain struct

Goal is to generate logger and tracer wraps around a certain struct

Feb 13, 2022
Go-generate-cv - Simple CV generator developed in Go

go-generate-cv Simple CV generator developed in Go Milestone Have a form to fill

Jan 10, 2022
Generate vector tiles for the entire planet on relatively low spec hardware.
Generate vector tiles for the entire planet on relatively low spec hardware.

Sequentially Generate Planet Mbtiles Sequentially generate and merge an entire planet.mbtiles vector tileset on low memory/power devices for free. com

Dec 21, 2022
Package httpretty prints the HTTP requests you make with Go pretty on your terminal.

httpretty Package httpretty prints the HTTP requests of your Go programs pretty on your terminal screen. It is mostly inspired in curl's --verbose mod

Jan 8, 2023
xlog is a logger for net/context aware HTTP applications
xlog is a logger for net/context aware HTTP applications

⚠️ Check zerolog, the successor of xlog. HTTP Handler Logger xlog is a logger for net/context aware HTTP applications. Unlike most loggers, xlog will

Sep 26, 2022
🌿 Automated HTTP (microservices) supervisor

Epazote ?? Automated HTTP (microservices) supervisor Epazote automatically update/add services specified in a file call epazote.yml. Periodically chec

Nov 9, 2022
TeaWeb-可视化的Web代理服务。DEMO: http://teaos.cn:7777
 TeaWeb-可视化的Web代理服务。DEMO: http://teaos.cn:7777

TeaWeb - 可视化的Web代理服务 TeaWeb是一款集静态资源、缓存、代理、统计、日志、安全、监控于一体的可视化智能WebServer。目标是 做一个能让程序员和运维工程师喝着茶、唱着歌,就能把事情完成的一个智能化的简单易用的产品。 TeaWeb使用Go语言实现,在高可定制化前提下,保证高性

Dec 25, 2022
HTTP request logger for Golang
HTTP request logger for Golang

Horus ?? Introduction Horus is a request logger and viewer for Go. It allows developers log and view http requests made to their web application. Inst

Dec 27, 2022
A simple http service that generates *.PDF reports from Grafana dashboards.
A simple http service that generates *.PDF reports from Grafana dashboards.

Grafana reporter A simple http service that generates *.PDF reports from Grafana dashboards. Requirements Runtime requirements pdflatex installed and

Dec 27, 2022
Package for easy logging to logstash http input from microservices

Micro Logger package for easy logging to logstash http input from microservices

Dec 28, 2021
Gin adapter for standard net/http middleware

midgin An adapter to use standard net/http middleware in Gin. Overview Gin is a very capable web framework, but it does not directly support standard

Feb 12, 2022
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.

The open-source platform for monitoring and observability. Grafana allows you to query, visualize, alert on and understand your metrics no matter wher

Jan 3, 2023
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Nov 10, 2022
Simple and configurable Logging in Go, with level, formatters and writers

go-log Logging package similar to log4j for the Golang. Support dynamic log level Support customized formatter TextFormatter JSONFormatter Support mul

Sep 26, 2022
A Go (golang) package providing high-performance asynchronous logging, message filtering by severity and category, and multiple message targets.

ozzo-log Other languages 简体中文 Русский Description ozzo-log is a Go package providing enhanced logging support for Go programs. It has the following fe

Dec 17, 2022
Cloudinsight Agent is a system tool that monitors system processes and services, and sends information back to your Cloudinsight account.

Cloudinsight Agent 中文版 README Cloudinsight Agent is written in Go for collecting metrics from the system it's running on, or from other services, and

Nov 3, 2022
Distributed simple and robust release management and monitoring system.
Distributed simple and robust release management and monitoring system.

Agente Distributed simple and robust release management and monitoring system. **This project on going work. Road map Core system First worker agent M

Nov 17, 2022