diff --git a/.gitignore b/.gitignore index 501c140..6017c92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build out .gradle +.idea diff --git a/src/main/java/io/lindstrom/m3u8/model/Variant.java b/src/main/java/io/lindstrom/m3u8/model/Variant.java index 8af11d7..962a2b3 100644 --- a/src/main/java/io/lindstrom/m3u8/model/Variant.java +++ b/src/main/java/io/lindstrom/m3u8/model/Variant.java @@ -109,6 +109,11 @@ public interface Variant { */ Optional pathwayId(); + /** + * @return variant attribute REQ-VIDEO-LAYOUT + */ + Optional> reqVideoLayout(); + static Builder builder() { return new Builder(); } @@ -122,4 +127,4 @@ public Builder videoRange(String value) { return videoRange(VideoRange.valueOf(value)); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/lindstrom/m3u8/parser/VariantAttribute.java b/src/main/java/io/lindstrom/m3u8/parser/VariantAttribute.java index cfb7d19..d720993 100644 --- a/src/main/java/io/lindstrom/m3u8/parser/VariantAttribute.java +++ b/src/main/java/io/lindstrom/m3u8/parser/VariantAttribute.java @@ -213,6 +213,20 @@ public void read(Variant.Builder builder, String value) throws PlaylistParserExc public void write(Variant value, TextBuilder textBuilder) { value.pathwayId().ifPresent(v -> textBuilder.addQuoted(key(), v)); } + }, + + REQ_VIDEO_LAYOUT { + @Override + public void read(Variant.Builder builder, String value) { + builder.reqVideoLayout(ParserUtils.split(value, ",")); + } + + @Override + public void write(Variant value, TextBuilder textBuilder) { + if (value.reqVideoLayout().isPresent()) { + textBuilder.addQuoted(key(), String.join(",", value.reqVideoLayout().get())); + } + } }; final static Map attributeMap = ParserUtils.toMap(values(), Attribute::key); @@ -223,4 +237,4 @@ static Variant parse(String attributes, String uri, ParsingMode parsingMode) thr builder.uri(uri); return builder.build(); } -} \ No newline at end of file +} diff --git a/src/test/java/io/lindstrom/m3u8/model/IFrameVariantTest.java b/src/test/java/io/lindstrom/m3u8/model/IFrameVariantTest.java index edd5daa..95a5cd6 100644 --- a/src/test/java/io/lindstrom/m3u8/model/IFrameVariantTest.java +++ b/src/test/java/io/lindstrom/m3u8/model/IFrameVariantTest.java @@ -16,7 +16,7 @@ public class IFrameVariantTest { public void allMethodsFromVariantExists() { // All attributes defined for the EXT-X-STREAM-INF tag (Section 4.4.6.2) // are also defined for the EXT-X-I-FRAME-STREAM-INF tag, except for the - // FRAME-RATE, AUDIO, SUBTITLES, and CLOSED-CAPTIONS attributes. + // FRAME-RATE, AUDIO, SUBTITLES, CLOSED-CAPTIONS and REQ-VIDEO-LAYOUTattributes. List ignoreMethods = new ArrayList<>(); ignoreMethods.add("frameRate"); ignoreMethods.add("audio"); @@ -24,6 +24,7 @@ public void allMethodsFromVariantExists() { ignoreMethods.add("frameRate"); ignoreMethods.add("closedCaptions"); ignoreMethods.add("closedCaptionsNone"); + ignoreMethods.add("reqVideoLayout"); for (Method m : Variant.class.getMethods()) { if (ignoreMethods.contains(m.getName())) { diff --git a/src/test/java/io/lindstrom/m3u8/parser/MultivariantPlaylistReadAndWrite.java b/src/test/java/io/lindstrom/m3u8/parser/MultivariantPlaylistReadAndWrite.java index f68d018..539152d 100644 --- a/src/test/java/io/lindstrom/m3u8/parser/MultivariantPlaylistReadAndWrite.java +++ b/src/test/java/io/lindstrom/m3u8/parser/MultivariantPlaylistReadAndWrite.java @@ -34,6 +34,8 @@ public static List data() { "open-m3u8/multivariantPlaylistWithAlternativeAudio.m3u8", "open-m3u8/multivariantPlaylistWithAlternativeVideo.m3u8", "open-m3u8/multivariantPlaylistWithIFrames.m3u8", + "spatial/with-1-variant.m3u8", + "spatial/with-5-variant.m3u8", "multivariant/content-steering.m3u8") .map(p -> Paths.get("src/test/resources/", p)) .collect(Collectors.toList()); diff --git a/src/test/resources/spatial/with-1-variant.m3u8 b/src/test/resources/spatial/with-1-variant.m3u8 new file mode 100644 index 0000000..fd72c3f --- /dev/null +++ b/src/test/resources/spatial/with-1-variant.m3u8 @@ -0,0 +1,13 @@ +#EXTM3U +#EXT-X-VERSION:12 +#EXT-X-MEDIA:TYPE=SUBTITLES,URI="en-US/en-US.m3u8",GROUP-ID="subs",LANGUAGE="en-US",NAME="English",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO +#EXT-X-STREAM-INF:BANDWIDTH=263248,AVERAGE-BANDWIDTH=271179,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=1920x1080,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +1080p/1080p.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=146501,AVERAGE-BANDWIDTH=154432,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=1280x720,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +720p/720p.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=109068,AVERAGE-BANDWIDTH=117006,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=960x540,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +540p/540p.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=83879,AVERAGE-BANDWIDTH=91809,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=640x360,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +360p/360p.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=72223,AVERAGE-BANDWIDTH=80153,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=480x270,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +270p/270p.m3u8 diff --git a/src/test/resources/spatial/with-5-variant.m3u8 b/src/test/resources/spatial/with-5-variant.m3u8 new file mode 100644 index 0000000..117e1e1 --- /dev/null +++ b/src/test/resources/spatial/with-5-variant.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:12 +#EXT-X-MEDIA:TYPE=SUBTITLES,URI="subs_en-US/en-US.m3u8",GROUP-ID="subs",LANGUAGE="en-US",NAME="English",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO +#EXT-X-STREAM-INF:BANDWIDTH=263248,AVERAGE-BANDWIDTH=271179,CODECS="hvc1.2.20000000.L123.B0",RESOLUTION=1920x1080,FRAME-RATE=60.0,SUBTITLES="subs",CLOSED-CAPTIONS=NONE,VIDEO-RANGE=SDR,REQ-VIDEO-LAYOUT="CH-STEREO" +1080p/1080p.m3u8 \ No newline at end of file