[FFmpeg-devel] [PATCH] avfomat/hlsenc: support fmp4 format in hls

Steven Liu lingjiujianke at gmail.com
Mon Jul 3 15:22:48 EEST 2017


2017-07-01 20:05 GMT+08:00 Steven Liu <lq at chinaffmpeg.org>:
> add the fmp4 format into hlsenc
> because the fmp4 format add into hls from version 7.
> the spec link is:
> https://tools.ietf.org/html/draft-pantos-http-live-streaming-20
> and the describe on WWDC
> https://developer.apple.com/videos/play/wwdc2017/515/
>
> Signed-off-by: Steven Liu <lq at chinaffmpeg.org>
> ---
>  doc/muxers.texi      |  17 ++++++++
>  libavformat/hlsenc.c | 107 +++++++++++++++++++++++++++++++++++++++++++--------
>  2 files changed, 108 insertions(+), 16 deletions(-)
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 0866142..94472ce 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -614,6 +614,23 @@ in the playlist.
>  Hex-coded 16byte initialization vector for every segment instead
>  of the autogenerated ones.
>
> + at item hls_segment_type @var{flags}
> +Possible values:
> +
> + at table @samp
> + at item mpegts
> +If this flag is set, the hls segment files will format to mpegts.
> +the mpegts files is used in all hls versions.
> +
> + at item fmp4
> +If this flag is set, the hls segment files will format to fragment mp4 looks like dash.
> +the fmp4 files is used in hls after version 7.
> +
> + at end table
> +
> + at item hls_fmp4_init_filename @var{filename}
> +set filename to the fragment files header file, default filename is @file{init.mp4}.
> +
>  @item hls_flags @var{flags}
>  Possible values:
>
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index 40143c8..a49b594 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -88,6 +88,11 @@ typedef enum HLSFlags {
>  } HLSFlags;
>
>  typedef enum {
> +    SEGMENT_TYPE_MPEGTS,
> +    SEGMENT_TYPE_FMP4,
> +} SegmentType;
> +
> +typedef enum {
>      PLAYLIST_TYPE_NONE,
>      PLAYLIST_TYPE_EVENT,
>      PLAYLIST_TYPE_VOD,
> @@ -115,6 +120,9 @@ typedef struct HLSContext {
>      uint32_t flags;        // enum HLSFlags
>      uint32_t pl_type;      // enum PlaylistType
>      char *segment_filename;
> +    char *fmp4_init_filename;
> +    int segment_type;
> +    int fmp4_init_mode;
>
>      int use_localtime;      ///< flag to expand filename with localtime
>      int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
> @@ -256,6 +264,16 @@ fail:
>      return -1;
>  }
>
> +static void write_styp(AVIOContext *pb)
> +{
> +    avio_wb32(pb, 24);
> +    ffio_wfourcc(pb, "styp");
> +    ffio_wfourcc(pb, "msdh");
> +    avio_wb32(pb, 0); /* minor */
> +    ffio_wfourcc(pb, "msdh");
> +    ffio_wfourcc(pb, "msix");
> +}
> +
>  static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls) {
>
>      HLSSegment *segment, *previous_segment = NULL;
> @@ -508,6 +526,7 @@ static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
>
>  static int hls_mux_init(AVFormatContext *s)
>  {
> +    AVDictionary *options = NULL;
>      HLSContext *hls = s->priv_data;
>      AVFormatContext *oc;
>      AVFormatContext *vtt_oc = NULL;
> @@ -553,7 +572,35 @@ static int hls_mux_init(AVFormatContext *s)
>      }
>      hls->start_pos = 0;
>      hls->new_start = 1;
> +    hls->fmp4_init_mode = 0;
>
> +    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
> +        hls->fmp4_init_mode = 1;
> +        if ((ret = s->io_open(s, &oc->pb, hls->fmp4_init_filename, AVIO_FLAG_WRITE, NULL)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", hls->fmp4_init_filename);
> +            return ret;
> +        }
> +
> +        if (hls->format_options_str) {
> +            ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0);
> +            if (ret < 0) {
> +                av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n",
> +                       hls->format_options_str);
> +                return ret;
> +            }
> +        }
> +
> +        av_dict_copy(&options, hls->format_options, 0);
> +        av_dict_set(&options, "fflags", "-autobsf", 0);
> +        av_dict_set(&options, "movflags", "frag_custom+dash+delay_moov", 0);
> +        ret = avformat_init_output(oc, &options);
> +        if (av_dict_count(options)) {
> +            av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", hls->format_options_str);
> +            av_dict_free(&options);
> +            return AVERROR(EINVAL);
> +        }
> +        av_dict_free(&options);
> +    }
>      return 0;
>  }
>
> @@ -922,6 +969,9 @@ static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version
>      }
>      avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
>      avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
> +    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
> +        avio_printf(out, "#EXT-X-MAP:URI=\"%s\"\n", hls->fmp4_init_filename);
> +    }
>      av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
>  }
>
> @@ -961,6 +1011,10 @@ static int hls_window(AVFormatContext *s, int last)
>          sequence = 0;
>      }
>
> +    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
> +        version = 7;
> +    }
> +
>      if (!use_rename && !warned_non_file++)
>          av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
>
> @@ -1194,15 +1248,19 @@ static int hls_start(AVFormatContext *s)
>      }
>      av_dict_free(&options);
>
> -    /* We only require one PAT/PMT per segment. */
> -    if (oc->oformat->priv_class && oc->priv_data) {
> -        char period[21];
> +    if (c->segment_type == SEGMENT_TYPE_FMP4) {
> +            write_styp(oc->pb);
> +    } else {
> +        /* We only require one PAT/PMT per segment. */
> +        if (oc->oformat->priv_class && oc->priv_data) {
> +            char period[21];
>
> -        snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);
> +            snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);
>
> -        av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
> -        av_opt_set(oc->priv_data, "sdt_period", period, 0);
> -        av_opt_set(oc->priv_data, "pat_period", period, 0);
> +            av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
> +            av_opt_set(oc->priv_data, "sdt_period", period, 0);
> +            av_opt_set(oc->priv_data, "pat_period", period, 0);
> +        }
>      }
>
>      if (c->vtt_basename) {
> @@ -1289,7 +1347,11 @@ static int hls_write_header(AVFormatContext *s)
>                 "More than a single video stream present, "
>                 "expect issues decoding it.\n");
>
> -    hls->oformat = av_guess_format("mpegts", NULL, NULL);
> +    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
> +        hls->oformat = av_guess_format("mp4", NULL, NULL);
> +    } else {
> +        hls->oformat = av_guess_format("mpegts", NULL, NULL);
> +    }
>
>      if (!hls->oformat) {
>          ret = AVERROR_MUXER_NOT_FOUND;
> @@ -1390,8 +1452,10 @@ static int hls_write_header(AVFormatContext *s)
>          }
>      }
>
> -    if ((ret = hls_start(s)) < 0)
> -        goto fail;
> +    if (hls->segment_type != SEGMENT_TYPE_FMP4) {
> +        if ((ret = hls_start(s)) < 0)
> +            goto fail;
> +    }
>
>      av_dict_copy(&options, hls->format_options, 0);
>      ret = avformat_write_header(hls->avf, &options);
> @@ -1448,7 +1512,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>      AVStream *st = s->streams[pkt->stream_index];
>      int64_t end_pts = hls->recording_time * hls->number;
>      int is_ref_pkt = 1;
> -    int ret, can_split = 1;
> +    int ret = 0, can_split = 1;
>      int stream_index = 0;
>
>      if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) {
> @@ -1495,7 +1559,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>          }
>
>      }
> -    if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
> +    if (hls->fmp4_init_mode || can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
>                                     end_pts, AV_TIME_BASE_Q) >= 0) {
>          int64_t new_start_pos;
>          char *old_filename = av_strdup(hls->avf->filename);
> @@ -1505,7 +1569,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>              return AVERROR(ENOMEM);
>          }
>
> -        av_write_frame(oc, NULL); /* Flush any buffered data */
> +        av_write_frame(hls->avf, NULL); /* Flush any buffered data */
>
>          new_start_pos = avio_tell(hls->avf->pb);
>          hls->size = new_start_pos - hls->start_pos;
> @@ -1518,12 +1582,18 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>          }
>          if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
>              if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0))
> -                if (hls->avf->oformat->priv_class && hls->avf->priv_data)
> +                if ((hls->avf->oformat->priv_class && hls->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4)
>                      av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
>              hls_rename_temp_file(s, oc);
>          }
>
> -        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
> +        if (hls->fmp4_init_mode) {
> +            hls->number--;
> +        }
> +
> +        if (!hls->fmp4_init_mode)
> +            ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
> +
>          hls->start_pos = new_start_pos;
>          if (ret < 0) {
>              av_free(old_filename);
> @@ -1533,6 +1603,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>          hls->end_pts = pkt->pts;
>          hls->duration = 0;
>
> +        hls->fmp4_init_mode = 0;
>          if (hls->flags & HLS_SINGLE_FILE) {
>              hls->number++;
>          } else if (hls->max_seg_size > 0) {
> @@ -1640,6 +1711,10 @@ static const AVOption options[] = {
>      {"hls_enc_key_url",    "url to access the key to decrypt the segments", OFFSET(key_url),      AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
>      {"hls_enc_iv",    "hex-coded 16 byte initialization vector", OFFSET(iv),      AV_OPT_TYPE_STRING, .flags = E},
>      {"hls_subtitle_path",     "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
> +    {"hls_segment_type",     "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "segment_type"},
> +    {"fmp4",   "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX,   E, "segment_type"},
> +    {"mpegts",   "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX,   E, "segment_type"},
> +    {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename),   AV_OPT_TYPE_STRING, {.str = "init.mp4"},            0,       0,         E},
>      {"hls_flags",     "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
>      {"single_file",   "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX,   E, "flags"},
>      {"temp_file", "write segment to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX,   E, "flags"},
> @@ -1682,7 +1757,7 @@ AVOutputFormat ff_hls_muxer = {
>      .audio_codec    = AV_CODEC_ID_AAC,
>      .video_codec    = AV_CODEC_ID_H264,
>      .subtitle_codec = AV_CODEC_ID_WEBVTT,
> -    .flags          = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
> +    .flags          = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
>      .write_header   = hls_write_header,
>      .write_packet   = hls_write_packet,
>      .write_trailer  = hls_write_trailer,
> --
> 2.10.1.382.ga23ca1b.dirty
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel



Pushed!


More information about the ffmpeg-devel mailing list