summaryrefslogtreecommitdiffstats
path: root/player/loadfile.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-02-05 23:19:56 +0100
committerwm4 <wm4@nowhere>2016-02-05 23:19:56 +0100
commitc0de087ba191a4daf3a152e0ab09b5687fab8449 (patch)
treeb00043c5e4055ef777a0afdba6b4a321e87864de /player/loadfile.c
parent45345d9c41f1491e9809153dc3b3950fba560d89 (diff)
downloadmpv-c0de087ba191a4daf3a152e0ab09b5687fab8449.tar.bz2
mpv-c0de087ba191a4daf3a152e0ab09b5687fab8449.tar.xz
player: add complex filter graph support
See --lavfi-complex option. This is still quite rough. There's no support for dynamic configuration of any kind. There are probably corner cases where playback might freeze or burn 100% CPU (due to dataflow problems when interaction with libavfilter). Future possible plans might include: - freely switch tracks by providing some sort of default track graph label - automatically enabling audio visualization - automatically mix audio or stack video when multiple tracks are selected at once (similar to how multiple sub tracks can be selected)
Diffstat (limited to 'player/loadfile.c')
-rw-r--r--player/loadfile.c136
1 files changed, 134 insertions, 2 deletions
diff --git a/player/loadfile.c b/player/loadfile.c
index 57c3588b9f..7ed4cb6201 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -564,6 +564,17 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
if (track == current)
return;
+ if (current && current->sink) {
+ MP_ERR(mpctx, "Can't disable input to complex filter.\n");
+ return;
+ }
+ if ((type == STREAM_VIDEO && mpctx->vo_chain && !mpctx->vo_chain->track) ||
+ (type == STREAM_AUDIO && mpctx->ao_chain && !mpctx->ao_chain->track))
+ {
+ MP_ERR(mpctx, "Can't switch away from complex filter output.\n");
+ return;
+ }
+
if (track && track->selected) {
// Track has been selected in a different order parameter.
MP_ERR(mpctx, "Track %d is already selected.\n", track->user_tid);
@@ -1016,6 +1027,115 @@ static void load_timeline(struct MPContext *mpctx)
print_timeline(mpctx);
}
+static void init_complex_filters(struct MPContext *mpctx)
+{
+ assert(!mpctx->lavfi);
+
+ char *graph = mpctx->opts->lavfi_complex;
+
+ if (!graph || !graph[0])
+ return;
+
+ if (mpctx->tl) {
+ MP_ERR(mpctx, "complex filters not supported with timeline\n");
+ return;
+ }
+
+ mpctx->lavfi = lavfi_create(mpctx->log, graph);
+ if (!mpctx->lavfi)
+ return;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+
+ char label[32];
+ char prefix;
+ switch (track->type) {
+ case STREAM_VIDEO: prefix = 'v'; break;
+ case STREAM_AUDIO: prefix = 'a'; break;
+ default: continue;
+ }
+ snprintf(label, sizeof(label), "%cid%d", prefix, track->user_tid);
+
+ struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, label);
+ if (!pad)
+ continue;
+ if (lavfi_pad_type(pad) != track->type)
+ continue;
+ if (lavfi_pad_direction(pad) != LAVFI_IN)
+ continue;
+ if (lavfi_get_connected(pad))
+ continue;
+
+ track->sink = pad;
+ lavfi_set_connected(pad, true);
+ track->selected = true;
+ }
+
+ struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, "vo");
+ if (pad && lavfi_pad_type(pad) == STREAM_VIDEO &&
+ lavfi_pad_direction(pad) == LAVFI_OUT)
+ {
+ lavfi_set_connected(pad, true);
+ reinit_video_chain_src(mpctx, pad);
+ }
+
+ pad = lavfi_find_pad(mpctx->lavfi, "ao");
+ if (pad && lavfi_pad_type(pad) == STREAM_AUDIO &&
+ lavfi_pad_direction(pad) == LAVFI_OUT)
+ {
+ lavfi_set_connected(pad, true);
+ reinit_audio_chain_src(mpctx, pad);
+ }
+}
+
+static bool init_complex_filter_decoders(struct MPContext *mpctx)
+{
+ if (!mpctx->lavfi)
+ return true;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->sink && track->type == STREAM_VIDEO) {
+ if (!init_video_decoder(mpctx, track))
+ return false;
+ }
+ if (track->sink && track->type == STREAM_AUDIO) {
+ if (!init_audio_decoder(mpctx, track))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void uninit_complex_filters(struct MPContext *mpctx)
+{
+ if (!mpctx->lavfi)
+ return;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+
+ if (track->d_video && !track->vo_c) {
+ video_uninit(track->d_video);
+ track->d_video = NULL;
+ }
+ if (track->d_audio && !track->ao_c) {
+ audio_uninit(track->d_audio);
+ track->d_audio = NULL;
+ }
+ }
+
+ if (mpctx->vo_chain && mpctx->vo_chain->filter_src)
+ uninit_video_chain(mpctx);
+ if (mpctx->ao_chain && mpctx->ao_chain->filter_src)
+ uninit_audio_chain(mpctx);
+
+ lavfi_destroy(mpctx->lavfi);
+ mpctx->lavfi = NULL;
+}
+
// Start playing the current playlist entry.
// Handle initialization and deinitialization.
static void play_current_file(struct MPContext *mpctx)
@@ -1133,10 +1253,18 @@ reopen_file:
check_previous_track_selection(mpctx);
+ init_complex_filters(mpctx);
+
assert(NUM_PTRACKS == 2); // opts->stream_id is hardcoded to 2
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
- for (int i = 0; i < NUM_PTRACKS; i++)
- mpctx->current_track[i][t] = select_default_track(mpctx, i, t);
+ for (int i = 0; i < NUM_PTRACKS; i++) {
+ struct track *sel = NULL;
+ bool taken = (t == STREAM_VIDEO && mpctx->vo_chain) ||
+ (t == STREAM_AUDIO && mpctx->ao_chain);
+ if (!taken)
+ sel = select_default_track(mpctx, i, t);
+ mpctx->current_track[i][t] = sel;
+ }
}
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
for (int i = 0; i < NUM_PTRACKS; i++) {
@@ -1171,6 +1299,9 @@ reopen_file:
update_playback_speed(mpctx);
+ if (!init_complex_filter_decoders(mpctx))
+ goto terminate_playback;
+
reinit_video_chain(mpctx);
reinit_audio_chain(mpctx);
reinit_sub_all(mpctx);
@@ -1239,6 +1370,7 @@ terminate_playback:
mp_cancel_trigger(mpctx->playback_abort);
// time to uninit all, except global stuff:
+ uninit_complex_filters(mpctx);
uninit_audio_chain(mpctx);
uninit_video_chain(mpctx);
uninit_sub_all(mpctx);