mirror of https://github.com/mpv-player/mpv
some shit
This commit is contained in:
parent
641d102101
commit
a9acfa82c3
|
@ -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
|
||||
================
|
||||
|
||||
|
|
|
@ -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")) {
|
||||
tl = add_part(root);
|
||||
// (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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
close_lazy_segments(demuxer, src);
|
||||
// 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, ¶ms,
|
||||
|
@ -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;
|
||||
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;
|
||||
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);
|
||||
new->demuxer_id = sh->demuxer_id;
|
||||
new->codec = sh->codec;
|
||||
new->title = sh->title;
|
||||
new->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;
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
demuxer->is_network |= tl->track_layout->is_network;
|
||||
demuxer->is_streaming |= tl->track_layout->is_streaming;
|
||||
if (tl->track_layout) {
|
||||
demuxer->is_network |= tl->track_layout->is_network;
|
||||
demuxer->is_streaming |= tl->track_layout->is_streaming;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.."]")
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue