From 96a45a16af5594900ca94e7d4abb18d1e6d5ed4a Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 7 Feb 2017 17:05:17 +0100 Subject: player: add experimental stream recording feature This is basically a WIP, but it can't remain in a branch forever. A warning is print when using it as it's still a bit "shaky". --- player/command.c | 22 +++++++++++++ player/core.h | 8 +++++ player/loadfile.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ player/playloop.c | 3 ++ 4 files changed, 126 insertions(+) (limited to 'player') diff --git a/player/command.c b/player/command.c index 6d335e535a..cde67ebe51 100644 --- a/player/command.c +++ b/player/command.c @@ -3549,6 +3549,26 @@ static int mp_property_cwd(void *ctx, struct m_property *prop, return M_PROPERTY_NOT_IMPLEMENTED; } +static int mp_property_record_file(void *ctx, struct m_property *prop, + int action, void *arg) +{ + struct MPContext *mpctx = ctx; + struct MPOpts *opts = mpctx->opts; + if (action == M_PROPERTY_SET) { + char *new = *(char **)arg; + if (!bstr_equals(bstr0(new), bstr0(opts->record_file))) { + talloc_free(opts->record_file); + opts->record_file = talloc_strdup(NULL, new); + open_recorder(mpctx, false); + // open_recorder() unsets it on failure. + if (new && !opts->record_file) + return M_PROPERTY_ERROR; + } + return M_PROPERTY_OK; + } + return mp_property_generic_option(mpctx, prop, action, arg); +} + static int mp_property_protocols(void *ctx, struct m_property *prop, int action, void *arg) { @@ -4020,6 +4040,8 @@ static const struct m_property mp_properties_base[] = { {"working-directory", mp_property_cwd}, + {"record-file", mp_property_record_file}, + {"protocol-list", mp_property_protocols}, {"decoder-list", mp_property_decoders}, {"encoder-list", mp_property_encoders}, diff --git a/player/core.h b/player/core.h index 4767830bc9..79120decd3 100644 --- a/player/core.h +++ b/player/core.h @@ -153,6 +153,9 @@ struct track { struct vo_chain *vo_c; struct ao_chain *ao_c; struct lavfi_pad *sink; + + // For stream recording (remuxing mode). + struct mp_recorder_sink *remux_sink; }; // Summarizes video filtering and output. @@ -421,6 +424,8 @@ typedef struct MPContext { // playback rate. Used to avoid showing it multiple times. bool drop_message_shown; + struct mp_recorder *recorder; + char *cached_watch_later_configdir; struct screenshot_ctx *screenshot_ctx; @@ -506,6 +511,9 @@ void autoload_external_files(struct MPContext *mpctx); struct track *select_default_track(struct MPContext *mpctx, int order, enum stream_type type); void prefetch_next(struct MPContext *mpctx); +void close_recorder(struct MPContext *mpctx); +void close_recorder_and_error(struct MPContext *mpctx); +void open_recorder(struct MPContext *mpctx, bool on_init); // main.c int mp_initialize(struct MPContext *mpctx, char **argv); diff --git a/player/loadfile.c b/player/loadfile.c index 524ff8f1b2..dba02ee828 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -40,6 +40,7 @@ #include "options/m_property.h" #include "common/common.h" #include "common/encode.h" +#include "common/recorder.h" #include "input/input.h" #include "audio/audio.h" @@ -469,6 +470,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type uninit_sub(mpctx, current); if (current) { + if (current->remux_sink) + close_recorder_and_error(mpctx); current->selected = false; reselect_demux_stream(mpctx, current); } @@ -1256,6 +1259,8 @@ reopen_file: if (mpctx->opts->pause) pause_player(mpctx); + open_recorder(mpctx, true); + playback_start = mp_time_sec(); mpctx->error_playing = 0; while (!mpctx->stop_play) @@ -1278,6 +1283,8 @@ terminate_playback: mp_abort_playback_async(mpctx); + close_recorder(mpctx); + // time to uninit all, except global stuff: uninit_complex_filters(mpctx); uninit_audio_chain(mpctx); @@ -1449,3 +1456,89 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e) mpctx->stop_play = PT_CURRENT_ENTRY; mp_wakeup_core(mpctx); } + +static void set_track_recorder_sink(struct track *track, + struct mp_recorder_sink *sink) +{ + if (track->d_sub) + sub_set_recorder_sink(track->d_sub, sink); + if (track->d_video) + track->d_video->recorder_sink = sink; + if (track->d_audio) + track->d_audio->recorder_sink = sink; + track->remux_sink = sink; +} + +void close_recorder(struct MPContext *mpctx) +{ + if (!mpctx->recorder) + return; + + for (int n = 0; n < mpctx->num_tracks; n++) + set_track_recorder_sink(mpctx->tracks[n], NULL); + + mp_recorder_destroy(mpctx->recorder); + mpctx->recorder = NULL; +} + +// Like close_recorder(), but also unset the option. Intended for use on errors. +void close_recorder_and_error(struct MPContext *mpctx) +{ + close_recorder(mpctx); + talloc_free(mpctx->opts->record_file); + mpctx->opts->record_file = NULL; + mp_notify_property(mpctx, "record-file"); + MP_ERR(mpctx, "Disabling stream recording.\n"); +} + +void open_recorder(struct MPContext *mpctx, bool on_init) +{ + if (!mpctx->playback_initialized) + return; + + close_recorder(mpctx); + + char *target = mpctx->opts->record_file; + if (!target || !target[0]) + return; + + struct sh_stream **streams = NULL; + int num_streams = 0; + + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (track->stream && track->selected && + (track->d_sub || track->d_video || track->d_audio)) + { + MP_TARRAY_APPEND(NULL, streams, num_streams, track->stream); + } + } + + mpctx->recorder = mp_recorder_create(mpctx->global, mpctx->opts->record_file, + streams, num_streams); + + if (!mpctx->recorder) { + talloc_free(streams); + close_recorder_and_error(mpctx); + return; + } + + if (!on_init) + mp_recorder_mark_discontinuity(mpctx->recorder); + + int n_stream = 0; + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (n_stream >= num_streams) + break; + // (We expect track->stream not to be reused on other tracks.) + if (track->stream == streams[n_stream]) { + set_track_recorder_sink(track, + mp_recorder_get_sink(mpctx->recorder, n_stream)); + n_stream++; + } + } + + talloc_free(streams); +} + diff --git a/player/playloop.c b/player/playloop.c index 232a75f814..e73ad61788 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -28,6 +28,7 @@ #include "options/options.h" #include "common/common.h" #include "common/encode.h" +#include "common/recorder.h" #include "options/m_config.h" #include "options/m_property.h" #include "common/playlist.h" @@ -330,6 +331,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) clear_audio_output_buffers(mpctx); reset_playback_state(mpctx); + if (mpctx->recorder) + mp_recorder_mark_discontinuity(mpctx->recorder); /* Use the target time as "current position" for further relative * seeks etc until a new video frame has been decoded */ -- cgit v1.2.3