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
>From 176ee7227e3991925ced0ad593ae670d871a489d Mon Sep 17 00:00:00 2001
From: Anshul Maheshwari <er.anshul.maheshw...@gmail.com>
Date: Thu, 26 Feb 2015 16:53:17 +0530
Subject: [PATCH] Adding WebVtt implementation in hls
---
libavformat/hlsenc.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 167 insertions(+), 11 deletions(-)
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 29bf30e..35260a1 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,11 +86,17 @@ 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;
AVIOContext *pb;
+ AVIOContext *sub_pb;
} HLSContext;
static int hls_delete_old_segments(HLSContext *hls) {
@@ -158,6 +168,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);
@@ -170,10 +181,25 @@ 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)))
- return AVERROR(ENOMEM);
+ if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE && hls->vtt_oformat) {
+ if (!(st = avformat_new_stream(vtt_oc, NULL)))
+ return AVERROR(ENOMEM);
+ } else {
+ if (!(st = avformat_new_stream(oc, NULL)))
+ return AVERROR(ENOMEM);
+ }
avcodec_copy_context(st->codec, s->streams[i]->codec);
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
st->time_base = s->streams[i]->time_base;
@@ -195,6 +221,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;
@@ -279,8 +308,37 @@ static int hls_window(AVFormatContext *s, int last)
if (last)
avio_printf(hls->pb, "#EXT-X-ENDLIST\n");
+ if( hls->vtt_m3u8_name ) {
+ if ((ret = avio_open2(&hls->sub_pb, hls->vtt_m3u8_name, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL)) < 0)
+ goto fail;
+ avio_printf(hls->sub_pb, "#EXTM3U\n");
+ avio_printf(hls->sub_pb, "#EXT-X-VERSION:%d\n", version);
+ if (hls->allowcache == 0 || hls->allowcache == 1) {
+ avio_printf(hls->sub_pb, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
+ }
+ avio_printf(hls->sub_pb, "#EXT-X-TARGETDURATION:%d\n", target_duration);
+ avio_printf(hls->sub_pb, "#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(hls->sub_pb, "#EXTINF:%f,\n", en->duration);
+ if (hls->flags & HLS_SINGLE_FILE)
+ avio_printf(hls->sub_pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
+ en->size, en->pos);
+ if (hls->baseurl)
+ avio_printf(hls->sub_pb, "%s", hls->baseurl);
+ avio_printf(hls->sub_pb, "%s\n", en->sub_filename);
+ }
+
+ if (last)
+ avio_printf(hls->sub_pb, "#EXT-X-ENDLIST\n");
+ }
fail:
avio_closep(&hls->pb);
+ avio_closep(&hls->sub_pb);
return ret;
}
@@ -288,26 +346,44 @@ 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
+ 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;
}
@@ -317,8 +393,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;
@@ -332,9 +410,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,
@@ -371,7 +452,41 @@ static int hls_write_header(AVFormatContext *s)
if (p)
*p = '\0';
av_strlcat(hls->basename, pattern, basename_size);
- }
+ }
+ if(hls->has_subtitle) {
+ av_log(NULL,AV_LOG_WARNING,"setting vtt_oformat\n");
+ hls->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
+ if (!hls->oformat) {
+ ret = AVERROR_MUXER_NOT_FOUND;
+ goto fail;
+ }
+ 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;
@@ -386,10 +501,14 @@ static int hls_write_header(AVFormatContext *s)
ret = AVERROR(EINVAL);
goto fail;
}
- 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];
avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
}
fail:
@@ -397,8 +516,11 @@ 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;
}
@@ -406,11 +528,22 @@ 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;
@@ -450,6 +583,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);
}
@@ -457,13 +592,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
if (ret < 0)
return ret;
- oc = hls->avf;
+ 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;
}
@@ -472,6 +610,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) {
@@ -479,8 +618,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);
+ hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+ }
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);
@@ -497,11 +650,13 @@ 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_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
+ {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
{"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"},
@@ -523,6 +678,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