m3u8 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,177 +3,251 @@ require 'spec_helper'
3
3
 
4
4
  describe M3u8::Writer do
5
5
  describe '#write' do
6
- it 'should render master playlist' do
7
- options = { uri: 'playlist_url', bandwidth: 6400,
8
- audio_codec: 'mp3' }
9
- item = M3u8::PlaylistItem.new(options)
10
- playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
11
- playlist.items << item
12
-
13
- output = "#EXTM3U\n" \
14
- "#EXT-X-VERSION:6\n" \
15
- "#EXT-X-INDEPENDENT-SEGMENTS\n" +
16
- %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
17
- ",BANDWIDTH=6400\nplaylist_url\n"
18
-
19
- io = StringIO.open
20
- writer = M3u8::Writer.new(io)
21
- writer.write(playlist)
22
- expect(io.string).to eq(output)
6
+ context 'when playlist is a master playlist' do
7
+ it 'writes playlist to io' do
8
+ playlist = M3u8::Playlist.new
9
+ options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
10
+ audio_codec: 'mp3' }
11
+ item = M3u8::PlaylistItem.new(options)
12
+ playlist.items << item
13
+ options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
14
+ width: 1920, height: 1080, profile: 'high', level: 4.1,
15
+ audio_codec: 'aac-lc' }
16
+ item = M3u8::PlaylistItem.new(options)
17
+ playlist.items << item
18
+ options = { data_id: 'com.test.movie.title', value: 'Test',
19
+ uri: 'http://test', language: 'en' }
20
+ item = M3u8::SessionDataItem.new(options)
21
+ playlist.items << item
22
+
23
+ expected = "#EXTM3U\n" +
24
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
25
+ ",BANDWIDTH=6400\nplaylist_url\n" \
26
+ '#EXT-X-STREAM-INF:PROGRAM-ID=2,' +
27
+ %(RESOLUTION=1920x1080,CODECS="avc1.640029,mp4a.40.2") +
28
+ ",BANDWIDTH=50000\nplaylist_url\n" +
29
+ %(#EXT-X-SESSION-DATA:DATA-ID="com.test.movie.title",) +
30
+ %(VALUE="Test",URI="http://test",LANGUAGE="en"\n)
31
+
32
+ io = StringIO.open
33
+ writer = described_class.new(io)
34
+ writer.write(playlist)
35
+ expect(io.string).to eq(expected)
36
+ end
37
+ end
23
38
 
24
- options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
25
- audio_codec: 'mp3' }
26
- item = M3u8::PlaylistItem.new(options)
27
- playlist = M3u8::Playlist.new
28
- playlist.items << item
39
+ context 'when playlist is a master playlist with single stream' do
40
+ it 'writes playlist to io' do
41
+ options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
42
+ audio_codec: 'mp3' }
43
+ item = M3u8::PlaylistItem.new(options)
44
+ playlist = M3u8::Playlist.new
45
+ playlist.items << item
46
+
47
+ expected = "#EXTM3U\n" +
48
+ %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
49
+ ",BANDWIDTH=6400\nplaylist_url\n"
50
+
51
+ io = StringIO.open
52
+ writer = described_class.new(io)
53
+ writer.write(playlist)
54
+ expect(io.string).to eq(expected)
55
+ end
56
+ end
29
57
 
30
- output = "#EXTM3U\n" +
31
- %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
32
- ",BANDWIDTH=6400\nplaylist_url\n"
58
+ context 'when playlist is a master playlist with header options' do
59
+ it 'writes playlist to io' do
60
+ options = { uri: 'playlist_url', bandwidth: 6400,
61
+ audio_codec: 'mp3' }
62
+ item = M3u8::PlaylistItem.new(options)
63
+ playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
64
+ playlist.items << item
65
+
66
+ expected = "#EXTM3U\n" \
67
+ "#EXT-X-VERSION:6\n" \
68
+ "#EXT-X-INDEPENDENT-SEGMENTS\n" +
69
+ %(#EXT-X-STREAM-INF:CODECS="mp4a.40.34") +
70
+ ",BANDWIDTH=6400\nplaylist_url\n"
71
+
72
+ io = StringIO.open
73
+ writer = described_class.new(io)
74
+ writer.write(playlist)
75
+ expect(io.string).to eq(expected)
76
+ end
77
+ end
33
78
 
34
- io = StringIO.open
35
- writer = M3u8::Writer.new(io)
36
- writer.write(playlist)
37
- expect(io.string).to eq(output)
79
+ context 'when playlist is a new master playlist' do
80
+ it 'writes playlist to io' do
81
+ io = StringIO.open
82
+ writer = described_class.new(io)
83
+ playlist = M3u8::Playlist.new(master: true)
84
+ writer.write(playlist)
85
+ expect(io.string).to eq("#EXTM3U\n")
86
+ end
87
+ end
38
88
 
39
- options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
40
- width: 1920, height: 1080, profile: 'high', level: 4.1,
41
- audio_codec: 'aac-lc' }
42
- item = M3u8::PlaylistItem.new(options)
43
- playlist = M3u8::Playlist.new
44
- playlist.items << item
89
+ context 'when playlist is a new media playlist' do
90
+ it 'writes playlist to io' do
91
+ io = StringIO.open
92
+ writer = described_class.new(io)
93
+ writer.write(M3u8::Playlist.new)
94
+
95
+ expected = "#EXTM3U\n" \
96
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
97
+ "#EXT-X-TARGETDURATION:10\n" \
98
+ "#EXT-X-ENDLIST\n"
99
+ expect(io.string).to eq(expected)
100
+ end
101
+ end
45
102
 
46
- output = "#EXTM3U\n" \
47
- '#EXT-X-STREAM-INF:PROGRAM-ID=2,RESOLUTION=1920x1080,' +
48
- %(CODECS="avc1.640029,mp4a.40.2",BANDWIDTH=50000\n) +
49
- "playlist_url\n"
103
+ context 'when playlist is a media playlist' do
104
+ it 'writes playlist to io' do
105
+ options = { version: 4, cache: false, target: 6.2, sequence: 1,
106
+ discontinuity_sequence: 10, type: 'EVENT',
107
+ iframes_only: true }
108
+ playlist = M3u8::Playlist.new(options)
109
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
110
+ item = M3u8::SegmentItem.new(options)
111
+ playlist.items << item
112
+
113
+ expected = "#EXTM3U\n" \
114
+ "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
115
+ "#EXT-X-VERSION:4\n" \
116
+ "#EXT-X-I-FRAMES-ONLY\n" \
117
+ "#EXT-X-MEDIA-SEQUENCE:1\n" \
118
+ "#EXT-X-DISCONTINUITY-SEQUENCE:10\n" \
119
+ "#EXT-X-ALLOW-CACHE:NO\n" \
120
+ "#EXT-X-TARGETDURATION:6\n" \
121
+ "#EXTINF:11.344644,\n" \
122
+ "1080-7mbps00000.ts\n" \
123
+ "#EXT-X-ENDLIST\n"
124
+
125
+ io = StringIO.open
126
+ writer = described_class.new(io)
127
+ writer.write(playlist)
128
+ expect(io.string).to eq(expected)
129
+ end
130
+ end
50
131
 
51
- io = StringIO.open
52
- writer = M3u8::Writer.new(io)
53
- writer.write(playlist)
54
- expect(io.string).to eq(output)
132
+ context 'when playlist is media playlist with keys' do
133
+ it 'writes playlist to io' do
134
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
135
+ item = M3u8::SegmentItem.new(options)
136
+ playlist = M3u8::Playlist.new(version: 7)
137
+ playlist.items << item
138
+
139
+ options = { method: 'AES-128', uri: 'http://test.key',
140
+ iv: 'D512BBF', key_format: 'identity',
141
+ key_format_versions: '1/3' }
142
+ item = M3u8::KeyItem.new(options)
143
+ playlist.items << item
144
+
145
+ options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
146
+ item = M3u8::SegmentItem.new(options)
147
+ playlist.items << item
148
+
149
+ expected = "#EXTM3U\n" \
150
+ "#EXT-X-VERSION:7\n" \
151
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
152
+ "#EXT-X-TARGETDURATION:10\n" \
153
+ "#EXTINF:11.344644,\n" \
154
+ "1080-7mbps00000.ts\n" +
155
+ %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
156
+ %(IV=D512BBF,KEYFORMAT="identity",) +
157
+ %(KEYFORMATVERSIONS="1/3"\n) +
158
+ "#EXTINF:11.261233,\n" \
159
+ "1080-7mbps00001.ts\n" \
160
+ "#EXT-X-ENDLIST\n"
161
+
162
+ io = StringIO.open
163
+ writer = described_class.new(io)
164
+ writer.write(playlist)
165
+ expect(io.string).to eq(expected)
166
+ end
167
+ end
55
168
 
169
+ it 'raises error if item types are mixed' do
56
170
  playlist = M3u8::Playlist.new
57
- options = { program_id: '1', uri: 'playlist_url', bandwidth: 6400,
58
- audio_codec: 'mp3' }
171
+ options = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
172
+ bandwidth: 540, playlist: 'test.url' }
59
173
  item = M3u8::PlaylistItem.new(options)
60
174
  playlist.items << item
61
- options = { program_id: '2', uri: 'playlist_url', bandwidth: 50_000,
62
- width: 1920, height: 1080, profile: 'high', level: 4.1,
63
- audio_codec: 'aac-lc' }
64
- item = M3u8::PlaylistItem.new(options)
65
- playlist.items << item
66
- options = { data_id: 'com.test.movie.title', value: 'Test',
67
- uri: 'http://test', language: 'en' }
68
- item = M3u8::SessionDataItem.new(options)
175
+
176
+ options = { duration: 10.991, segment: 'test.ts' }
177
+ item = M3u8::SegmentItem.new(options)
69
178
  playlist.items << item
70
179
 
71
- output = "#EXTM3U\n" +
72
- %(#EXT-X-STREAM-INF:PROGRAM-ID=1,CODECS="mp4a.40.34") +
73
- ",BANDWIDTH=6400\nplaylist_url\n#EXT-X-STREAM-INF:PROGRAM-ID=2," +
74
- %(RESOLUTION=1920x1080,CODECS="avc1.640029,mp4a.40.2") +
75
- ",BANDWIDTH=50000\nplaylist_url\n" +
76
- %(#EXT-X-SESSION-DATA:DATA-ID="com.test.movie.title",) +
77
- %(VALUE="Test",URI="http://test",LANGUAGE="en"\n)
78
-
79
- io = StringIO.open
80
- writer = M3u8::Writer.new(io)
81
- writer.write(playlist)
82
- expect(io.string).to eq(output)
180
+ message = 'Playlist is invalid.'
181
+ io = StringIO.new
182
+ writer = described_class.new(io)
183
+ expect { writer.write(playlist) }
184
+ .to raise_error(M3u8::PlaylistTypeError, message)
83
185
  end
186
+ end
84
187
 
85
- it 'should render playlist' do
86
- options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
87
- item = M3u8::SegmentItem.new(options)
88
- playlist = M3u8::Playlist.new(version: 7)
89
- playlist.items << item
188
+ describe '#write_footer' do
189
+ context 'when playlist is a master playlist' do
190
+ it 'does nothing' do
191
+ io = StringIO.open
192
+ writer = described_class.new(io)
193
+ playlist = M3u8::Playlist.new(master: true)
194
+ writer.write_footer(playlist)
90
195
 
91
- output = "#EXTM3U\n" \
92
- "#EXT-X-VERSION:7\n" \
93
- "#EXT-X-MEDIA-SEQUENCE:0\n" \
94
- "#EXT-X-TARGETDURATION:10\n" \
95
- "#EXTINF:11.344644,\n" \
96
- "1080-7mbps00000.ts\n" \
97
- "#EXT-X-ENDLIST\n"
98
- io = StringIO.open
99
- writer = M3u8::Writer.new(io)
100
- writer.write(playlist)
101
- expect(io.string).to eq(output)
102
-
103
- options = { method: 'AES-128', uri: 'http://test.key',
104
- iv: 'D512BBF', key_format: 'identity',
105
- key_format_versions: '1/3' }
106
- item = M3u8::KeyItem.new(options)
107
- playlist.items << item
196
+ expect(io.string).to be_empty
197
+ end
198
+ end
108
199
 
109
- options = { duration: 11.261233, segment: '1080-7mbps00001.ts' }
110
- item = M3u8::SegmentItem.new(options)
111
- playlist.items << item
200
+ context 'when playlist is a media playlist' do
201
+ it 'writes end list tag' do
202
+ io = StringIO.open
203
+ writer = described_class.new(io)
204
+ playlist = M3u8::Playlist.new(master: false)
205
+ writer.write_footer(playlist)
112
206
 
113
- output = "#EXTM3U\n" \
114
- "#EXT-X-VERSION:7\n" \
115
- "#EXT-X-MEDIA-SEQUENCE:0\n" \
116
- "#EXT-X-TARGETDURATION:10\n" \
117
- "#EXTINF:11.344644,\n" \
118
- "1080-7mbps00000.ts\n" +
119
- %(#EXT-X-KEY:METHOD=AES-128,URI="http://test.key",) +
120
- %(IV=D512BBF,KEYFORMAT="identity",KEYFORMATVERSIONS="1/3"\n) +
121
- "#EXTINF:11.261233,\n" \
122
- "1080-7mbps00001.ts\n" \
123
- "#EXT-X-ENDLIST\n"
124
- io = StringIO.open
125
- writer = M3u8::Writer.new(io)
126
- writer.write(playlist)
127
- expect(io.string).to eq(output)
128
-
129
- options = { version: 4, cache: false, target: 12, sequence: 1,
130
- type: 'EVENT', iframes_only: true }
131
- playlist = M3u8::Playlist.new(options)
132
- options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
133
- item = M3u8::SegmentItem.new(options)
134
- playlist.items << item
135
-
136
- output = "#EXTM3U\n" \
137
- "#EXT-X-PLAYLIST-TYPE:EVENT\n" \
138
- "#EXT-X-VERSION:4\n" \
139
- "#EXT-X-I-FRAMES-ONLY\n" \
140
- "#EXT-X-MEDIA-SEQUENCE:1\n" \
141
- "#EXT-X-ALLOW-CACHE:NO\n" \
142
- "#EXT-X-TARGETDURATION:12\n" \
143
- "#EXTINF:11.344644,\n" \
144
- "1080-7mbps00000.ts\n" \
145
- "#EXT-X-ENDLIST\n"
146
- io = StringIO.open
147
- writer = M3u8::Writer.new(io)
148
- writer.write(playlist)
149
- expect(io.string).to eq(output)
207
+ expect(io.string).to eq("#EXT-X-ENDLIST\n")
208
+ end
150
209
  end
210
+ end
151
211
 
152
- it 'should render the target duration as a decimal-integer' do
153
- playlist = M3u8::Playlist.new(target: 6.2)
154
- io = StringIO.open
155
- writer = M3u8::Writer.new(io)
156
- writer.write(playlist)
157
- expect(io.string).to include('#EXT-X-TARGETDURATION:6')
212
+ describe '#write_header' do
213
+ context 'when playlist is a master playlist' do
214
+ it 'writes header content only' do
215
+ playlist = M3u8::Playlist.new(version: 6, independent_segments: true)
216
+ options = { uri: 'playlist_url', bandwidth: 6400,
217
+ audio_codec: 'mp3' }
218
+ item = M3u8::PlaylistItem.new(options)
219
+ playlist.items << item
220
+ expect(playlist.master?).to be true
221
+
222
+ expected = "#EXTM3U\n" \
223
+ "#EXT-X-VERSION:6\n" \
224
+ "#EXT-X-INDEPENDENT-SEGMENTS\n"
225
+
226
+ io = StringIO.open
227
+ writer = described_class.new(io)
228
+ writer.write_header(playlist)
229
+ expect(io.string).to eq(expected)
230
+ end
158
231
  end
159
232
 
160
- it 'should raise error on write if item types are mixed' do
161
- playlist = M3u8::Playlist.new
233
+ context 'when playlist is a media playlist' do
234
+ it 'writes header content only' do
235
+ playlist = M3u8::Playlist.new(version: 7)
236
+ options = { duration: 11.344644, segment: '1080-7mbps00000.ts' }
237
+ item = M3u8::SegmentItem.new(options)
238
+ playlist.items << item
162
239
 
163
- hash = { program_id: 1, width: 1920, height: 1080, codecs: 'avc',
164
- bandwidth: 540, playlist: 'test.url' }
165
- item = M3u8::PlaylistItem.new(hash)
166
- playlist.items << item
240
+ io = StringIO.open
241
+ writer = described_class.new(io)
242
+ writer.write_header(playlist)
167
243
 
168
- hash = { duration: 10.991, segment: 'test.ts' }
169
- item = M3u8::SegmentItem.new(hash)
170
- playlist.items << item
244
+ expected = "#EXTM3U\n" \
245
+ "#EXT-X-VERSION:7\n" \
246
+ "#EXT-X-MEDIA-SEQUENCE:0\n" \
247
+ "#EXT-X-TARGETDURATION:10\n"
171
248
 
172
- message = 'Playlist is invalid.'
173
- io = StringIO.new
174
- writer = M3u8::Writer.new(io)
175
- expect { writer.write(playlist) }
176
- .to raise_error(M3u8::PlaylistTypeError, message)
249
+ expect(io.string).to eq(expected)
250
+ end
177
251
  end
178
252
  end
179
253
  end
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.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Deckard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
195
  version: '0'
196
196
  requirements: []
197
197
  rubyforge_project:
198
- rubygems_version: 2.5.2
198
+ rubygems_version: 2.6.11
199
199
  signing_key:
200
200
  specification_version: 4
201
201
  summary: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).