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}