Compare commits
6 Commits
main
...
dev/smooth
Author | SHA1 | Date |
---|---|---|
Ninja Jiraiya | 7ad49b423e | |
Ninja Jiraiya | 5511102b0c | |
Ninja Jiraiya | 3ddf77aacd | |
Ninja Jiraiya | 144e49d077 | |
Ninja Jiraiya | cf63c0ec19 | |
Ninja Jiraiya | 04f4d85e2a |
|
@ -1145,14 +1145,14 @@ static void print_program_info(int flags, int level)
|
|||
{
|
||||
const char *indent = flags & INDENT? " " : "";
|
||||
|
||||
av_log(NULL, level, "%s version - Customized for MPD-PHP" FFMPEG_VERSION, program_name);
|
||||
av_log(NULL, level, "%s customized for MPD-PHP version " FFMPEG_VERSION, program_name);
|
||||
if (flags & SHOW_COPYRIGHT)
|
||||
av_log(NULL, level, " Copyright (c) %d-%d the FFmpeg developers",
|
||||
program_birth_year, CONFIG_THIS_YEAR);
|
||||
av_log(NULL, level, "\n");
|
||||
av_log(NULL, level, "%sbuilt with %s\n", indent, CC_IDENT);
|
||||
// av_log(NULL, level, "%sbuilt with %s\n", indent, CC_IDENT);
|
||||
|
||||
av_log(NULL, level, "%sconfiguration: " FFMPEG_CONFIGURATION "\n", indent);
|
||||
// av_log(NULL, level, "%sconfiguration: " FFMPEG_CONFIGURATION "\n", indent);
|
||||
}
|
||||
|
||||
static void print_buildconf(int flags, int level)
|
||||
|
@ -1185,7 +1185,6 @@ void show_banner(int argc, char **argv, const OptionDef *options)
|
|||
{
|
||||
int idx = locate_option(argc, argv, options, "version");
|
||||
if (hide_banner || idx)
|
||||
print_program_info (INDENT|SHOW_COPYRIGHT, AV_LOG_INFO);
|
||||
return;
|
||||
|
||||
print_program_info (INDENT|SHOW_COPYRIGHT, AV_LOG_INFO);
|
||||
|
|
|
@ -148,6 +148,7 @@ OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o
|
|||
OBJS-$(CONFIG_DATA_MUXER) += rawenc.o
|
||||
OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o
|
||||
OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o
|
||||
OBJS-$(CONFIG_SMOOTHSTREAMING_DEMUXER) += smoothstreamingdec.o
|
||||
OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o
|
||||
OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o
|
||||
OBJS-$(CONFIG_DCSTR_DEMUXER) += dcstr.o
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "internal.h"
|
||||
#include "avio_internal.h"
|
||||
#include "dash.h"
|
||||
#include "libavformat/isom.h"
|
||||
|
||||
#define INITIAL_BUFFER_SIZE 32768
|
||||
#define MAX_MANIFEST_SIZE 50 * 1024
|
||||
|
@ -1878,6 +1879,7 @@ static int reopen_demux_for_component(AVFormatContext *s, struct representation
|
|||
ff_const59 AVInputFormat *in_fmt = NULL;
|
||||
AVDictionary *in_fmt_opts = NULL;
|
||||
uint8_t *avio_ctx_buffer = NULL;
|
||||
|
||||
int ret = 0, i;
|
||||
|
||||
if (pls->ctx) {
|
||||
|
@ -1926,7 +1928,28 @@ static int reopen_demux_for_component(AVFormatContext *s, struct representation
|
|||
pls->ctx->io_open = nested_io_open;
|
||||
|
||||
if (c->cenc_decryption_key)
|
||||
// if cenc_decryption_key not contains any : or , it is a hex string
|
||||
if (strchr(c->cenc_decryption_key, ':') == NULL && strchr(c->cenc_decryption_key, ',') == NULL) {
|
||||
av_dict_set(&in_fmt_opts, "decryption_key", c->cenc_decryption_key, 0);
|
||||
} else {
|
||||
DecryptionKey *decryption_key_map = av_mallocz(sizeof(DecryptionKey *) * 2);
|
||||
// if cenc_decryption_key contains : or , split it based on ,
|
||||
char *key_id = av_strtok(c->cenc_decryption_key, ",", &c->cenc_decryption_key);
|
||||
// if cenc_decryption_key contains : split it based on :
|
||||
//create DecryptionKey array
|
||||
*key_count = 0;
|
||||
while (key_id != NULL && *key_count < 2) {
|
||||
char* colon = strchr(key_id, ':');
|
||||
if (colon != NULL) {
|
||||
*colon = '\0'; // Replace ':' with null-terminator
|
||||
strncpy(decryption_key_map[*key_count].key_id, key_id, MAX_STRING_LENGTH - 1);
|
||||
strncpy(decryption_key_map[*key_count].second, colon + 1, MAX_STRING_LENGTH - 1);
|
||||
(*key_count)++;
|
||||
}
|
||||
key_id = av_strtok(NULL, ",");
|
||||
}
|
||||
av_dict_set(&in_fmt_opts, "decryption_key_map", decryption_key_map, 0);
|
||||
}
|
||||
|
||||
// provide additional information from mpd if available
|
||||
ret = avformat_open_input(&pls->ctx, "", in_fmt, &in_fmt_opts); //pls->init_section->url
|
||||
|
|
|
@ -66,7 +66,9 @@ enum KeyType {
|
|||
};
|
||||
|
||||
struct segment {
|
||||
int64_t previous_duration;
|
||||
int64_t duration;
|
||||
int64_t start_time;
|
||||
int64_t url_offset;
|
||||
int64_t size;
|
||||
char *url;
|
||||
|
@ -716,7 +718,7 @@ static int parse_playlist(HLSContext *c, const char *url,
|
|||
struct playlist *pls, AVIOContext *in)
|
||||
{
|
||||
int ret = 0, is_segment = 0, is_variant = 0;
|
||||
int64_t duration = 0;
|
||||
int64_t duration = 0, previous_duration1 = 0, previous_duration = 0, total_duration = 0;
|
||||
enum KeyType key_type = KEY_NONE;
|
||||
uint8_t iv[16] = "";
|
||||
int has_iv = 0;
|
||||
|
@ -874,6 +876,8 @@ static int parse_playlist(HLSContext *c, const char *url,
|
|||
} else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
|
||||
if (pls)
|
||||
pls->finished = 1;
|
||||
} else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) {
|
||||
previous_duration = previous_duration1;
|
||||
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
|
||||
is_segment = 1;
|
||||
duration = atof(ptr) * AV_TIME_BASE;
|
||||
|
@ -903,6 +907,14 @@ static int parse_playlist(HLSContext *c, const char *url,
|
|||
ret = AVERROR(ENOMEM);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
previous_duration1 += duration;
|
||||
seg->previous_duration = previous_duration;
|
||||
seg->start_time = total_duration;
|
||||
total_duration += duration;
|
||||
seg->duration = duration;
|
||||
seg->key_type = key_type;
|
||||
|
||||
if (has_iv) {
|
||||
memcpy(seg->iv, iv, sizeof(iv));
|
||||
} else {
|
||||
|
@ -949,8 +961,9 @@ static int parse_playlist(HLSContext *c, const char *url,
|
|||
" set to default value to 1ms.\n", seg->url);
|
||||
duration = 0.001 * AV_TIME_BASE;
|
||||
}
|
||||
seg->duration = duration;
|
||||
seg->key_type = key_type;
|
||||
// seg->duration = duration;
|
||||
// seg->key_type = key_type;
|
||||
|
||||
dynarray_add(&pls->segments, &pls->n_segments, seg);
|
||||
is_segment = 0;
|
||||
|
||||
|
@ -2257,6 +2270,29 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||
ist->time_base,
|
||||
AV_TIME_BASE_Q);
|
||||
|
||||
if (c->playlists[minplaylist]->finished) {
|
||||
struct playlist *pls = c->playlists[minplaylist];
|
||||
int seq_no = pls->cur_seq_no - pls->start_seq_no;
|
||||
if (seq_no < pls->n_segments && s->streams[pkt->stream_index]) {
|
||||
struct segment *seg = pls->segments[seq_no];
|
||||
int64_t pred = av_rescale_q(seg->previous_duration,
|
||||
AV_TIME_BASE_Q,
|
||||
s->streams[pkt->stream_index]->time_base);
|
||||
int64_t max_ts = av_rescale_q(seg->start_time + seg->duration,
|
||||
AV_TIME_BASE_Q,
|
||||
s->streams[pkt->stream_index]->time_base);
|
||||
/* EXTINF duration is not precise enough */
|
||||
max_ts += 2 * AV_TIME_BASE;
|
||||
if (s->start_time > 0) {
|
||||
max_ts += av_rescale_q(s->start_time,
|
||||
AV_TIME_BASE_Q,
|
||||
s->streams[pkt->stream_index]->time_base);
|
||||
}
|
||||
if (pkt->dts != AV_NOPTS_VALUE && pkt->dts + pred < max_ts) pkt->dts += pred;
|
||||
if (pkt->pts != AV_NOPTS_VALUE && pkt->pts + pred < max_ts) pkt->pts += pred;
|
||||
}
|
||||
}
|
||||
|
||||
/* There may be more situations where this would be useful, but this at least
|
||||
* handles newly probed codecs properly (i.e. request_probe by mpegts). */
|
||||
if (ist->codecpar->codec_id != st->codecpar->codec_id) {
|
||||
|
|
|
@ -155,6 +155,11 @@ typedef struct MOVIndexRange {
|
|||
int64_t end;
|
||||
} MOVIndexRange;
|
||||
|
||||
typedef struct DecryptionKey {
|
||||
uint8_t key_id;
|
||||
uint8_t decryption_key;
|
||||
} DecryptionKey;
|
||||
|
||||
typedef struct MOVStreamContext {
|
||||
AVIOContext *pb;
|
||||
int pb_is_copied;
|
||||
|
@ -291,6 +296,8 @@ typedef struct MOVContext {
|
|||
int decryption_key_len;
|
||||
int enable_drefs;
|
||||
int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
|
||||
DecryptionKey *decryption_key_map; // array of decryption keys for each track
|
||||
int decryption_key_map_size;
|
||||
} MOVContext;
|
||||
|
||||
int ff_mp4_read_descr_len(AVIOContext *pb);
|
||||
|
|
|
@ -6700,6 +6700,62 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cenc_decrypt_with_key(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, uint8_t *decryption_key, int size)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if (sample->scheme != MKBETAG('c','e','n','c') || sample->crypt_byte_block != 0 || sample->skip_byte_block != 0) {
|
||||
av_log(c->fc, AV_LOG_ERROR, "Only the 'cenc' encryption scheme is supported\n");
|
||||
return AVERROR_PATCHWELCOME;
|
||||
}
|
||||
|
||||
if (!sc->cenc.aes_ctr) {
|
||||
/* initialize the cipher */
|
||||
sc->cenc.aes_ctr = av_aes_ctr_alloc();
|
||||
if (!sc->cenc.aes_ctr) {
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
ret = av_aes_ctr_init(sc->cenc.aes_ctr, decryption_key);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
av_aes_ctr_set_full_iv(sc->cenc.aes_ctr, sample->iv);
|
||||
|
||||
if (!sample->subsample_count)
|
||||
{
|
||||
/* decrypt the whole packet */
|
||||
av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < sample->subsample_count; i++)
|
||||
{
|
||||
if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
|
||||
av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
/* skip the clear bytes */
|
||||
input += sample->subsamples[i].bytes_of_clear_data;
|
||||
size -= sample->subsamples[i].bytes_of_clear_data;
|
||||
|
||||
/* decrypt the encrypted bytes */
|
||||
av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, sample->subsamples[i].bytes_of_protected_data);
|
||||
input += sample->subsamples[i].bytes_of_protected_data;
|
||||
size -= sample->subsamples[i].bytes_of_protected_data;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
|
||||
{
|
||||
MOVFragmentStreamInfo *frag_stream_info;
|
||||
|
@ -6749,6 +6805,40 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
|
|||
|
||||
if (mov->decryption_key) {
|
||||
return cenc_decrypt(mov, sc, encrypted_sample, pkt->data, pkt->size);
|
||||
} else if (mov->decryption_key_map) {
|
||||
uint8_t *key_id = encrypted_sample->key_id;
|
||||
uint8_t *dec_key = NULL;
|
||||
// check size of decryption_key_map size
|
||||
if (mov->decryption_key_map_size == 1) {
|
||||
dec_key = mov->decryption_key_map[0].decryption_key;
|
||||
} else {
|
||||
// find the decryption key for the given key ID
|
||||
for (int i = 0; i < mov->decryption_key_map_size; i++) {
|
||||
// if the key id is equals with the key id in the map
|
||||
if (key_id == mov->decryption_key_map[i].key_id) {
|
||||
dec_key = mov->decryption_key_map[i].decryption_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dec_key) {
|
||||
av_log(mov->fc, AV_LOG_ERROR, "No decryption key found for the given key ID\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
if (!encrypted_sample->subsample_count) {
|
||||
// Decrypt the whole packet.
|
||||
ret = av_aes_ctr_init(&sc->cenc.aes_ctr, dec_key);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
av_aes_ctr_crypt(&sc->cenc.aes_ctr, pkt->data, pkt->data, pkt->size);
|
||||
} else {
|
||||
// Decrypt the subsamples.
|
||||
ret = cenc_decrypt_with_key(mov, sc, encrypted_sample, pkt->data, dec_key, pkt->size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t size;
|
||||
uint8_t *side_data = av_encryption_info_add_side_data(encrypted_sample, &size);
|
||||
|
@ -8219,6 +8309,7 @@ static const AVOption mov_options[] = {
|
|||
AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
|
||||
.flags = AV_OPT_FLAG_DECODING_PARAM },
|
||||
{ "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
|
||||
{ "decryption_key_map", "The media decryption kid:key (hex) support up to 2", OFFSET(decryption_key_map), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
|
||||
{ "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
|
||||
{.i64 = 0}, 0, 1, FLAGS },
|
||||
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* HTTP Smooth Streaming based on DASH demuxer
|
||||
* 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 "libavutil/intreadwrite.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "libavutil/time.h"
|
||||
#include "libavutil/parseutils.h"
|
||||
#include "internal.h"
|
||||
#include "avio_internal.h"
|
||||
#include "dash.h"
|
||||
|
||||
#define INITIAL_BUFFER_SIZE 32768
|
||||
#define MAX_MANIFEST_SIZE 50 * 1024
|
||||
#define DEFAULT_MANIFEST_SIZE 8 * 1024
|
||||
|
||||
typedef struct SmoothStreamingContext {
|
||||
const AVClass *class;
|
||||
char *base_url;
|
||||
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;
|
||||
|
||||
} SmoothStreamingContext;
|
||||
|
||||
static int ishttp(char *url)
|
||||
{
|
||||
const char *proto_name = avio_find_protocol_name(url);
|
||||
return av_strstart(proto_name, "http", NULL);
|
||||
}
|
||||
|
||||
static int aligned(int val)
|
||||
{
|
||||
return ((val + 0x3F) >> 6) << 6;
|
||||
}
|
||||
|
||||
static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
|
||||
AVDictionary **opts, AVDictionary *opts2, int *is_http)
|
||||
{
|
||||
SmoothStreamingContext *c = s->priv_data;
|
||||
AVDictionary *tmp = NULL;
|
||||
const char *proto_name = NULL;
|
||||
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;
|
||||
|
||||
// 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, strlen(proto_name)) && url[strlen(proto_name)] == ':')
|
||||
;
|
||||
else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':')
|
||||
;
|
||||
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 = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp);
|
||||
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);
|
||||
|
||||
if (rep_id_val) {
|
||||
url = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
|
||||
if (!url) {
|
||||
goto end;
|
||||
}
|
||||
av_strlcpy(tmp_str, url, max_url_size);
|
||||
}
|
||||
if (rep_bandwidth_val && tmp_str[0] != '\0') {
|
||||
// free any previously assigned url before reassigning
|
||||
av_free(url);
|
||||
url = av_strireplace(tmp_str, "$Bandwidth$", (const char*)rep_bandwidth_val);
|
||||
if (!url) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
end:
|
||||
av_free(tmp_str);
|
||||
return url;
|
||||
}
|
||||
|
||||
static char *get_val_from_nodes_tab(xmlNodePtr *nodes, const int n_nodes, const char *attrname)
|
||||
{
|
||||
int i;
|
||||
char *val;
|
||||
|
||||
for (i = 0; i < n_nodes; ++i) {
|
||||
if (nodes[i]) {
|
||||
val = xmlGetProp(nodes[i], attrname);
|
||||
if (val)
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xmlNodePtr find_child_node_by_name(xmlNodePtr rootnode, const char *nodename)
|
||||
{
|
||||
xmlNodePtr node = rootnode;
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = xmlFirstElementChild(node);
|
||||
while (node) {
|
||||
if (!av_strcasecmp(node->name, nodename)) {
|
||||
return node;
|
||||
}
|
||||
node = xmlNextElementSibling(node);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum AVMediaType get_content_type(xmlNodePtr node)
|
||||
{
|
||||
enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
|
||||
int i = 0;
|
||||
const char *attr;
|
||||
char *val = NULL;
|
||||
|
||||
if (node) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
attr = i ? "mimeType" : "contentType";
|
||||
val = xmlGetProp(node, attr);
|
||||
if (val) {
|
||||
if (av_stristr((const char *)val, "video")) {
|
||||
type = AVMEDIA_TYPE_VIDEO;
|
||||
} else if (av_stristr((const char *)val, "audio")) {
|
||||
type = AVMEDIA_TYPE_AUDIO;
|
||||
} else if (av_stristr((const char *)val, "text")) {
|
||||
type = AVMEDIA_TYPE_SUBTITLE;
|
||||
}
|
||||
xmlFree(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static int save_avio_options(AVFormatContext *s)
|
||||
{
|
||||
SmoothStreamingContext *c = s->priv_data;
|
||||
const char *opts[] = {
|
||||
"headers", "user_agent", "cookies", "http_proxy", "referer", "rw_timeout", "icy", NULL };
|
||||
const char **opt = opts;
|
||||
uint8_t *buf = NULL;
|
||||
int ret = 0;
|
||||
|
||||
while (*opt) {
|
||||
if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
|
||||
if (buf[0] != '\0') {
|
||||
ret = av_dict_set(&c->avio_opts, *opt, buf, AV_DICT_DONT_STRDUP_VAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
av_freep(&buf);
|
||||
}
|
||||
}
|
||||
opt++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
|
||||
int flags, AVDictionary **opts)
|
||||
{
|
||||
av_log(s, AV_LOG_ERROR,
|
||||
"A SmoothStreaming playlist item '%s' referred to an external file '%s'. "
|
||||
"Opening this file was forbidden for security reasons\n",
|
||||
s->url, url);
|
||||
return AVERROR(EPERM);
|
||||
}
|
||||
|
||||
#define OFFSET(x) offsetof(DASHContext, x)
|
||||
#define FLAGS AV_OPT_FLAG_DECODING_PARAM
|
||||
static const AVOption dash_options[] = {
|
||||
{"allowed_extensions", "List of file extensions that dash is allowed to access",
|
||||
OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
|
||||
{.str = "aac,m4a,m4s,m4v,mov,mp4,webm,ts"},
|
||||
INT_MIN, INT_MAX, FLAGS},
|
||||
{ "cenc_decryption_key", "Media decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static const AVClass dash_class = {
|
||||
.class_name = "dash",
|
||||
.item_name = av_default_item_name,
|
||||
.option = dash_options,
|
||||
.version = LIBAVUTIL_VERSION_INT,
|
||||
};
|
||||
|
||||
AVInputFormat ff_dash_demuxer = {
|
||||
.name = "dash",
|
||||
.long_name = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
|
||||
.priv_class = &dash_class,
|
||||
.priv_data_size = sizeof(DASHContext),
|
||||
.read_probe = dash_probe,
|
||||
.read_header = dash_read_header,
|
||||
.read_packet = dash_read_packet,
|
||||
.read_close = dash_close,
|
||||
.read_seek = dash_read_seek,
|
||||
.flags = AVFMT_NO_BYTE_SEEK,
|
||||
};
|
Loading…
Reference in New Issue