[FFmpeg-devel] [PATCH 3/4] V13 - SCTE-35 support in hlsenc

Anssi Hannula anssi.hannula at iki.fi
Sat Oct 15 11:44:49 EEST 2016


04.10.2016, 02:14, Carlos Fernandez Sanz kirjoitti:
> From: Carlos Fernandez <carlos at ccextractor.org>
> 
> Signed-off-by: Carlos Fernandez <carlos at ccextractor.org>
> ---
>  libavformat/Makefile  |   2 +-
>  libavformat/hlsenc.c  | 108 +++++++++--
>  libavformat/scte_35.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/scte_35.h |  86 ++++++++
>  4 files changed, 701 insertions(+), 22 deletions(-)
>  create mode 100644 libavformat/scte_35.c
>  create mode 100644 libavformat/scte_35.h
> 
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 5d827d31..9218606 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -205,7 +205,7 @@ OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
>  OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>  OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
>  OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> -OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o
> +OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o scte_35.o
>  OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
>  OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
>  OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index c161937..44259ec 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -38,6 +38,7 @@
>  #include "avio_internal.h"
>  #include "internal.h"
>  #include "os_support.h"
> +#include "scte_35.h"
>  
>  #define KEYSIZE 16
>  #define LINE_BUFFER_SIZE 1024
> @@ -48,6 +49,10 @@ typedef struct HLSSegment {
>      double duration; /* in seconds */
>      int64_t pos;
>      int64_t size;
> +    struct scte_35_event *event;
> +    int out;

At least 'out' should be documented (or renamed) here as it is not
obvious what it means.

> +    int adv_count;
> +    int64_t start_pts;
>  
>      char key_uri[LINE_BUFFER_SIZE + 1];
>      char iv_string[KEYSIZE*2 + 1];
> @@ -92,6 +97,8 @@ typedef struct HLSContext {
>      uint32_t flags;        // enum HLSFlags
>      uint32_t pl_type;      // enum PlaylistType
>      char *segment_filename;
> +    char *adv_filename;
> +    char *adv_subfilename;
>  
>      int use_localtime;      ///< flag to expand filename with localtime
>      int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
> @@ -108,6 +115,8 @@ typedef struct HLSContext {
>      int nb_entries;
>      int discontinuity_set;
>  
> +    int adv_count;
> +    struct scte_35_interface *scte_iface;
>      HLSSegment *segments;
>      HLSSegment *last_segment;
>      HLSSegment *old_segments;
> @@ -241,6 +250,8 @@ static int hls_delete_old_segments(HLSContext *hls) {
>          av_freep(&path);
>          previous_segment = segment;
>          segment = previous_segment->next;
> +        if (hls->scte_iface)
> +            hls->scte_iface->unref_scte35_event(&previous_segment->event);
>          av_free(previous_segment);
>      }
>  
> @@ -360,8 +371,8 @@ static int hls_mux_init(AVFormatContext *s)
>  }
>  
>  /* Create a new segment and append it to the segment list */
> -static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double duration,
> -                              int64_t pos, int64_t size)
> +static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double duration, int64_t pos,
> +                              int64_t start_pts, struct scte_35_event *event, int64_t size)
>  {
>      HLSSegment *en = av_malloc(sizeof(*en));
>      char *tmp, *p;
> @@ -397,9 +408,23 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double
>  
>      en->duration = duration;
>      en->pos      = pos;
> +    en->event    = event;
>      en->size     = size;
> +    en->start_pts  = start_pts;
>      en->next     = NULL;
>  
> +    if (hls->scte_iface) {
> +        if (hls->scte_iface->event_state == EVENT_OUT_CONT) {
> +            en->adv_count = hls->adv_count;;

Double semicolon.

> +            hls->adv_count++;
> +            en->out = hls->scte_iface->event_state;
> +        } else {
> +            hls->adv_count = 0;
> +            en->out = hls->scte_iface->event_state;

Assignment could be moved outside the if condition.

> +        }
> +    }
> +
> +
>      if (hls->key_info_file) {
>          av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri));
>          av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string));
> @@ -473,7 +498,7 @@ static int parse_playlist(AVFormatContext *s, const char *url)
>                  new_start_pos = avio_tell(hls->avf->pb);
>                  hls->size = new_start_pos - hls->start_pos;
>                  av_strlcpy(hls->avf->filename, line, sizeof(line));
> -                ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
> +                ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, 0, NULL, hls->size);
>                  if (ret < 0)
>                      goto fail;
>                  hls->start_pos = new_start_pos;
> @@ -603,9 +628,23 @@ static int hls_window(AVFormatContext *s, int last)
>              avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
>              prog_date_time += en->duration;
>          }
> -        if (hls->baseurl)
> -            avio_printf(out, "%s", hls->baseurl);
> -        avio_printf(out, "%s\n", en->filename);
> +        if (hls->scte_iface && (en->event || en->out) ) {

Drop extra space between ')'. Same for other instances.

> +            char *str;
> +            char fname[1024] = "";
> +            if (hls->adv_filename) {
> +                str = hls->scte_iface->get_hls_string(hls->scte_iface, en->event, hls->adv_filename, en->out, en->adv_count, en->start_pts);
> +            } else {
> +                if (hls->baseurl)
> +                    strncat(fname, hls->baseurl, 1024);
> +                strncat(fname, en->filename, 1024 - strlen(fname) );

Still missing sizeof (Michael's comment on Aug 10).

strncat(fname, hls->baseurl, sizeof(fname));


[...]
> diff --git a/libavformat/scte_35.c b/libavformat/scte_35.c
> new file mode 100644
> index 0000000..1a01d14
> --- /dev/null
> +++ b/libavformat/scte_35.c
> @@ -0,0 +1,527 @@
> +/*
> + * SCTE 35 decoder
> + * Copyright (c) 2016 Carlos Fernandez
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +/*
> + * Refrence Material Used

Reference material:

> + *
> + * ANSI/SCTE 35 2013 (Digital Program Insertion Cueing Message for Cable)
> + *
> + * SCTE 67 2014 (Recommended Practice for SCTE 35
> + *          Digital Program Insertion Cueing Message for Cable)
> + */
> +
> +
> +
[...]
> diff --git a/libavformat/scte_35.h b/libavformat/scte_35.h
> new file mode 100644
> index 0000000..cac49ea
> --- /dev/null
> +++ b/libavformat/scte_35.h
> @@ -0,0 +1,86 @@
> +/*
> + * SCTE-35 parser
> + * Copyright (c) 2016 Carlos Fernandez
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +#ifndef AVFORMAT_SCTE_35_H
> +#define AVFORMAT_SCTE_35_H
> +
> +#include "libavutil/bprint.h"
> +
> +struct scte_35_event {
> +    /* ID given for each seprate event */
> +    int32_t id;
> +    /* pts specify time when event starts */
> +    uint64_t in_pts;
> +    uint64_t nearest_in_pts;
> +    /* pts specify ehen events end */
> +    uint64_t out_pts;
> +    /* duration of the event */
> +    int64_t duration;
> +    int64_t start_pos;
> +    int running;
> +    int ref_count;
> +    /* to traverse the list of events */
> +    struct scte_35_event *next;
> +    struct scte_35_event *prev;
> +};
> +
> +enum event_state {
> +    /* NO event */
> +    EVENT_NONE,
> +    /* Commercials need to end */
> +    EVENT_IN,
> +    /* Commercials can start from here */
> +    EVENT_OUT,
> +    /* commercial can continue */
> +    EVENT_OUT_CONT,
> +};
> +
> +struct scte_35_interface {
> +    /* contain all  the events */
> +    struct scte_35_event *event_list;
> +    /* state of current event */
> +    enum event_state event_state;
> +    /* time base of pts used in parser */
> +    AVRational timebase;
> +    struct scte_35_event *current_event;
> +    /* saved previous state to correctly transition
> +        the event state */
> +    int prev_event_state;
> +    //TODO use AV_BASE64_SIZE to dynamically allocate the array
> +    char pkt_base64[1024];
> +    /* keep context of its parent for log */
> +    void *parent;
> +    /* general purpose str */
> +    AVBPrint avbstr;
> +
> +    void (*update_video_pts)(struct scte_35_interface *iface, uint64_t pts);
> +    struct scte_35_event* (*update_event_state)(struct scte_35_interface *iface);
> +    char* (*get_hls_string)(struct scte_35_interface *iface, struct scte_35_event *event,
> +               const char *adv_filename, int out_state, int seg_count, int64_t pos);
> +
> +    void (*unref_scte35_event)(struct scte_35_event **event);
> +    void (*ref_scte35_event)(struct scte_35_event *event);

Function pointers are not useful here as they always point to the same
functions.

Also, the members of this "interface" struct seem to be mostly scte_35.c
internal stuff that shouldn't be in a header.

What I'd probably do is to just call the functions directly, rename the
scte_35_interface to scte_35_parser and move the struct definition to
scte_35.c.

> +};
> +
> +int ff_parse_scte35_pkt(struct scte_35_interface *iface, const AVPacket *avpkt);
> +
> +struct scte_35_interface* ff_alloc_scte35_parser(void *parent, AVRational timebase);
> +void ff_delete_scte35_parser(struct scte_35_interface* iface);
> +#endif
> 


-- 
Anssi Hannula


More information about the ffmpeg-devel mailing list