some shit

This commit is contained in:
wm4 2020-02-15 02:19:20 +01:00
parent 641d102101
commit a9acfa82c3
7 changed files with 194 additions and 35 deletions

View File

@ -182,6 +182,71 @@ this will use a unified cache for all streams.
The ``new_stream`` header is not part of the core EDL format. It may be changed
or removed at any time, depending on mpv's internal requirements.
Ff the first ``!new_stream`` is redundant, it is ignored. This is the same
example as above::
# mpv EDL v0
!new_stream
video.mkv
!new_stream
audio.mkv
Note that ``!new_stream`` must be the first header. Whether the parser accepts
(i.e. ignores) or rejects other headers before that is implementation specific.
Track metadata
==============
The special ``track_meta`` header can set some specific metadata fields of the
current ``!new_stream`` partition. The tags are applied to all tracks within
the partition. It is not possible to set the metadata for individual tracks (the
feature was needed only for single-track media).
It provides following parameters change track metadata:
``lang``
Set the language tag.
``title``
Set the title tag.
Example::
# mpv EDL v0
!track_meta,lang=bla,title=blabla
file.mkv
!new_stream
!track_meta,title=ducks
sub.srt
If ``file.mkv`` has an audio and a video stream, both will use ``blabla`` as
title. The subtitle stream will use ``ducks`` as title.
The ``track_meta`` header is not part of the core EDL format. It may be changed
or removed at any time, depending on mpv's internal requirements.
Delayed media opening
=====================
The special ``delay_open`` header can be used to open the media URL of the
stream only when the track is selected for the first time. This is supposed to
be an optimization to speed up opening of a remote stream if there are many
tracks for whatever reasons.
This has various tricky restrictions, and also will defer failure to open a
stream to "later". By design, it's supposed to be used for single-track streams.
Using multiple segments requires you to specify all offsets and durations (also
it was never tested whether it works at all). Interaction with ``mp4_dash`` may
be strange.
This requires specifying the ``media_type`` parameter, which has to be set to
``video``, ``audio``, or ``sub``. Other tracks in the opened URL are ignored.
This is the minimum metadata that must be provided.
The ``delay_open`` header is not part of the core EDL format. It may be changed
or removed at any time, depending on mpv's internal requirements.
Timestamp format
================

View File

@ -49,6 +49,9 @@ struct tl_parts {
bool disable_chapters;
bool dash, no_clip;
char *init_fragment_url;
char *title, *lang;
bool delay_open;
enum stream_type delay_open_st;
struct tl_part *parts;
int num_parts;
struct tl_parts *next;
@ -102,6 +105,9 @@ static struct tl_root *parse_edl(bstr str)
bool is_header = bstr_eatstart0(&str, "!");
bstr f_type = {0};
bstr f_init = {0};
bstr f_lang = {0};
bstr f_title = {0};
bstr f_mt = {0};
struct tl_part p = { .length = -1 };
int nparam = 0;
while (1) {
@ -135,6 +141,12 @@ static struct tl_root *parse_edl(bstr str)
f_type = val;
} else if (bstr_equals0(name, "init")) {
f_init = val;
} else if (bstr_equals0(name, "lang")) {
f_lang = val;
} else if (bstr_equals0(name, "title")) {
f_title = val;
} else if (bstr_equals0(name, "media_type")) {
f_mt = val;
}
} else {
if (bstr_equals0(name, "file")) {
@ -168,9 +180,28 @@ static struct tl_root *parse_edl(bstr str)
} else if (bstr_equals0(f_type, "no_clip")) {
tl->no_clip = true;
} else if (bstr_equals0(f_type, "new_stream")) {
// (Special case: ignore "redundant" headers at the start for
// general symmetry.)
if (root->num_pars > 1 || tl->num_parts)
tl = add_part(root);
} else if (bstr_equals0(f_type, "no_chapters")) {
tl->disable_chapters = true;
} else if (bstr_equals0(f_type, "track_meta")) {
if (f_lang.start)
tl->lang = bstrto0(tl, f_lang);
if (f_title.start)
tl->title = bstrto0(tl, f_title);
} else if (bstr_equals0(f_type, "delay_open")) {
if (bstr_equals0(f_mt, "video")) {
tl->delay_open_st = STREAM_VIDEO;
} else if (bstr_equals0(f_mt, "audio")) {
tl->delay_open_st = STREAM_AUDIO;
} else if (bstr_equals0(f_mt, "sub")) {
tl->delay_open_st = STREAM_SUB;
} else {
goto error;
}
tl->delay_open = true;
} else {
goto error;
}
@ -265,6 +296,10 @@ static struct timeline_par *build_timeline(struct timeline *root,
tl->track_layout = NULL;
tl->dash = parts->dash;
tl->no_clip = parts->no_clip;
tl->title = talloc_strdup(tl, parts->title);
tl->lang = talloc_strdup(tl, parts->lang);
tl->delay_open = parts->delay_open;
tl->delay_open_st = parts->delay_open_st;
if (parts->init_fragment_url && parts->init_fragment_url[0]) {
MP_VERBOSE(root, "Opening init fragment...\n");
@ -309,6 +344,15 @@ static struct timeline_par *build_timeline(struct timeline *root,
if (!tl->track_layout)
tl->track_layout = open_source(root, tl, part->filename);
} else if (tl->delay_open) {
if (n == 0 && !part->offset_set) {
part->offset = starttime;
part->offset_set = true;
}
if (part->chapter_ts || (part->length < 0 && !tl->no_clip)) {
MP_ERR(root, "Incomplete specification for delay_open stream.\n");
goto error;
}
} else {
MP_VERBOSE(root, "Opening segment %d...\n", n);
@ -372,6 +416,9 @@ static struct timeline_par *build_timeline(struct timeline *root,
tl->num_parts++;
}
if (tl->no_clip && tl->num_parts > 1)
MP_WARN(root, "Multiple parts with no_clip. Undefined behavior ahead.\n");
if (!tl->track_layout) {
// Use a heuristic to select the "broadest" part as layout.
for (int n = 0; n < parts->num_parts; n++) {
@ -384,7 +431,7 @@ static struct timeline_par *build_timeline(struct timeline *root,
}
}
if (!tl->track_layout)
if (!tl->track_layout && !tl->delay_open)
goto error;
if (!root->meta)
root->meta = tl->track_layout;

View File

@ -56,7 +56,7 @@ struct virtual_stream {
struct virtual_source {
struct timeline_par *tl;
bool dash, no_clip;
bool dash, no_clip, delay_open;
struct segment **segments;
int num_segments;
@ -207,11 +207,15 @@ static void reopen_lazy_segments(struct demuxer *demuxer,
if (src->current->d)
return;
// Note: in delay_open mode, we must _not_ close segments during demuxing,
// because demuxed packets have demux_packet.codec set to objects owned
// by the segments. Closing them would create dangling pointers.
if (!src->delay_open)
close_lazy_segments(demuxer, src);
struct demuxer_params params = {
.init_fragment = src->tl->init_fragment,
.skip_lavf_probing = true,
.skip_lavf_probing = src->tl->dash,
.stream_flags = demuxer->stream_origin,
};
src->current->d = demux_open_url(src->current->url, &params,
@ -418,10 +422,12 @@ static bool do_read_next_packet(struct demuxer *demuxer,
if (pkt->stream < 0 || pkt->stream >= seg->num_stream_map)
goto drop;
if (!src->no_clip) {
if (!src->no_clip || src->delay_open) {
pkt->segmented = true;
if (!pkt->codec)
pkt->codec = demux_get_stream(seg->d, pkt->stream)->codec;
}
if (!src->no_clip) {
if (pkt->start == MP_NOPTS_VALUE || pkt->start < seg->start)
pkt->start = seg->start;
if (pkt->end == MP_NOPTS_VALUE || pkt->end > seg->end)
@ -509,14 +515,14 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
demuxer->num_chapters = p->tl->num_chapters;
struct demuxer *meta = p->tl->meta;
if (!meta)
return -1;
if (meta) {
demuxer->metadata = meta->metadata;
demuxer->attachments = meta->attachments;
demuxer->num_attachments = meta->num_attachments;
demuxer->editions = meta->editions;
demuxer->num_editions = meta->num_editions;
demuxer->edition = meta->edition;
}
for (int n = 0; n < p->tl->num_pars; n++) {
if (!add_tl(demuxer, p->tl->pars[n]))
@ -536,9 +542,11 @@ static int d_open(struct demuxer *demuxer, enum demux_check check)
demuxer->seekable = true;
demuxer->partially_seekable = false;
demuxer->filetype = talloc_asprintf(p, "%s/%s",
p->tl->format,
meta->filetype ? meta->filetype : meta->desc->name);
const char *format_name = "unknown";
if (meta)
format_name = meta->filetype ? meta->filetype : meta->desc->name;
demuxer->filetype = talloc_asprintf(p, "%s/%s", p->tl->format, format_name);
reselect_streams(demuxer);
@ -553,11 +561,12 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl)
*src = (struct virtual_source){
.tl = tl,
.dash = tl->dash,
.delay_open = tl->delay_open,
.no_clip = tl->no_clip || tl->dash,
.dts = MP_NOPTS_VALUE,
};
if (!tl->num_parts || !tl->track_layout)
if (!tl->num_parts)
return false;
MP_TARRAY_APPEND(p, p->sources, p->num_sources, src);
@ -566,19 +575,32 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl)
struct demuxer *meta = tl->track_layout;
int num_streams = demux_get_num_stream(meta);
// delay_open streams normally have meta==0, and 1 virtual stream
int num_streams = meta ? demux_get_num_stream(meta) : tl->delay_open;
for (int n = 0; n < num_streams; n++) {
struct sh_stream *sh = demux_get_stream(meta, n);
struct sh_stream *new = demux_alloc_sh_stream(sh->type);
struct sh_stream *sh = meta ? demux_get_stream(meta, n) : NULL;
struct sh_stream *new = NULL;
if (sh) {
new = demux_alloc_sh_stream(sh->type);
new->demuxer_id = sh->demuxer_id;
new->codec = sh->codec;
new->title = sh->title;
new->lang = sh->lang;
new->title = tl->title ? tl->title : sh->title;
new->lang = tl->lang ? tl->lang : sh->lang;
new->default_track = sh->default_track;
new->forced_track = sh->forced_track;
new->hls_bitrate = sh->hls_bitrate;
new->missing_timestamps = sh->missing_timestamps;
new->attached_picture = sh->attached_picture;
} else {
assert(tl->delay_open);
new = demux_alloc_sh_stream(tl->delay_open_st);
new->codec->type = new->type;
new->codec->codec = "null";
new->title = tl->title ? tl->title : NULL;
new->lang = tl->lang ? tl->lang : NULL;
}
demux_add_sh_stream(demuxer, new);
struct virtual_stream *vs = talloc_ptrtype(p, vs);
*vs = (struct virtual_stream){
@ -600,6 +622,9 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl)
demuxer->is_streaming |= part->source->is_streaming;
}
if (!part->source)
assert(tl->dash || tl->delay_open);
struct segment *seg = talloc_ptrtype(src, seg);
*seg = (struct segment){
.d = part->source,
@ -616,8 +641,10 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl)
MP_TARRAY_APPEND(src, src->segments, src->num_segments, seg);
}
if (tl->track_layout) {
demuxer->is_network |= tl->track_layout->is_network;
demuxer->is_streaming |= tl->track_layout->is_streaming;
}
return true;
}

View File

@ -1,6 +1,8 @@
#ifndef MP_TIMELINE_H_
#define MP_TIMELINE_H_
#include "common/common.h"
// Single segment in a timeline.
struct timeline_part {
// (end time must match with start time of the next part)
@ -22,6 +24,12 @@ struct timeline_par {
bstr init_fragment;
bool dash, no_clip;
bool delay_open;
enum stream_type delay_open_st; // valid if delay_open=true, promised type
char *lang;
char *title;
// Segments to play, ordered by time.
struct timeline_part *parts;
int num_parts;

View File

@ -199,13 +199,16 @@ bool mp_decoder_wrapper_reinit(struct mp_decoder_wrapper *d)
const struct mp_decoder_fns *driver = NULL;
struct mp_decoder_list *list = NULL;
char *user_list = NULL;
char *fallback = NULL;
if (p->codec->type == STREAM_VIDEO) {
driver = &vd_lavc;
user_list = opts->video_decoders;
fallback = "h264";
} else if (p->codec->type == STREAM_AUDIO) {
driver = &ad_lavc;
user_list = opts->audio_decoders;
fallback = "aac";
if (p->public.try_spdif && p->codec->codec) {
struct mp_decoder_list *spdif =
@ -223,7 +226,10 @@ bool mp_decoder_wrapper_reinit(struct mp_decoder_wrapper *d)
struct mp_decoder_list *full = talloc_zero(NULL, struct mp_decoder_list);
if (driver)
driver->add_decoders(full);
list = mp_select_decoders(p->log, full, p->codec->codec, user_list);
const char *codec = p->codec->codec;
if (codec && strcmp(codec, "null") == 0)
codec = fallback;
list = mp_select_decoders(p->log, full, codec, user_list);
talloc_free(full);
}

View File

@ -386,7 +386,9 @@ local function add_single_video(json)
end
if not (sub == nil) then
mp.commandv("sub-add", sub,
local edl = "edl://!no_clip;!delay_open,media_type=sub;"
.. edl_escape(sub)
mp.commandv("sub-add", edl,
"auto", sub_info.ext, lang)
else
msg.verbose("No subtitle data/url for ["..lang.."]")

View File

@ -155,7 +155,11 @@ static int init(struct sd *sd)
char *extradata = sd->codec->extradata;
int extradata_size = sd->codec->extradata_size;
if (strcmp(sd->codec->codec, "ass") != 0) {
// Note: accept "null" as alias for "ass", so EDL delay_open subtitle
// streams work.
if (strcmp(sd->codec->codec, "ass") != 0 &&
strcmp(sd->codec->codec, "null") != 0)
{
ctx->is_converted = true;
ctx->converter = lavc_conv_create(sd->log, sd->codec->codec, extradata,
extradata_size);