package com.zhan_dui.utils.m3u8; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author dkuffner */ final class M3uConstants { private M3uConstants() { throw new AssertionError("Not allowed"); } final static String COMMENT_PREFIX = "#"; final static String EX_PREFIX = "#EXT"; /** * An Extended M3U file is distinguished from a basic M3U file by its first line, which MUST be #EXTM3U. */ final static String EXTM3U = "#EXTM3U"; /** * EXTINF is a record marker that describes the media file identified by the URI that follows it. * Each media file URI MUST be preceded by an EXTINF tag. Its format is: #EXTINF:<duration>,<title> * <p/> * "duration" is an integer that specifies the duration of the media file in seconds. * Durations SHOULD be rounded to the nearest integer. The remainder of the line following * the comma is the title of the media file. */ final static String EXTINF = "#EXTINF"; /** * The EXT-X-TARGETDURATION tag indicates the approximate duration of the next media file that will be added to * the main presentation. It MUST appear in the Playlist file. Its format is: #EXT-X-TARGETDURATION:<seconds> * <p/> * The actual duration of the media file MAY differ slightly from the target duration. */ final static String EXT_X_TARGET_DURATION = "#EXT-X-TARGETDURATION"; /** * Each media file URI in a Playlist has a unique sequence number. The sequence number * of a URI is equal to the sequence number of the URI that preceded it plus one. * The EXT-X-MEDIA-SEQUENCE tag indicates the sequence number of the first URI that appears * in a Playlist file. Its format is: #EXT-X-MEDIA-SEQUENCE:<number> * <p/> * If the Playlist file does not contain an EXT-X-MEDIA-SEQUENCE tag then the sequence number * of the first URI in the playlist SHALL be considered to be 1. */ final static String EXT_X_MEDIA_SEQUENCE = "#EXT-X-MEDIA-SEQUENCE"; /** * Media files MAY be encrypted. The EXT-X-KEY tag provides information necessary to decrypt media files * that follow it. Its format is: #EXT-X-KEY:METHOD=<method>[,URI="<URI>"] * <p/> * The METHOD parameter specifies the encryption method. The URI parameter, if present, * specifies how to obtain the key. * <p/> * Version 1.0 of the protocol defines two encryption methods: NONE and AES-128. An encryption * method of NONE means that media files are not encrypted. * <p/> * An encryption method of AES-128 means that media files are encrypted using the Advanced Encryption * Standard [AES_128] with a 128-bit key and PKCS7 padding [RFC3852]. * <p/> * A new EXT-X-KEY supersedes any prior EXT-X-KEY. * If no EXT-X-KEY tag is present then media files are not encrypted. */ final static String EXT_X_KEY = "#EXT-X-KEY"; /** * The EXT-X-PROGRAM-DATE-TIME tag associates the beginning of the next * media file with an absolute date and/or time. The date/time * representation is ISO/IEC 8601:2004 [ISO_8601] and SHOULD indicate a * time zone. For example: #EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ> */ final static String EXT_X_PROGRAM_DATE_TIME = "#EXT-X-PROGRAM-DATE-TIME"; /** * The EXT-X-ALLOW-CACHE tag indicates whether the client MAY cache * downloaded media files for later replay. Its format is: * <p/> * #EXT-X-ALLOW-CACHE:<YES|NO> */ final static String EXT_X_ALLOW_CACHE = "#EXT-X-ALLOW-CACHE"; /** * The EXT-X-STREAM-INF tag indicates that the next URI in the Playlist * file identifies another Playlist file. Its format is: * <p/> * #EXT-X-STREAM-INF:[attribute=value][,attribute=value]* * <URI> * <p/> * The following attributes are defined for the EXT-X-STREAM-INF tag: * <p/> * BANDWIDTH=<n> * <p/> * where n is an approximate upper bound of the stream bitrate, * expressed as a number of bits per second. * <p/> * PROGRAM-ID=<i> * <p/> * where i is a number that uniquely identifies a particular * presentation within the scope of the Playlist file. * <p/> * A Playlist file MAY contain multiple EXT-X-STREAM-INF URIs with the * same PROGRAM-ID to describe variant streams of the same presentation. */ final static String EXT_X_STREAM_INF = "#EXT-X-STREAM-INF"; /** * The EXT-X-ENDLIST tag indicates that no more media files will be * added to the Playlist file. */ final static String EXT_X_ENDLIST = "#EXT-X-ENDLIST"; /** * The EXT-X-DISCONTINUITY tag indicates that the media file following * it has different characteristics than the one that preceded it. The * set of characteristics that MAY change is: * <p/> * file format * <p/> * number and type of tracks * <p/> * encoding parameters * <p/> * encoding sequence * <p/> * timestamp sequence * <p/> * Its format is: * <p/> * #EXT-X-DISCONTINUITY */ final static String EXT_X_DISCONTINUITY = "#EXT-X-DISCONTINUITY"; /** * A holder class for Patterns. */ static class Patterns { private Patterns() { throw new AssertionError(); } final static Pattern EXTINF = Pattern.compile(tagPattern(M3uConstants.EXTINF) + "\\s*(-1|[0-9\\.]*)\\s*(?:,((.*)))?"); private static String tagPattern(String tagName) { return "\\s*" + tagName + "\\s*:\\s*"; } // #EXT-X-KEY:METHOD=<method>[,URI="<URI>" // #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52" final static Pattern EXT_X_KEY = Pattern.compile(tagPattern(M3uConstants.EXT_X_KEY) + "METHOD=([0-9A-Za-z\\-]*)(,URI=\"(([^\\\\\"]*.*))\")?"); final static Pattern EXT_X_TARGET_DURATION = Pattern.compile(tagPattern(M3uConstants.EXT_X_TARGET_DURATION) + "([0-9]*)"); final static Pattern EXT_X_MEDIA_SEQUENCE = Pattern.compile(tagPattern(M3uConstants.EXT_X_MEDIA_SEQUENCE) + "([0-9]*)"); // YYYY-MM-DDThh:mm:ss final static Pattern EXT_X_PROGRAM_DATE_TIME = Pattern.compile(tagPattern(M3uConstants.EXT_X_PROGRAM_DATE_TIME) + "(.*)"); // YYYY MM DD hh mm ss /** * Helper method to create a date object for EXT_X_PROGRAM_DATE_TIME pattern. * * @param line the line to parse. * @param lineNumber line number. * @return date as long. * @throws ParseException in case of date time cannot be parsed. */ static long toDate(String line, int lineNumber) throws ParseException { Matcher matcher = Patterns.EXT_X_PROGRAM_DATE_TIME.matcher(line); if (!matcher.find() || !matcher.matches() || matcher.groupCount() < 1) { throw new ParseException(line, lineNumber, " must specify date-time"); } SimpleDateFormat ISO8601FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); System.out.println(ISO8601FORMAT.format(new Date())); String dateTime = matcher.group(1); try { return ISO8601FORMAT.parse(dateTime).getTime(); } catch (java.text.ParseException e) { throw new ParseException(line, lineNumber, e); } /*int index = 1; Calendar c = Calendar.getInstance(); c.set(Calendar.YEAR, toInt(matcher, line, lineNumber, index)); c.set(Calendar.MONTH, toInt(matcher, line, lineNumber, ++index)-1); c.set(Calendar.DAY_OF_MONTH, toInt(matcher, line, lineNumber, ++index)); c.set(Calendar.HOUR, toInt(matcher, line, lineNumber, ++index)); c.set(Calendar.MINUTE, toInt(matcher, line, lineNumber, ++index)); c.set(Calendar.SECOND, toInt(matcher, line, lineNumber, ++index)); return c.getTime().getTime();*/ } /* private static int toInt(Matcher matcher, String line, int lineNumber, int index) throws IllegalTypeParseException { try { return Integer.valueOf(matcher.group(index)); } catch(NumberFormatException e) { throw new IllegalTypeParseException(line, lineNumber, e); } } */ } }