On 03/12/2015 08:03 PM, Deron wrote:
On 2/26/15 4:26 AM, Anshul wrote:

On 02/25/2015 10:04 PM, Deron wrote:
On 2/21/15 8:05 AM, Deron wrote:
On 2/15/15 5:44 AM, Anshul wrote:

attached another cleaned patch.

-Anshul

Not sure if I should be posting here, privately, or do the user list since it is an unaccepted patch... This patch applies cleanly to ffmpeg git of that day, and with minor adjustment to current git, but either crashes the same for me right away. Here is the back trace from gdb:


Program received signal SIGSEGV, Segmentation fault.
__GI___libc_realloc (oldmem=0x70, bytes=8) at malloc.c:2977
2977    malloc.c: No such file or directory.
(gdb) bt
#0  __GI___libc_realloc (oldmem=0x70, bytes=8) at malloc.c:2977
#1 0x00007ffff7609b99 in avformat_new_stream (s=s@entry=0xe2dbc0, c=c@entry=0x0) at libavformat/utils.c:3655 #2 0x00007ffff75451c4 in hls_mux_init (s=0x6787c0) at libavformat/hlsenc.c:194
#3  hls_write_header (s=0x6787c0) at libavformat/hlsenc.c:490
#4 0x00007ffff75999ec in avformat_write_header (s=s@entry=0x6787c0, options=0x6a9948) at libavformat/mux.c:406
#5  0x0000000000424c00 in transcode_init () at ffmpeg.c:3096
#6  0x0000000000407581 in transcode () at ffmpeg.c:3815
#7  main (argc=13, argv=0x7fffffffe5a8) at ffmpeg.c:4022


The command (I've tried all sorts of combinations. If I don't provide a subtitle stream, it does not crash. Otherwise it does. Unpatched can generate a webvtt from the subtitle stream without crashing.)

ffmpeg -loglevel debug -f lavfi -i movie=out.ts\[out0+subcc\] -f hls -hls_segment_filename /var/www/html/stream/kota/v.low.%d.ts -hls_subtitle_path /var/www/html/stream/kota/ -y /var/www/html/stream/kota/v.low.m3u8


I did find the problem. The patch does not properly initialize hls->vtt_oformat (etc) if you provide the "-hls_segment_filename" parameter as I have done.

Deron

I have attached new patch with correctly initializing the webvtt, can you please check this patch again.

-Anshul

Besides needing some minor changes to apply to git head, this works. I'm not sure that the CC is 100% correct, but I have not sat down and compared to any other output yet. Certainly close, it just seems a little off and I can't put my finger on it. Not a timing issue, just seems jumpy and hard to read. Could just be the Apple player, or could be that the segmenter is not duplicating items that span multiple segments.

I do have a couple requests, but once this is accepted I think I can make the patch. One is the support m3u8 rename like the mpegts segments do, and the other is to support WebVTT segmenting on a subtitle only stream. I'd like to see/submit an audio only stream segmenter patch as well, but that is well outside this patch :-)

Either way, thanks!

Deron
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Hello,

This patch is working in my system from months.
I have also tested most of the option's of hls with this implementation.
I have updated this to most recent git version.

Its a big patch, it take time to again merge with latest git version.
so i am expecting it to merge it with master.

-Please comment
Anshul
>From 50e3fc7b9c6bf1a46afb3ea7908dc0043d9a8b49 Mon Sep 17 00:00:00 2001
From: Anshul Maheshwari <er.anshul.maheshw...@gmail.com>
Date: Mon, 27 Apr 2015 21:24:12 +0530
Subject: [PATCH] Adding Webvtt in hls

Signed-off-by: Anshul Maheshwari <er.anshul.maheshw...@gmail.com>
---
 libavformat/hlsenc.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 175 insertions(+), 8 deletions(-)

diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 7885351..b0912c4 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -39,6 +39,7 @@
 
 typedef struct HLSSegment {
     char filename[1024];
+    char sub_filename[1024];
     double duration; /* in seconds */
     int64_t pos;
     int64_t size;
@@ -58,8 +59,10 @@ typedef struct HLSContext {
     int64_t sequence;
     int64_t start_sequence;
     AVOutputFormat *oformat;
+    AVOutputFormat *vtt_oformat;
 
     AVFormatContext *avf;
+    AVFormatContext *vtt_avf;
 
     float time;            // Set by a private option.
     int max_nb_segments;   // Set by a private option.
@@ -70,6 +73,7 @@ typedef struct HLSContext {
     int allowcache;
     int64_t recording_time;
     int has_video;
+    int has_subtitle;
     int64_t start_pts;
     int64_t end_pts;
     double duration;      // last segment duration computed so far, in seconds
@@ -82,9 +86,15 @@ typedef struct HLSContext {
     HLSSegment *old_segments;
 
     char *basename;
+    char *vtt_basename;
+    char *vtt_m3u8_name;
     char *baseurl;
     char *format_options_str;
+    char *vtt_format_options_str;
+    char *subtitle_filename;
     AVDictionary *format_options;
+    AVDictionary *vtt_format_options;
+
 } HLSContext;
 
 static int hls_delete_old_segments(HLSContext *hls) {
@@ -156,6 +166,7 @@ static int hls_mux_init(AVFormatContext *s)
 {
     HLSContext *hls = s->priv_data;
     AVFormatContext *oc;
+    AVFormatContext *vtt_oc;
     int i, ret;
 
     ret = avformat_alloc_output_context2(&hls->avf, hls->oformat, NULL, NULL);
@@ -168,9 +179,24 @@ static int hls_mux_init(AVFormatContext *s)
     oc->max_delay          = s->max_delay;
     av_dict_copy(&oc->metadata, s->metadata, 0);
 
+    if(hls->vtt_oformat) {
+        ret = avformat_alloc_output_context2(&hls->vtt_avf, hls->vtt_oformat, NULL, NULL);
+        if (ret < 0)
+            return ret;
+        vtt_oc          = hls->vtt_avf;
+        vtt_oc->oformat = hls->vtt_oformat;
+        av_dict_copy(&vtt_oc->metadata, s->metadata, 0);
+    }
+
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st;
-        if (!(st = avformat_new_stream(oc, NULL)))
+        AVFormatContext *loc;
+        if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
+            loc = vtt_oc;
+        else
+            loc = oc;
+
+        if (!(st = avformat_new_stream(loc, NULL)))
             return AVERROR(ENOMEM);
         avcodec_copy_context(st->codec, s->streams[i]->codec);
         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
@@ -193,6 +219,9 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
 
     av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
 
+    if(hls->has_subtitle)
+        av_strlcpy(en->sub_filename, av_basename(hls->vtt_avf->filename), sizeof(en->sub_filename));
+
     en->duration = duration;
     en->pos      = pos;
     en->size     = size;
@@ -242,6 +271,7 @@ static int hls_window(AVFormatContext *s, int last)
     int target_duration = 0;
     int ret = 0;
     AVIOContext *out = NULL;
+    AVIOContext *sub_out = NULL;
     char temp_filename[1024];
     int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
     int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
@@ -286,8 +316,39 @@ static int hls_window(AVFormatContext *s, int last)
     if (last)
         avio_printf(out, "#EXT-X-ENDLIST\n");
 
+    if( hls->vtt_m3u8_name ) {
+        if ((ret = avio_open2(&sub_out, hls->vtt_m3u8_name, AVIO_FLAG_WRITE,
+                          &s->interrupt_callback, NULL)) < 0)
+            goto fail;
+        avio_printf(sub_out, "#EXTM3U\n");
+        avio_printf(sub_out, "#EXT-X-VERSION:%d\n", version);
+        if (hls->allowcache == 0 || hls->allowcache == 1) {
+            avio_printf(sub_out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
+        }
+        avio_printf(sub_out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
+        avio_printf(sub_out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+
+        av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
+               sequence);
+
+        for (en = hls->segments; en; en = en->next) {
+            avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
+            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 (last)
+            avio_printf(sub_out, "#EXT-X-ENDLIST\n");
+
+    }
+
 fail:
     avio_closep(&out);
+    avio_closep(&sub_out);
     if (ret >= 0 && use_rename)
         ff_rename(temp_filename, s->filename, s);
     return ret;
@@ -297,26 +358,46 @@ static int hls_start(AVFormatContext *s)
 {
     HLSContext *c = s->priv_data;
     AVFormatContext *oc = c->avf;
+    AVFormatContext *vtt_oc = c->vtt_avf;
     int err = 0;
 
-    if (c->flags & HLS_SINGLE_FILE)
+    if (c->flags & HLS_SINGLE_FILE) {
         av_strlcpy(oc->filename, c->basename,
                    sizeof(oc->filename));
-    else
+        if (c->vtt_basename)
+            av_strlcpy(vtt_oc->filename, c->vtt_basename,
+                  sizeof(vtt_oc->filename));
+    } else {
         if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
                                   c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
             av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
             return AVERROR(EINVAL);
         }
+        if( c->vtt_basename) {
+            if (av_get_frame_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
+                              c->vtt_basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
+                av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
+                return AVERROR(EINVAL);
+            }
+       }
+    }
     c->number++;
 
     if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                           &s->interrupt_callback, NULL)) < 0)
         return err;
+    if (c->vtt_basename) {
+        if ((err = avio_open2(&vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE,
+                         &s->interrupt_callback, NULL)) < 0)
+            return err;
+    }
 
     if (oc->oformat->priv_class && oc->priv_data)
         av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
 
+    if (c->vtt_basename)
+        avformat_write_header(vtt_oc,NULL);
+
     return 0;
 }
 
@@ -326,8 +407,10 @@ static int hls_write_header(AVFormatContext *s)
     int ret, i;
     char *p;
     const char *pattern = "%d.ts";
+    const char *vtt_pattern = "%d.vtt";
     AVDictionary *options = NULL;
     int basename_size;
+    int vtt_basename_size;
 
     hls->sequence       = hls->start_sequence;
     hls->recording_time = hls->time * AV_TIME_BASE;
@@ -341,9 +424,12 @@ static int hls_write_header(AVFormatContext *s)
         }
     }
 
-    for (i = 0; i < s->nb_streams; i++)
+    for (i = 0; i < s->nb_streams; i++) {
         hls->has_video +=
             s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO;
+        hls->has_subtitle +=
+            s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE;
+    }
 
     if (hls->has_video > 1)
         av_log(s, AV_LOG_WARNING,
@@ -357,6 +443,14 @@ static int hls_write_header(AVFormatContext *s)
         goto fail;
     }
 
+    if(hls->has_subtitle) {
+        hls->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
+        if (!hls->oformat) {
+            ret = AVERROR_MUXER_NOT_FOUND;
+            goto fail;
+        }
+    }
+
     if (hls->segment_filename) {
         hls->basename = av_strdup(hls->segment_filename);
         if (!hls->basename) {
@@ -382,6 +476,35 @@ static int hls_write_header(AVFormatContext *s)
         av_strlcat(hls->basename, pattern, basename_size);
     }
 
+    if(hls->has_subtitle) {
+
+        if (hls->flags & HLS_SINGLE_FILE)
+            vtt_pattern = ".vtt";
+        vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
+        hls->vtt_basename = av_malloc(vtt_basename_size);
+        if (!hls->vtt_basename) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        hls->vtt_m3u8_name = av_malloc(vtt_basename_size);
+        if (!hls->vtt_m3u8_name ) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        av_strlcpy(hls->vtt_basename, s->filename, vtt_basename_size);
+        p = strrchr(hls->vtt_basename, '.');
+        if (p)
+            *p = '\0';
+
+        if( hls->subtitle_filename ) {
+            strcpy(hls->vtt_m3u8_name, hls->subtitle_filename);
+        } else {
+            strcpy(hls->vtt_m3u8_name, hls->vtt_basename);
+            av_strlcat(hls->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
+        }
+        av_strlcat(hls->vtt_basename, vtt_pattern, vtt_basename_size);
+    }
+
     if ((ret = hls_mux_init(s)) < 0)
         goto fail;
 
@@ -395,10 +518,19 @@ static int hls_write_header(AVFormatContext *s)
         ret = AVERROR(EINVAL);
         goto fail;
     }
-    av_assert0(s->nb_streams == hls->avf->nb_streams);
+    //av_assert0(s->nb_streams == hls->avf->nb_streams);
     for (i = 0; i < s->nb_streams; i++) {
-        AVStream *inner_st  = hls->avf->streams[i];
+        AVStream *inner_st;
         AVStream *outer_st = s->streams[i];
+        if (outer_st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE)
+            inner_st = hls->avf->streams[i];
+        else if (hls->vtt_avf)
+            inner_st = hls->vtt_avf->streams[0];
+        else {
+            /* We have subtitle stream, when user dont want */
+            inner_st = NULL;
+            continue;
+        }
         avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
     }
 fail:
@@ -406,8 +538,12 @@ fail:
     av_dict_free(&options);
     if (ret < 0) {
         av_freep(&hls->basename);
+        av_freep(&hls->vtt_basename);
         if (hls->avf)
             avformat_free_context(hls->avf);
+        if (hls->vtt_avf)
+            avformat_free_context(hls->vtt_avf);
+
     }
     return ret;
 }
@@ -415,12 +551,20 @@ fail:
 static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
 {
     HLSContext *hls = s->priv_data;
-    AVFormatContext *oc = hls->avf;
+    AVFormatContext *oc = NULL;
     AVStream *st = s->streams[pkt->stream_index];
     int64_t end_pts = hls->recording_time * hls->number;
     int is_ref_pkt = 1;
     int ret, can_split = 1;
+    int stream_index = 0;
 
+    if( st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
+        oc = hls->vtt_avf;
+        stream_index = 0;
+    } else {
+        oc = hls->avf;
+        stream_index = pkt->stream_index;
+    }
     if (hls->start_pts == AV_NOPTS_VALUE) {
         hls->start_pts = pkt->pts;
         hls->end_pts   = pkt->pts;
@@ -459,6 +603,8 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
             hls->number++;
         } else {
             avio_closep(&oc->pb);
+            if (hls->vtt_avf)
+                avio_close(hls->vtt_avf->pb);
 
             ret = hls_start(s);
         }
@@ -466,13 +612,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
         if (ret < 0)
             return ret;
 
+        if( st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE )
+            oc = hls->vtt_avf;
+        else
         oc = hls->avf;
 
         if ((ret = hls_window(s, 0)) < 0)
             return ret;
     }
 
-    ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0);
+    ret = ff_write_chained(oc, stream_index, pkt, s, 0);
 
     return ret;
 }
@@ -481,6 +630,7 @@ static int hls_write_trailer(struct AVFormatContext *s)
 {
     HLSContext *hls = s->priv_data;
     AVFormatContext *oc = hls->avf;
+    AVFormatContext *vtt_oc = hls->vtt_avf;
 
     av_write_trailer(oc);
     if (oc->pb) {
@@ -488,8 +638,22 @@ static int hls_write_trailer(struct AVFormatContext *s)
         avio_closep(&oc->pb);
         hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
     }
+
+    if (vtt_oc) {
+        if (vtt_oc->pb)
+            av_write_trailer(vtt_oc);
+        hls->size = avio_tell(hls->vtt_avf->pb) - hls->start_pos;
+        avio_closep(&vtt_oc->pb);
+    }
     av_freep(&hls->basename);
     avformat_free_context(oc);
+
+    if (vtt_oc) {
+        av_freep(&hls->vtt_basename);
+        av_freep(&hls->vtt_m3u8_name);
+        avformat_free_context(vtt_oc);
+    }
+
     hls->avf = NULL;
     hls_window(s, 1);
 
@@ -505,10 +669,12 @@ static const AVOption options[] = {
     {"hls_time",      "set segment length in seconds",           OFFSET(time),    AV_OPT_TYPE_FLOAT,  {.dbl = 2},     0, FLT_MAX, E},
     {"hls_list_size", "set maximum number of playlist entries",  OFFSET(max_nb_segments),    AV_OPT_TYPE_INT,    {.i64 = 5},     0, INT_MAX, E},
     {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
+    {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
     {"hls_wrap",      "set number after which the index wraps",  OFFSET(wrap),    AV_OPT_TYPE_INT,    {.i64 = 0},     0, INT_MAX, E},
     {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E},
     {"hls_base_url",  "url to prepend to each playlist entry",   OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E},
     {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename),   AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
+    {"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"},
@@ -531,6 +697,7 @@ AVOutputFormat ff_hls_muxer = {
     .priv_data_size = sizeof(HLSContext),
     .audio_codec    = AV_CODEC_ID_AAC,
     .video_codec    = AV_CODEC_ID_H264,
+    .subtitle_codec = AV_CODEC_ID_WEBVTT,
     .flags          = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
     .write_header   = hls_write_header,
     .write_packet   = hls_write_packet,
-- 
2.1.4

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to