m3u8 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 862441ecee4bbc8fe057efdefe16649ba31b6276
4
- data.tar.gz: 2f9995c6089dcb771fc6a6781936952b4ad03200
3
+ metadata.gz: afc4ec1f4627283241fc7e4f5eeb33d3ee69b17b
4
+ data.tar.gz: 4e04ba6b95728b5d5aa0c448e27d12331c37e5c5
5
5
  SHA512:
6
- metadata.gz: 94a2443f80f7f7f8d780bdd2d0fbc7ef2b8fe22c79e4753a292e1e48dfa740fd02817774408658adf2545fb82aecd93d56be156a1b0affe18d302376f2ff5394
7
- data.tar.gz: 278b7d96d867e107d693874a35ee101c10b51800379b261e6d7b7c7728c55fec0b8a37e08af6080a01bd9e303c35edfbaff496d85c0eb1b6f0c1b473368b0bbe
6
+ metadata.gz: d360dd63410659e7e98b8d001886ae76ef925a8118c6850b7876cd6bbd1a88fc7917565cb407846b3e498aab15f510a3e5c921e36b46d76daa52059ff5c26266
7
+ data.tar.gz: 02dc01399f31cf0111c20123c06c86316ff1c54bb76238e8cbc884af44d2de5d974dda25a81287b19c44872ed12d088c661c462983762d06d361b357ffa4ca30
@@ -0,0 +1,7 @@
1
+ ### 0.2.1 (11/26/2014) - Moved codec generation / validation to PlaylistItem, allowing for the flexibility to either specify :audio, :level, :profile when creating new instances (codecs value will be automatically generated) or just set the codecs attribute directly.
2
+
3
+ ### 0.2.0 (11/25/2014) - Added ability to parse m3u8 playlists with new Reader class. Introduced two new classes to represent the items that make up a Playlist: PlaylistItem and SegmentItem.
4
+
5
+ ### 0.1.3 (11/23/2014) - Fixed bug in codec string generation.
6
+
7
+ ### 0.1.0 (10/06/2014) - Initial release, provides ability to generate m3u8 playlists (master and segments).
data/README.md CHANGED
@@ -31,24 +31,32 @@ Or install it yourself as:
31
31
 
32
32
  #create a master playlist and add child playlists for adaptive bitrate streaming:
33
33
  playlist = M3u8::Playlist.new
34
- options = { :width => 1920, :height => 1080, :profile => 'high', :level => 4.1, :audio => 'aac-lc'}
35
- playlist.add_playlist '2', 'http://playlist_url_or_path_file', 50000, options
34
+ #create a new playlist item with options
35
+ options = { program_id: 1, width: 1920, height: 1080, width: 1920, height: 1080,
36
+ profile: 'high', level: 4.1, audio: 'aac-lc', bitrate: 540,
37
+ playlist: 'test.url' }
38
+ item = M3u8::PlaylistItem.new options
39
+ playlist.items.push item
40
+
41
+ #alternatively you can set codecs rather than having it generated automatically:
42
+ options = { program_id: 1, width: 1920, height: 1080, width: 1920, height: 1080,
43
+ codecs: 'avc1.66.30,mp4a.40.2', bitrate: 540, playlist: 'test.url' }
44
+ item = M3u8::PlaylistItem.new options
36
45
 
37
- #create a standard playlist and add TS segments:
46
+ #create a standard playlist and add MPEG-TS segments:
38
47
  playlist = M3u8::Playlist.new
39
- playlist.add_segment 11.344644, "1080-7mbps00000.ts"
40
-
41
- #you can also add PlaylistItem or SegmentItem instances directly to the array of items in the playlist (new in v.2.0):
42
- item = SegmentItem.new(duration: 11, segment: 'test.ts')
48
+ #create a new segment item with options
49
+ item = M3u8::SegmentItem.new duration: 11, segment: 'test.ts'
43
50
  playlist.items.push item
44
51
 
45
52
  #just get the codec string for custom use
46
- options = { :profile => 'baseline', :level => 3.0, :audio => 'aac-lc' }
53
+ options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
47
54
  codecs = M3u8::Playlist.codecs options
48
- => "avc1.66.30,mp4a.40.2"
55
+ # => "avc1.66.30,mp4a.40.2"
49
56
 
50
- #specify options for playlist, these are ignored if playlist becomes a master playlist (child playlist added):
51
- options = { :version => 1, :cache => false, :target => 12, :sequence => 1}
57
+ #specify options for playlist, these are ignored if playlist becomes a master playlist
58
+ # (child playlist added):
59
+ options = { version: 1, cache: false, target: 12, sequence: 1 }
52
60
  playlist = M3u8::Playlist.new options
53
61
 
54
62
  #You can pass an IO object to the write method
@@ -76,13 +84,23 @@ Or install it yourself as:
76
84
  reader = M3u8::Reader.new
77
85
  playlist = reader.read file
78
86
  playlist.master?
79
- => true
87
+ # => true
80
88
 
81
89
  #acess items in playlist:
82
90
  playlist.items
83
91
 
84
92
  playlist.items.first
85
- => #<M3u8::PlaylistItem:0x007fa569bc7698 @program_id="1", @resolution="1920x1080", @codecs="avc1.640028,mp4a.40.2", @bandwidth="5042000", @playlist="hls/1080-7mbps/1080-7mbps.m3u8">
93
+ # => #<M3u8::PlaylistItem:0x007fa569bc7698 @program_id="1", @resolution="1920x1080",
94
+ # @codecs="avc1.640028,mp4a.40.2", @bandwidth="5042000",
95
+ # @playlist="hls/1080-7mbps/1080-7mbps.m3u8">
96
+
97
+ #create a new playlist item with options
98
+ options = { program_id: 1, width: 1920, height: 1080, width: 1920, height: 1080,
99
+ profile: 'high', level: 4.1, audio: 'aac-lc', bitrate: 540,
100
+ playlist: 'test.url' }
101
+ item = M3u8::PlaylistItem.new options
102
+ #add it to the top of the playlist
103
+ playlist.items.insert 0, item
86
104
 
87
105
  ## Features
88
106
  * Distinction between segment and master playlists is handled automatically (no need to use a different class).
@@ -1,7 +1,6 @@
1
1
  module M3u8
2
2
  class Playlist
3
3
  attr_accessor :items, :version, :cache, :target, :sequence
4
- MISSING_CODEC_MESSAGE = 'An audio or video codec should be provided.'
5
4
  NON_MASTER_ERROR_MESSAGE = 'Playlist is not a master playlist, playlist' \
6
5
  ' can not be added.'
7
6
  MASTER_ERROR_MESSAGE = 'Playlist is a master playlist, segment can not ' \
@@ -13,43 +12,18 @@ module M3u8
13
12
  self.items = []
14
13
  end
15
14
 
16
- def assign_options(options)
17
- options = {
18
- version: 3,
19
- sequence: 0,
20
- cache: true,
21
- target: 10
22
- }.merge options
23
-
24
- self.version = options[:version]
25
- self.sequence = options[:sequence]
26
- self.cache = options[:cache]
27
- self.target = options[:target]
28
- end
29
-
30
15
  def self.codecs(options = {})
31
- playlist = Playlist.new
32
- playlist.codecs options
16
+ item = PlaylistItem.new options
17
+ item.codecs
33
18
  end
34
19
 
35
20
  def add_playlist(program_id, playlist, bitrate, options = {})
36
- options = {
37
- width: nil,
38
- height: nil,
39
- profile: nil,
40
- level: nil,
41
- audio: nil
42
- }.merge options
43
-
44
21
  validate_playlist_type true
45
22
 
46
- codecs = codecs(audio: options[:audio], profile: options[:profile],
47
- level: options[:level])
48
- fail MissingCodecError, MISSING_CODEC_MESSAGE if codecs.nil?
49
-
50
23
  params = { program_id: program_id, playlist: playlist, bitrate: bitrate,
51
24
  width: options[:width], height: options[:height],
52
- codecs: codecs }
25
+ audio: options[:audio], profile: options[:profile],
26
+ level: options[:level] }
53
27
  item = PlaylistItem.new params
54
28
  items.push item
55
29
  end
@@ -62,27 +36,6 @@ module M3u8
62
36
  items.push item
63
37
  end
64
38
 
65
- def codecs(options = {})
66
- options = {
67
- audio: nil,
68
- profile: nil,
69
- level: nil
70
- }.merge options
71
-
72
- audio_codec = audio_codec options[:audio]
73
- video_codec = video_codec options[:profile], options[:level]
74
-
75
- if video_codec.nil?
76
- return audio_codec
77
- else
78
- if audio_codec.nil?
79
- return video_codec
80
- else
81
- return "#{video_codec},#{audio_codec}"
82
- end
83
- end
84
- end
85
-
86
39
  def write(output)
87
40
  validate
88
41
 
@@ -115,6 +68,20 @@ module M3u8
115
68
 
116
69
  private
117
70
 
71
+ def assign_options(options)
72
+ options = {
73
+ version: 3,
74
+ sequence: 0,
75
+ cache: true,
76
+ target: 10
77
+ }.merge options
78
+
79
+ self.version = options[:version]
80
+ self.sequence = options[:sequence]
81
+ self.cache = options[:cache]
82
+ self.target = options[:target]
83
+ end
84
+
118
85
  def validate
119
86
  return if valid?
120
87
  fail PlaylistTypeError, MIXED_TYPE_ERROR_MESSAGE
@@ -147,26 +114,5 @@ module M3u8
147
114
  def cache_string
148
115
  cache ? 'YES' : 'NO'
149
116
  end
150
-
151
- def audio_codec(audio)
152
- return if audio.nil?
153
- return 'mp4a.40.2' if audio.downcase == 'aac-lc'
154
- return 'mp4a.40.5' if audio.downcase == 'he-aac'
155
- return 'mp4a.40.34' if audio.downcase == 'mp3'
156
- end
157
-
158
- def video_codec(profile, level)
159
- return if profile.nil? || level.nil?
160
-
161
- profile = profile.downcase
162
- return 'avc1.66.30' if profile == 'baseline' && level == 3.0
163
- return 'avc1.42001f' if profile == 'baseline' && level == 3.1
164
- return 'avc1.77.30' if profile == 'main' && level == 3.0
165
- return 'avc1.4d001f' if profile == 'main' && level == 3.1
166
- return 'avc1.4d0028' if profile == 'main' && level == 4.0
167
- return 'avc1.64001f' if profile == 'high' && level == 3.1
168
- return 'avc1.640028' if profile == 'high' &&
169
- (level == 4.0 || level == 4.1)
170
- end
171
117
  end
172
118
  end
@@ -1,6 +1,8 @@
1
1
  module M3u8
2
2
  class PlaylistItem
3
- attr_accessor :program_id, :width, :height, :codecs, :bitrate, :playlist
3
+ attr_accessor :program_id, :width, :height, :codecs, :bitrate, :playlist,
4
+ :audio, :level, :profile
5
+ MISSING_CODEC_MESSAGE = 'Audio or video codec info should be provided.'
4
6
 
5
7
  def initialize(params = {})
6
8
  params.each do |key, value|
@@ -13,16 +15,59 @@ module M3u8
13
15
  "#{width}x#{height}"
14
16
  end
15
17
 
18
+ def codecs
19
+ return @codecs unless @codecs.nil?
20
+
21
+ audio_codec = audio_codec audio
22
+ video_codec = video_codec profile, level
23
+
24
+ if video_codec.nil?
25
+ return audio_codec
26
+ else
27
+ if audio_codec.nil?
28
+ return video_codec
29
+ else
30
+ return "#{video_codec},#{audio_codec}"
31
+ end
32
+ end
33
+ end
34
+
16
35
  def to_s
36
+ validate
17
37
  "#EXT-X-STREAM-INF:PROGRAM-ID=#{program_id},#{resolution_format}" +
18
38
  %(CODECS="#{codecs}",BANDWIDTH=#{bitrate}\n#{playlist})
19
39
  end
20
40
 
21
41
  private
22
42
 
43
+ def validate
44
+ fail MissingCodecError, MISSING_CODEC_MESSAGE if codecs.nil?
45
+ end
46
+
23
47
  def resolution_format
24
48
  return if resolution.nil?
25
49
  "RESOLUTION=#{resolution},"
26
50
  end
51
+
52
+ def audio_codec(audio)
53
+ return if audio.nil?
54
+ return 'mp4a.40.2' if audio.downcase == 'aac-lc'
55
+ return 'mp4a.40.5' if audio.downcase == 'he-aac'
56
+ return 'mp4a.40.34' if audio.downcase == 'mp3'
57
+ end
58
+
59
+ def video_codec(profile, level)
60
+ return if profile.nil? || level.nil?
61
+
62
+ profile = profile.downcase
63
+ return 'avc1.66.30' if profile == 'baseline' && level == 3.0
64
+ return 'avc1.42001f' if profile == 'baseline' && level == 3.1
65
+ return 'avc1.77.30' if profile == 'main' && level == 3.0
66
+ return 'avc1.4d001f' if profile == 'main' && level == 3.1
67
+ return 'avc1.4d0028' if profile == 'main' && level == 4.0
68
+ return 'avc1.64001f' if profile == 'high' && level == 3.1
69
+ return 'avc1.640028' if profile == 'high' &&
70
+ (level == 4.0 || level == 4.1)
71
+ end
27
72
  end
28
73
  end
@@ -1,3 +1,3 @@
1
1
  module M3u8
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -30,4 +30,88 @@ describe M3u8::PlaylistItem do
30
30
  %(CODECS="avc",BANDWIDTH=540\ntest.url)
31
31
  expect(output).to eq expected
32
32
  end
33
+
34
+ it 'should generate codecs string' do
35
+ item = M3u8::PlaylistItem.new
36
+ expect(item.codecs).to be_nil
37
+
38
+ item = M3u8::PlaylistItem.new codecs: 'test'
39
+ expect(item.codecs).to eq 'test'
40
+
41
+ item = M3u8::PlaylistItem.new audio: 'aac-lc'
42
+ expect(item.codecs).to eq 'mp4a.40.2'
43
+
44
+ item = M3u8::PlaylistItem.new audio: 'AAC-LC'
45
+ expect(item.codecs).to eq 'mp4a.40.2'
46
+
47
+ item = M3u8::PlaylistItem.new audio: 'he-aac'
48
+ expect(item.codecs).to eq 'mp4a.40.5'
49
+
50
+ item = M3u8::PlaylistItem.new audio: 'HE-AAC'
51
+ expect(item.codecs).to eq 'mp4a.40.5'
52
+
53
+ item = M3u8::PlaylistItem.new audio: 'he-acc1'
54
+ expect(item.codecs).to be_nil
55
+
56
+ item = M3u8::PlaylistItem.new audio: 'mp3'
57
+ expect(item.codecs).to eq 'mp4a.40.34'
58
+
59
+ item = M3u8::PlaylistItem.new audio: 'MP3'
60
+ expect(item.codecs).to eq 'mp4a.40.34'
61
+
62
+ options = { profile: 'baseline', level: 3.0 }
63
+ item = M3u8::PlaylistItem.new options
64
+ expect(item.codecs).to eq 'avc1.66.30'
65
+
66
+ options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
67
+ item = M3u8::PlaylistItem.new options
68
+ expect(item.codecs).to eq 'avc1.66.30,mp4a.40.2'
69
+
70
+ options = { profile: 'baseline', level: 3.0, audio: 'mp3' }
71
+ item = M3u8::PlaylistItem.new options
72
+ expect(item.codecs).to eq 'avc1.66.30,mp4a.40.34'
73
+
74
+ options = { profile: 'baseline', level: 3.1 }
75
+ item = M3u8::PlaylistItem.new options
76
+ expect(item.codecs).to eq 'avc1.42001f'
77
+
78
+ options = { profile: 'baseline', level: 3.1, audio: 'he-aac' }
79
+ item = M3u8::PlaylistItem.new options
80
+ expect(item.codecs).to eq 'avc1.42001f,mp4a.40.5'
81
+
82
+ options = { profile: 'main', level: 3.0 }
83
+ item = M3u8::PlaylistItem.new options
84
+ expect(item.codecs).to eq 'avc1.77.30'
85
+
86
+ options = { profile: 'main', level: 3.0, audio: 'aac-lc' }
87
+ item = M3u8::PlaylistItem.new options
88
+ expect(item.codecs).to eq 'avc1.77.30,mp4a.40.2'
89
+
90
+ options = { profile: 'main', level: 3.1 }
91
+ item = M3u8::PlaylistItem.new options
92
+ expect(item.codecs).to eq 'avc1.4d001f'
93
+
94
+ options = { profile: 'main', level: 4.0 }
95
+ item = M3u8::PlaylistItem.new options
96
+ expect(item.codecs).to eq 'avc1.4d0028'
97
+
98
+ options = { profile: 'high', level: 3.1 }
99
+ item = M3u8::PlaylistItem.new options
100
+ expect(item.codecs).to eq 'avc1.64001f'
101
+
102
+ options = { profile: 'high', level: 4.0 }
103
+ item = M3u8::PlaylistItem.new options
104
+ expect(item.codecs).to eq 'avc1.640028'
105
+
106
+ options = { profile: 'high', level: 4.1 }
107
+ item = M3u8::PlaylistItem.new options
108
+ expect(item.codecs).to eq 'avc1.640028'
109
+ end
110
+
111
+ it 'should raise error if codecs are missing' do
112
+ params = { program_id: 1, bitrate: 540, playlist: 'test.url' }
113
+ item = M3u8::PlaylistItem.new params
114
+ message = 'Audio or video codec info should be provided.'
115
+ expect { item.to_s }.to raise_error(M3u8::MissingCodecError, message)
116
+ end
33
117
  end
@@ -2,77 +2,9 @@ require 'spec_helper'
2
2
 
3
3
  describe M3u8::Playlist do
4
4
  it 'should generate codecs string' do
5
- codecs = M3u8::Playlist.codecs
6
- expect(codecs).to be_nil
7
-
8
- codecs = M3u8::Playlist.codecs audio: 'aac-lc'
9
- expect(codecs).to eq 'mp4a.40.2'
10
-
11
- codecs = M3u8::Playlist.codecs audio: 'AAC-LC'
12
- expect(codecs).to eq 'mp4a.40.2'
13
-
14
- codecs = M3u8::Playlist.codecs audio: 'he-aac'
15
- expect(codecs).to eq 'mp4a.40.5'
16
-
17
- codecs = M3u8::Playlist.codecs audio: 'HE-AAC'
18
- expect(codecs).to eq 'mp4a.40.5'
19
-
20
- codecs = M3u8::Playlist.codecs audio: 'he-acc1'
21
- expect(codecs).to be_nil
22
-
23
- codecs = M3u8::Playlist.codecs audio: 'mp3'
24
- expect(codecs).to eq 'mp4a.40.34'
25
-
26
- codecs = M3u8::Playlist.codecs audio: 'MP3'
27
- expect(codecs).to eq 'mp4a.40.34'
28
-
29
- options = { profile: 'baseline', level: 3.0 }
30
- codecs = M3u8::Playlist.codecs options
31
- expect(codecs).to eq 'avc1.66.30'
32
-
33
5
  options = { profile: 'baseline', level: 3.0, audio: 'aac-lc' }
34
6
  codecs = M3u8::Playlist.codecs options
35
7
  expect(codecs).to eq 'avc1.66.30,mp4a.40.2'
36
-
37
- options = { profile: 'baseline', level: 3.0, audio: 'mp3' }
38
- codecs = M3u8::Playlist.codecs options
39
- expect(codecs).to eq 'avc1.66.30,mp4a.40.34'
40
-
41
- options = { profile: 'baseline', level: 3.1 }
42
- codecs = M3u8::Playlist.codecs options
43
- expect(codecs).to eq 'avc1.42001f'
44
-
45
- options = { profile: 'baseline', level: 3.1, audio: 'he-aac' }
46
- codecs = M3u8::Playlist.codecs options
47
- expect(codecs).to eq 'avc1.42001f,mp4a.40.5'
48
-
49
- options = { profile: 'main', level: 3.0 }
50
- codecs = M3u8::Playlist.codecs options
51
- expect(codecs).to eq 'avc1.77.30'
52
-
53
- options = { profile: 'main', level: 3.0, audio: 'aac-lc' }
54
- codecs = M3u8::Playlist.codecs options
55
- expect(codecs).to eq 'avc1.77.30,mp4a.40.2'
56
-
57
- options = { profile: 'main', level: 3.1 }
58
- codecs = M3u8::Playlist.codecs options
59
- expect(codecs).to eq 'avc1.4d001f'
60
-
61
- options = { profile: 'main', level: 4.0 }
62
- codecs = M3u8::Playlist.codecs options
63
- expect(codecs).to eq 'avc1.4d0028'
64
-
65
- options = { profile: 'high', level: 3.1 }
66
- codecs = M3u8::Playlist.codecs options
67
- expect(codecs).to eq 'avc1.64001f'
68
-
69
- options = { profile: 'high', level: 4.0 }
70
- codecs = M3u8::Playlist.codecs options
71
- expect(codecs).to eq 'avc1.640028'
72
-
73
- options = { profile: 'high', level: 4.1 }
74
- codecs = M3u8::Playlist.codecs options
75
- expect(codecs).to eq 'avc1.640028'
76
8
  end
77
9
 
78
10
  it 'should render master playlist' do
@@ -244,13 +176,6 @@ describe M3u8::Playlist do
244
176
  expect(playlist.valid?).to be false
245
177
  end
246
178
 
247
- it 'should raise error if codecs are missing' do
248
- playlist = M3u8::Playlist.new
249
- message = 'An audio or video codec should be provided.'
250
- expect { playlist.add_playlist '1', 'playlist_url', 6400 }
251
- .to raise_error(M3u8::MissingCodecError, message)
252
- end
253
-
254
179
  it 'should expose options as attributes' do
255
180
  options = { version: 1, cache: false, target: 12, sequence: 1 }
256
181
  playlist = M3u8::Playlist.new options
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: m3u8
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Deckard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-25 00:00:00.000000000 Z
11
+ date: 2014-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -62,6 +62,7 @@ files:
62
62
  - ".gitignore"
63
63
  - ".rspec"
64
64
  - ".travis.yml"
65
+ - CHANGELOG.md
65
66
  - Gemfile
66
67
  - LICENSE.txt
67
68
  - README.md