m3u8 0.8.2 → 1.8.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 +5 -5
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +31 -0
- data/CHANGELOG.md +107 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +1 -1
- data/README.md +524 -40
- data/Rakefile +1 -0
- data/bin/m3u8 +6 -0
- data/lib/m3u8/attribute_formatter.rb +47 -0
- data/lib/m3u8/bitrate_item.rb +31 -0
- data/lib/m3u8/builder.rb +48 -0
- data/lib/m3u8/byte_range.rb +10 -0
- data/lib/m3u8/cli/inspect_command.rb +97 -0
- data/lib/m3u8/cli/validate_command.rb +24 -0
- data/lib/m3u8/cli.rb +116 -0
- data/lib/m3u8/codecs.rb +89 -0
- data/lib/m3u8/content_steering_item.rb +45 -0
- data/lib/m3u8/date_range_item.rb +135 -64
- data/lib/m3u8/define_item.rb +54 -0
- data/lib/m3u8/discontinuity_item.rb +3 -0
- data/lib/m3u8/encryptable.rb +27 -30
- data/lib/m3u8/error.rb +1 -0
- data/lib/m3u8/gap_item.rb +14 -0
- data/lib/m3u8/key_item.rb +7 -0
- data/lib/m3u8/map_item.rb +16 -5
- data/lib/m3u8/media_item.rb +48 -76
- data/lib/m3u8/part_inf_item.rb +35 -0
- data/lib/m3u8/part_item.rb +67 -0
- data/lib/m3u8/playback_start.rb +19 -12
- data/lib/m3u8/playlist.rb +221 -13
- data/lib/m3u8/playlist_item.rb +128 -124
- data/lib/m3u8/preload_hint_item.rb +54 -0
- data/lib/m3u8/reader.rb +86 -28
- data/lib/m3u8/rendition_report_item.rb +48 -0
- data/lib/m3u8/scte35.rb +130 -0
- data/lib/m3u8/scte35_bit_reader.rb +51 -0
- data/lib/m3u8/scte35_segmentation_descriptor.rb +54 -0
- data/lib/m3u8/scte35_splice_insert.rb +62 -0
- data/lib/m3u8/scte35_splice_null.rb +8 -0
- data/lib/m3u8/scte35_time_signal.rb +19 -0
- data/lib/m3u8/segment_item.rb +37 -3
- data/lib/m3u8/server_control_item.rb +69 -0
- data/lib/m3u8/session_data_item.rb +17 -28
- data/lib/m3u8/session_key_item.rb +8 -1
- data/lib/m3u8/skip_item.rb +48 -0
- data/lib/m3u8/time_item.rb +10 -0
- data/lib/m3u8/version.rb +1 -1
- data/lib/m3u8/writer.rb +24 -1
- data/lib/m3u8.rb +30 -6
- data/m3u8.gemspec +12 -12
- data/spec/fixtures/content_steering.m3u8 +10 -0
- data/spec/fixtures/daterange_playlist.m3u8 +14 -0
- data/spec/fixtures/encrypted_discontinuity.m3u8 +17 -0
- data/spec/fixtures/event_playlist.m3u8 +18 -0
- data/spec/fixtures/gap_playlist.m3u8 +14 -0
- data/spec/fixtures/ll_hls_advanced.m3u8 +18 -0
- data/spec/fixtures/ll_hls_playlist.m3u8 +20 -0
- data/spec/fixtures/master_full.m3u8 +14 -0
- data/spec/fixtures/master_v13.m3u8 +8 -0
- data/spec/lib/m3u8/bitrate_item_spec.rb +26 -0
- data/spec/lib/m3u8/builder_spec.rb +352 -0
- data/spec/lib/m3u8/byte_range_spec.rb +1 -0
- data/spec/lib/m3u8/cli/inspect_command_spec.rb +102 -0
- data/spec/lib/m3u8/cli/validate_command_spec.rb +39 -0
- data/spec/lib/m3u8/cli_spec.rb +104 -0
- data/spec/lib/m3u8/content_steering_item_spec.rb +56 -0
- data/spec/lib/m3u8/date_range_item_spec.rb +159 -31
- data/spec/lib/m3u8/define_item_spec.rb +59 -0
- data/spec/lib/m3u8/discontinuity_item_spec.rb +1 -0
- data/spec/lib/m3u8/gap_item_spec.rb +12 -0
- data/spec/lib/m3u8/key_item_spec.rb +1 -0
- data/spec/lib/m3u8/map_item_spec.rb +1 -0
- data/spec/lib/m3u8/media_item_spec.rb +34 -0
- data/spec/lib/m3u8/part_inf_item_spec.rb +27 -0
- data/spec/lib/m3u8/part_item_spec.rb +67 -0
- data/spec/lib/m3u8/playback_start_spec.rb +4 -5
- data/spec/lib/m3u8/playlist_item_spec.rb +130 -17
- data/spec/lib/m3u8/playlist_spec.rb +545 -13
- data/spec/lib/m3u8/preload_hint_item_spec.rb +57 -0
- data/spec/lib/m3u8/reader_spec.rb +376 -29
- data/spec/lib/m3u8/rendition_report_item_spec.rb +56 -0
- data/spec/lib/m3u8/round_trip_spec.rb +152 -0
- data/spec/lib/m3u8/scte35_bit_reader_spec.rb +106 -0
- data/spec/lib/m3u8/scte35_segmentation_descriptor_spec.rb +143 -0
- data/spec/lib/m3u8/scte35_spec.rb +94 -0
- data/spec/lib/m3u8/scte35_splice_insert_spec.rb +185 -0
- data/spec/lib/m3u8/scte35_splice_null_spec.rb +12 -0
- data/spec/lib/m3u8/scte35_time_signal_spec.rb +50 -0
- data/spec/lib/m3u8/segment_item_spec.rb +47 -0
- data/spec/lib/m3u8/server_control_item_spec.rb +64 -0
- data/spec/lib/m3u8/session_data_item_spec.rb +1 -0
- data/spec/lib/m3u8/session_key_item_spec.rb +1 -0
- data/spec/lib/m3u8/skip_item_spec.rb +48 -0
- data/spec/lib/m3u8/time_item_spec.rb +1 -0
- data/spec/lib/m3u8/writer_spec.rb +69 -30
- data/spec/lib/m3u8_spec.rb +1 -0
- data/spec/spec_helper.rb +4 -87
- metadata +70 -129
- data/.hound.yml +0 -3
- data/.travis.yml +0 -8
- data/Guardfile +0 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
describe M3u8::MediaItem do
|
|
@@ -24,6 +25,16 @@ describe M3u8::MediaItem do
|
|
|
24
25
|
expect(item.characteristics).to eq('public.html')
|
|
25
26
|
expect(item.channels).to eq('6')
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
it 'assigns v13 attributes from options' do
|
|
30
|
+
options = { type: 'AUDIO', group_id: 'aac', name: 'English',
|
|
31
|
+
stable_rendition_id: 'audio-en', bit_depth: 16,
|
|
32
|
+
sample_rate: 44_100 }
|
|
33
|
+
item = described_class.new(options)
|
|
34
|
+
expect(item.stable_rendition_id).to eq('audio-en')
|
|
35
|
+
expect(item.bit_depth).to eq(16)
|
|
36
|
+
expect(item.sample_rate).to eq(44_100)
|
|
37
|
+
end
|
|
27
38
|
end
|
|
28
39
|
|
|
29
40
|
describe '.parse' do
|
|
@@ -48,6 +59,16 @@ describe M3u8::MediaItem do
|
|
|
48
59
|
expect(item.characteristics).to eq('public.html')
|
|
49
60
|
expect(item.channels).to eq('6')
|
|
50
61
|
end
|
|
62
|
+
|
|
63
|
+
it 'parses v13 attributes' do
|
|
64
|
+
tag = '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",' \
|
|
65
|
+
'NAME="English",STABLE-RENDITION-ID="audio-en",' \
|
|
66
|
+
'BIT-DEPTH=16,SAMPLE-RATE=44100'
|
|
67
|
+
item = described_class.parse(tag)
|
|
68
|
+
expect(item.stable_rendition_id).to eq('audio-en')
|
|
69
|
+
expect(item.bit_depth).to eq(16)
|
|
70
|
+
expect(item.sample_rate).to eq(44_100)
|
|
71
|
+
end
|
|
51
72
|
end
|
|
52
73
|
|
|
53
74
|
describe '#to_s' do
|
|
@@ -86,5 +107,18 @@ describe M3u8::MediaItem do
|
|
|
86
107
|
expect(output).to eq(expected)
|
|
87
108
|
end
|
|
88
109
|
end
|
|
110
|
+
|
|
111
|
+
context 'when v13 attributes are assigned' do
|
|
112
|
+
it 'returns tag text with new attributes' do
|
|
113
|
+
options = { type: 'AUDIO', group_id: 'aac', name: 'English',
|
|
114
|
+
channels: '2', stable_rendition_id: 'audio-en',
|
|
115
|
+
bit_depth: 16, sample_rate: 44_100 }
|
|
116
|
+
item = M3u8::MediaItem.new(options)
|
|
117
|
+
output = item.to_s
|
|
118
|
+
expect(output).to include('STABLE-RENDITION-ID="audio-en"')
|
|
119
|
+
expect(output).to include('BIT-DEPTH=16')
|
|
120
|
+
expect(output).to include('SAMPLE-RATE=44100')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
89
123
|
end
|
|
90
124
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe M3u8::PartInfItem do
|
|
6
|
+
describe '.new' do
|
|
7
|
+
it 'assigns attributes from options' do
|
|
8
|
+
item = described_class.new(part_target: 0.5)
|
|
9
|
+
expect(item.part_target).to eq(0.5)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '.parse' do
|
|
14
|
+
it 'parses tag' do
|
|
15
|
+
tag = '#EXT-X-PART-INF:PART-TARGET=0.5'
|
|
16
|
+
item = described_class.parse(tag)
|
|
17
|
+
expect(item.part_target).to eq(0.5)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe '#to_s' do
|
|
22
|
+
it 'returns m3u8 format representation' do
|
|
23
|
+
item = described_class.new(part_target: 0.5)
|
|
24
|
+
expect(item.to_s).to eq('#EXT-X-PART-INF:PART-TARGET=0.5')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe M3u8::PartItem do
|
|
6
|
+
describe '.new' do
|
|
7
|
+
it 'assigns attributes from options' do
|
|
8
|
+
options = { uri: 'part1.ts', duration: 1.5, independent: true,
|
|
9
|
+
gap: false }
|
|
10
|
+
item = described_class.new(options)
|
|
11
|
+
expect(item.uri).to eq('part1.ts')
|
|
12
|
+
expect(item.duration).to eq(1.5)
|
|
13
|
+
expect(item.independent).to be true
|
|
14
|
+
expect(item.gap).to be false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe '.parse' do
|
|
19
|
+
it 'parses tag with all attributes' do
|
|
20
|
+
tag = '#EXT-X-PART:DURATION=1.5,URI="part1.ts",' \
|
|
21
|
+
'INDEPENDENT=YES,BYTERANGE="500@0",GAP=YES'
|
|
22
|
+
item = described_class.parse(tag)
|
|
23
|
+
expect(item.duration).to eq(1.5)
|
|
24
|
+
expect(item.uri).to eq('part1.ts')
|
|
25
|
+
expect(item.independent).to be true
|
|
26
|
+
expect(item.byterange.length).to eq(500)
|
|
27
|
+
expect(item.byterange.start).to eq(0)
|
|
28
|
+
expect(item.gap).to be true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'parses tag without optional attributes' do
|
|
32
|
+
tag = '#EXT-X-PART:DURATION=1.5,URI="part1.ts"'
|
|
33
|
+
item = described_class.parse(tag)
|
|
34
|
+
expect(item.duration).to eq(1.5)
|
|
35
|
+
expect(item.uri).to eq('part1.ts')
|
|
36
|
+
expect(item.independent).to be false
|
|
37
|
+
expect(item.byterange).to be_nil
|
|
38
|
+
expect(item.gap).to be false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#to_s' do
|
|
43
|
+
it 'returns tag with all attributes' do
|
|
44
|
+
options = { duration: 1.5, uri: 'part1.ts', independent: true,
|
|
45
|
+
byterange: M3u8::ByteRange.new(length: 500, start: 0),
|
|
46
|
+
gap: true }
|
|
47
|
+
item = described_class.new(options)
|
|
48
|
+
expected = '#EXT-X-PART:DURATION=1.5,URI="part1.ts",' \
|
|
49
|
+
'INDEPENDENT=YES,BYTERANGE="500@0",GAP=YES'
|
|
50
|
+
expect(item.to_s).to eq(expected)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'returns tag with only required attributes' do
|
|
54
|
+
options = { duration: 1.5, uri: 'part1.ts' }
|
|
55
|
+
item = described_class.new(options)
|
|
56
|
+
expected = '#EXT-X-PART:DURATION=1.5,URI="part1.ts"'
|
|
57
|
+
expect(item.to_s).to eq(expected)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'should render small float values as floating-point number instead of scientific notation' do
|
|
61
|
+
options = { duration: 0.00001, uri: 'part1.ts' }
|
|
62
|
+
item = described_class.new(options)
|
|
63
|
+
expected = '#EXT-X-PART:DURATION=0.00001,URI="part1.ts"'
|
|
64
|
+
expect(item.to_s).to eq(expected)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
describe M3u8::PlaybackStart do
|
|
@@ -10,20 +11,18 @@ describe M3u8::PlaybackStart do
|
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
describe '
|
|
14
|
+
describe '.parse' do
|
|
14
15
|
it 'parses tag with all attributes' do
|
|
15
|
-
start = described_class.new
|
|
16
16
|
tag = '#EXT-X-START:TIME-OFFSET=20.0,PRECISE=YES'
|
|
17
|
-
start.parse(tag)
|
|
17
|
+
start = described_class.parse(tag)
|
|
18
18
|
|
|
19
19
|
expect(start.time_offset).to eq(20.0)
|
|
20
20
|
expect(start.precise).to be true
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
it 'parses tag without optional attributes' do
|
|
24
|
-
start = described_class.new
|
|
25
24
|
tag = '#EXT-X-START:TIME-OFFSET=-12.9'
|
|
26
|
-
start.parse(tag)
|
|
25
|
+
start = described_class.parse(tag)
|
|
27
26
|
|
|
28
27
|
expect(start.time_offset).to eq(-12.9)
|
|
29
28
|
expect(start.precise).to be_nil
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
|
|
4
5
|
describe M3u8::PlaylistItem do
|
|
@@ -32,28 +33,29 @@ describe M3u8::PlaylistItem do
|
|
|
32
33
|
expect(item.name).to eq('test_name')
|
|
33
34
|
expect(item.hdcp_level).to eq('TYPE-0')
|
|
34
35
|
end
|
|
35
|
-
end
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
item = described_class.
|
|
46
|
-
expect(item).to
|
|
37
|
+
it 'assigns new v13 attributes from options' do
|
|
38
|
+
options = { bandwidth: 540, uri: 'test.url',
|
|
39
|
+
stable_variant_id: 'hd-1080', video_range: 'SDR',
|
|
40
|
+
allowed_cpc: 'com.example.drm:SMART-TV/PC',
|
|
41
|
+
pathway_id: 'CDN-A',
|
|
42
|
+
req_video_layout: 'CH-MONO',
|
|
43
|
+
supplemental_codecs: 'dvh1.05.06/db4g',
|
|
44
|
+
score: 12.5 }
|
|
45
|
+
item = described_class.new(options)
|
|
46
|
+
expect(item.stable_variant_id).to eq('hd-1080')
|
|
47
|
+
expect(item.video_range).to eq('SDR')
|
|
48
|
+
expect(item.allowed_cpc).to eq('com.example.drm:SMART-TV/PC')
|
|
49
|
+
expect(item.pathway_id).to eq('CDN-A')
|
|
50
|
+
expect(item.req_video_layout).to eq('CH-MONO')
|
|
51
|
+
expect(item.supplemental_codecs).to eq('dvh1.05.06/db4g')
|
|
52
|
+
expect(item.score).to eq(12.5)
|
|
47
53
|
end
|
|
48
54
|
end
|
|
49
55
|
|
|
50
|
-
describe '
|
|
56
|
+
describe '.parse' do
|
|
51
57
|
it 'assigns values from parsed tag' do
|
|
52
|
-
input =
|
|
53
|
-
%(PROGRAM-ID=1,RESOLUTION=1920x1080,FRAME-RATE=23.976,) +
|
|
54
|
-
%(AVERAGE-BANDWIDTH=550,AUDIO="test",VIDEO="test2",) +
|
|
55
|
-
%(SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url",) +
|
|
56
|
-
%(NAME="1080p",HDCP-LEVEL=TYPE-0)
|
|
58
|
+
input = '#EXT-X-STREAM-INF:CODECS="avc",BANDWIDTH=540,PROGRAM-ID=1,RESOLUTION=1920x1080,FRAME-RATE=23.976,AVERAGE-BANDWIDTH=550,AUDIO="test",VIDEO="test2",SUBTITLES="subs",CLOSED-CAPTIONS="caps",URI="test.url",NAME="1080p",HDCP-LEVEL=TYPE-0'
|
|
57
59
|
item = M3u8::PlaylistItem.parse(input)
|
|
58
60
|
expect(item.program_id).to eq '1'
|
|
59
61
|
expect(item.codecs).to eq 'avc'
|
|
@@ -71,6 +73,24 @@ describe M3u8::PlaylistItem do
|
|
|
71
73
|
expect(item.iframe).to be false
|
|
72
74
|
expect(item.hdcp_level).to eq('TYPE-0')
|
|
73
75
|
end
|
|
76
|
+
|
|
77
|
+
it 'parses v13 attributes' do
|
|
78
|
+
input = '#EXT-X-STREAM-INF:BANDWIDTH=5000000,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,STABLE-VARIANT-ID="hd-1080",VIDEO-RANGE=SDR,ALLOWED-CPC="com.example.drm:SMART-TV/PC",PATHWAY-ID="CDN-A",SCORE=12.5,SUPPLEMENTAL-CODECS="dvh1.05.06/db4g",REQ-VIDEO-LAYOUT="CH-MONO"'
|
|
79
|
+
item = M3u8::PlaylistItem.parse(input)
|
|
80
|
+
expect(item.stable_variant_id).to eq('hd-1080')
|
|
81
|
+
expect(item.video_range).to eq('SDR')
|
|
82
|
+
expect(item.allowed_cpc).to eq('com.example.drm:SMART-TV/PC')
|
|
83
|
+
expect(item.pathway_id).to eq('CDN-A')
|
|
84
|
+
expect(item.score).to eq(12.5)
|
|
85
|
+
expect(item.supplemental_codecs).to eq('dvh1.05.06/db4g')
|
|
86
|
+
expect(item.req_video_layout).to eq('CH-MONO')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'keeps bandwidth nil when BANDWIDTH is missing' do
|
|
90
|
+
input = '#EXT-X-STREAM-INF:CODECS="avc",URI="test.url"'
|
|
91
|
+
item = M3u8::PlaylistItem.parse(input)
|
|
92
|
+
expect(item.bandwidth).to be_nil
|
|
93
|
+
end
|
|
74
94
|
end
|
|
75
95
|
|
|
76
96
|
describe '#to_s' do
|
|
@@ -193,6 +213,27 @@ describe M3u8::PlaylistItem do
|
|
|
193
213
|
end
|
|
194
214
|
end
|
|
195
215
|
|
|
216
|
+
context 'when v13 attributes are present' do
|
|
217
|
+
it 'returns tag with new attributes' do
|
|
218
|
+
options = { codecs: 'avc1.640028', bandwidth: 5_000_000,
|
|
219
|
+
uri: '1080p.m3u8', stable_variant_id: 'hd-1080',
|
|
220
|
+
video_range: 'SDR', pathway_id: 'CDN-A',
|
|
221
|
+
score: 12.5,
|
|
222
|
+
supplemental_codecs: 'dvh1.05.06/db4g',
|
|
223
|
+
allowed_cpc: 'com.example.drm:SMART-TV/PC',
|
|
224
|
+
req_video_layout: 'CH-MONO' }
|
|
225
|
+
item = described_class.new(options)
|
|
226
|
+
output = item.to_s
|
|
227
|
+
expect(output).to include('STABLE-VARIANT-ID="hd-1080"')
|
|
228
|
+
expect(output).to include('VIDEO-RANGE=SDR')
|
|
229
|
+
expect(output).to include('PATHWAY-ID="CDN-A"')
|
|
230
|
+
expect(output).to include('SCORE=12.5')
|
|
231
|
+
expect(output).to include('SUPPLEMENTAL-CODECS="dvh1.05.06/db4g"')
|
|
232
|
+
expect(output).to include('ALLOWED-CPC="com.example.drm:SMART-TV/PC"')
|
|
233
|
+
expect(output).to include('REQ-VIDEO-LAYOUT="CH-MONO"')
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
196
237
|
context 'when iframe is enabled' do
|
|
197
238
|
it 'returns EXT-X-I-FRAME-STREAM-INF tag' do
|
|
198
239
|
options = { codecs: 'avc', bandwidth: 540, uri: 'test.url',
|
|
@@ -289,4 +330,76 @@ describe M3u8::PlaylistItem do
|
|
|
289
330
|
item = M3u8::PlaylistItem.new options
|
|
290
331
|
expect(item.codecs).to eq 'avc1.640033'
|
|
291
332
|
end
|
|
333
|
+
|
|
334
|
+
it 'generates HEVC codec strings' do
|
|
335
|
+
options = { profile: 'hevc-main', level: 3.1 }
|
|
336
|
+
item = M3u8::PlaylistItem.new options
|
|
337
|
+
expect(item.codecs).to eq 'hvc1.1.6.L93.B0'
|
|
338
|
+
|
|
339
|
+
options = { profile: 'hevc-main', level: 4.0 }
|
|
340
|
+
item = M3u8::PlaylistItem.new options
|
|
341
|
+
expect(item.codecs).to eq 'hvc1.1.6.L120.B0'
|
|
342
|
+
|
|
343
|
+
options = { profile: 'hevc-main', level: 5.0 }
|
|
344
|
+
item = M3u8::PlaylistItem.new options
|
|
345
|
+
expect(item.codecs).to eq 'hvc1.1.6.L150.B0'
|
|
346
|
+
|
|
347
|
+
options = { profile: 'hevc-main', level: 5.1 }
|
|
348
|
+
item = M3u8::PlaylistItem.new options
|
|
349
|
+
expect(item.codecs).to eq 'hvc1.1.6.L153.B0'
|
|
350
|
+
|
|
351
|
+
options = { profile: 'hevc-main-10', level: 4.0 }
|
|
352
|
+
item = M3u8::PlaylistItem.new options
|
|
353
|
+
expect(item.codecs).to eq 'hvc1.2.4.L120.B0'
|
|
354
|
+
|
|
355
|
+
options = { profile: 'hevc-main-10', level: 5.1 }
|
|
356
|
+
item = M3u8::PlaylistItem.new options
|
|
357
|
+
expect(item.codecs).to eq 'hvc1.2.4.L153.B0'
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it 'generates AV1 codec strings' do
|
|
361
|
+
options = { profile: 'av1-main', level: 4.0 }
|
|
362
|
+
item = M3u8::PlaylistItem.new options
|
|
363
|
+
expect(item.codecs).to eq 'av01.0.08M.08'
|
|
364
|
+
|
|
365
|
+
options = { profile: 'av1-main', level: 5.1 }
|
|
366
|
+
item = M3u8::PlaylistItem.new options
|
|
367
|
+
expect(item.codecs).to eq 'av01.0.13M.08'
|
|
368
|
+
|
|
369
|
+
options = { profile: 'av1-high', level: 4.0 }
|
|
370
|
+
item = M3u8::PlaylistItem.new options
|
|
371
|
+
expect(item.codecs).to eq 'av01.1.08H.10'
|
|
372
|
+
|
|
373
|
+
options = { profile: 'av1-high', level: 5.1 }
|
|
374
|
+
item = M3u8::PlaylistItem.new options
|
|
375
|
+
expect(item.codecs).to eq 'av01.1.13H.10'
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it 'generates modern audio codec strings' do
|
|
379
|
+
item = M3u8::PlaylistItem.new audio_codec: 'ac-3'
|
|
380
|
+
expect(item.codecs).to eq 'ac-3'
|
|
381
|
+
|
|
382
|
+
item = M3u8::PlaylistItem.new audio_codec: 'ec-3'
|
|
383
|
+
expect(item.codecs).to eq 'ec-3'
|
|
384
|
+
|
|
385
|
+
item = M3u8::PlaylistItem.new audio_codec: 'e-ac-3'
|
|
386
|
+
expect(item.codecs).to eq 'ec-3'
|
|
387
|
+
|
|
388
|
+
item = M3u8::PlaylistItem.new audio_codec: 'flac'
|
|
389
|
+
expect(item.codecs).to eq 'fLaC'
|
|
390
|
+
|
|
391
|
+
item = M3u8::PlaylistItem.new audio_codec: 'opus'
|
|
392
|
+
expect(item.codecs).to eq 'Opus'
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
it 'accepts integer levels for codec lookup' do
|
|
396
|
+
item = M3u8::PlaylistItem.new(profile: 'main', level: 4)
|
|
397
|
+
expect(item.codecs).to eq 'avc1.4d0028'
|
|
398
|
+
|
|
399
|
+
item = M3u8::PlaylistItem.new(profile: 'baseline', level: 3)
|
|
400
|
+
expect(item.codecs).to eq 'avc1.66.30'
|
|
401
|
+
|
|
402
|
+
item = M3u8::PlaylistItem.new(profile: 'high', level: 4)
|
|
403
|
+
expect(item.codecs).to eq 'avc1.640028'
|
|
404
|
+
end
|
|
292
405
|
end
|