m3u8 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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