diff mbox

[FFmpeg-devel,3/4] V8 - SCTE-35 support in hlsenc

Message ID 1472665179-1373-4-git-send-email-carlos@ccextractor.org
State Superseded
Headers show

Commit Message

Carlos Fernandez Sanz Aug. 31, 2016, 5:39 p.m. UTC
From: Carlos Fernandez <carlos@ccextractor.org>

Signed-off-by: Carlos Fernandez <carlos@ccextractor.org>
---
 libavformat/Makefile  |   2 +-
 libavformat/hlsenc.c  | 110 ++++++++---
 libavformat/scte_35.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/scte_35.h |  86 ++++++++
 4 files changed, 702 insertions(+), 23 deletions(-)
 create mode 100644 libavformat/scte_35.c
 create mode 100644 libavformat/scte_35.h

Comments

Steven Liu Aug. 31, 2016, 11:10 p.m. UTC | #1
2016-09-01 1:39 GMT+08:00 Carlos Fernandez Sanz <carlos@ccextractor.org>:

> From: Carlos Fernandez <carlos@ccextractor.org>
>
> Signed-off-by: Carlos Fernandez <carlos@ccextractor.org>
> ---
>  libavformat/Makefile  |   2 +-
>  libavformat/hlsenc.c  | 110 ++++++++---
>  libavformat/scte_35.c | 527 ++++++++++++++++++++++++++++++
> ++++++++++++++++++++
>  libavformat/scte_35.h |  86 ++++++++
>  4 files changed, 702 insertions(+), 23 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 81b7efa..25092f7 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -1,4 +1,4 @@
> -/*
> + /*
>   * Apple HTTP Live Streaming segmenter
>   * Copyright (c) 2012, Luca Barbato
>   *
> @@ -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;
> +    int adv_count;
> +    int64_t start_pts;
>
>      char key_uri[LINE_BUFFER_SIZE + 1];
>      char iv_string[KEYSIZE*2 + 1];
> @@ -91,6 +96,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
> @@ -106,6 +113,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;
> @@ -205,6 +214,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);
>      }
>
> @@ -324,8 +335,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;
> @@ -361,9 +372,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;;
> +            hls->adv_count++;
> +            en->out = hls->scte_iface->event_state;
> +        } else {
> +            hls->adv_count = 0;
> +            en->out = hls->scte_iface->event_state;
> +        }
> +    }
> +
> +
>      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));
> @@ -434,7 +459,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;
> @@ -533,9 +558,23 @@ static int hls_window(AVFormatContext *s, int last)
>          if (hls->flags & HLS_SINGLE_FILE)
>               avio_printf(out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
>                           en->size, en->pos);
> -        if (hls->baseurl)
> -            avio_printf(out, "%s", hls->baseurl);
> -        avio_printf(out, "%s\n", en->filename);
> +        if (hls->scte_iface && (en->event || en->out) ) {
> +            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);

Overflow here

> +                str = hls->scte_iface->get_hls_string(hls->scte_iface,
> en->event, fname, en->out, -1, en->start_pts);
> +            }
> +            avio_printf(out, "%s", str);
> +        } else {
> +            if (hls->baseurl)
> +                avio_printf(out, "%s", hls->baseurl);
> +            avio_printf(out, "%s\n", en->filename);
> +        }
>      }
>
>      if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
> @@ -560,9 +599,15 @@ static int hls_window(AVFormatContext *s, int last)
>              if (hls->flags & HLS_SINGLE_FILE)
>                   avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"
> PRIi64"\n",
>                           en->size, en->pos);
> -            if (hls->baseurl)
> -                avio_printf(sub_out, "%s", hls->baseurl);
> -            avio_printf(sub_out, "%s\n", en->sub_filename);
> +            if (hls->scte_iface && (en->event || en->out) ) {
> +                char *str = hls->scte_iface->get_hls_string(hls->scte_iface,
> en->event, hls->adv_subfilename, en->out, en->adv_count, en->pos);
> +                avio_printf(out, "%s", str);
> +            } else {
> +                if (hls->baseurl)
> +                    avio_printf(out, "%s", hls->baseurl);
> +                avio_printf(sub_out, "%s\n", en->sub_filename);
> +            }
> +
>          }
>
>          if (last)
> @@ -705,6 +750,7 @@ static int hls_write_header(AVFormatContext *s)
>      AVDictionary *options = NULL;
>      int basename_size;
>      int vtt_basename_size;
> +    int ts_index = 0;
>
>      hls->sequence       = hls->start_sequence;
>      hls->recording_time = (hls->init_time ? hls->init_time : hls->time) *
> AV_TIME_BASE;
> @@ -831,19 +877,21 @@ static int hls_write_header(AVFormatContext *s)
>          goto fail;
>      }
>      //av_assert0(s->nb_streams == hls->avf->nb_streams);
> -    for (i = 0; i < s->nb_streams; i++) {
> +    for (ts_index = 0, i = 0; i < s->nb_streams; i++) {
>          AVStream *inner_st;
>          AVStream *outer_st = s->streams[i];
> -        if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
> -            inner_st = hls->avf->streams[i];
> -        else if (hls->vtt_avf)
> +        if (hls->vtt_avf && outer_st->codecpar->codec_type ==
> AVMEDIA_TYPE_SUBTITLE) {
>              inner_st = hls->vtt_avf->streams[0];
> -        else {
> -            /* We have a subtitle stream, when the user does not want one
> */
> -            inner_st = NULL;
> +            avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits,
> inner_st->time_base.num, inner_st->time_base.den);
> +        } else if (outer_st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
> +            inner_st = hls->avf->streams[ts_index];
> +            hls->scte_iface = ff_alloc_scte35_parser(hls,
> outer_st->time_base);
>              continue;
> +        } else {
> +            inner_st = hls->avf->streams[ts_index];
> +            avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits,
> inner_st->time_base.num, inner_st->time_base.den);
> +            ts_index++;
>          }
> -        avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits,
> inner_st->time_base.num, inner_st->time_base.den);
>      }
>  fail:
>
> @@ -869,6 +917,12 @@ static int hls_write_packet(AVFormatContext *s,
> AVPacket *pkt)
>      int is_ref_pkt = 1;
>      int ret, can_split = 1;
>      int stream_index = 0;
> +    struct scte_35_event *event = NULL;
> +
> +    if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
> +        ret = ff_parse_scte35_pkt(hls->scte_iface, pkt);
> +        return ret;
> +    }
>
>      if (hls->sequence - hls->nb_entries > hls->start_sequence &&
> hls->init_time > 0) {
>          /* reset end_pts, hls->recording_time at end of the init hls list
> */
> @@ -902,14 +956,24 @@ static int hls_write_packet(AVFormatContext *s,
> AVPacket *pkt)
>          hls->duration = (double)(pkt->pts - hls->end_pts)
>                                     * st->time_base.num /
> st->time_base.den;
>
> -    if (can_split && av_compare_ts(pkt->pts - hls->start_pts,
> st->time_base,
> -                                   end_pts, AV_TIME_BASE_Q) >= 0) {
> +    if (hls->scte_iface)
> +        hls->scte_iface->update_video_pts(hls->scte_iface, pkt->pts *
> st->time_base.num / st->time_base.den);
> +
> +
> +    if (can_split && (( av_compare_ts(pkt->pts - hls->start_pts,
> st->time_base,
> +                                   end_pts, AV_TIME_BASE_Q) >= 0) ||
> +         (hls->scte_iface && hls->scte_iface->event_state == EVENT_OUT))
> ) {
>          int64_t new_start_pos;
>          av_write_frame(oc, NULL); /* Flush any buffered data */
>
>          new_start_pos = avio_tell(hls->avf->pb);
>          hls->size = new_start_pos - hls->start_pos;
> -        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos,
> hls->size);
> +        if (hls->scte_iface) {
> +            event = hls->scte_iface->update_event_state(hls->scte_iface);
> +            if (event)
> +                hls->scte_iface->ref_scte35_event(event);
> +        }
> +        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos,
> pkt->pts, event, hls->size);
>          hls->start_pos = new_start_pos;
>          if (ret < 0)
>              return ret;
> @@ -956,7 +1020,7 @@ static int hls_write_trailer(struct AVFormatContext
> *s)
>      if (oc->pb) {
>          hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
>          ff_format_io_close(s, &oc->pb);
> -        hls_append_segment(s, hls, hls->duration, hls->start_pos,
> hls->size);
> +        hls_append_segment(s, hls, hls->duration, hls->start_pos,
> hls->end_pts, NULL, hls->size);
>      }
>
>      if (vtt_oc) {
> @@ -977,6 +1041,7 @@ static int hls_write_trailer(struct AVFormatContext
> *s)
>      hls->avf = NULL;
>      hls_window(s, 1);
>
> +    ff_delete_scte35_parser(hls->scte_iface);
>      hls_free_segments(hls->segments);
>      hls_free_segments(hls->old_segments);
>      return 0;
> @@ -1031,6 +1096,7 @@ AVOutputFormat ff_hls_muxer = {
>      .audio_codec    = AV_CODEC_ID_AAC,
>      .video_codec    = AV_CODEC_ID_H264,
>      .subtitle_codec = AV_CODEC_ID_WEBVTT,
> +    .data_codec     = AV_CODEC_ID_SCTE_35,
>      .flags          = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
>      .write_header   = hls_write_header,
>      .write_packet   = hls_write_packet,
> diff --git a/libavformat/scte_35.c b/libavformat/scte_35.c
> new file mode 100644
> index 0000000..64e5f67
> --- /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
> + *
> + * 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 )
> + */
> +
> +
> +
> +#include "libavcodec/bytestream.h"
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/get_bits.h"
> +#include "libavutil/buffer_internal.h"
> +#include "libavutil/base64.h"
> +#include "scte_35.h"
> +
> +#define SCTE_CMD_NULL                  0x00
> +#define SCTE_CMD_SCHEDULE              0x04
> +#define SCTE_CMD_INSERT                0x05
> +#define SCTE_CMD_SIGNAL                0x06
> +#define SCTE_CMD_BANDWIDTH_RESERVATION 0x07
> +
> +
> +static char* get_hls_string(struct scte_35_interface *iface, struct
> scte_35_event *event,
> +                 const char *filename, int out_state, int seg_count,
> int64_t pos)
> +{
> +    int ret;
> +    av_bprint_clear(&iface->avbstr);
> +    if (out_state == EVENT_IN ) {
> +        av_bprintf(&iface->avbstr, "#EXT-OATCLS-SCTE35:%s\n",
> iface->pkt_base64);
> +        av_bprintf(&iface->avbstr, "#EXT-X-CUE-IN\n");
> +        av_bprintf(&iface->avbstr, "#EXT-X-DISCONTINUITY\n");
> +    } else if (out_state == EVENT_OUT) {
> +        if (event) {
> +            av_bprintf(&iface->avbstr, "#EXT-OATCLS-SCTE35:%s\n",
> iface->pkt_base64);
> +            if(event->duration != AV_NOPTS_VALUE) {
> +                int64_t dur = ceil(((double)event->duration *
> iface->timebase.num) /iface->timebase.den);
> +                av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT:%"PRIu64"\n",
> dur);
> +            } else {
> +                av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT\n");
> +            }
> +            av_bprintf(&iface->avbstr, "#EXT-X-DISCONTINUITY\n");
> +        }
> +    } else if (out_state == EVENT_OUT_CONT) {
> +        if(event && event->duration != AV_NOPTS_VALUE) {
> +            int64_t dur = ceil(((double)event->duration *
> iface->timebase.num) /iface->timebase.den);
> +            int64_t elapsed_time = ceil(((double)pos *
> iface->timebase.num) /iface->timebase.den) - event->out_pts;
> +            av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT-CONT:
> ElapsedTime=%"PRIu64",Duration=%"PRIu64",SCTE35=%s\n",
> +                elapsed_time,  dur, iface->pkt_base64);
> +        } else {
> +            av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT-CONT:SCTE35=%s\n",
> iface->pkt_base64);
> +        }
> +    }
> +    if (seg_count >= 0)
> +        av_bprintf(&iface->avbstr, filename, seg_count);
> +    else
> +        av_bprintf(&iface->avbstr, "%s", filename);
> +    av_bprintf(&iface->avbstr, "\n");
> +
> +    ret = av_bprint_is_complete(&iface->avbstr);
> +    if( ret == 0) {
> +        av_log(iface->parent, AV_LOG_DEBUG, "Out of Memory");
> +        return NULL;
> +    }
> +
> +    av_log(iface->parent, AV_LOG_DEBUG, "%s", iface->avbstr.str);
> +    return iface->avbstr.str;
> +}
> +
> +static struct scte_35_event* alloc_scte35_event(int id)
> +{
> +    struct scte_35_event* event = av_malloc(sizeof(struct scte_35_event));
> +    if (!event)
> +        return NULL;
> +
> +    event->id = id;
> +    event->in_pts = AV_NOPTS_VALUE;
> +    event->nearest_in_pts = AV_NOPTS_VALUE;
> +    event->out_pts = AV_NOPTS_VALUE;
> +    event->running = 0;
> +    event->next = NULL;
> +    event->prev = NULL;
> +    return event;
> +}
> +
> +static void ref_scte35_event(struct scte_35_event *event)
> +{
> +    event->ref_count++;
> +}
> +
> +static void unref_scte35_event(struct scte_35_event **event)
> +{
> +    if(!(*event))
> +        return;
> +    if(!(*event)->ref_count) {
> +        av_freep(event);
> +    } else {
> +        (*event)->ref_count--;
> +    }
> +}
> +
> +static void unlink_scte35_event(struct scte_35_interface *iface, struct
> scte_35_event *event)
> +{
> +    if (!event)
> +        return;
> +    if (!event->prev)
> +        iface->event_list = event->next;
> +    else
> +        event->prev->next = event->next;
> +    if(event->next)
> +        event->next->prev = event->prev;
> +    unref_scte35_event(&event);
> +}
> +
> +static struct scte_35_event* get_event_id(struct scte_35_interface
> *iface, int id)
> +{
> +    struct scte_35_event *event = iface->event_list;
> +    struct scte_35_event *pevent = NULL;
> +
> +    while(event) {
> +
> +        if(event->id == id)
> +            break;
> +        pevent = event;
> +        event = event->next;
> +    }
> +    if (!event) {
> +        event = alloc_scte35_event(id);
> +        if (pevent)
> +            pevent->next = event;
> +        else
> +            iface->event_list = event;
> +    }
> +
> +    return event;
> +}
> +
> +/**
> + * save the parsed time in ctx pts_time
> +   @return length of buffer consumed
> +*/
> +static int parse_splice_time(struct scte_35_interface *iface, const
> uint8_t *buf, uint64_t *pts, int64_t pts_adjust)
> +{
> +    GetBitContext gb;
> +    int ret;
> +    init_get_bits(&gb, buf, 40);
> +    /* is time specified */
> +    ret =  get_bits(&gb, 1);
> +    if(ret) {
> +        skip_bits(&gb, 6);
> +        *pts = get_bits64(&gb,33) + pts_adjust;
> +        return 5;
> +    } else {
> +        skip_bits(&gb, 7);
> +        return 1;
> +    }
> +}
> +
> +static int parse_schedule_cmd(struct scte_35_interface *iface, const
> uint8_t *buf)
> +{
> +    const uint8_t *sbuf = buf;
> +    av_log(iface->parent, AV_LOG_DEBUG, "Schedule cmd\n");
> +    return buf - sbuf;
> +}
> +/**
> +     @return length of buffer used
> + */
> +static int parse_insert_cmd(struct scte_35_interface *iface,
> +    const uint8_t *buf,const int len, int64_t pts_adjust, int64_t
> current_pts)
> +{
> +    GetBitContext gb;
> +    int ret;
> +    const uint8_t *sbuf = buf;
> +    int program_splice_flag;
> +    int duration_flag;
> +    int splice_immediate_flag;
> +    int component_tag;
> +    int auto_return;
> +    uint16_t u_program_id;
> +    uint8_t avail_num;
> +    uint8_t avail_expect;
> +    int inout;
> +    int event_id;
> +    struct scte_35_event *event;
> +    char buffer[128];
> +    int cancel;
> +
> +
> +    av_log(iface->parent, AV_LOG_DEBUG, "%s Insert cmd\n", buffer);
> +    event_id  = AV_RB32(buf);
> +    av_log(iface->parent, AV_LOG_DEBUG, "event_id  = %x\n", event_id);
> +    event = get_event_id(iface, event_id);
> +    buf +=4;
> +    cancel = *buf & 0x80;
> +    av_log(iface->parent, AV_LOG_DEBUG, "splice_event_cancel_indicator  =
> %d\n", cancel);
> +    buf++;
> +
> +    if (!cancel) {
> +        init_get_bits(&gb, buf, 8);
> +        inout =  get_bits(&gb, 1);
> +        program_splice_flag =  get_bits(&gb, 1);
> +        duration_flag =  get_bits(&gb, 1);
> +        splice_immediate_flag =  get_bits(&gb, 1);
> +        skip_bits(&gb, 4);
> +
> +    } else {
> +        /*   Delete event only if its not already started */
> +        if (!event->running) {
> +            unlink_scte35_event(iface, event);
> +        }
> +    }
> +    buf++;
> +
> +
> +    av_log(iface->parent, AV_LOG_DEBUG, "out_of_network_indicator  =
> %d\n", inout);
> +    av_log(iface->parent, AV_LOG_DEBUG, "program_splice_flag  = %d\n",
> program_splice_flag);
> +    av_log(iface->parent, AV_LOG_DEBUG, "duration_flag  = %d\n",
> duration_flag);
> +    av_log(iface->parent, AV_LOG_DEBUG, "splice_immediate_flag  = %d\n",
> splice_immediate_flag);
> +
> +    if (program_splice_flag &&  !splice_immediate_flag) {
> +        if(inout) {
> +            ret = parse_splice_time(iface, buf, &event->out_pts,
> pts_adjust);
> +            event->out_pts = event->out_pts * iface->timebase.num /
> iface->timebase.den;
> +        } else {
> +            ret = parse_splice_time(iface, buf, &event->in_pts,
> pts_adjust);
> +            event->in_pts = event->in_pts * iface->timebase.num /
> iface->timebase.den;
> +        }
> +
> +        buf += ret;
> +    } else if (program_splice_flag && splice_immediate_flag) {
> +        if(inout)
> +            event->out_pts = current_pts * iface->timebase.num /
> iface->timebase.den;
> +        else
> +            event->in_pts = current_pts * iface->timebase.num /
> iface->timebase.den;
> +    }
> +    if ( program_splice_flag == 0) {
> +        int comp_cnt = *buf++;
> +        int  i;
> +        av_log(iface->parent, AV_LOG_DEBUG, "component_count  = %d\n",
> comp_cnt);
> +        for ( i = 0; i < comp_cnt; i++) {
> +            component_tag = *buf++;
> +            av_log(iface->parent, AV_LOG_DEBUG, "component_tag  = %d\n",
> component_tag);
> +            if (splice_immediate_flag) {
> +                if(inout)
> +                    ret = parse_splice_time(iface, buf, &event->in_pts,
> pts_adjust);
> +                else
> +                    ret = parse_splice_time(iface, buf, &event->out_pts,
> pts_adjust);
> +                buf += ret;
> +            }
> +        }
> +    }
> +    if ( duration_flag ) {
> +        init_get_bits(&gb, buf, 40);
> +        auto_return =  get_bits(&gb, 1);
> +        av_log(iface->parent, AV_LOG_DEBUG, "autoreturn  = %d\n",
> auto_return);
> +        skip_bits(&gb, 6);
> +        event->duration = get_bits64(&gb,33) + pts_adjust;
> +        buf += 5;
> +    }
> +    u_program_id = AV_RB16(buf);
> +    av_log(iface->parent, AV_LOG_DEBUG, "u_program_id  = %hd\n",
> u_program_id);
> +    buf += 2;
> +    avail_num = *buf++;
> +    av_log(iface->parent, AV_LOG_DEBUG, "avail_num  = %hhd\n", avail_num);
> +    avail_expect = *buf++;
> +    av_log(iface->parent, AV_LOG_DEBUG, "avail_expect  = %hhd\n",
> avail_expect);
> +
> +    return buf - sbuf;
> +}
> +static int parse_time_signal_cmd(struct scte_35_interface *iface, const
> uint8_t *buf)
> +{
> +    const uint8_t *sbuf = buf;
> +    av_log(iface->parent, AV_LOG_DEBUG, "Time Signal cmd\n");
> +    return buf - sbuf;
> +}
> +static int parse_bandwidth_reservation_cmd(struct scte_35_interface
> *iface, const uint8_t *buf)
> +{
> +    const uint8_t *sbuf = buf;
> +    av_log(iface->parent, AV_LOG_DEBUG, "Band Width reservation cmd\n");
> +    return buf - sbuf;
> +}
> +
> +int ff_parse_scte35_pkt(struct scte_35_interface *iface, const AVPacket
> *avpkt)
> +{
> +    const uint8_t *buf = avpkt->data;
> +    int section_length;
> +    int cmd_length;
> +    uint8_t cmd_type;
> +    int16_t tier;
> +    GetBitContext gb;
> +    int ret;
> +    int64_t pts_adjust;
> +
> +    if (!buf)
> +        return AVERROR_EOF;
> +
> +
> +    /* check table id */
> +    if (*buf != 0xfc)
> +        av_log(iface->parent, AV_LOG_ERROR, "Invalid SCTE packet\n");
> +
> +
> +    init_get_bits(&gb, buf + 1, 104);
> +
> +    /* section_syntax_indicator should be 0 */
> +    ret = get_bits(&gb,1);
> +    if (ret)
> +        av_log(iface->parent, AV_LOG_DEBUG, "Section indicator should be
> 0, since MPEG short sections are to be used.\n");
> +
> +    /* private indicator */
> +    ret = get_bits(&gb,1);
> +    if (ret)
> +        av_log(iface->parent, AV_LOG_DEBUG, "corrupt packet\n");
> +
> +    skip_bits(&gb,2);
> +
> +    /* section length may be there */
> +    section_length = get_bits(&gb,12);
> +    if( section_length > 4093)
> +    if(ret) {
> +        av_log(iface->parent, AV_LOG_ERROR, "Invalid length of
> section\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    av_base64_encode( iface->pkt_base64, AV_BASE64_SIZE(section_length +
> 3), buf, section_length + 3);
> +
> +    /* protocol version */
> +    skip_bits(&gb,8);
> +
> +    ret = get_bits(&gb,1);
> +    if(ret) {
> +        av_log(iface->parent, AV_LOG_ERROR, "Encrytion not yet
> supported\n");
> +        return AVERROR_PATCHWELCOME;
> +    }
> +    /* encryption algo */
> +    skip_bits(&gb,6);
> +
> +    pts_adjust =  get_bits64(&gb, 33);
> +
> +    /* cw_index: used in encryption */
> +    skip_bits(&gb,8);
> +
> +
> +    /* tier */
> +    tier = get_bits(&gb,12);
> +    if( (tier & 0xfff) == 0xfff)
> +        tier = -1;
> +
> +    cmd_length = get_bits(&gb,12);
> +    if(cmd_length == 0xfff ) {
> +        /* Setting max limit to  cmd_len so it does not cross memory
> barrier */
> +        cmd_length = section_length - 17;
> +    } else if ( cmd_length != 0xfff && ( cmd_length > (section_length -
> 17) ) ) {
> +        av_log(iface->parent, AV_LOG_ERROR, "Command length %d
> invalid\n", cmd_length);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    cmd_type = get_bits(&gb,8);
> +    switch(cmd_type) {
> +    case SCTE_CMD_NULL:
> +        av_log(iface->parent, AV_LOG_DEBUG, "SCTE-35 Ping Recieved");
> +        break;
> +    case SCTE_CMD_SCHEDULE:
> +        ret = parse_schedule_cmd(iface, buf + 14);
> +        break;
> +    case SCTE_CMD_INSERT:
> +        ret = parse_insert_cmd(iface, buf + 14, cmd_length, pts_adjust,
> avpkt->pts);
> +        break;
> +    case SCTE_CMD_SIGNAL:
> +        ret = parse_time_signal_cmd(iface, buf + 14);
> +        break;
> +    case SCTE_CMD_BANDWIDTH_RESERVATION:
> +        ret = parse_bandwidth_reservation_cmd(iface, buf + 14);
> +        break;
> +    default:
> +        break;
> +    /* reserved yet */
> +    }
> +    if(ret < 0)
> +        goto fail;
> +    buf += ret;
> +
> +fail:
> +    return ret;
> +}
> +
> +/*
> + * return event, if there is any event whose starting time aka out_pts is
> less then
> + * current pts. This condition also means that event starting time has
> already been passed.
> + * This function will look for event in events list which resides inside
> iface.
> + */
> +static struct scte_35_event* get_event_ciel_out(struct scte_35_interface
> *iface, uint64_t pts)
> +{
> +    struct scte_35_event *event = iface->event_list;
> +    while(event) {
> +        if(!event->running && event->out_pts < pts) {
> +            iface->event_state = EVENT_OUT;
> +            break;
> +        }
> +        event = event->next;
> +    }
> +    return event;
> +}
> +
> +/*
> + * return event, if current event is in running state
> + * and check that in_pts is less then current pts.
> + * Event from this function specify commerial ends and
> + * mainstream should be coupled in.
> + * This event is generally last event to be consumed.
> + */
> +static struct scte_35_event* get_event_floor_in(struct scte_35_interface
> *iface, uint64_t pts)
> +{
> +    struct scte_35_event *event = iface->event_list;
> +    struct scte_35_event *sevent = NULL;
> +    while(event) {
> +        if(event->in_pts != AV_NOPTS_VALUE && event->in_pts < pts &&
> +          (event->nearest_in_pts == AV_NOPTS_VALUE || pts <=
> event->nearest_in_pts) ) {
> +            event->nearest_in_pts = pts;
> +            unlink_scte35_event(iface, event);
> +            /* send in_event only when that event was in running state */
> +            if (event->running) {
> +                iface->event_state = EVENT_IN;
> +                sevent = event;
> +            }
> +        }
> +        event = event->next;
> +    }
> +    return sevent;
> +}
> +
> +
> +/*
> + *  If there is no running event, then search for an event which have
> + *  the pts matching to current pts. otherwise only give event when
> + *  its time to end the commercial.
> + *  if we have some event to be presented at this video then cache it
> + *  for later use.
> + */
> +static void update_video_pts(struct scte_35_interface *iface, uint64_t
> pts)
> +{
> +    struct scte_35_event *event = NULL;
> +    if(iface->event_state == EVENT_NONE) {
> +        event = get_event_ciel_out(iface, pts);
> +        if(event)
> +            event->running = 1;
> +    } else {
> +        event = get_event_floor_in(iface, pts);
> +    }
> +    if (event)
> +        iface->current_event = event;
> +}
> +
> +/*
> + * update the state of scte-35 parser
> + * return current event
> + */
> +static struct scte_35_event* update_event_state(struct scte_35_interface
> *iface)
> +{
> +
> +    struct scte_35_event* event = iface->current_event;
> +    if (iface->prev_event_state == EVENT_IN)
> +        iface->event_state = EVENT_NONE;
> +    else if (iface->prev_event_state == EVENT_OUT)
> +        iface->event_state = EVENT_OUT_CONT;
> +
> +    if(iface->event_state == EVENT_NONE)
> +        iface->current_event = NULL;
> +
> +    iface->prev_event_state = iface->event_state;
> +    return event;
> +}
> +
> +
> +/*
> + * Allocate scte_35 parser
> + * using function pointer so that this module reveals least interface
> + * to API uses
> + */
> +struct scte_35_interface* ff_alloc_scte35_parser(void *parent, AVRational
> timebase)
> +{
> +    struct scte_35_interface* iface = av_mallocz(sizeof(struct
> scte_35_interface));
> +    if(!iface)
> +       return NULL;
> +
> +    iface->parent = parent;
> +    iface->timebase = timebase;
> +    iface->update_video_pts = update_video_pts;
> +    iface->update_event_state = update_event_state;
> +    av_bprint_init(&iface->avbstr, 0, AV_BPRINT_SIZE_UNLIMITED);
> +    iface->get_hls_string = get_hls_string;
> +    iface->unref_scte35_event = unref_scte35_event;
> +    iface->ref_scte35_event = ref_scte35_event;
> +    iface->event_state = EVENT_NONE;
> +    iface->prev_event_state = EVENT_NONE;
> +    return iface;
> +}
> +
> +void ff_delete_scte35_parser(struct scte_35_interface* iface)
> +{
> +    if(!iface)
> +        return;
> +    av_bprint_finalize(&iface->avbstr, NULL);
> +    av_freep(&iface);
> +}
> 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);
> +};
> +
> +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
> --
> 2.7.4
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Carlos Fernandez Sanz Sept. 1, 2016, 8:38 p.m. UTC | #2
On Wed, Aug 31, 2016 at 4:10 PM, Steven Liu <lingjiujianke@gmail.com> wrote:
>
> Overflow here

Thanks - corrected in V9 (sent a couple hours ago).

Looks OK other than this?
Steven Liu Sept. 2, 2016, 10:11 p.m. UTC | #3
Carlos Fernandez Sanz <carlos@ccextractor.org>于2016年9月3日 周六上午1:13写道:

> On Thu, Sep 1, 2016 at 3:40 PM, Steven Liu <lingjiujianke@gmail.com>
> wrote:
> >
> > Maybe give a warning message and exit is a better way, if the basename is
>
> Checked with some users - none of them want ffmpeg to terminate due to
> invalid SCTE-35 data.
>
> Keep in mind that the actual use for SCTE-35 is to signal real time
> events, so terminating the process means becoming off-air :-) That's
> the absolute worst thing we can do.
>
but the process is a fetal error,
if the basename's length is 1023, or 1024, the filename will no space to
catch,
This is no people want it happen, but this is an error. FFmpeg can give an
error
for the error of the toooooooo long name.
diff mbox

Patch

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 81b7efa..25092f7 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -1,4 +1,4 @@ 
-/*
+ /*
  * Apple HTTP Live Streaming segmenter
  * Copyright (c) 2012, Luca Barbato
  *
@@ -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;
+    int adv_count;
+    int64_t start_pts;
 
     char key_uri[LINE_BUFFER_SIZE + 1];
     char iv_string[KEYSIZE*2 + 1];
@@ -91,6 +96,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
@@ -106,6 +113,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;
@@ -205,6 +214,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);
     }
 
@@ -324,8 +335,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;
@@ -361,9 +372,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;;
+            hls->adv_count++;
+            en->out = hls->scte_iface->event_state;
+        } else {
+            hls->adv_count = 0;
+            en->out = hls->scte_iface->event_state;
+        }
+    }
+
+
     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));
@@ -434,7 +459,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;
@@ -533,9 +558,23 @@  static int hls_window(AVFormatContext *s, int last)
         if (hls->flags & HLS_SINGLE_FILE)
              avio_printf(out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
                          en->size, en->pos);
-        if (hls->baseurl)
-            avio_printf(out, "%s", hls->baseurl);
-        avio_printf(out, "%s\n", en->filename);
+        if (hls->scte_iface && (en->event || en->out) ) {
+            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);
+                str = hls->scte_iface->get_hls_string(hls->scte_iface, en->event, fname, en->out, -1, en->start_pts);
+            }
+            avio_printf(out, "%s", str);
+        } else {
+            if (hls->baseurl)
+                avio_printf(out, "%s", hls->baseurl);
+            avio_printf(out, "%s\n", en->filename);
+        }
     }
 
     if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
@@ -560,9 +599,15 @@  static int hls_window(AVFormatContext *s, int last)
             if (hls->flags & HLS_SINGLE_FILE)
                  avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
                          en->size, en->pos);
-            if (hls->baseurl)
-                avio_printf(sub_out, "%s", hls->baseurl);
-            avio_printf(sub_out, "%s\n", en->sub_filename);
+            if (hls->scte_iface && (en->event || en->out) ) {
+                char *str = hls->scte_iface->get_hls_string(hls->scte_iface, en->event, hls->adv_subfilename, en->out, en->adv_count, en->pos);
+                avio_printf(out, "%s", str);
+            } else {
+                if (hls->baseurl)
+                    avio_printf(out, "%s", hls->baseurl);
+                avio_printf(sub_out, "%s\n", en->sub_filename);
+            }
+
         }
 
         if (last)
@@ -705,6 +750,7 @@  static int hls_write_header(AVFormatContext *s)
     AVDictionary *options = NULL;
     int basename_size;
     int vtt_basename_size;
+    int ts_index = 0;
 
     hls->sequence       = hls->start_sequence;
     hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
@@ -831,19 +877,21 @@  static int hls_write_header(AVFormatContext *s)
         goto fail;
     }
     //av_assert0(s->nb_streams == hls->avf->nb_streams);
-    for (i = 0; i < s->nb_streams; i++) {
+    for (ts_index = 0, i = 0; i < s->nb_streams; i++) {
         AVStream *inner_st;
         AVStream *outer_st = s->streams[i];
-        if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
-            inner_st = hls->avf->streams[i];
-        else if (hls->vtt_avf)
+        if (hls->vtt_avf && outer_st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
             inner_st = hls->vtt_avf->streams[0];
-        else {
-            /* We have a subtitle stream, when the user does not want one */
-            inner_st = NULL;
+            avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
+        } else if (outer_st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
+            inner_st = hls->avf->streams[ts_index];
+            hls->scte_iface = ff_alloc_scte35_parser(hls, outer_st->time_base);
             continue;
+        } else {
+            inner_st = hls->avf->streams[ts_index];
+            avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
+            ts_index++;
         }
-        avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
     }
 fail:
 
@@ -869,6 +917,12 @@  static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
     int is_ref_pkt = 1;
     int ret, can_split = 1;
     int stream_index = 0;
+    struct scte_35_event *event = NULL;
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+        ret = ff_parse_scte35_pkt(hls->scte_iface, pkt);
+        return ret;
+    }
 
     if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) {
         /* reset end_pts, hls->recording_time at end of the init hls list */
@@ -902,14 +956,24 @@  static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
         hls->duration = (double)(pkt->pts - hls->end_pts)
                                    * st->time_base.num / st->time_base.den;
 
-    if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
-                                   end_pts, AV_TIME_BASE_Q) >= 0) {
+    if (hls->scte_iface)
+        hls->scte_iface->update_video_pts(hls->scte_iface, pkt->pts * st->time_base.num / st->time_base.den);
+
+
+    if (can_split && (( av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
+                                   end_pts, AV_TIME_BASE_Q) >= 0) ||
+         (hls->scte_iface && hls->scte_iface->event_state == EVENT_OUT)) ) {
         int64_t new_start_pos;
         av_write_frame(oc, NULL); /* Flush any buffered data */
 
         new_start_pos = avio_tell(hls->avf->pb);
         hls->size = new_start_pos - hls->start_pos;
-        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
+        if (hls->scte_iface) {
+            event = hls->scte_iface->update_event_state(hls->scte_iface);
+            if (event)
+                hls->scte_iface->ref_scte35_event(event);
+        }
+        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, pkt->pts, event, hls->size);
         hls->start_pos = new_start_pos;
         if (ret < 0)
             return ret;
@@ -956,7 +1020,7 @@  static int hls_write_trailer(struct AVFormatContext *s)
     if (oc->pb) {
         hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
         ff_format_io_close(s, &oc->pb);
-        hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
+        hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->end_pts, NULL, hls->size);
     }
 
     if (vtt_oc) {
@@ -977,6 +1041,7 @@  static int hls_write_trailer(struct AVFormatContext *s)
     hls->avf = NULL;
     hls_window(s, 1);
 
+    ff_delete_scte35_parser(hls->scte_iface);
     hls_free_segments(hls->segments);
     hls_free_segments(hls->old_segments);
     return 0;
@@ -1031,6 +1096,7 @@  AVOutputFormat ff_hls_muxer = {
     .audio_codec    = AV_CODEC_ID_AAC,
     .video_codec    = AV_CODEC_ID_H264,
     .subtitle_codec = AV_CODEC_ID_WEBVTT,
+    .data_codec     = AV_CODEC_ID_SCTE_35,
     .flags          = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
     .write_header   = hls_write_header,
     .write_packet   = hls_write_packet,
diff --git a/libavformat/scte_35.c b/libavformat/scte_35.c
new file mode 100644
index 0000000..64e5f67
--- /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
+ *
+ * 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 )
+ */
+
+
+
+#include "libavcodec/bytestream.h"
+#include "libavcodec/avcodec.h"
+#include "libavcodec/get_bits.h"
+#include "libavutil/buffer_internal.h"
+#include "libavutil/base64.h"
+#include "scte_35.h"
+
+#define SCTE_CMD_NULL                  0x00
+#define SCTE_CMD_SCHEDULE              0x04
+#define SCTE_CMD_INSERT                0x05
+#define SCTE_CMD_SIGNAL                0x06
+#define SCTE_CMD_BANDWIDTH_RESERVATION 0x07
+
+
+static char* get_hls_string(struct scte_35_interface *iface, struct scte_35_event *event,
+                 const char *filename, int out_state, int seg_count, int64_t pos)
+{
+    int ret;
+    av_bprint_clear(&iface->avbstr);
+    if (out_state == EVENT_IN ) {
+        av_bprintf(&iface->avbstr, "#EXT-OATCLS-SCTE35:%s\n", iface->pkt_base64);
+        av_bprintf(&iface->avbstr, "#EXT-X-CUE-IN\n");
+        av_bprintf(&iface->avbstr, "#EXT-X-DISCONTINUITY\n");
+    } else if (out_state == EVENT_OUT) {
+        if (event) {
+            av_bprintf(&iface->avbstr, "#EXT-OATCLS-SCTE35:%s\n", iface->pkt_base64);
+            if(event->duration != AV_NOPTS_VALUE) {
+                int64_t dur = ceil(((double)event->duration * iface->timebase.num) /iface->timebase.den);
+                av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT:%"PRIu64"\n", dur);
+            } else {
+                av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT\n");
+            }
+            av_bprintf(&iface->avbstr, "#EXT-X-DISCONTINUITY\n");
+        }
+    } else if (out_state == EVENT_OUT_CONT) {
+        if(event && event->duration != AV_NOPTS_VALUE) {
+            int64_t dur = ceil(((double)event->duration * iface->timebase.num) /iface->timebase.den);
+            int64_t elapsed_time = ceil(((double)pos * iface->timebase.num) /iface->timebase.den) - event->out_pts;
+            av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT-CONT:ElapsedTime=%"PRIu64",Duration=%"PRIu64",SCTE35=%s\n",
+                elapsed_time,  dur, iface->pkt_base64);
+        } else {
+            av_bprintf(&iface->avbstr, "#EXT-X-CUE-OUT-CONT:SCTE35=%s\n", iface->pkt_base64);
+        }
+    }
+    if (seg_count >= 0)
+        av_bprintf(&iface->avbstr, filename, seg_count);
+    else
+        av_bprintf(&iface->avbstr, "%s", filename);
+    av_bprintf(&iface->avbstr, "\n");
+
+    ret = av_bprint_is_complete(&iface->avbstr);
+    if( ret == 0) {
+        av_log(iface->parent, AV_LOG_DEBUG, "Out of Memory");
+        return NULL;
+    }
+
+    av_log(iface->parent, AV_LOG_DEBUG, "%s", iface->avbstr.str);
+    return iface->avbstr.str;
+}
+
+static struct scte_35_event* alloc_scte35_event(int id)
+{
+    struct scte_35_event* event = av_malloc(sizeof(struct scte_35_event));
+    if (!event)
+        return NULL;
+
+    event->id = id;
+    event->in_pts = AV_NOPTS_VALUE;
+    event->nearest_in_pts = AV_NOPTS_VALUE;
+    event->out_pts = AV_NOPTS_VALUE;
+    event->running = 0;
+    event->next = NULL;
+    event->prev = NULL;
+    return event;
+}
+
+static void ref_scte35_event(struct scte_35_event *event)
+{
+    event->ref_count++;
+}
+
+static void unref_scte35_event(struct scte_35_event **event)
+{
+    if(!(*event))
+        return;
+    if(!(*event)->ref_count) {
+        av_freep(event);
+    } else {
+        (*event)->ref_count--;
+    }
+}
+
+static void unlink_scte35_event(struct scte_35_interface *iface, struct scte_35_event *event)
+{
+    if (!event)
+        return;
+    if (!event->prev)
+        iface->event_list = event->next;
+    else
+        event->prev->next = event->next;
+    if(event->next)
+        event->next->prev = event->prev;
+    unref_scte35_event(&event);
+}
+
+static struct scte_35_event* get_event_id(struct scte_35_interface *iface, int id)
+{
+    struct scte_35_event *event = iface->event_list;
+    struct scte_35_event *pevent = NULL;
+
+    while(event) {
+
+        if(event->id == id)
+            break;
+        pevent = event;
+        event = event->next;
+    }
+    if (!event) {
+        event = alloc_scte35_event(id);
+        if (pevent)
+            pevent->next = event;
+        else
+            iface->event_list = event;
+    }
+
+    return event;
+}
+
+/**
+ * save the parsed time in ctx pts_time
+   @return length of buffer consumed
+*/
+static int parse_splice_time(struct scte_35_interface *iface, const uint8_t *buf, uint64_t *pts, int64_t pts_adjust)
+{
+    GetBitContext gb;
+    int ret;
+    init_get_bits(&gb, buf, 40);
+    /* is time specified */
+    ret =  get_bits(&gb, 1);
+    if(ret) {
+        skip_bits(&gb, 6);
+        *pts = get_bits64(&gb,33) + pts_adjust;
+        return 5;
+    } else {
+        skip_bits(&gb, 7);
+        return 1;
+    }
+}
+
+static int parse_schedule_cmd(struct scte_35_interface *iface, const uint8_t *buf)
+{
+    const uint8_t *sbuf = buf;
+    av_log(iface->parent, AV_LOG_DEBUG, "Schedule cmd\n");
+    return buf - sbuf;
+}
+/**
+     @return length of buffer used
+ */
+static int parse_insert_cmd(struct scte_35_interface *iface,
+    const uint8_t *buf,const int len, int64_t pts_adjust, int64_t current_pts)
+{
+    GetBitContext gb;
+    int ret;
+    const uint8_t *sbuf = buf;
+    int program_splice_flag;
+    int duration_flag;
+    int splice_immediate_flag;
+    int component_tag;
+    int auto_return;
+    uint16_t u_program_id;
+    uint8_t avail_num;
+    uint8_t avail_expect;
+    int inout;
+    int event_id;
+    struct scte_35_event *event;
+    char buffer[128];
+    int cancel;
+
+
+    av_log(iface->parent, AV_LOG_DEBUG, "%s Insert cmd\n", buffer);
+    event_id  = AV_RB32(buf);
+    av_log(iface->parent, AV_LOG_DEBUG, "event_id  = %x\n", event_id);
+    event = get_event_id(iface, event_id);
+    buf +=4;
+    cancel = *buf & 0x80;
+    av_log(iface->parent, AV_LOG_DEBUG, "splice_event_cancel_indicator  = %d\n", cancel);
+    buf++;
+
+    if (!cancel) {
+        init_get_bits(&gb, buf, 8);
+        inout =  get_bits(&gb, 1);
+        program_splice_flag =  get_bits(&gb, 1);
+        duration_flag =  get_bits(&gb, 1);
+        splice_immediate_flag =  get_bits(&gb, 1);
+        skip_bits(&gb, 4);
+
+    } else {
+        /*   Delete event only if its not already started */
+        if (!event->running) {
+            unlink_scte35_event(iface, event);
+        }
+    }
+    buf++;
+
+
+    av_log(iface->parent, AV_LOG_DEBUG, "out_of_network_indicator  = %d\n", inout);
+    av_log(iface->parent, AV_LOG_DEBUG, "program_splice_flag  = %d\n", program_splice_flag);
+    av_log(iface->parent, AV_LOG_DEBUG, "duration_flag  = %d\n", duration_flag);
+    av_log(iface->parent, AV_LOG_DEBUG, "splice_immediate_flag  = %d\n", splice_immediate_flag);
+
+    if (program_splice_flag &&  !splice_immediate_flag) {
+        if(inout) {
+            ret = parse_splice_time(iface, buf, &event->out_pts, pts_adjust);
+            event->out_pts = event->out_pts * iface->timebase.num / iface->timebase.den;
+        } else {
+            ret = parse_splice_time(iface, buf, &event->in_pts, pts_adjust);
+            event->in_pts = event->in_pts * iface->timebase.num / iface->timebase.den;
+        }
+
+        buf += ret;
+    } else if (program_splice_flag && splice_immediate_flag) {
+        if(inout)
+            event->out_pts = current_pts * iface->timebase.num / iface->timebase.den;
+        else
+            event->in_pts = current_pts * iface->timebase.num / iface->timebase.den;
+    }
+    if ( program_splice_flag == 0) {
+        int comp_cnt = *buf++;
+        int  i;
+        av_log(iface->parent, AV_LOG_DEBUG, "component_count  = %d\n", comp_cnt);
+        for ( i = 0; i < comp_cnt; i++) {
+            component_tag = *buf++;
+            av_log(iface->parent, AV_LOG_DEBUG, "component_tag  = %d\n", component_tag);
+            if (splice_immediate_flag) {
+                if(inout)
+                    ret = parse_splice_time(iface, buf, &event->in_pts, pts_adjust);
+                else
+                    ret = parse_splice_time(iface, buf, &event->out_pts, pts_adjust);
+                buf += ret;
+            }
+        }
+    }
+    if ( duration_flag ) {
+        init_get_bits(&gb, buf, 40);
+        auto_return =  get_bits(&gb, 1);
+        av_log(iface->parent, AV_LOG_DEBUG, "autoreturn  = %d\n", auto_return);
+        skip_bits(&gb, 6);
+        event->duration = get_bits64(&gb,33) + pts_adjust;
+        buf += 5;
+    }
+    u_program_id = AV_RB16(buf);
+    av_log(iface->parent, AV_LOG_DEBUG, "u_program_id  = %hd\n", u_program_id);
+    buf += 2;
+    avail_num = *buf++;
+    av_log(iface->parent, AV_LOG_DEBUG, "avail_num  = %hhd\n", avail_num);
+    avail_expect = *buf++;
+    av_log(iface->parent, AV_LOG_DEBUG, "avail_expect  = %hhd\n", avail_expect);
+
+    return buf - sbuf;
+}
+static int parse_time_signal_cmd(struct scte_35_interface *iface, const uint8_t *buf)
+{
+    const uint8_t *sbuf = buf;
+    av_log(iface->parent, AV_LOG_DEBUG, "Time Signal cmd\n");
+    return buf - sbuf;
+}
+static int parse_bandwidth_reservation_cmd(struct scte_35_interface *iface, const uint8_t *buf)
+{
+    const uint8_t *sbuf = buf;
+    av_log(iface->parent, AV_LOG_DEBUG, "Band Width reservation cmd\n");
+    return buf - sbuf;
+}
+
+int ff_parse_scte35_pkt(struct scte_35_interface *iface, const AVPacket *avpkt)
+{
+    const uint8_t *buf = avpkt->data;
+    int section_length;
+    int cmd_length;
+    uint8_t cmd_type;
+    int16_t tier;
+    GetBitContext gb;
+    int ret;
+    int64_t pts_adjust;
+
+    if (!buf)
+        return AVERROR_EOF;
+
+
+    /* check table id */
+    if (*buf != 0xfc)
+        av_log(iface->parent, AV_LOG_ERROR, "Invalid SCTE packet\n");
+
+
+    init_get_bits(&gb, buf + 1, 104);
+
+    /* section_syntax_indicator should be 0 */
+    ret = get_bits(&gb,1);
+    if (ret)
+        av_log(iface->parent, AV_LOG_DEBUG, "Section indicator should be 0, since MPEG short sections are to be used.\n");
+
+    /* private indicator */
+    ret = get_bits(&gb,1);
+    if (ret)
+        av_log(iface->parent, AV_LOG_DEBUG, "corrupt packet\n");
+
+    skip_bits(&gb,2);
+
+    /* section length may be there */
+    section_length = get_bits(&gb,12);
+    if( section_length > 4093)
+    if(ret) {
+        av_log(iface->parent, AV_LOG_ERROR, "Invalid length of section\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    av_base64_encode( iface->pkt_base64, AV_BASE64_SIZE(section_length + 3), buf, section_length + 3);
+
+    /* protocol version */
+    skip_bits(&gb,8);
+
+    ret = get_bits(&gb,1);
+    if(ret) {
+        av_log(iface->parent, AV_LOG_ERROR, "Encrytion not yet supported\n");
+        return AVERROR_PATCHWELCOME;
+    }
+    /* encryption algo */
+    skip_bits(&gb,6);
+
+    pts_adjust =  get_bits64(&gb, 33);
+
+    /* cw_index: used in encryption */
+    skip_bits(&gb,8);
+
+
+    /* tier */
+    tier = get_bits(&gb,12);
+    if( (tier & 0xfff) == 0xfff)
+        tier = -1;
+
+    cmd_length = get_bits(&gb,12);
+    if(cmd_length == 0xfff ) {
+        /* Setting max limit to  cmd_len so it does not cross memory barrier */
+        cmd_length = section_length - 17;
+    } else if ( cmd_length != 0xfff && ( cmd_length > (section_length - 17) ) ) {
+        av_log(iface->parent, AV_LOG_ERROR, "Command length %d invalid\n", cmd_length);
+        return AVERROR_INVALIDDATA;
+    }
+
+    cmd_type = get_bits(&gb,8);
+    switch(cmd_type) {
+    case SCTE_CMD_NULL:
+        av_log(iface->parent, AV_LOG_DEBUG, "SCTE-35 Ping Recieved");
+        break;
+    case SCTE_CMD_SCHEDULE:
+        ret = parse_schedule_cmd(iface, buf + 14);
+        break;
+    case SCTE_CMD_INSERT:
+        ret = parse_insert_cmd(iface, buf + 14, cmd_length, pts_adjust, avpkt->pts);
+        break;
+    case SCTE_CMD_SIGNAL:
+        ret = parse_time_signal_cmd(iface, buf + 14);
+        break;
+    case SCTE_CMD_BANDWIDTH_RESERVATION:
+        ret = parse_bandwidth_reservation_cmd(iface, buf + 14);
+        break;
+    default:
+        break;
+    /* reserved yet */
+    }
+    if(ret < 0)
+        goto fail;
+    buf += ret;
+
+fail:
+    return ret;
+}
+
+/*
+ * return event, if there is any event whose starting time aka out_pts is less then
+ * current pts. This condition also means that event starting time has already been passed.
+ * This function will look for event in events list which resides inside iface.
+ */
+static struct scte_35_event* get_event_ciel_out(struct scte_35_interface *iface, uint64_t pts)
+{
+    struct scte_35_event *event = iface->event_list;
+    while(event) {
+        if(!event->running && event->out_pts < pts) {
+            iface->event_state = EVENT_OUT;
+            break;
+        }
+        event = event->next;
+    }
+    return event;
+}
+
+/*
+ * return event, if current event is in running state
+ * and check that in_pts is less then current pts.
+ * Event from this function specify commerial ends and
+ * mainstream should be coupled in.
+ * This event is generally last event to be consumed.
+ */
+static struct scte_35_event* get_event_floor_in(struct scte_35_interface *iface, uint64_t pts)
+{
+    struct scte_35_event *event = iface->event_list;
+    struct scte_35_event *sevent = NULL;
+    while(event) {
+        if(event->in_pts != AV_NOPTS_VALUE && event->in_pts < pts &&
+          (event->nearest_in_pts == AV_NOPTS_VALUE || pts <= event->nearest_in_pts) ) {
+            event->nearest_in_pts = pts;
+            unlink_scte35_event(iface, event);
+            /* send in_event only when that event was in running state */
+            if (event->running) {
+                iface->event_state = EVENT_IN;
+                sevent = event;
+            }
+        }
+        event = event->next;
+    }
+    return sevent;
+}
+
+
+/*
+ *  If there is no running event, then search for an event which have
+ *  the pts matching to current pts. otherwise only give event when
+ *  its time to end the commercial.
+ *  if we have some event to be presented at this video then cache it
+ *  for later use.
+ */
+static void update_video_pts(struct scte_35_interface *iface, uint64_t pts)
+{
+    struct scte_35_event *event = NULL;
+    if(iface->event_state == EVENT_NONE) {
+        event = get_event_ciel_out(iface, pts);
+        if(event)
+            event->running = 1;
+    } else {
+        event = get_event_floor_in(iface, pts);
+    }
+    if (event)
+        iface->current_event = event;
+}
+
+/*
+ * update the state of scte-35 parser
+ * return current event
+ */
+static struct scte_35_event* update_event_state(struct scte_35_interface *iface)
+{
+
+    struct scte_35_event* event = iface->current_event;
+    if (iface->prev_event_state == EVENT_IN)
+        iface->event_state = EVENT_NONE;
+    else if (iface->prev_event_state == EVENT_OUT)
+        iface->event_state = EVENT_OUT_CONT;
+
+    if(iface->event_state == EVENT_NONE)
+        iface->current_event = NULL;
+
+    iface->prev_event_state = iface->event_state;
+    return event;
+}
+
+
+/*
+ * Allocate scte_35 parser
+ * using function pointer so that this module reveals least interface
+ * to API uses
+ */
+struct scte_35_interface* ff_alloc_scte35_parser(void *parent, AVRational timebase)
+{
+    struct scte_35_interface* iface = av_mallocz(sizeof(struct scte_35_interface));
+    if(!iface)
+       return NULL;
+
+    iface->parent = parent;
+    iface->timebase = timebase;
+    iface->update_video_pts = update_video_pts;
+    iface->update_event_state = update_event_state;
+    av_bprint_init(&iface->avbstr, 0, AV_BPRINT_SIZE_UNLIMITED);
+    iface->get_hls_string = get_hls_string;
+    iface->unref_scte35_event = unref_scte35_event;
+    iface->ref_scte35_event = ref_scte35_event;
+    iface->event_state = EVENT_NONE;
+    iface->prev_event_state = EVENT_NONE;
+    return iface;
+}
+
+void ff_delete_scte35_parser(struct scte_35_interface* iface)
+{
+    if(!iface)
+        return;
+    av_bprint_finalize(&iface->avbstr, NULL);
+    av_freep(&iface);
+}
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);
+};
+
+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