/** * See {@link Channels#newReader(java.nio.channels.ReadableByteChannel, String)} See {@link * java.io.StringReader} * * @param source the source. * @return a playlist. * @throws ParseException parsing fails. */ public Playlist parse(Readable source) throws ParseException { final Scanner scanner = new Scanner(source); boolean firstLine = true; int lineNumber = 0; final List<Element> elements = new ArrayList<Element>(10); final ElementBuilder builder = new ElementBuilder(); boolean endListSet = false; int targetDuration = -1; int mediaSequenceNumber = -1; EncryptionInfo currentEncryption = null; while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); if (line.length() > 0) { if (line.startsWith(EX_PREFIX)) { if (firstLine) { checkFirstLine(lineNumber, line); firstLine = false; } else if (line.startsWith(EXTINF)) { parseExtInf(line, lineNumber, builder); } else if (line.startsWith(EXT_X_ENDLIST)) { endListSet = true; } else if (line.startsWith(EXT_X_TARGET_DURATION)) { if (targetDuration != -1) { throw new ParseException(line, lineNumber, EXT_X_TARGET_DURATION + " duplicated"); } targetDuration = parseTargetDuration(line, lineNumber); } else if (line.startsWith(EXT_X_MEDIA_SEQUENCE)) { if (mediaSequenceNumber != -1) { throw new ParseException(line, lineNumber, EXT_X_MEDIA_SEQUENCE + " duplicated"); } mediaSequenceNumber = parseMediaSequence(line, lineNumber); } else if (line.startsWith(EXT_X_PROGRAM_DATE_TIME)) { long programDateTime = parseProgramDateTime(line, lineNumber); builder.programDate(programDateTime); } else if (line.startsWith(EXT_X_STREAM_INF)) { if (!parsePlayListInfo(builder, line)) { throw new ParseException( line, lineNumber, "Failed to parse EXT-X-STREAM-INF element"); } } else if (line.startsWith(EXT_X_KEY)) { currentEncryption = parseEncryption(line, lineNumber); } else { log.log(Level.FINE, "Unknown: '" + line + "'"); } } else if (line.startsWith(COMMENT_PREFIX)) { // no first line check because comments will be ignored. // comment do nothing if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "----- Comment: " + line); } } else { if (firstLine) { checkFirstLine(lineNumber, line); } // No prefix: must be the media uri. builder.encrypted(currentEncryption); builder.uri(toURI(line)); elements.add(builder.create()); // a new element begins. builder.reset(); } } lineNumber++; } return new Playlist( Collections.unmodifiableList(elements), endListSet, targetDuration, mediaSequenceNumber); }