From patchwork Wed Nov 29 15:47:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jeyapal, Karthick" X-Patchwork-Id: 6452 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.161.94 with SMTP id m30csp4654141jah; Wed, 29 Nov 2017 07:47:53 -0800 (PST) X-Google-Smtp-Source: AGs4zMY9wKvvg9KjPogqTWXLmSdUdqZvWDXB1YNWUnHyQWhFYZTzEwXBrrJ0WiGD8G7stQKx1Cif X-Received: by 10.28.166.216 with SMTP id p207mr3008622wme.147.1511970473041; Wed, 29 Nov 2017 07:47:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511970472; cv=none; d=google.com; s=arc-20160816; b=xR1Tp+jor+1K+bV5PIi0Xt7Nx+9UW92LL0qme6CPbz33GPnUgI7iu4NfANWTE04Xt5 KsTTWsmpWvRszlXIuV1u74PfwOy03//wAO+G4qKMTVJ05GzINlKBjK39VXzteu6fDkEA p+frUvHriZAOzTFkrc97CvU216zbmOwcEXsl8rGBkOx+BbZaIifzsrDjIAQG0tn1CvbE 459b4nj9ALyJMpxXXThjVSKqGzDtfIA35oR7GwExTkVGVOMM2+9jrEkSoLSePWJT2GfM DYhvFGV6vWRQDfF3DxsG6zNOQCKC/Y3AXtnSbGRpVXx89ooQ3pkeR1Qtw/SaFgXX4wiB Reig== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:content-id:content-language :accept-language:message-id:date:thread-index:thread-topic:to:from :dkim-signature:delivered-to:arc-authentication-results; bh=bqgZzTcsYwdNLyUocMan8Vtcjrjlfdk0BQma0aUVAEA=; b=OFJ4DWbR8cKi90ffF9mjevnpjPukpWRMT5394rHGj/OVhrU22xnJYGlfEx+eNnfQ/9 FfSJo2Mo3kVuyg/pyoShff2Mnma+SDsSx/4H0t0n/31X0p/DEitV06wka4tiYYYw1BtR KoqHA8hIdoS6YOLdJQBgATnDuUPMPtjN86xg8N1k+Qt1dOABhcX+QqhqsH7LZvij4gJu eAOIZObQuzGWdMDpXeQSs1ogcXAK0PMHAmkhXWO3RmbwN+51ZarYPr6g8eZhA9nkBleZ WY5P7g4hmPeSxgQT8LUk+JtgVRHhBde+FcnGX4Z8XTkyci72P7HJJK/vdwyrcXWnBEYD tg1A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@akamai.com header.s=jan2016.eng header.b=fdIbOM52; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h64si1552874wmi.85.2017.11.29.07.47.52; Wed, 29 Nov 2017 07:47:52 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@akamai.com header.s=jan2016.eng header.b=fdIbOM52; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CC61968A4C1; Wed, 29 Nov 2017 17:47:48 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mx0b-00190b01.pphosted.com (mx0b-00190b01.pphosted.com [67.231.157.127]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 647E068A417 for ; Wed, 29 Nov 2017 17:47:41 +0200 (EET) Received: from pps.filterd (m0050102.ppops.net [127.0.0.1]) by mx0b-00190b01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vATFkY3x022521 for ; Wed, 29 Nov 2017 15:47:43 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akamai.com; h=from : to : subject : date : message-id : content-type : content-id : content-transfer-encoding : mime-version; s=jan2016.eng; bh=pIW02DyuB3+L5TqOTwYG/F8UHOf9kE2PjQttgjxKj0k=; b=fdIbOM52hHbY4sWQj1vav0SG/Ra267akfCapo8cgAsXv3CZoojxRZ1ShaAUlYP12KBhl Xu/HvinAhC8opYvxfGOGrSavaTUIBVXiY/CN3UhswEQYKTAPxKgeR+9IIb2uMXrWwVRX NbLiJ2bWe/uYhdjZM14ktJLaejz5xR5MpiJzscr5uEgcb+Cs08mQJ+6rWFeq5NW/Afbs /vU3g2dEhEDpsVHUkQQ4WTUvHm7OxbwspIzXxTdIHfEpHYgU+nAN0wmv/roqTulaFDoB CU5wCdHdkJsSOSFE44jcDxwbmj4IJ8Emr0CyL42GtZB89rRDf5v3BW2RUglRmAPFZQIr Xg== Received: from prod-mail-ppoint1 (prod-mail-ppoint1.akamai.com [184.51.33.18]) by m0050102.ppops.net-00190b01. with ESMTP id 2egvre5b68-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Wed, 29 Nov 2017 15:47:42 +0000 Received: from pps.filterd (prod-mail-ppoint1.akamai.com [127.0.0.1]) by prod-mail-ppoint1.akamai.com (8.16.0.21/8.16.0.21) with SMTP id vATFkJiv006610 for ; Wed, 29 Nov 2017 10:47:42 -0500 Received: from email.msg.corp.akamai.com ([172.27.155.30]) by prod-mail-ppoint1.akamai.com with ESMTP id 2ef4qy8uug-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT) for ; Wed, 29 Nov 2017 10:47:42 -0500 Received: from ASIN1EX-DAG2MB2.msg.corp.akamai.com (172.27.155.102) by asin1ex-dag2mb1.msg.corp.akamai.com (172.27.155.101) with Microsoft SMTP Server (TLS) id 15.0.1263.5; Wed, 29 Nov 2017 21:17:38 +0530 Received: from ASIN1EX-DAG2MB2.msg.corp.akamai.com ([172.27.155.102]) by asin1ex-dag2mb2.msg.corp.akamai.com ([172.27.155.102]) with mapi id 15.00.1263.000; Wed, 29 Nov 2017 21:17:38 +0530 From: "Jeyapal, Karthick" To: FFmpeg development discussions and patches Thread-Topic: [PATCH 2/2] avformat/dashenc: Option to generate hls playlist as well Thread-Index: AQHTaSlhj4dn5Mi2kUWSohn8YB9ZTw== Date: Wed, 29 Nov 2017 15:47:37 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-messagesentrepresentingtype: 1 x-ms-exchange-transport-fromentityheader: Hosted x-originating-ip: [172.19.221.57] Content-ID: <9D2804576FD9EC42B1F96B11F44186A3@akamai.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-11-29_05:, , signatures=0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1711290206 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-11-29_05:, , signatures=0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1711290206 Subject: Re: [FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Option to generate hls playlist as well X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Please ignore the below patch and use v2. Sorry for the inconvenience. Regards, Karthick On 11/29/17, 8:47 PM, "Karthick J" wrote: This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/Makefile | 2 +- libavformat/dashenc.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 5 deletions(-) -- 1.9.1 diff --git a/doc/muxers.texi b/doc/muxers.texi index 8ec48c2..3d0c7bf 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ 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" @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +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. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/Makefile b/libavformat/Makefile index fd8b9f9..4bffdf2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -135,7 +135,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER) += rawenc.o -OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o +OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 0fee3cd..efc2012 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; + int hls_playlist; + int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { + if (base_url) + snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); + else + snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, + int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } + if (c->hls_playlist && start_index < os->nb_segments) + { + int timescale = os->ctx->streams[0]->time_base.den; + char temp_filename_hls[1024]; + char filename_hls[1024]; + AVIOContext *out_hls = NULL; + AVDictionary *http_opts = NULL; + int target_duration = 0; + const char *proto = avio_find_protocol_name(c->dirname); + int use_rename = proto && !strcmp(proto, "file"); + + get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + + snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + + set_http_options(&http_opts, c); + avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); + av_dict_free(&http_opts); + for (i = start_index; i < os->nb_segments; i++) { + Segment *seg = os->segments[i]; + double duration = (double) seg->duration / timescale; + if (target_duration <= duration) + target_duration = hls_get_int_from_double(duration); + } + + ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + + ff_hls_write_init_file(out_hls, os->initfile, c->single_file, + os->init_range_length, os->init_start_pos); + + for (i = start_index; i < os->nb_segments; i++) { + Segment *seg = os->segments[i]; + ff_hls_write_file_entry(out_hls, 0, c->single_file, + (double) seg->duration / timescale, 0, + seg->range_length, seg->start_pos, NULL, + c->single_file ? os->initfile : seg->file, + NULL); + } + + if (final) + ff_hls_write_end_list(out_hls); + + avio_close(out_hls); + if (use_rename) + avpriv_io_move(temp_filename_hls, filename_hls); + } + } static char *xmlescape(const char *str) { @@ -391,7 +452,8 @@ static void format_date_now(char *buf, int size) } } -static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_index) +static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_index, + int final) { DASHContext *c = s->priv_data; AdaptationSet *as = &c->as[as_index]; @@ -430,7 +492,7 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind avio_printf(out, "\t\t\t\t\n", s->streams[i]->codecpar->channels); } - output_segment_list(os, out, c); + output_segment_list(os, out, c, i, final); avio_printf(out, "\t\t\t\n"); } avio_printf(out, "\t\t\n"); @@ -650,7 +712,7 @@ static int write_manifest(AVFormatContext *s, int final) } for (i = 0; i < c->nb_as; i++) { - if ((ret = write_adaptation_set(s, out, i)) < 0) + if ((ret = write_adaptation_set(s, out, i, final)) < 0) return ret; } avio_printf(out, "\t\n"); @@ -665,6 +727,38 @@ static int write_manifest(AVFormatContext *s, int final) if (use_rename) return avpriv_io_move(temp_filename, s->filename); + if (c->hls_playlist && !c->master_playlist_created) { + char filename_hls[1024]; + + if (c->dirname) + snprintf(filename_hls, sizeof(filename_hls), "%s/master.m3u8", c->dirname); + else + snprintf(filename_hls, sizeof(filename_hls), "master.m3u8"); + + snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", filename_hls); + + set_http_options(&opts, c); + ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE, NULL, &opts); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); + return ret; + } + av_dict_free(&opts); + + ff_hls_write_playlist_version(out, 6); + + for (i = 0; i < s->nb_streams; i++) { + char playlist_file[64]; + AVStream *st = s->streams[i]; + get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); + ff_hls_write_stream_info(st, out, st->codecpar->bit_rate, playlist_file); + } + avio_close(out); + if (use_rename) + avpriv_io_move(temp_filename, filename_hls); + c->master_playlist_created = 1; + } + return 0; } @@ -1206,6 +1300,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 }, { "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}, + { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { NULL }, };