001package net.bramp.ffmpeg.builder; 002 003import static com.google.common.base.Preconditions.checkArgument; 004import static com.google.common.base.Preconditions.checkNotNull; 005import static net.bramp.ffmpeg.FFmpegUtils.millisToSeconds; 006import static net.bramp.ffmpeg.Preconditions.checkNotEmpty; 007 008import com.google.common.base.Strings; 009import com.google.common.collect.ImmutableList; 010import java.util.ArrayList; 011import java.util.List; 012import java.util.concurrent.TimeUnit; 013import javax.annotation.CheckReturnValue; 014 015/** Builder for FFmpeg HLS (HTTP Live Streaming) output arguments. */ 016public class FFmpegHlsOutputBuilder extends AbstractFFmpegOutputBuilder<FFmpegHlsOutputBuilder> { 017 018 public Long hls_time; 019 public String hls_segment_filename; 020 public Long hls_init_time; 021 public Integer hls_list_size; 022 public String hls_base_url; 023 public String hls_playlist_type; 024 public String master_pl_name; 025 public String var_stream_map; 026 027 private final List<HlsVariant> variants = new ArrayList<>(); 028 029 /** Constructs a new HLS output builder with the given filename. */ 030 protected FFmpegHlsOutputBuilder(FFmpegBuilder parent, String filename) { 031 super(parent, filename); 032 this.format = "hls"; 033 } 034 035 @Override 036 public FFmpegHlsOutputBuilder setFormat(String format) { 037 if (format == null || !format.equals("hls")) { 038 throw new IllegalArgumentException( 039 "Format cannot be set to anything else except 'hls' for FFmpegHlsOutputBuilder"); 040 } 041 super.setFormat(format); 042 043 return this; 044 } 045 046 /** 047 * Set the target segment length. Default value is 2 seconds. 048 * 049 * @param duration hls_time to set 050 * @param units The units the offset is in 051 * @return {@link FFmpegHlsOutputBuilder} 052 */ 053 public FFmpegHlsOutputBuilder setHlsTime(long duration, TimeUnit units) { 054 checkNotNull(units); 055 this.hls_time = units.toMillis(duration); 056 057 return this; 058 } 059 060 /** 061 * hls_segment_filename Examples <br> 062 * <br> 063 * "file%03d.ts" segment files: file000.ts, file001.ts, file002.ts, etc. 064 * 065 * @param filename hls_segment_file_name to set 066 * @return {@link FFmpegHlsOutputBuilder} 067 */ 068 public FFmpegHlsOutputBuilder setHlsSegmentFileName(String filename) { 069 this.hls_segment_filename = checkNotEmpty(filename, "filename must not be empty"); 070 071 return this; 072 } 073 074 /** 075 * <strong>Segment will be cut on the next key frame after this time has passed on the first m3u8 076 * list.</strong> <br> 077 * 078 * @param duration hls_init_time to set 079 * @param units The units the offset is in 080 * @return {@link FFmpegHlsOutputBuilder} 081 */ 082 public FFmpegHlsOutputBuilder setHlsInitTime(long duration, TimeUnit units) { 083 checkNotNull(units); 084 this.hls_init_time = units.toMillis(duration); 085 086 return this; 087 } 088 089 /** 090 * <strong>Set the maximum number of playlist entries. If set to 0 the list file will contain all 091 * the segments .</strong> <br> 092 * Default value is 5 <br> 093 * 094 * @param size hls_time to set 095 * @return {@link FFmpegHlsOutputBuilder} 096 */ 097 public FFmpegHlsOutputBuilder setHlsListSize(int size) { 098 checkArgument(size >= 0, "Size cannot be less than 0."); 099 this.hls_list_size = size; 100 101 return this; 102 } 103 104 /** 105 * <strong>Append baseurl to every entry in the playlist. Useful to generate playlists with 106 * absolute paths. <br> 107 * Note that the playlist sequence number must be unique for each segment and it is not to be 108 * confused with the segment filename sequence number which can be cyclic, for example if the wrap 109 * option is specified.</strong> <br> 110 * 111 * @param baseurl hls_base_url to set 112 * @return {@link FFmpegHlsOutputBuilder} 113 */ 114 public FFmpegHlsOutputBuilder setHlsBaseUrl(String baseurl) { 115 this.hls_base_url = checkNotEmpty(baseurl, "baseurl must not be empty"); 116 117 return this; 118 } 119 120 /** 121 * Set the playlist type. 122 * 123 * @param type The playlist type (e.g. "event", "vod") 124 * @return {@link FFmpegHlsOutputBuilder} 125 */ 126 public FFmpegHlsOutputBuilder setHlsPlaylistType(String type) { 127 this.hls_playlist_type = checkNotEmpty(type, "type must not be empty"); 128 return this; 129 } 130 131 /** 132 * Set the master playlist name. 133 * 134 * @param name The master playlist name 135 * @return {@link FFmpegHlsOutputBuilder} 136 */ 137 public FFmpegHlsOutputBuilder setMasterPlName(String name) { 138 this.master_pl_name = checkNotEmpty(name, "name must not be empty"); 139 return this; 140 } 141 142 /** 143 * Set the variant stream map string manually. 144 * 145 * <p>Prefer using {@link #addVariant(HlsVariant)} for a cleaner API. 146 * 147 * @param map The variant stream map (e.g. "v:0,a:0 v:1,a:1") 148 * @return {@link FFmpegHlsOutputBuilder} 149 */ 150 public FFmpegHlsOutputBuilder setVarStreamMap(String map) { 151 this.var_stream_map = checkNotEmpty(map, "map must not be empty"); 152 return this; 153 } 154 155 /** 156 * Adds an HLS variant to this output. 157 * 158 * @param variant The variant configuration. 159 * @return {@link FFmpegHlsOutputBuilder} 160 */ 161 public FFmpegHlsOutputBuilder addVariant(HlsVariant variant) { 162 this.variants.add(checkNotNull(variant)); 163 return this; 164 } 165 166 @Override 167 protected void addFormatArgs(ImmutableList.Builder<String> args) { 168 super.addFormatArgs(args); 169 if (hls_time != null) { 170 args.add("-hls_time", millisToSeconds(hls_time)); 171 } 172 173 if (!Strings.isNullOrEmpty(hls_segment_filename)) { 174 args.add("-hls_segment_filename", hls_segment_filename); 175 } 176 177 if (hls_init_time != null) { 178 args.add("-hls_init_time", millisToSeconds(hls_init_time)); 179 } 180 181 if (hls_list_size != null) { 182 args.add("-hls_list_size", hls_list_size.toString()); 183 } 184 185 if (!Strings.isNullOrEmpty(hls_base_url)) { 186 args.add("-hls_base_url", hls_base_url); 187 } 188 189 if (!Strings.isNullOrEmpty(hls_playlist_type)) { 190 args.add("-hls_playlist_type", hls_playlist_type); 191 } 192 193 if (!Strings.isNullOrEmpty(master_pl_name)) { 194 args.add("-master_pl_name", master_pl_name); 195 } 196 197 if (!Strings.isNullOrEmpty(var_stream_map)) { 198 args.add("-var_stream_map", var_stream_map); 199 } else if (!variants.isEmpty()) { 200 StringBuilder sb = new StringBuilder(); 201 for (int i = 0; i < variants.size(); i++) { 202 if (i > 0) { 203 sb.append(" "); 204 } 205 sb.append(variants.get(i).toString()); 206 } 207 args.add("-var_stream_map", sb.toString()); 208 } 209 } 210 211 @CheckReturnValue 212 @Override 213 protected FFmpegHlsOutputBuilder getThis() { 214 return this; 215 } 216}