2371 lines · 79.8 KB · C
RawDownload
/* * Dynamic Adaptive Streaming over HTTP demux * Copyright (c) 2017 samsamsam@o2.pl based on HLS demux * Copyright (c) 2017 Steven Liu * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */#include <libxml/parser.h>#include <time.h>#include "libavutil/bprint.h"#include "libavutil/mem.h"#include "libavutil/opt.h"#include "libavutil/time.h"#include "libavutil/parseutils.h"#include "internal.h"#include "avio_internal.h"#include "dash.h"#include "demux.h"#include "url.h" #define INITIAL_BUFFER_SIZE 32768 struct fragment {    int64_t url_offset;    int64_t size;    char *url;}; /* * reference to : ISO_IEC_23009-1-DASH-2012 * Section: 5.3.9.6.2 * Table: Table 17 — Semantics of SegmentTimeline element * */struct timeline {    /* starttime: Element or Attribute Name     * specifies the MPD start time, in @timescale units,     * the first Segment in the series starts relative to the beginning of the Period.     * The value of this attribute must be equal to or greater than the sum of the previous S     * element earliest presentation time and the sum of the contiguous Segment durations.     * If the value of the attribute is greater than what is expressed by the previous S element,     * it expresses discontinuities in the timeline.     * If not present then the value shall be assumed to be zero for the first S element     * and for the subsequent S elements, the value shall be assumed to be the sum of     * the previous S element's earliest presentation time and contiguous duration     * (i.e. previous S@starttime + @duration * (@repeat + 1)).     * */    int64_t starttime;    /* repeat: Element or Attribute Name     * specifies the repeat count of the number of following contiguous Segments with     * the same duration expressed by the value of @duration. This value is zero-based     * (e.g. a value of three means four Segments in the contiguous series).     * */    int64_t repeat;    /* duration: Element or Attribute Name     * specifies the Segment duration, in units of the value of the @timescale.     * */    int64_t duration;}; /* * Each playlist has its own demuxer. If it is currently active, * it has an opened AVIOContext too, and potentially an AVPacket * containing the next packet from this stream. */struct representation {    char *url_template;    FFIOContext pb;    AVIOContext *input;    AVFormatContext *parent;    AVFormatContext *ctx;    int stream_index;     char *id;    char *lang;    int bandwidth;    AVRational framerate;    AVStream *assoc_stream; /* demuxer stream associated with this representation */     int n_fragments;    struct fragment **fragments; /* VOD list of fragment for profile */     int n_timelines;    struct timeline **timelines;     int64_t first_seq_no;    int64_t last_seq_no;    int64_t start_number; /* used in case when we have dynamic list of segment to know which segments are new one*/     int64_t fragment_duration;    int64_t fragment_timescale;     int64_t presentation_timeoffset;     int64_t cur_seq_no;    int64_t cur_seg_offset;    int64_t cur_seg_size;    struct fragment *cur_seg;     /* Currently active Media Initialization Section */    struct fragment *init_section;    uint8_t *init_sec_buf;    uint32_t init_sec_buf_size;    uint32_t init_sec_data_len;    uint32_t init_sec_buf_read_offset;    int64_t cur_timestamp;    int is_restart_needed;}; typedef struct DASHContext {    const AVClass *class;    char *base_url;     int n_videos;    struct representation **videos;    int n_audios;    struct representation **audios;    int n_subtitles;    struct representation **subtitles;     /* MediaPresentationDescription Attribute */    uint64_t media_presentation_duration;    uint64_t suggested_presentation_delay;    uint64_t availability_start_time;    uint64_t availability_end_time;    uint64_t publish_time;    uint64_t minimum_update_period;    uint64_t time_shift_buffer_depth;    uint64_t min_buffer_time;     /* Period Attribute */    uint64_t period_duration;    uint64_t period_start;     /* AdaptationSet Attribute */    char *adaptionset_lang;     int is_live;    AVIOInterruptCB *interrupt_callback;    char *allowed_extensions;    AVDictionary *avio_opts;    int max_url_size;    char *cenc_decryption_key;     /* Flags for init section*/    int is_init_section_common_video;    int is_init_section_common_audio;    int is_init_section_common_subtitle; } DASHContext; static int ishttp(char *url){    const char *proto_name = avio_find_protocol_name(url);    return proto_name && av_strstart(proto_name, "http", NULL);} static int aligned(int val){    return ((val + 0x3F) >> 6) << 6;} static uint64_t get_current_time_in_sec(void){    return  av_gettime() / 1000000;} static uint64_t get_utc_date_time_insec(AVFormatContext *s, const char *datetime){    struct tm timeinfo;    int year = 0;    int month = 0;    int day = 0;    int hour = 0;    int minute = 0;    int ret = 0;    float second = 0.0;     /* ISO-8601 date parser */    if (!datetime)        return 0;     ret = sscanf(datetime, "%d-%d-%dT%d:%d:%fZ", &year, &month, &day, &hour, &minute, &second);    /* year, month, day, hour, minute, second  6 arguments */    if (ret != 6) {        av_log(s, AV_LOG_WARNING, "get_utc_date_time_insec get a wrong time format\n");    }    timeinfo.tm_year = year - 1900;    timeinfo.tm_mon  = month - 1;    timeinfo.tm_mday = day;    timeinfo.tm_hour = hour;    timeinfo.tm_min  = minute;    timeinfo.tm_sec  = (int)second;     return av_timegm(&timeinfo);} static uint32_t get_duration_insec(AVFormatContext *s, const char *duration){    /* ISO-8601 duration parser */    uint32_t days = 0;    uint32_t hours = 0;    uint32_t mins = 0;    uint32_t secs = 0;    int size = 0;    float value = 0;    char type = '\0';    const char *ptr = duration;     while (*ptr) {        if (*ptr == 'P' || *ptr == 'T') {            ptr++;            continue;        }         if (sscanf(ptr, "%f%c%n", &value, &type, &size) != 2) {            av_log(s, AV_LOG_WARNING, "get_duration_insec get a wrong time format\n");            return 0; /* parser error */        }        switch (type) {        case 'D':            days = (uint32_t)value;            break;        case 'H':            hours = (uint32_t)value;            break;        case 'M':            mins = (uint32_t)value;            break;        case 'S':            secs = (uint32_t)value;            break;        default:            // handle invalid type            break;        }        ptr += size;    }    return  ((days * 24 + hours) * 60 + mins) * 60 + secs;} static int64_t get_segment_start_time_based_on_timeline(struct representation *pls, int64_t cur_seq_no){    int64_t start_time = 0;    int64_t i = 0;    int64_t j = 0;    int64_t num = 0;     if (pls->n_timelines) {        for (i = 0; i < pls->n_timelines; i++) {            if (pls->timelines[i]->starttime > 0) {                start_time = pls->timelines[i]->starttime;            }            if (num == cur_seq_no)                goto finish;             start_time += pls->timelines[i]->duration;             if (pls->timelines[i]->repeat == -1) {                start_time = pls->timelines[i]->duration * cur_seq_no;                goto finish;            }             for (j = 0; j < pls->timelines[i]->repeat; j++) {                num++;                if (num == cur_seq_no)                    goto finish;                start_time += pls->timelines[i]->duration;            }            num++;        }    }finish:    return start_time;} static int64_t calc_next_seg_no_from_timelines(struct representation *pls, int64_t cur_time){    int64_t i = 0;    int64_t j = 0;    int64_t num = 0;    int64_t start_time = 0;     for (i = 0; i < pls->n_timelines; i++) {        if (pls->timelines[i]->starttime > 0) {            start_time = pls->timelines[i]->starttime;        }        if (start_time > cur_time)            goto finish;         start_time += pls->timelines[i]->duration;        for (j = 0; j < pls->timelines[i]->repeat; j++) {            num++;            if (start_time > cur_time)                goto finish;            start_time += pls->timelines[i]->duration;        }        num++;    }     return -1; finish:    return num;} static void free_fragment(struct fragment **seg){    if (!(*seg)) {        return;    }    av_freep(&(*seg)->url);    av_freep(seg);} static void free_fragment_list(struct representation *pls){    int i;     for (i = 0; i < pls->n_fragments; i++) {        free_fragment(&pls->fragments[i]);    }    av_freep(&pls->fragments);    pls->n_fragments = 0;} static void free_timelines_list(struct representation *pls){    int i;     for (i = 0; i < pls->n_timelines; i++) {        av_freep(&pls->timelines[i]);    }    av_freep(&pls->timelines);    pls->n_timelines = 0;} static void free_representation(struct representation *pls){    free_fragment_list(pls);    free_timelines_list(pls);    free_fragment(&pls->cur_seg);    free_fragment(&pls->init_section);    av_freep(&pls->init_sec_buf);    av_freep(&pls->pb.pub.buffer);    ff_format_io_close(pls->parent, &pls->input);    if (pls->ctx) {        pls->ctx->pb = NULL;        avformat_close_input(&pls->ctx);    }     av_freep(&pls->url_template);    av_freep(&pls->lang);    av_freep(&pls->id);    av_freep(&pls);} static void free_video_list(DASHContext *c){    int i;    for (i = 0; i < c->n_videos; i++) {        struct representation *pls = c->videos[i];        free_representation(pls);    }    av_freep(&c->videos);    c->n_videos = 0;} static void free_audio_list(DASHContext *c){    int i;    for (i = 0; i < c->n_audios; i++) {        struct representation *pls = c->audios[i];        free_representation(pls);    }    av_freep(&c->audios);    c->n_audios = 0;} static void free_subtitle_list(DASHContext *c){    int i;    for (i = 0; i < c->n_subtitles; i++) {        struct representation *pls = c->subtitles[i];        free_representation(pls);    }    av_freep(&c->subtitles);    c->n_subtitles = 0;} static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,                    AVDictionary **opts, AVDictionary *opts2, int *is_http){    DASHContext *c = s->priv_data;    AVDictionary *tmp = NULL;    const char *proto_name = NULL;    int proto_name_len;    int ret;     if (av_strstart(url, "crypto", NULL)) {        if (url[6] == '+' || url[6] == ':')            proto_name = avio_find_protocol_name(url + 7);    }     if (!proto_name)        proto_name = avio_find_protocol_name(url);     if (!proto_name)        return AVERROR_INVALIDDATA;     proto_name_len = strlen(proto_name);    // only http(s) & file are allowed    if (av_strstart(proto_name, "file", NULL)) {        if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) {            av_log(s, AV_LOG_ERROR,                   "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n"                   "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n",                   url);            return AVERROR_INVALIDDATA;        }    } else if (av_strstart(proto_name, "http", NULL)) {        ;    } else        return AVERROR_INVALIDDATA;     if (!strncmp(proto_name, url, proto_name_len) && url[proto_name_len] == ':')        ;    else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, proto_name_len) && url[7 + proto_name_len] == ':')        ;    else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))        return AVERROR_INVALIDDATA;     av_freep(pb);    av_dict_copy(&tmp, *opts, 0);    av_dict_copy(&tmp, opts2, 0);    ret = ffio_open_whitelist(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp, s->protocol_whitelist, s->protocol_blacklist);    if (ret >= 0) {        // update cookies on http response with setcookies.        char *new_cookies = NULL;         if (!(s->flags & AVFMT_FLAG_CUSTOM_IO))            av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies);         if (new_cookies) {            av_dict_set(opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL);        }     }     av_dict_free(&tmp);     if (is_http)        *is_http = av_strstart(proto_name, "http", NULL);     return ret;} static char *get_content_url(xmlNodePtr *baseurl_nodes,                             int n_baseurl_nodes,                             int max_url_size,                             char *rep_id_val,                             char *rep_bandwidth_val,                             char *val){    int i;    char *text;    char *url = NULL;    char *tmp_str = av_mallocz(max_url_size);     if (!tmp_str)        return NULL;     for (i = 0; i < n_baseurl_nodes; ++i) {        if (baseurl_nodes[i] &&            baseurl_nodes[i]->children &&            baseurl_nodes[i]->children->type == XML_TEXT_NODE) {            text = xmlNodeGetContent(baseurl_nodes[i]->children);            if (text) {                memset(tmp_str, 0, max_url_size);                ff_make_absolute_url(tmp_str, max_url_size, "", text);                xmlFree(text);            }        }    }     if (val)        ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val);

Lines 1–500 of 2371.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t