On 01/12/2015 02:48 AM, Michael Niedermayer wrote: > On Mon, Jan 12, 2015 at 01:09:11AM +0530, Anshul wrote: >> On 12/31/2014 07:13 PM, Michael Niedermayer wrote: >>> So SCTE-35 is basically about segmenting a video timewise (primarely >>> to mark Ads but not always) We already have a API to segment videos >>> timewise, its AVFormatContext.chapters that may need some changes to >>> handle incrementally added (and on the muxer side incrementally >>> stored) data or we might even choose a different system entirely than >>> that AVChapter array for such incrementally stored segments but i dont >>> think data decoders with a completely opaque input and output are a >>> reasonable API for communicating such temporal segmenting [...] >> I have not looked at it yet, due to some disadvantage told me on irc, >> sry but I have forgotten those disadvantage of chapters. > It would be better if the reasons behind a design decission are > understood and documented >
Yes, I studied the document of AVChapter, just now its only used for mostly header and sometimes trailer. Its structure match very much to interface of scte_35, but it is not sufficient I have to have locking mechanism there, so that I would know whether I am still using it or not. These chapters also look very static, I did not find any logic to cancel the event at last moment. modification to my previous patch were possible with AVChapter, but now I feel i don't require to communicate from demuxer or decoder, because I have written a parser in AVFormat and only used in hls muxer. and If later I would use that parser in filter, ubitux gave me idea to use ff_ap >> if any one here still believe that chapters approach will be better, I >> will look at it. >> >> Though I have done some new implementation, it is out of avcodec folder. >> I have tweaked slightly AVFormat public structure. >> >> for details please review attached draft patch. >> >> I would appreciate, if someone pinpoint architecture issue first. >> I really get demoralized when I have to throw all my work after >> considering all review comments. >> then at last some architecture comments. >> >> lots of memory leakage are still there, please ignore it for time being, >> i am working on it. >> >> follwing is the command which is also added in commit message to use >> this patch >> ./ffmpeg_g -vsync 0 -copyts -i ~/test_videos/mpegwithscte35.ts >> -hls_list_size 1000 -dcodec copy tmp/some.m3u8 >> >> -Anshul >> ffmpeg.c | 6 +++++- >> ffmpeg_opt.c | 10 ++++++++++ >> libavcodec/avcodec.h | 1 + >> libavcodec/codec_desc.c | 6 ++++++ >> libavformat/Makefile | 1 + >> libavformat/avformat.h | 17 +++++++++++++++++ >> libavformat/format.c | 2 ++ >> libavformat/hlsenc.c | 39 +++++++++++++++++++++++++++++++++++---- >> libavformat/mpegts.c | 45 +++++++++++++++++++++++++++++++++++++++------ >> libavformat/utils.c | 1 + > theres some file missing > libavformat/hlsenc.c:39:21: fatal error: scte_35.h: No such file or directory I always forget this if I don't check the ffmpeg codec checklist. hope i will gradually get into this habit. and I am sorry for being so annoying to all. attached new patch. -Anshul
>From 4c0e25ea5827b03b61d2a49eee287515d7dc031d Mon Sep 17 00:00:00 2001 From: Anshul Maheshwari <anshul.ffm...@gmail.com> Date: Mon, 12 Jan 2015 17:40:10 +0530 Subject: [PATCH] Adding SCTE 35 implementation in avformat Use following cmd to test it. ./ffmpeg -vsync 0 -copyts -i ~/test_videos/mpegwithscte35.ts -hls_list_size 1000 -dcodec copy tmp/some.m3u8 --- ffmpeg.c | 6 +- ffmpeg_opt.c | 10 ++ libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 6 + libavformat/Makefile | 1 + libavformat/avformat.h | 17 +++ libavformat/format.c | 2 + libavformat/hlsenc.c | 39 +++++- libavformat/mpegts.c | 45 ++++++- libavformat/scte_35.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/scte_35.h | 48 +++++++ libavformat/utils.c | 1 + 12 files changed, 513 insertions(+), 11 deletions(-) create mode 100644 libavformat/scte_35.c create mode 100644 libavformat/scte_35.h diff --git a/ffmpeg.c b/ffmpeg.c index ddf4272..a7f078b 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -60,6 +60,7 @@ #include "libavutil/bprint.h" #include "libavutil/time.h" #include "libavutil/threadmessage.h" +#include "libavutil/buffer_internal.h" #include "libavformat/os_support.h" #include "libavformat/ffm.h" // not public API @@ -2907,6 +2908,8 @@ static int transcode_init(void) enc_ctx->height = input_streams[ost->source_index]->st->codec->height; } break; + case AVMEDIA_TYPE_DATA: + break; default: abort(); break; @@ -3464,7 +3467,8 @@ static int process_input(int file_index) OutputStream *ost = output_streams[j]; if (ost->source_index == ifile->ist_index + i && - (ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE)) + (ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE + || ost->enc->type == AVMEDIA_TYPE_DATA )) finish_output_stream(ost); } } diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index ac93eb5..f5a7b35 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -804,6 +804,7 @@ static int open_input_file(OptionsContext *o, const char *filename) char * video_codec_name = NULL; char * audio_codec_name = NULL; char *subtitle_codec_name = NULL; + char * data_codec_name = NULL; int scan_all_pmts_set = 0; if (o->format) { @@ -864,6 +865,8 @@ static int open_input_file(OptionsContext *o, const char *filename) find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0)->id : AV_CODEC_ID_NONE; ic->subtitle_codec_id= subtitle_codec_name ? find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0)->id : AV_CODEC_ID_NONE; + ic->data_codec_id = data_codec_name ? + find_codec_or_die(data_codec_name, AVMEDIA_TYPE_DATA, 0)->id : AV_CODEC_ID_NONE; if (video_codec_name) av_format_set_video_codec (ic, find_codec_or_die(video_codec_name , AVMEDIA_TYPE_VIDEO , 0)); @@ -871,6 +874,8 @@ static int open_input_file(OptionsContext *o, const char *filename) av_format_set_audio_codec (ic, find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0)); if (subtitle_codec_name) av_format_set_subtitle_codec(ic, find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0)); + if (data_codec_name) + av_format_set_data_codec(ic, find_codec_or_die(data_codec_name, AVMEDIA_TYPE_DATA, 0)); ic->flags |= AVFMT_FLAG_NONBLOCK; ic->interrupt_callback = int_cb; @@ -1926,6 +1931,11 @@ static int open_output_file(OptionsContext *o, const char *filename) } } /* do something with data? */ + if (!o->data_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_DATA) != AV_CODEC_ID_NONE) { + for (i = 0; i < nb_input_streams; i++) + if (input_streams[i]->st->codec->codec_type == AVMEDIA_TYPE_DATA) + new_data_stream(o, oc, i); + } } else { for (i = 0; i < o->nb_stream_maps; i++) { StreamMap *map = &o->stream_maps[i]; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 99467bb..edb6389 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -523,6 +523,7 @@ enum AVCodecID { /* other specific kind of codecs (generally used for attachments) */ AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. AV_CODEC_ID_TTF = 0x18000, + AV_CODEC_ID_SCTE_35 = MKBETAG('C','U','E','I'), AV_CODEC_ID_BINTEXT = MKBETAG('B','T','X','T'), AV_CODEC_ID_XBIN = MKBETAG('X','B','I','N'), AV_CODEC_ID_IDF = MKBETAG( 0 ,'I','D','F'), diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 0af66f4..ae96210 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2739,6 +2739,12 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("binary data"), .mime_types= MT("application/octet-stream"), }, + { + .id = AV_CODEC_ID_SCTE_35, + .type = AVMEDIA_TYPE_DATA, + .name = "scte_35", + .long_name = NULL_IF_CONFIG_SMALL("SCTE 35 Message Queue"), + }, /* deprecated codec ids */ { diff --git a/libavformat/Makefile b/libavformat/Makefile index 7e4beac..4539ed8 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -23,6 +23,7 @@ OBJS = allformats.o \ seek.o \ url.o \ utils.o \ + scte_35.o \ OBJS-$(CONFIG_NETWORK) += network.o OBJS-$(CONFIG_RIFFDEC) += riffdec.o diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 2e54ed1..4cccbe8 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -551,6 +551,7 @@ typedef struct AVOutputFormat { * @see avdevice_capabilities_free() for more details. */ int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + enum AVCodecID data_codec; /**< default data codec */ } AVOutputFormat; /** * @} @@ -1705,6 +1706,14 @@ typedef struct AVFormatContext { AVCodec *subtitle_codec; /** + * Forced data codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user via av_format_set_data_codec (NO direct access). + */ + AVCodec *data_codec; + + /** * Number of bytes to be written as padding in a metadata header. * Demuxing: Unused. * Muxing: Set by user via av_format_set_metadata_header_padding. @@ -1755,6 +1764,12 @@ typedef struct AVFormatContext { * - demuxing: Set by user. */ uint8_t *dump_separator; + + /** + * Forced Data codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID data_codec_id; } AVFormatContext; int av_format_get_probe_score(const AVFormatContext *s); @@ -1764,6 +1779,8 @@ AVCodec * av_format_get_audio_codec(const AVFormatContext *s); void av_format_set_audio_codec(AVFormatContext *s, AVCodec *c); AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s); void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c); +AVCodec * av_format_get_data_codec(const AVFormatContext *s); +void av_format_set_data_codec(AVFormatContext *s, AVCodec *c); int av_format_get_metadata_header_padding(const AVFormatContext *s); void av_format_set_metadata_header_padding(AVFormatContext *s, int c); void * av_format_get_opaque(const AVFormatContext *s); diff --git a/libavformat/format.c b/libavformat/format.c index 97f5657..fa94b7d 100644 --- a/libavformat/format.c +++ b/libavformat/format.c @@ -151,6 +151,8 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, return fmt->audio_codec; else if (type == AVMEDIA_TYPE_SUBTITLE) return fmt->subtitle_codec; + else if (type == AVMEDIA_TYPE_DATA) + return fmt->data_codec; else return AV_CODEC_ID_NONE; } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index f46e8d4..af1fae3 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -36,12 +36,14 @@ #include "avformat.h" #include "internal.h" #include "os_support.h" +#include "scte_35.h" typedef struct HLSSegment { char filename[1024]; double duration; /* in seconds */ int64_t pos; int64_t size; + struct scte_35_event *event; struct HLSSegment *next; } HLSSegment; @@ -77,6 +79,8 @@ typedef struct HLSContext { int64_t size; // last segment size int nb_entries; + struct scte_35_interface *scte_iface; + int event_out; HLSSegment *segments; HLSSegment *last_segment; HLSSegment *old_segments; @@ -184,8 +188,8 @@ static int hls_mux_init(AVFormatContext *s) } /* Create a new segment and append it to the segment list */ -static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, - int64_t size) +static int hls_append_segment(HLSContext *hls, double duration, struct scte_35_event *event, + int64_t pos, int64_t size) { HLSSegment *en = av_malloc(sizeof(*en)); int ret; @@ -197,6 +201,7 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, en->duration = duration; en->pos = pos; + en->event = event; en->size = size; en->next = NULL; @@ -268,6 +273,12 @@ static int hls_window(AVFormatContext *s, int last) for (en = hls->segments; en; en = en->next) { avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration); + if (en->event) { + if (en->event->in_pts != AV_NOPTS_VALUE ) + avio_printf(hls->pb, "#EXT-X-CUE-IN\n"); + else if(en->event->out_pts != AV_NOPTS_VALUE ) + avio_printf(hls->pb, "#EXT-X-CUE-OUT:DURATION=%Ld\n",en->event->duration); + } if (hls->flags & HLS_SINGLE_FILE) avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n", en->size, en->pos); @@ -392,6 +403,7 @@ static int hls_write_header(AVFormatContext *s) AVStream *outer_st = s->streams[i]; avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); } + hls->scte_iface = ff_alloc_scte35_parser(hls); fail: av_dict_free(&options); @@ -412,6 +424,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) int is_ref_pkt = 1; int ret, can_split = 1; + if (st->codec->codec_id == AV_CODEC_ID_SCTE_35) { + ret = ff_parse_scte35_pkt(hls->scte_iface, pkt); + return ret; + } if (hls->start_pts == AV_NOPTS_VALUE) { hls->start_pts = pkt->pts; hls->end_pts = pkt->pts; @@ -432,11 +448,25 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, end_pts, AV_TIME_BASE_Q) >= 0) { int64_t new_start_pos; + struct scte_35_event *event = NULL; 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(hls, hls->duration, hls->start_pos, hls->size); + + if(!hls->event_out) { + event = hls->scte_iface->get_event_ciel_out(hls->scte_iface, pkt->pts); + if(event) { + event->lock = 1; + hls->event_out = 1; + } + } else { + event = hls->scte_iface->get_event_floor_in(hls->scte_iface, pkt->pts); + if(event) { + hls->event_out = 0; + } + } + ret = hls_append_segment(hls, hls->duration, event, hls->start_pos, hls->size); hls->start_pos = new_start_pos; if (ret) return ret; @@ -477,7 +507,7 @@ static int hls_write_trailer(struct AVFormatContext *s) if (oc->pb) { hls->size = avio_tell(hls->avf->pb) - hls->start_pos; avio_closep(&oc->pb); - hls_append_segment(hls, hls->duration, hls->start_pos, hls->size); + hls_append_segment(hls, hls->duration, NULL, hls->start_pos, hls->size); } av_freep(&hls->basename); avformat_free_context(oc); @@ -523,6 +553,7 @@ AVOutputFormat ff_hls_muxer = { .priv_data_size = sizeof(HLSContext), .audio_codec = AV_CODEC_ID_AAC, .video_codec = AV_CODEC_ID_H264, + .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/mpegts.c b/libavformat/mpegts.c index f61388b..6971e93 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -57,6 +57,7 @@ enum MpegTSFilterType { MPEGTS_PES, MPEGTS_SECTION, MPEGTS_PCR, + MPEGTS_DATA, }; typedef struct MpegTSFilter MpegTSFilter; @@ -498,6 +499,11 @@ static MpegTSFilter *mpegts_open_pcr_filter(MpegTSContext *ts, unsigned int pid) return mpegts_open_filter(ts, pid, MPEGTS_PCR); } +static MpegTSFilter *mpegts_open_data_filter(MpegTSContext *ts, unsigned int pid) +{ + return mpegts_open_filter(ts, pid, MPEGTS_DATA); +} + static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) { int pid; @@ -676,6 +682,7 @@ static const StreamType ISO_types[] = { { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, + { 0x86, AVMEDIA_TYPE_DATA, AV_CODEC_ID_SCTE_35 }, { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { 0 }, @@ -824,6 +831,12 @@ static void reset_pes_packet_state(PESContext *pes) av_buffer_unref(&pes->buffer); } +static void new_data_packet(const uint8_t *buffer, int len, AVPacket *pkt) +{ + av_init_packet(pkt); + pkt->data = buffer; + pkt->size = len; +} static void new_pes_packet(PESContext *pes, AVPacket *pkt) { av_init_packet(pkt); @@ -1886,7 +1899,20 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len pes->st->id = pes->pid; } st = pes->st; - } else if (stream_type != 0x13) { + } else if (stream_type == 0x86 ) { + int idx = ff_find_stream_index(ts->stream, pid); + if (idx >= 0) { + st = ts->stream->streams[idx]; + } else { + st = avformat_new_stream(ts->stream, NULL); + if (!st) + goto out; + st->id = pid; + st->codec->codec_type = AVMEDIA_TYPE_DATA; + mpegts_find_stream_type(st, stream_type, ISO_types); + mpegts_open_data_filter(ts, pid); + } + }else if (stream_type != 0x13 ) { if (ts->pids[pid]) mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably pes = add_pes_stream(ts, pid, pcr_pid); @@ -2222,14 +2248,17 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } } - } else { + } else if (tss->type == MPEGTS_DATA) { + /* may be pointer field is 0 and to be ignored*/ + p++; + new_data_packet(p,p_end - p, ts->pkt); + ts->stop_parse = 1; + } else if (tss->type == MPEGTS_PES) { int ret; // Note: The position here points actually behind the current packet. - if (tss->type == MPEGTS_PES) { - if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, + if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, pos - ts->raw_packet_size)) < 0) - return ret; - } + return ret; } return 0; @@ -2616,6 +2645,10 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt) ret = 0; break; } + } else if (ts->pids[i] && ts->pids[i]->type == MPEGTS_DATA) { + // ret = 0; + //ts->pkt->size = 0; + return ret; } } diff --git a/libavformat/scte_35.c b/libavformat/scte_35.c new file mode 100644 index 0000000..c399892 --- /dev/null +++ b/libavformat/scte_35.c @@ -0,0 +1,348 @@ +/* + * SCTE 35 decoder + * Copyright (c) 2014 Anshul Maheshwaari + * + * 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 "scte_35.h" +#include "libavutil/buffer_internal.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 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; + return event; +} +static void unref_scte35_event(struct scte_35_event *event) { + + if(!event->lock) { + event->prev->next = event->next; + event->next->prev = event->prev; + } +} +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; + av_log(iface->parent, AV_LOG_DEBUG, "time = %lld\n", *pts/90000); + 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, int64_t pts_adjust) +{ + 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; + + + av_log(iface->parent, AV_LOG_DEBUG, "Insert cmd\n"); + 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 { + unref_scte35_event(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); + else + ret = parse_splice_time(iface, buf, &event->in_pts, pts_adjust); + + buf += ret; + } + 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; + av_log(iface->parent, AV_LOG_DEBUG, "duration = %lld\n", event->duration/90000); + 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; + } + + /* 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) == 0xfff ) + cmd_length = -1; + + cmd_type = get_bits(&gb,8); + switch(cmd_type) { + case SCTE_CMD_NULL: + av_log(iface->parent, AV_LOG_DEBUG, "NULL packet\n"); + break; + case SCTE_CMD_SCHEDULE: + ret = parse_schedule_cmd(iface, buf + 14); + break; + case SCTE_CMD_INSERT: + ret = parse_insert_cmd(iface, buf + 14, pts_adjust); + 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) + 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; + break; + } + event = event->next; + } + return event; +} + +struct scte_35_interface* ff_alloc_scte35_parser(void *parent) +{ + struct scte_35_interface* iface = av_mallocz(sizeof(struct scte_35_interface)); + + iface->parent = parent; + iface->get_event_ciel_out = get_event_ciel_out; + iface->get_event_floor_in = get_event_floor_in; + return iface; +} + +void ff_delete_scte35_parser(struct scte_35_interface* iface) +{ + freep(&iface); +} diff --git a/libavformat/scte_35.h b/libavformat/scte_35.h new file mode 100644 index 0000000..59df9be --- /dev/null +++ b/libavformat/scte_35.h @@ -0,0 +1,48 @@ +/* + * SCTE 35 decoder + * Copyright (c) 2014 Anshul Maheshwaari + * + * 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 + +struct scte_35_event { + int32_t id; + uint64_t in_pts; + uint64_t nearest_in_pts; + uint64_t out_pts; + int64_t duration; + int cancel; + /*if advertisement have already started cancel command can't delete advertisement */ + volatile int lock; + struct scte_35_event *next; + struct scte_35_event *prev; +}; +struct scte_35_interface { + struct scte_35_event *event_list; + /* keep context of its parent for log */ + void *parent; + + struct scte_35_event* (*get_event_ciel_out)(struct scte_35_interface *iface, uint64_t pts); + struct scte_35_event* (*get_event_floor_in)(struct scte_35_interface *iface, uint64_t pts); +}; +int ff_parse_scte35_pkt(struct scte_35_interface *iface, const AVPacket *avpkt); + +struct scte_35_interface* ff_alloc_scte35_parser(void *parent); +void ff_delete_scte35_parser(struct scte_35_interface* iface); +#endif diff --git a/libavformat/utils.c b/libavformat/utils.c index 752270d..b8fef74 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -110,6 +110,7 @@ MAKE_ACCESSORS(AVStream, stream, char *, recommended_encoder_configuration) MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec) MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec) MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec) +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, data_codec) MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding) MAKE_ACCESSORS(AVFormatContext, format, void *, opaque) MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb) -- 1.8.1.4
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel