[HLS] Support AVERAGE-BANDWIDTH
Issue #361 Change-Id: Id8eb8283675cba5ec7234d13c4ac235f34e3bec9
This commit is contained in:
parent
f97413bf6e
commit
99a2ad03af
|
@ -38,7 +38,7 @@ These are the available fields:
|
|||
|
||||
:bandwidth (bw):
|
||||
|
||||
Optional value which contains a user-specified content bit rate for the
|
||||
Optional value which contains a user-specified maximum bit rate for the
|
||||
stream, in bits/sec. If specified, this value is propagated to (HLS)
|
||||
EXT-X-STREAM-INF:BANDWIDTH or (DASH) Representation@bandwidth and the
|
||||
$Bandwidth$ template parameter for segment names. If not specified, the
|
||||
|
|
|
@ -67,7 +67,7 @@ const char kUsage[] =
|
|||
" split into multiple files. Its presence should be consistent across\n"
|
||||
" streams.\n"
|
||||
" - bandwidth (bw): Optional value which contains a user-specified\n"
|
||||
" content bit rate for the stream, in bits/sec. If specified, this\n"
|
||||
" maximum bit rate for the stream, in bits/sec. If specified, this\n"
|
||||
" value is propagated to (HLS) EXT-X-STREAM-INF:BANDWIDTH or (DASH)\n"
|
||||
" Representation@bandwidth and the $Bandwidth$ template parameter for\n"
|
||||
" segment names. If not specified, the bandwidth value is estimated\n"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",LANGUAGE="pt",NAME="stream_0",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ac3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1168321,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1168321,AVERAGE-BANDWIDTH=1046257,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ac3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,AVERAGE-BANDWIDTH=153342,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ac3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1242863,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1242863,AVERAGE-BANDWIDTH=1142132,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ac3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ac3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1242863,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1242863,AVERAGE-BANDWIDTH=1142132,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ac3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1154999,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1154999,AVERAGE-BANDWIDTH=1046205,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ac3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1216578,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1216578,AVERAGE-BANDWIDTH=1115973,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ac3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ac3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#EXTM3U
|
||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1183949,CODECS="avc1.64001f",RESOLUTION=1024x436
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1183949,AVERAGE-BANDWIDTH=80147,CODECS="avc1.64001f",RESOLUTION=1024x436
|
||||
sintel-1024x436-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=447591,CODECS="avc1.64001f",RESOLUTION=1024x436,URI="sintel-1024x436-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=447591,AVERAGE-BANDWIDTH=6232,CODECS="avc1.64001f",RESOLUTION=1024x436,URI="sintel-1024x436-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1109509,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ec3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1174214,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1174214,AVERAGE-BANDWIDTH=1030678,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ec3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ec3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,AVERAGE-BANDWIDTH=155992,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ec3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ec3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1216655,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1216655,AVERAGE-BANDWIDTH=1095969,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-ec3-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ec3-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=153967,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-ec3-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1152421,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1152421,AVERAGE-BANDWIDTH=986776,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,AVERAGE-BANDWIDTH=155643,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,URI="bear-subtitle-english-text.m3u8",GROUP-ID="default-text-group",NAME="stream_0",AUTOSELECT=YES
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1150006,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1150006,AVERAGE-BANDWIDTH=983710,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,AVERAGE-BANDWIDTH=153342,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio/audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1105131,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1105131,AVERAGE-BANDWIDTH=974563,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
video/video.m3u8
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,URI="stream_0.m3u8",GROUP-ID="default-text-group",NAME="stream_0",AUTOSELECT=YES
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1105131,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1105131,AVERAGE-BANDWIDTH=974563,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
stream_2.m3u8
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1152421,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1152421,AVERAGE-BANDWIDTH=986776,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,AVERAGE-BANDWIDTH=155643,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1111149,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1111149,AVERAGE-BANDWIDTH=977986,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,AVERAGE-BANDWIDTH=155643,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="stream_0.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=55555,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=55555,AVERAGE-BANDWIDTH=974563,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
stream_1.m3u8
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,URI="stream_0.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=556353,CODECS="vp08.00.10.08.01.02.02.02.00,vorbis",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=556353,AVERAGE-BANDWIDTH=378244,CODECS="vp08.00.10.08.01.02.02.02.00,vorbis",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||
stream_1.m3u8
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,URI="bear-subtitle-english-text.m3u8",GROUP-ID="default-text-group",NAME="stream_0",AUTOSELECT=YES
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1150006,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1150006,AVERAGE-BANDWIDTH=983710,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group"
|
||||
bear-640x360-video.m3u8
|
||||
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=214292,AVERAGE-BANDWIDTH=153342,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||
|
|
|
@ -34,18 +34,36 @@ void AppendVersionString(std::string* content) {
|
|||
GetPackagerProjectUrl().c_str(), version.c_str());
|
||||
}
|
||||
|
||||
// This structure roughly maps to the Variant stream in HLS specification.
|
||||
// Each variant specifies zero or one audio group and zero or one text group.
|
||||
struct Variant {
|
||||
std::set<std::string> audio_codecs;
|
||||
std::set<std::string> text_codecs;
|
||||
const std::string* audio_group_id = nullptr;
|
||||
const std::string* text_group_id = nullptr;
|
||||
uint64_t audio_bitrate = 0;
|
||||
// The bitrates should be the sum of audio bitrate and text bitrate.
|
||||
// However, given the contraints and assumptions, it makes sense to exclude
|
||||
// text bitrate out of the calculation:
|
||||
// - Text streams usually have a very small negligible bitrate.
|
||||
// - Text does not have constant bitrates. To avoid fluctuation, an arbitrary
|
||||
// value is assigned to the text bitrates in the parser. It does not make
|
||||
// sense to take that text bitrate into account here.
|
||||
uint64_t max_audio_bitrate = 0;
|
||||
uint64_t avg_audio_bitrate = 0;
|
||||
};
|
||||
|
||||
uint64_t MaxBitrate(const std::list<const MediaPlaylist*> playlists) {
|
||||
uint64_t GetMaximumMaxBitrate(const std::list<const MediaPlaylist*> playlists) {
|
||||
uint64_t max = 0;
|
||||
for (const auto& playlist : playlists) {
|
||||
max = std::max(max, playlist->Bitrate());
|
||||
max = std::max(max, playlist->MaxBitrate());
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
uint64_t GetMaximumAvgBitrate(const std::list<const MediaPlaylist*> playlists) {
|
||||
uint64_t max = 0;
|
||||
for (const auto& playlist : playlists) {
|
||||
max = std::max(max, playlist->AvgBitrate());
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
@ -78,7 +96,8 @@ std::list<Variant> AudioGroupsToVariants(
|
|||
for (const auto& group : groups) {
|
||||
Variant variant;
|
||||
variant.audio_group_id = &group.first;
|
||||
variant.audio_bitrate = MaxBitrate(group.second);
|
||||
variant.max_audio_bitrate = GetMaximumMaxBitrate(group.second);
|
||||
variant.avg_audio_bitrate = GetMaximumAvgBitrate(group.second);
|
||||
variant.audio_codecs = GetGroupCodecString(group.second);
|
||||
|
||||
variants.push_back(variant);
|
||||
|
@ -153,7 +172,8 @@ std::list<Variant> BuildVariants(
|
|||
variant.text_codecs = subtitle_variant.text_codecs;
|
||||
variant.audio_group_id = audio_variant.audio_group_id;
|
||||
variant.text_group_id = subtitle_variant.text_group_id;
|
||||
variant.audio_bitrate = audio_variant.audio_bitrate;
|
||||
variant.max_audio_bitrate = audio_variant.max_audio_bitrate;
|
||||
variant.avg_audio_bitrate = audio_variant.avg_audio_bitrate;
|
||||
|
||||
merged.push_back(variant);
|
||||
}
|
||||
|
@ -185,8 +205,9 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
|||
}
|
||||
Tag tag(tag_name, out);
|
||||
|
||||
const uint64_t bitrate = playlist.Bitrate() + variant.audio_bitrate;
|
||||
tag.AddNumber("BANDWIDTH", bitrate);
|
||||
tag.AddNumber("BANDWIDTH", playlist.MaxBitrate() + variant.max_audio_bitrate);
|
||||
tag.AddNumber("AVERAGE-BANDWIDTH",
|
||||
playlist.AvgBitrate() + variant.avg_audio_bitrate);
|
||||
|
||||
std::vector<std::string> all_codecs;
|
||||
all_codecs.push_back(playlist.codec());
|
||||
|
|
|
@ -35,7 +35,8 @@ const uint32_t kHeight = 600;
|
|||
std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
|
||||
const std::string& filename,
|
||||
const std::string& codec,
|
||||
uint64_t bitrate) {
|
||||
uint64_t max_bitrate,
|
||||
uint64_t avg_bitrate) {
|
||||
const char kNoName[] = "";
|
||||
const char kNoGroup[] = "";
|
||||
|
||||
|
@ -46,9 +47,12 @@ std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
|
|||
MediaPlaylist::MediaPlaylistStreamType::kVideo);
|
||||
playlist->SetCodecForTesting(codec);
|
||||
|
||||
EXPECT_CALL(*playlist, Bitrate())
|
||||
EXPECT_CALL(*playlist, MaxBitrate())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Return(bitrate));
|
||||
.WillRepeatedly(Return(max_bitrate));
|
||||
EXPECT_CALL(*playlist, AvgBitrate())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Return(avg_bitrate));
|
||||
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull()))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(kWidth), SetArgPointee<1>(kHeight),
|
||||
Return(true)));
|
||||
|
@ -59,8 +63,10 @@ std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
|
|||
std::unique_ptr<MockMediaPlaylist> CreateIframePlaylist(
|
||||
const std::string& filename,
|
||||
const std::string& codec,
|
||||
uint64_t bitrate) {
|
||||
auto playlist = CreateVideoPlaylist(filename, codec, bitrate);
|
||||
uint64_t max_bitrate,
|
||||
uint64_t avg_bitrate) {
|
||||
auto playlist =
|
||||
CreateVideoPlaylist(filename, codec, max_bitrate, avg_bitrate);
|
||||
playlist->SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly);
|
||||
return playlist;
|
||||
|
@ -73,7 +79,8 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
|||
const std::string& codec,
|
||||
const std::string& language,
|
||||
uint64_t channels,
|
||||
uint64_t bitrate) {
|
||||
uint64_t max_bitrate,
|
||||
uint64_t avg_bitrate) {
|
||||
std::unique_ptr<MockMediaPlaylist> playlist(
|
||||
new MockMediaPlaylist(filename, name, group));
|
||||
|
||||
|
@ -84,9 +91,12 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
|
|||
playlist->SetCodecForTesting(codec);
|
||||
playlist->SetLanguageForTesting(language);
|
||||
|
||||
EXPECT_CALL(*playlist, Bitrate())
|
||||
EXPECT_CALL(*playlist, MaxBitrate())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Return(bitrate));
|
||||
.WillRepeatedly(Return(max_bitrate));
|
||||
EXPECT_CALL(*playlist, AvgBitrate())
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Return(avg_bitrate));
|
||||
EXPECT_CALL(*playlist, GetDisplayResolution(NotNull(), NotNull())).Times(0);
|
||||
|
||||
return playlist;
|
||||
|
@ -128,10 +138,11 @@ class MasterPlaylistTest : public ::testing::Test {
|
|||
};
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
||||
const uint64_t kBitRate = 435889;
|
||||
const uint64_t kMaxBitrate = 435889;
|
||||
const uint64_t kAvgBitrate = 235889;
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> mock_playlist =
|
||||
CreateVideoPlaylist("media1.m3u8", "avc1", kBitRate);
|
||||
CreateVideoPlaylist("media1.m3u8", "avc1", kMaxBitrate, kAvgBitrate);
|
||||
|
||||
const char kBaseUrl[] = "http://myplaylistdomain.com/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_,
|
||||
|
@ -145,17 +156,19 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
|||
"## Generated with https://github.com/google/shaka-packager version "
|
||||
"test\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=435889,CODECS=\"avc1\",RESOLUTION=800x600\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=435889,AVERAGE-BANDWIDTH=235889,"
|
||||
"CODECS=\"avc1\",RESOLUTION=800x600\n"
|
||||
"http://myplaylistdomain.com/media1.m3u8\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneIframePlaylist) {
|
||||
const uint64_t kBitRate = 435889;
|
||||
const uint64_t kMaxBitrate = 435889;
|
||||
const uint64_t kAvgBitrate = 235889;
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> mock_playlist =
|
||||
CreateIframePlaylist("media1.m3u8", "avc1", kBitRate);
|
||||
CreateIframePlaylist("media1.m3u8", "avc1", kMaxBitrate, kAvgBitrate);
|
||||
|
||||
const char kBaseUrl[] = "http://myplaylistdomain.com/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_,
|
||||
|
@ -169,39 +182,44 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneIframePlaylist) {
|
|||
"## Generated with https://github.com/google/shaka-packager version "
|
||||
"test\n"
|
||||
"\n"
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=435889,CODECS=\"avc1\",RESOLUTION="
|
||||
"800x600,URI=\"http://myplaylistdomain.com/media1.m3u8\"\n";
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=435889,AVERAGE-BANDWIDTH=235889,"
|
||||
"CODECS=\"avc1\",RESOLUTION=800x600,"
|
||||
"URI=\"http://myplaylistdomain.com/media1.m3u8\"\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
||||
const uint64_t kVideo1BitRate = 300000;
|
||||
const uint64_t kVideo2BitRate = 700000;
|
||||
const uint64_t kVideo1MaxBitrate = 300000;
|
||||
const uint64_t kVideo1AvgBitrate = 200000;
|
||||
const uint64_t kVideo2MaxBitrate = 700000;
|
||||
const uint64_t kVideo2AvgBitrate = 400000;
|
||||
|
||||
const uint64_t kAudio1BitRate = 50000;
|
||||
const uint64_t kAudio2BitRate = 60000;
|
||||
const uint64_t kAudio1MaxBitrate = 50000;
|
||||
const uint64_t kAudio1AvgBitrate = 40000;
|
||||
const uint64_t kAudio2MaxBitrate = 60000;
|
||||
const uint64_t kAudio2AvgBitrate = 30000;
|
||||
|
||||
const uint64_t kAudio1Channels = 2;
|
||||
const uint64_t kAudio2Channels = 5;
|
||||
|
||||
// First video, sd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> sd_video_playlist =
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", kVideo1BitRate);
|
||||
std::unique_ptr<MockMediaPlaylist> sd_video_playlist = CreateVideoPlaylist(
|
||||
"sd.m3u8", "sdvideocodec", kVideo1MaxBitrate, kVideo1AvgBitrate);
|
||||
|
||||
// Second video, hd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> hd_video_playlist =
|
||||
CreateVideoPlaylist("hd.m3u8", "hdvideocodec", kVideo2BitRate);
|
||||
std::unique_ptr<MockMediaPlaylist> hd_video_playlist = CreateVideoPlaylist(
|
||||
"hd.m3u8", "hdvideocodec", kVideo2MaxBitrate, kVideo2AvgBitrate);
|
||||
|
||||
// First audio, english.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> english_playlist =
|
||||
CreateAudioPlaylist("eng.m3u8", "english", "audiogroup", "audiocodec",
|
||||
"en", kAudio1Channels, kAudio1BitRate);
|
||||
std::unique_ptr<MockMediaPlaylist> english_playlist = CreateAudioPlaylist(
|
||||
"eng.m3u8", "english", "audiogroup", "audiocodec", "en", kAudio1Channels,
|
||||
kAudio1MaxBitrate, kAudio1AvgBitrate);
|
||||
|
||||
// Second audio, spanish.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> spanish_playlist =
|
||||
CreateAudioPlaylist("spa.m3u8", "espanol", "audiogroup", "audiocodec",
|
||||
"es", kAudio2Channels, kAudio2BitRate);
|
||||
std::unique_ptr<MockMediaPlaylist> spanish_playlist = CreateAudioPlaylist(
|
||||
"spa.m3u8", "espanol", "audiogroup", "audiocodec", "es", kAudio2Channels,
|
||||
kAudio2MaxBitrate, kAudio2AvgBitrate);
|
||||
|
||||
const char kBaseUrl[] = "http://playlists.org/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -224,10 +242,12 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|||
"GROUP-ID=\"audiogroup\",LANGUAGE=\"es\",NAME=\"espanol\","
|
||||
"AUTOSELECT=YES,CHANNELS=\"5\"\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=360000,CODECS=\"sdvideocodec,audiocodec\","
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=360000,AVERAGE-BANDWIDTH=240000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
|
||||
"http://playlists.org/sd.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=760000,CODECS=\"hdvideocodec,audiocodec\","
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=760000,AVERAGE-BANDWIDTH=440000,"
|
||||
"CODECS=\"hdvideocodec,audiocodec\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
|
||||
"http://playlists.org/hd.m3u8\n";
|
||||
|
||||
|
@ -235,27 +255,30 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|||
}
|
||||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
||||
const uint64_t kVideoBitRate = 300000;
|
||||
const uint64_t kVideoMaxBitrate = 300000;
|
||||
const uint64_t kVideoAvgBitrate = 200000;
|
||||
|
||||
const uint64_t kAudio1BitRate = 50000;
|
||||
const uint64_t kAudio2BitRate = 100000;
|
||||
const uint64_t kAudio1MaxBitrate = 50000;
|
||||
const uint64_t kAudio1AvgBitrate = 40000;
|
||||
const uint64_t kAudio2MaxBitrate = 100000;
|
||||
const uint64_t kAudio2AvgBitrate = 70000;
|
||||
|
||||
const uint64_t kAudio1Channels = 1;
|
||||
const uint64_t kAudio2Channels = 8;
|
||||
|
||||
// First video, sd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video_playlist =
|
||||
CreateVideoPlaylist("video.m3u8", "videocodec", kVideoBitRate);
|
||||
std::unique_ptr<MockMediaPlaylist> video_playlist = CreateVideoPlaylist(
|
||||
"video.m3u8", "videocodec", kVideoMaxBitrate, kVideoAvgBitrate);
|
||||
|
||||
// First audio, eng_lo.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> eng_lo_playlist = CreateAudioPlaylist(
|
||||
"eng_lo.m3u8", "english_lo", "audio_lo", "audiocodec_lo", "en",
|
||||
kAudio1Channels, kAudio1BitRate);
|
||||
kAudio1Channels, kAudio1MaxBitrate, kAudio1AvgBitrate);
|
||||
|
||||
// Second audio, eng_hi.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> eng_hi_playlist = CreateAudioPlaylist(
|
||||
"eng_hi.m3u8", "english_hi", "audio_hi", "audiocodec_hi", "en",
|
||||
kAudio2Channels, kAudio2BitRate);
|
||||
kAudio2Channels, kAudio2MaxBitrate, kAudio2AvgBitrate);
|
||||
|
||||
const char kBaseUrl[] = "http://anydomain.com/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -277,11 +300,13 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|||
"GROUP-ID=\"audio_lo\",LANGUAGE=\"en\",NAME=\"english_lo\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec_hi\","
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=400000,AVERAGE-BANDWIDTH=270000,"
|
||||
"CODECS=\"videocodec,audiocodec_hi\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audio_hi\"\n"
|
||||
"http://anydomain.com/video.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"videocodec,audiocodec_lo\","
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=240000,"
|
||||
"CODECS=\"videocodec,audiocodec_lo\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audio_lo\"\n"
|
||||
"http://anydomain.com/video.m3u8\n";
|
||||
|
||||
|
@ -291,14 +316,14 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
|
||||
// First video, video.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video_playlist =
|
||||
CreateVideoPlaylist("video.m3u8", "videocodec", 300000);
|
||||
CreateVideoPlaylist("video.m3u8", "videocodec", 300000, 200000);
|
||||
|
||||
// First audio, eng_lo.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> eng_lo_playlist = CreateAudioPlaylist(
|
||||
"eng_lo.m3u8", "english", "audio", "audiocodec", "en", 1, 50000);
|
||||
"eng_lo.m3u8", "english", "audio", "audiocodec", "en", 1, 50000, 40000);
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> eng_hi_playlist = CreateAudioPlaylist(
|
||||
"eng_hi.m3u8", "english", "audio", "audiocodec", "en", 8, 100000);
|
||||
"eng_hi.m3u8", "english", "audio", "audiocodec", "en", 8, 100000, 80000);
|
||||
|
||||
const char kBaseUrl[] = "http://anydomain.com/";
|
||||
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
|
||||
|
@ -319,8 +344,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
|
|||
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
|
||||
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\",CHANNELS=\"8\"\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec\","
|
||||
"RESOLUTION=800x600,AUDIO=\"audio\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=400000,AVERAGE-BANDWIDTH=280000,"
|
||||
"CODECS=\"videocodec,audiocodec\",RESOLUTION=800x600,AUDIO=\"audio\"\n"
|
||||
"http://anydomain.com/video.m3u8\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
@ -329,11 +354,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
|
|||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
||||
// Video, sd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video1 =
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000);
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000, 200000);
|
||||
|
||||
// Video, hd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video2 =
|
||||
CreateVideoPlaylist("hd.m3u8", "sdvideocodec", 600000);
|
||||
CreateVideoPlaylist("hd.m3u8", "sdvideocodec", 600000, 500000);
|
||||
|
||||
// Text, eng.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_eng =
|
||||
|
@ -362,11 +387,13 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
|||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\","
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\","
|
||||
"RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"SUBTITLES=\"textgroup\"\n"
|
||||
"http://playlists.org/sd.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=600000,CODECS=\"sdvideocodec,textcodec\","
|
||||
"RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=600000,AVERAGE-BANDWIDTH=500000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"SUBTITLES=\"textgroup\"\n"
|
||||
"http://playlists.org/hd.m3u8\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
@ -375,7 +402,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
|||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
||||
// Video, sd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video =
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000);
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000, 200000);
|
||||
|
||||
// Text, eng.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text_eng = CreateTextPlaylist(
|
||||
|
@ -405,12 +432,14 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
|||
"GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\","
|
||||
"AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\","
|
||||
"RESOLUTION=800x600,SUBTITLES=\"en-text-group\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"SUBTITLES=\"en-text-group\"\n"
|
||||
"http://playlists.org/sd.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\","
|
||||
"RESOLUTION=800x600,SUBTITLES=\"fr-text-group\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"SUBTITLES=\"fr-text-group\"\n"
|
||||
"http://playlists.org/sd.m3u8\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
@ -419,11 +448,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
|||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
||||
// Video, sd.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> video =
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000);
|
||||
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000, 200000);
|
||||
|
||||
// Audio, english.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> audio = CreateAudioPlaylist(
|
||||
"eng.m3u8", "english", "audiogroup", "audiocodec", "en", 2, 50000);
|
||||
"eng.m3u8", "english", "audiogroup", "audiocodec", "en", 2, 50000, 30000);
|
||||
|
||||
// Text, english.m3u8.
|
||||
std::unique_ptr<MockMediaPlaylist> text =
|
||||
|
@ -449,9 +478,9 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
|||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
|
||||
"AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES="
|
||||
"\"textgroup\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=230000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audiogroup\",SUBTITLES=\"textgroup\"\n"
|
||||
"http://playlists.org/sd.m3u8\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
@ -459,16 +488,21 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
|||
|
||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
||||
const uint64_t kAudioChannels = 2;
|
||||
const uint64_t kAudioBitRate = 50000;
|
||||
const uint64_t kVideoBitRate = 300000;
|
||||
const uint64_t kIframeBitRate = 100000;
|
||||
const uint64_t kAudioMaxBitrate = 50000;
|
||||
const uint64_t kAudioAvgBitrate = 30000;
|
||||
const uint64_t kVideoMaxBitrate = 300000;
|
||||
const uint64_t kVideoAvgBitrate = 100000;
|
||||
const uint64_t kIframeMaxBitrate = 100000;
|
||||
const uint64_t kIframeAvgBitrate = 80000;
|
||||
|
||||
std::unique_ptr<MockMediaPlaylist> media_playlists[] = {
|
||||
// AUDIO
|
||||
CreateAudioPlaylist("audio-1.m3u8", "audio 1", "audio-group-1",
|
||||
"audiocodec", "en", kAudioChannels, kAudioBitRate),
|
||||
"audiocodec", "en", kAudioChannels, kAudioMaxBitrate,
|
||||
kAudioAvgBitrate),
|
||||
CreateAudioPlaylist("audio-2.m3u8", "audio 2", "audio-group-2",
|
||||
"audiocodec", "en", kAudioChannels, kAudioBitRate),
|
||||
"audiocodec", "en", kAudioChannels, kAudioMaxBitrate,
|
||||
kAudioAvgBitrate),
|
||||
|
||||
// SUBTITLES
|
||||
CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "textcodec",
|
||||
|
@ -477,12 +511,16 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
|||
"en"),
|
||||
|
||||
// VIDEO
|
||||
CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoBitRate),
|
||||
CreateVideoPlaylist("video-2.m3u8", "sdvideocodec", kVideoBitRate),
|
||||
CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoMaxBitrate,
|
||||
kVideoAvgBitrate),
|
||||
CreateVideoPlaylist("video-2.m3u8", "sdvideocodec", kVideoMaxBitrate,
|
||||
kVideoAvgBitrate),
|
||||
|
||||
// I-Frame
|
||||
CreateIframePlaylist("iframe-1.m3u8", "sdvideocodec", kIframeBitRate),
|
||||
CreateIframePlaylist("iframe-2.m3u8", "sdvideocodec", kIframeBitRate),
|
||||
CreateIframePlaylist("iframe-1.m3u8", "sdvideocodec", kIframeMaxBitrate,
|
||||
kIframeAvgBitrate),
|
||||
CreateIframePlaylist("iframe-2.m3u8", "sdvideocodec", kIframeMaxBitrate,
|
||||
kIframeAvgBitrate),
|
||||
};
|
||||
|
||||
// Add all the media playlists to the master playlist.
|
||||
|
@ -517,46 +555,48 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
|||
"GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
|
||||
"group-1\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n"
|
||||
"http://playlists.org/video-1.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
|
||||
"group-1\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n"
|
||||
"http://playlists.org/video-2.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
|
||||
"group-2\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n"
|
||||
"http://playlists.org/video-1.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
|
||||
"group-2\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n"
|
||||
"http://playlists.org/video-2.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
|
||||
"group-1\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n"
|
||||
"http://playlists.org/video-1.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
|
||||
"group-1\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n"
|
||||
"http://playlists.org/video-2.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
|
||||
"group-2\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n"
|
||||
"http://playlists.org/video-1.m3u8\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
|
||||
"textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
|
||||
"group-2\"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
"CODECS=\"sdvideocodec,audiocodec,textcodec\",RESOLUTION=800x600,"
|
||||
"AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n"
|
||||
"http://playlists.org/video-2.m3u8\n"
|
||||
"\n"
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\","
|
||||
"RESOLUTION=800x600,URI=\"http://playlists.org/iframe-1.m3u8\"\n"
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\","
|
||||
"RESOLUTION=800x600,URI=\"http://playlists.org/iframe-2.m3u8\"\n";
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,AVERAGE-BANDWIDTH=80000,"
|
||||
"CODECS=\"sdvideocodec\",RESOLUTION=800x600,"
|
||||
"URI=\"http://playlists.org/iframe-1.m3u8\"\n"
|
||||
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,AVERAGE-BANDWIDTH=80000,"
|
||||
"CODECS=\"sdvideocodec\",RESOLUTION=800x600,"
|
||||
"URI=\"http://playlists.org/iframe-2.m3u8\"\n";
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
|
|
@ -460,12 +460,16 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uint64_t MediaPlaylist::Bitrate() const {
|
||||
uint64_t MediaPlaylist::MaxBitrate() const {
|
||||
if (media_info_.has_bandwidth())
|
||||
return media_info_.bandwidth();
|
||||
return bandwidth_estimator_.Max();
|
||||
}
|
||||
|
||||
uint64_t MediaPlaylist::AvgBitrate() const {
|
||||
return bandwidth_estimator_.Estimate();
|
||||
}
|
||||
|
||||
double MediaPlaylist::GetLongestSegmentDuration() const {
|
||||
return longest_segment_duration_;
|
||||
}
|
||||
|
|
|
@ -152,8 +152,13 @@ class MediaPlaylist {
|
|||
|
||||
/// If bitrate is specified in MediaInfo then it will use that value.
|
||||
/// Otherwise, returns the max bitrate.
|
||||
/// @return the bitrate (in bits per second) of this MediaPlaylist.
|
||||
virtual uint64_t Bitrate() const;
|
||||
/// @return the max bitrate (in bits per second) of this MediaPlaylist.
|
||||
virtual uint64_t MaxBitrate() const;
|
||||
|
||||
/// Unlike @a MaxBitrate, AvgBitrate is always computed from the segment size
|
||||
/// and duration.
|
||||
/// @return The average bitrate (in bits per second) of this MediaPlaylist.
|
||||
virtual uint64_t AvgBitrate() const;
|
||||
|
||||
/// @return the longest segment’s duration. This will return 0 if no
|
||||
/// segments have been added.
|
||||
|
|
|
@ -248,7 +248,7 @@ TEST_F(MediaPlaylistMultiSegmentTest, WriteToFile) {
|
|||
TEST_F(MediaPlaylistMultiSegmentTest, UseBitrateInMediaInfo) {
|
||||
valid_video_media_info_.set_bandwidth(8191);
|
||||
ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_));
|
||||
EXPECT_EQ(8191u, media_playlist_->Bitrate());
|
||||
EXPECT_EQ(8191u, media_playlist_->MaxBitrate());
|
||||
}
|
||||
|
||||
// If bitrate (bandwidth) is not set in the MediaInfo, then calculate from the
|
||||
|
@ -263,7 +263,8 @@ TEST_F(MediaPlaylistMultiSegmentTest, GetBitrateFromSegments) {
|
|||
kZeroByteOffset, 5 * kMBytes);
|
||||
|
||||
// Max bitrate is 2000Kb/s.
|
||||
EXPECT_EQ(2000000u, media_playlist_->Bitrate());
|
||||
EXPECT_EQ(2000000u, media_playlist_->MaxBitrate());
|
||||
EXPECT_EQ(1142858u, media_playlist_->AvgBitrate());
|
||||
}
|
||||
|
||||
TEST_F(MediaPlaylistMultiSegmentTest, GetLongestSegmentDuration) {
|
||||
|
|
|
@ -43,7 +43,8 @@ class MockMediaPlaylist : public MediaPlaylist {
|
|||
const std::string& key_format_versions));
|
||||
MOCK_METHOD0(AddPlacementOpportunity, void());
|
||||
MOCK_METHOD1(WriteToFile, bool(const std::string& file_path));
|
||||
MOCK_CONST_METHOD0(Bitrate, uint64_t());
|
||||
MOCK_CONST_METHOD0(MaxBitrate, uint64_t());
|
||||
MOCK_CONST_METHOD0(AvgBitrate, uint64_t());
|
||||
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
||||
MOCK_METHOD1(SetTargetDuration, void(uint32_t target_duration));
|
||||
MOCK_CONST_METHOD0(GetNumChannels, int());
|
||||
|
|
Loading…
Reference in New Issue