2016-07-22 6:31 GMT+08:00 Carlos Fernandez Sanz <car...@ccextractor.org>:
> From: carlos <car...@ccextractor.org> > > Signed-off-by: carlos <car...@ccextractor.org> > --- > libavformat/Makefile | 1 + > libavformat/hlsenc.c | 179 +++++++++++------- > libavformat/scte_35.c | 489 > ++++++++++++++++++++++++++++++++++++++++++++++++++ > libavformat/scte_35.h | 76 ++++++++ > 4 files changed, 680 insertions(+), 65 deletions(-) > create mode 100644 libavformat/scte_35.c > create mode 100644 libavformat/scte_35.h > > diff --git a/libavformat/Makefile b/libavformat/Makefile > index c3f38b4..59f5046 100644 > --- a/libavformat/Makefile > +++ b/libavformat/Makefile > @@ -21,6 +21,7 @@ OBJS = allformats.o \ > qtpalette.o \ > protocols.o \ > riff.o \ > + scte_35.o \ > sdp.o \ > url.o \ > utils.o \ > diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c > index 5dc518d..0b1052b 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; > + int adv_count; > + int64_t start_pts; > > char key_uri[LINE_BUFFER_SIZE + 1]; > char iv_string[KEYSIZE*2 + 1]; > @@ -89,6 +94,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 > @@ -104,6 +111,7 @@ typedef struct HLSContext { > int nb_entries; > int discontinuity_set; > > + struct scte_35_interface *scte_iface; > HLSSegment *segments; > HLSSegment *last_segment; > HLSSegment *old_segments; > @@ -132,9 +140,10 @@ static int hls_delete_old_segments(HLSContext *hls) { > > HLSSegment *segment, *previous_segment = NULL; > float playlist_duration = 0.0f; > - int ret = 0, path_size, sub_path_size; > - char *dirname = NULL, *p, *sub_path; > - char *path = NULL; > + int ret = 0; > + size_t dir_size; > + const char *dirname; > + char *base, *path; > > segment = hls->segments; > while (segment) { > @@ -147,69 +156,55 @@ static int hls_delete_old_segments(HLSContext *hls) { > playlist_duration -= segment->duration; > previous_segment = segment; > segment = previous_segment->next; > - if (playlist_duration <= -previous_segment->duration) { > + //if (playlist_duration <= -previous_segment->duration) { > + if (playlist_duration <= 9) { > previous_segment->next = NULL; > break; > } > } > > - if (segment) { > - if (hls->segment_filename) { > - dirname = av_strdup(hls->segment_filename); > - } else { > - dirname = av_strdup(hls->avf->filename); > - } > - if (!dirname) { > - ret = AVERROR(ENOMEM); > - goto fail; > - } > - p = (char *)av_basename(dirname); > - *p = '\0'; > + if (!segment) > + return ret; > + > + if (hls->segment_filename) { > + dirname = hls->segment_filename; > + } else { > + dirname = hls->avf->filename; > + } > + > + dir_size = av_basename(dirname) - dirname; > + path = av_malloc(PATH_MAX); > + if (!path) { > + return (ret = AVERROR(ENOMEM)); > } > + (void)av_strlcpy(path, dirname, PATH_MAX); > + base = &path[dir_size]; > > while (segment) { > av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n", > segment->filename); > - path_size = strlen(dirname) + strlen(segment->filename) + 1; > - path = av_malloc(path_size); > - if (!path) { > - ret = AVERROR(ENOMEM); > - goto fail; > - } > > - av_strlcpy(path, dirname, path_size); > - av_strlcat(path, segment->filename, path_size); > + av_strlcat(base, segment->filename, PATH_MAX); > if (unlink(path) < 0) { > av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: > %s\n", > path, strerror(errno)); > } > > - if (segment->sub_filename[0] != '\0') { > - sub_path_size = strlen(dirname) + > strlen(segment->sub_filename) + 1; > - sub_path = av_malloc(sub_path_size); > - if (!sub_path) { > - ret = AVERROR(ENOMEM); > - goto fail; > - } > - > - av_strlcpy(sub_path, dirname, sub_path_size); > - av_strlcat(sub_path, segment->sub_filename, sub_path_size); > - if (unlink(sub_path) < 0) { > + if (hls->has_subtitle) { > + av_strlcat(base, segment->sub_filename, PATH_MAX); > + if (unlink(path) < 0) { > av_log(hls, AV_LOG_ERROR, "failed to delete old segment > %s: %s\n", > - sub_path, strerror(errno)); > + path, strerror(errno)); > } > - av_free(sub_path); > } > - 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); > } > > -fail: > av_free(path); > - av_free(dirname); > - > return ret; > } > > @@ -314,8 +309,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; > @@ -349,11 +344,25 @@ static int hls_append_segment(struct AVFormatContext > *s, HLSContext *hls, double > else > en->sub_filename[0] = '\0'; > > - en->duration = duration; > - en->pos = pos; > - en->size = size; > + 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_out == EVENT_OUT_CONT) { > + en->adv_count = hls->scte_iface->adv_count; > + hls->scte_iface->adv_count++; > + en->out = hls->scte_iface->event_out; > + } else { > + hls->scte_iface->adv_count = 0; > + en->out = hls->scte_iface->event_out; > + } > + } > + > + > 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)); > @@ -475,9 +484,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) > @@ -502,9 +525,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) > @@ -645,6 +674,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->time * AV_TIME_BASE; > @@ -761,19 +791,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: > > @@ -799,7 +831,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( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) { > oc = hls->vtt_avf; > stream_index = 0; > @@ -824,14 +861,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->get_event_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_out == 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->get_event_cache(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; > @@ -878,7 +925,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); > Just use hls maybe better for this function. > } > > if (vtt_oc) { > @@ -899,6 +946,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; > @@ -920,7 +968,7 @@ static const AVOption options[] = { > {"hls_subtitle_path", "set path of hls subtitles", > OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 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"}, > - {"delete_segments", "delete segment files that are no longer part of > the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, > UINT_MAX, E, "flags"}, > + {"delete_segments", "delete segment files that are no longer part of > the playlist", 1, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, > UINT_MAX, E, "flags"}, > Keep the flags 0 default > {"round_durations", "round durations in m3u8 to whole numbers", 0, > AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX, E, > "flags"}, > {"discont_start", "start the playlist with a discontinuity tag", 0, > AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, "flags"}, > {"omit_endlist", "Do not append an endlist when ending stream", 0, > AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, "flags"}, > @@ -951,6 +999,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..c50811b > --- /dev/null > +++ b/libavformat/scte_35.c > @@ -0,0 +1,489 @@ > +/* > + * 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(NULL, AV_LOG_WARNING, "Out of Memory"); > + return NULL; > + } > + > + av_log(iface->parent, AV_LOG_WARNING, "%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)); > + event->id = id; > + event->in_pts = AV_NOPTS_VALUE; > + event->nearest_in_pts = AV_NOPTS_VALUE; > + event->out_pts = AV_NOPTS_VALUE; > + event->lock = 0; > + event->cancel = 1; > + 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; > + time_t rawtime; > + struct tm * timeinfo; > + char buffer[128]; > + > + time ( &rawtime ); > + timeinfo = localtime ( &rawtime ); > + strftime(buffer, 25, "%Y:%m:%d%H:%M:%S", timeinfo); > + > + > + av_log(iface->parent, AV_LOG_WARNING, "%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; > + event->cancel = *buf & 0x80; > + av_log(iface->parent, AV_LOG_DEBUG, "splice_event_cancel_indicator = > %d\n", event->cancel ); > + buf++; > + > + if (!event->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->lock) { > + 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 len = avpkt->size; > + 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_WARNING, "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_WARNING, "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: > + 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; > +} > +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->lock && event->out_pts < pts) { > + iface->event_out = EVENT_OUT; > + break; > + } > + event = event->next; > + } > + return event; > +} > +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; > + while(event) { > + if(event->lock && 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; > + iface->event_out = EVENT_IN; > + break; > + } > + event = event->next; > + } > + return event; > +} > + > +static struct scte_35_event* get_event_pts(struct scte_35_interface > *iface, uint64_t pts) > +{ > + struct scte_35_event *event = NULL; > + if(iface->event_out == EVENT_NONE) { > + event = get_event_ciel_out(iface, pts); > + if(event) > + event->lock = 1; > + } else { > + event = get_event_floor_in(iface, pts); > + unlink_scte35_event(iface, event); > + } > + if (event) > + iface->cache_event = event; > + > + return event; > +} > + > +static struct scte_35_event* get_event_cache(struct scte_35_interface > *iface) > +{ > + > + struct scte_35_event* event = iface->cache_event; > + if (iface->prev_event_state == EVENT_IN) > + iface->event_out = EVENT_NONE; > + else if (iface->prev_event_state == EVENT_OUT) > + iface->event_out = EVENT_OUT_CONT; > + > + if(iface->event_out == EVENT_NONE) > + iface->cache_event = NULL; > + > + iface->prev_event_state = iface->event_out; > + return event; > +} > + > +struct scte_35_interface* ff_alloc_scte35_parser(void *parent, AVRational > timebase) > +{ > + struct scte_35_interface* iface = av_mallocz(sizeof(struct > scte_35_interface)); > + > + iface->parent = parent; > + iface->timebase = timebase; > + iface->get_event_pts = get_event_pts; > + iface->get_event_cache = get_event_cache; > + 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_out = EVENT_NONE; > + iface->prev_event_state = EVENT_NONE; > + return iface; > +} > + > +void ff_delete_scte35_parser(struct scte_35_interface* iface) > +{ > + av_freep(&iface); > +} > diff --git a/libavformat/scte_35.h b/libavformat/scte_35.h > new file mode 100644 > index 0000000..add9826 > --- /dev/null > +++ b/libavformat/scte_35.h > @@ -0,0 +1,76 @@ > +/* > + * SCTE-35 parser > + * Copyright (c) 2014 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 SCTE_35_H > +#define SCTE_35_H > + > +#include "libavutil/bprint.h" > + > +struct scte_35_event { > + int32_t id; > + uint64_t in_pts; > + uint64_t nearest_in_pts; > + uint64_t out_pts; > + int64_t duration; > + int64_t start_pos; > + int cancel; > + /*if advertisement have already started cancel command can't delete > advertisement */ > + volatile int lock; > + volatile int ref_count; > + struct scte_35_event *next; > + struct scte_35_event *prev; > +}; > +struct scte_35_interface { > + struct scte_35_event *event_list; > + char adv_filename[1024]; > + char filename[1024]; > + int event_out; > + AVRational timebase; > + int adv_count; > + struct scte_35_event *cache_event; > + 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; > + > + struct scte_35_event* (*get_event_pts)(struct scte_35_interface > *iface, uint64_t pts); > + struct scte_35_event* (*get_event_cache)(struct scte_35_interface > *iface); > + /* general purpose str */ > + AVBPrint avbstr; > + 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); > +}; > + > +enum event_state { > + EVENT_NONE, > + EVENT_IN, > + EVENT_OUT, > + EVENT_OUT_CONT, > +}; > + > +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 > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel