Commit 0c78b6a4 authored by Karthick Jeyapal's avatar Karthick Jeyapal Committed by Steven Liu

avformat/dashenc: Persistent HTTP connections supported as an option

Reviewed-by: 's avatarAman Gupta <aman@tmm1.net>
Reviewed-by: 's avatarSteven Liu <lq@onvideo.cn>
parent e8f71ef3
...@@ -249,6 +249,8 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep ...@@ -249,6 +249,8 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep
URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso" URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"
@item -http_user_agent @var{user_agent} @item -http_user_agent @var{user_agent}
Override User-Agent field in HTTP header. Applicable only for HTTP output. Override User-Agent field in HTTP header. Applicable only for HTTP output.
@item -http_persistent @var{http_persistent}
Use persistent HTTP connections. Applicable only for HTTP output.
@item -hls_playlist @var{hls_playlist} @item -hls_playlist @var{hls_playlist}
Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8.
One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc.
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#include "avformat.h" #include "avformat.h"
#include "avio_internal.h" #include "avio_internal.h"
#include "hlsplaylist.h" #include "hlsplaylist.h"
#if CONFIG_HTTP_PROTOCOL
#include "http.h"
#endif
#include "internal.h" #include "internal.h"
#include "isom.h" #include "isom.h"
#include "os_support.h" #include "os_support.h"
...@@ -103,7 +106,10 @@ typedef struct DASHContext { ...@@ -103,7 +106,10 @@ typedef struct DASHContext {
const char *utc_timing_url; const char *utc_timing_url;
const char *user_agent; const char *user_agent;
int hls_playlist; int hls_playlist;
int http_persistent;
int master_playlist_created; int master_playlist_created;
AVIOContext *mpd_out;
AVIOContext *m3u8_out;
} DASHContext; } DASHContext;
static struct codec_string { static struct codec_string {
...@@ -117,6 +123,36 @@ static struct codec_string { ...@@ -117,6 +123,36 @@ static struct codec_string {
{ 0, NULL } { 0, NULL }
}; };
static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename,
AVDictionary **options) {
DASHContext *c = s->priv_data;
int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
int err = AVERROR_MUXER_NOT_FOUND;
if (!*pb || !http_base_proto || !c->http_persistent) {
err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options);
#if CONFIG_HTTP_PROTOCOL
} else {
URLContext *http_url_context = ffio_geturlcontext(*pb);
av_assert0(http_url_context);
err = ff_http_do_new_request(http_url_context, filename);
#endif
}
return err;
}
static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) {
DASHContext *c = s->priv_data;
int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
if (!http_base_proto || !c->http_persistent) {
ff_format_io_close(s, pb);
#if CONFIG_HTTP_PROTOCOL
} else {
avio_flush(*pb);
#endif
}
}
static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
char *str, int size) char *str, int size)
{ {
...@@ -218,6 +254,8 @@ static void set_http_options(AVDictionary **options, DASHContext *c) ...@@ -218,6 +254,8 @@ static void set_http_options(AVDictionary **options, DASHContext *c)
{ {
if (c->user_agent) if (c->user_agent)
av_dict_set(options, "user_agent", c->user_agent, 0); av_dict_set(options, "user_agent", c->user_agent, 0);
if (c->http_persistent)
av_dict_set_int(options, "multiple_requests", 1, 0);
} }
static void get_hls_playlist_name(char *playlist_name, int string_size, static void get_hls_playlist_name(char *playlist_name, int string_size,
...@@ -273,9 +311,10 @@ static void dash_free(AVFormatContext *s) ...@@ -273,9 +311,10 @@ static void dash_free(AVFormatContext *s)
av_freep(&c->streams); av_freep(&c->streams);
} }
static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatContext *s,
int representation_id, int final) int representation_id, int final)
{ {
DASHContext *c = s->priv_data;
int i, start_index = 0, start_number = 1; int i, start_index = 0, start_number = 1;
if (c->window_size) { if (c->window_size) {
start_index = FFMAX(os->nb_segments - c->window_size, 0); start_index = FFMAX(os->nb_segments - c->window_size, 0);
...@@ -339,7 +378,6 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext ...@@ -339,7 +378,6 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
int timescale = os->ctx->streams[0]->time_base.den; int timescale = os->ctx->streams[0]->time_base.den;
char temp_filename_hls[1024]; char temp_filename_hls[1024];
char filename_hls[1024]; char filename_hls[1024];
AVIOContext *out_hls = NULL;
AVDictionary *http_opts = NULL; AVDictionary *http_opts = NULL;
int target_duration = 0; int target_duration = 0;
int ret = 0; int ret = 0;
...@@ -352,7 +390,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext ...@@ -352,7 +390,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls);
set_http_options(&http_opts, c); set_http_options(&http_opts, c);
avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts);
av_dict_free(&http_opts); av_dict_free(&http_opts);
for (i = start_index; i < os->nb_segments; i++) { for (i = start_index; i < os->nb_segments; i++) {
Segment *seg = os->segments[i]; Segment *seg = os->segments[i];
...@@ -361,15 +399,15 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext ...@@ -361,15 +399,15 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
target_duration = lrint(duration); target_duration = lrint(duration);
} }
ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration,
start_number, PLAYLIST_TYPE_NONE); start_number, PLAYLIST_TYPE_NONE);
ff_hls_write_init_file(out_hls, os->initfile, c->single_file, ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file,
os->init_range_length, os->init_start_pos); os->init_range_length, os->init_start_pos);
for (i = start_index; i < os->nb_segments; i++) { for (i = start_index; i < os->nb_segments; i++) {
Segment *seg = os->segments[i]; Segment *seg = os->segments[i];
ret = ff_hls_write_file_entry(out_hls, 0, c->single_file, ret = ff_hls_write_file_entry(c->m3u8_out, 0, c->single_file,
(double) seg->duration / timescale, 0, (double) seg->duration / timescale, 0,
seg->range_length, seg->start_pos, NULL, seg->range_length, seg->start_pos, NULL,
c->single_file ? os->initfile : seg->file, c->single_file ? os->initfile : seg->file,
...@@ -380,9 +418,10 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext ...@@ -380,9 +418,10 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext
} }
if (final) if (final)
ff_hls_write_end_list(out_hls); ff_hls_write_end_list(c->m3u8_out);
dashenc_io_close(s, &c->m3u8_out, temp_filename_hls);
avio_close(out_hls);
if (use_rename) if (use_rename)
if (avpriv_io_move(temp_filename_hls, filename_hls) < 0) { if (avpriv_io_move(temp_filename_hls, filename_hls) < 0) {
av_log(os->ctx, AV_LOG_WARNING, "renaming file %s to %s failed\n\n", temp_filename_hls, filename_hls); av_log(os->ctx, AV_LOG_WARNING, "renaming file %s to %s failed\n\n", temp_filename_hls, filename_hls);
...@@ -498,7 +537,7 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind ...@@ -498,7 +537,7 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
s->streams[i]->codecpar->channels); s->streams[i]->codecpar->channels);
} }
output_segment_list(os, out, c, i, final); output_segment_list(os, out, s, i, final);
avio_printf(out, "\t\t\t</Representation>\n"); avio_printf(out, "\t\t\t</Representation>\n");
} }
avio_printf(out, "\t\t</AdaptationSet>\n"); avio_printf(out, "\t\t</AdaptationSet>\n");
...@@ -657,11 +696,12 @@ static int write_manifest(AVFormatContext *s, int final) ...@@ -657,11 +696,12 @@ static int write_manifest(AVFormatContext *s, int final)
snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
set_http_options(&opts, c); set_http_options(&opts, c);
ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &opts); ret = dashenc_io_open(s, &c->mpd_out, temp_filename, &opts);
if (ret < 0) { if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
return ret; return ret;
} }
out = c->mpd_out;
av_dict_free(&opts); av_dict_free(&opts);
avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
avio_printf(out, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" avio_printf(out, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
...@@ -728,7 +768,7 @@ static int write_manifest(AVFormatContext *s, int final) ...@@ -728,7 +768,7 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "</MPD>\n"); avio_printf(out, "</MPD>\n");
avio_flush(out); avio_flush(out);
ff_format_io_close(s, &out); dashenc_io_close(s, &c->mpd_out, temp_filename);
if (use_rename) { if (use_rename) {
if ((ret = avpriv_io_move(temp_filename, s->filename)) < 0) if ((ret = avpriv_io_move(temp_filename, s->filename)) < 0)
...@@ -1115,7 +1155,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) ...@@ -1115,7 +1155,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename); snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename);
snprintf(temp_path, sizeof(temp_path), use_rename ? "%s.tmp" : "%s", full_path); snprintf(temp_path, sizeof(temp_path), use_rename ? "%s.tmp" : "%s", full_path);
set_http_options(&opts, c); set_http_options(&opts, c);
ret = s->io_open(s, &os->out, temp_path, AVIO_FLAG_WRITE, &opts); ret = dashenc_io_open(s, &os->out, temp_path, &opts);
if (ret < 0) if (ret < 0)
break; break;
av_dict_free(&opts); av_dict_free(&opts);
...@@ -1133,7 +1173,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) ...@@ -1133,7 +1173,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
if (c->single_file) { if (c->single_file) {
find_index_range(s, full_path, os->pos, &index_length); find_index_range(s, full_path, os->pos, &index_length);
} else { } else {
ff_format_io_close(s, &os->out); dashenc_io_close(s, &os->out, temp_path);
if (use_rename) { if (use_rename) {
ret = avpriv_io_move(temp_path, full_path); ret = avpriv_io_move(temp_path, full_path);
...@@ -1330,6 +1370,7 @@ static const AVOption options[] = { ...@@ -1330,6 +1370,7 @@ static const AVOption options[] = {
{ "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E }, { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E },
{ "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E }, { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
{ "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, { "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
{ "http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
{ "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ NULL }, { NULL },
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment