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. 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
>From 1837975dc5f8b1e29d70eec3d0117b7ab6b96c19 Mon Sep 17 00:00:00 2001 From: Anshul Maheshwari <anshul.ffm...@gmail.com> Date: Mon, 12 Jan 2015 00:53:08 +0530 Subject: [PATCH] Adding SCTE 35 implementation in avformat Use follwing cmd to test it. /ffmpeg_g -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/utils.c | 1 + 10 files changed, 117 insertions(+), 11 deletions(-) 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/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