From patchwork Thu Nov 26 14:21:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vignesh Ravichandran X-Patchwork-Id: 24046 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id A231544867C for ; Thu, 26 Nov 2020 16:52:46 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7844168B861; Thu, 26 Nov 2020 16:52:46 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f195.google.com (mail-pl1-f195.google.com [209.85.214.195]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B652B68B783 for ; Thu, 26 Nov 2020 16:52:39 +0200 (EET) Received: by mail-pl1-f195.google.com with SMTP id bj5so1252445plb.4 for ; Thu, 26 Nov 2020 06:52:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=xMpoa69sJC2fj2m34EJP1gP9PbTk1s+8OGXpUcgRYoE=; b=uZJztfZ1CqKm/JdEcoo+euqhp9wTm1FVFkGxing3ECXUhRWNzLyp1BmXpHNrkjQDgE zHJTQ7erd8QyUMuWRIxzZQ2Ezvl0nnJC2BAp8OvRw5mNiv7GNPJ1fLTtbosEBnw8pgyO eCLl3YWshNQrdO5rq79WKgcwvfKG58dQpE3se3LH//AhcfEqYY0mHG6ymZWcz9pfgzGB yk0NEi6V/ZGQmXuPLoZsHymCx39JdkFBrbWiR9xEAZMobicCVDrZgoosqTCh8QzcfRs0 E66o4a9++GuJfPIA07hV1vY8bcwakIpbdb0e6N3v+acvEvxtHKQxuJdp4ke1Qgbd6Xzl /n4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=xMpoa69sJC2fj2m34EJP1gP9PbTk1s+8OGXpUcgRYoE=; b=ONw8/iqrSiKQoyTnZPAd4q1fjKTozhiApRnOv9aeIajQSPfZB66oMjIPgnpd2ghHf+ RBHK+rmQAh1kK/Crl+tNm6803Q9z2H/aXyq0gKOk0izaHBYmojiL/swkIbTb2j3/Cmto hAVAzz892rT1ObIv4Yp2fwXmmLprkKzR08TReovyNPd89vkIEjZD8sLLsVqoW9XK7qFL rLWt+rMiqtCsPYMnseKmth2mcVtx7HnaD+3HOW26i9eOxcdf55xKnE/KkIIRBQ7m7SDq GCfXwJi+xsio5H9ejc6IUcHCZF45CWvcuTf/M/zdYBH7n5diIyTgSszAZaNzr5rJbBvU iK4g== X-Gm-Message-State: AOAM531PajYeujx+SiXafNdKhM0b4bqgFFCxsynxU1ZA6A/+Bw2F5URn qWpiYeIZaliBNhbSQCs2pVnaMNVftuIouw== X-Google-Smtp-Source: ABdhPJzsKTPR8cZ9kx+23durCD8xwu6zQ8Ax0tsRFrx4Qfb8V3qGN4St7Z/Wp8dRYKjxQPS+DlQC/g== X-Received: by 2002:a17:902:ec01:b029:da:13f5:1aaa with SMTP id l1-20020a170902ec01b02900da13f51aaamr2914721pld.0.1606400497960; Thu, 26 Nov 2020 06:21:37 -0800 (PST) Received: from localhost.localdomain ([27.57.42.71]) by smtp.gmail.com with ESMTPSA id c134sm2368847pfc.200.2020.11.26.06.21.35 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 26 Nov 2020 06:21:37 -0800 (PST) From: Vignesh Ravichandran To: ffmpeg-devel@ffmpeg.org Date: Thu, 26 Nov 2020 19:51:26 +0530 Message-Id: <20201126142126.64043-1-vignesh.ravichandran02@gmail.com> X-Mailer: git-send-email 2.24.2 (Apple Git-127) MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] avformat/hls: Fixes overwriting existing #EXT-X-PROGRAM-DATE-TIME value in HLS playlist Bug ID: 8989 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 Cc: Vignesh Ravichandran Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This is is due to the following behavior in the current code: 1. The initial_prog_date_time gets set to the current local time 2. The existing playlist (.m3u8) file gets parsed and the segments present are added to the variant stream 3. The new segment is created and added 4. The existing segments and the new segment are written to the playlist file. The initial_prog_date_time from point 1 is used for calculating "#EXT-X-PROGRAM-DATE-TIME" for the segments, which results in incorrect "#EXT-X-PROGRAM-DATE-TIME" values for existing segments The following approach fixes this bug: 1. Add a new variable "discont_program_date_time" of type double to HLSSegment struct 2. Store the "EXT-X-PROGRAM-DATE-TIME" value from the existing segments in this variable 3. When writing to playlist file if "discont_program_date_time" is set, then use that value for "EXT-X-PROGRAM-DATE-TIME" else use the value present in vs->initial_prog_date_time Signed-off-by: Vignesh Ravichandran --- libavformat/hlsenc.c | 83 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index cbfd8f7c0d..9346b2cfea 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -25,6 +25,10 @@ #include #if HAVE_UNISTD_H #include +#include +#include +#include +#include #endif #if CONFIG_GCRYPT @@ -88,6 +92,7 @@ typedef struct HLSSegment { char iv_string[KEYSIZE*2 + 1]; struct HLSSegment *next; + double discont_program_date_time; } HLSSegment; typedef enum HLSFlags { @@ -1124,6 +1129,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, en->keyframe_size = vs->video_keyframe_size; en->next = NULL; en->discont = 0; + en->discont_program_date_time = 0; if (vs->discontinuity) { en->discont = 1; @@ -1148,7 +1154,8 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, if (hls->max_nb_segments && vs->nb_entries >= hls->max_nb_segments) { en = vs->segments; - vs->initial_prog_date_time += en->duration; + if (!en->next->discont_program_date_time && !en->discont_program_date_time) + vs->initial_prog_date_time += en->duration; vs->segments = en->next; if (en && hls->flags & HLS_DELETE_SEGMENTS && #if FF_API_HLS_WRAP @@ -1173,6 +1180,44 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, return 0; } +static int check_program_date_time(const char *prog_date_time) { + char s[strlen(prog_date_time)], *sptr0, *sptr1, *sptr2, *sptr3; + char *err = NULL; + char *arr[6] = {NULL}; + int i = 0; + av_strlcpy(s, prog_date_time, strlen(prog_date_time)); + char *p = strtok_r(strtok_r(s, "+", &sptr0), "T", &sptr1); + while (p) { + char *q = strtok_r(p, "-", &sptr2); + while (q) { + char *r = strtok_r(q, ":", &sptr3); + while (r) { + arr[i] = r; + i++; + r = strtok_r(NULL, ":", &sptr3); + } + q = strtok_r(NULL, "-", &sptr2); + } + p = strtok_r(NULL, "T", &sptr1); + } + + for (i=0; i < 5; i++) { + errno=0; + long number = strtol(arr[i], &err, 10); + if((arr[i] == err) || (errno == ERANGE && number == LONG_MIN) || (errno == ERANGE && number == LONG_MAX) + || (errno == EINVAL) || (errno != 0 && number == 0) || (errno == 0 && arr[i] && *err != 0)) + return AVERROR_INVALIDDATA; + } + + errno=0; + double number = strtod(arr[i], &err); + if((arr[i] == err) || (errno == ERANGE && number == HUGE_VALF) || (errno == ERANGE && number == HUGE_VALL) + || (errno == EINVAL) || (errno != 0 && number == 0) || (errno == 0 && arr[i] && *err != 0)) + return AVERROR_INVALIDDATA; + + return 0; +} + static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs) { HLSContext *hls = s->priv_data; @@ -1182,6 +1227,8 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs char line[MAX_URL_SIZE]; const char *ptr; const char *end; + int is_discont_detected = 0; + double discont_program_date_time = 0; if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ, &s->interrupt_callback, NULL, @@ -1211,6 +1258,7 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs } else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) { is_segment = 1; vs->discontinuity = 1; + is_discont_detected = 1; } else if (av_strstart(line, "#EXTINF:", &ptr)) { is_segment = 1; vs->duration = atof(ptr); @@ -1236,7 +1284,31 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs av_strlcpy(vs->iv_string, ptr, sizeof(vs->iv_string)); } } + } else if (av_strstart(line, "#EXT-X-PROGRAM-DATE-TIME:", &ptr)) { + struct tm program_date_time; + int y,M,d,h,m,s; + double ms; + + if (check_program_date_time(ptr) != 0) { + av_log(hls, AV_LOG_VERBOSE, + "Found invalid program date time %s when parsing playlist. " + "Current and subsequent existing segments will be ignored", ptr); + ret = AVERROR_INVALIDDATA; + goto fail; + } + sscanf(ptr, "%d-%d-%dT%d:%d:%d.%lf", &y, &M, &d, &h, &m, &s, &ms); + program_date_time.tm_year = y - 1900; + program_date_time.tm_mon = M - 1; + program_date_time.tm_mday = d; + program_date_time.tm_hour = h; + program_date_time.tm_min = m; + program_date_time.tm_sec = s; + program_date_time.tm_isdst = -1; + + discont_program_date_time = mktime(&program_date_time); + discont_program_date_time += (double)(ms / 1000); + is_discont_detected = 0; } else if (av_strstart(line, "#", NULL)) { continue; } else if (line[0]) { @@ -1250,8 +1322,9 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs is_segment = 0; new_start_pos = avio_tell(vs->avf->pb); vs->size = new_start_pos - vs->start_pos; - vs->initial_prog_date_time -= vs->duration; // this is a previously existing segment ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size); + vs->last_segment->discont_program_date_time = discont_program_date_time; + discont_program_date_time += vs->duration; if (ret < 0) goto fail; vs->start_pos = new_start_pos; @@ -1572,7 +1645,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ret = ff_hls_write_file_entry(byterange_mode ? hls->m3u8_out : vs->out, en->discont, byterange_mode, en->duration, hls->flags & HLS_ROUND_DURATIONS, en->size, en->pos, hls->baseurl, - en->filename, prog_date_time_p, en->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY); + en->filename, + en->discont_program_date_time ? &en->discont_program_date_time : prog_date_time_p, + en->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY); + if (en->discont_program_date_time) + en->discont_program_date_time -= en->duration; if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); }