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