mirror of https://github.com/mpv-player/mpv
Compare commits
9 Commits
c523f8e672
...
11067ed0ae
Author | SHA1 | Date |
---|---|---|
stax76 | 11067ed0ae | |
ferreum | 773c5e2ae0 | |
ferreum | 190b15c827 | |
nanahi | 51e01e9772 | |
nanahi | 7f0961479a | |
Dudemanguy | 76367dae35 | |
stax76 | cac5d06c9c | |
stax76 | f90929ac67 | |
stax76 | 65d9c7f8ba |
|
@ -8,7 +8,7 @@
|
|||
#include "options/m_option.h"
|
||||
|
||||
struct priv {
|
||||
struct mp_scaletempo2 data;
|
||||
struct mp_scaletempo2 *data;
|
||||
struct mp_pin *in_pin;
|
||||
struct mp_aframe *cur_format;
|
||||
struct mp_aframe_pool *out_pool;
|
||||
|
@ -29,7 +29,7 @@ static void af_scaletempo2_process(struct mp_filter *f)
|
|||
return;
|
||||
|
||||
while (!p->initialized || !p->pending ||
|
||||
!mp_scaletempo2_frames_available(&p->data, p->speed))
|
||||
!mp_scaletempo2_frames_available(p->data, p->speed))
|
||||
{
|
||||
bool eof = false;
|
||||
if (!p->pending || !mp_aframe_get_size(p->pending)) {
|
||||
|
@ -64,16 +64,16 @@ static void af_scaletempo2_process(struct mp_filter *f)
|
|||
if (p->pending && !format_change && !p->sent_final) {
|
||||
int frame_size = mp_aframe_get_size(p->pending);
|
||||
uint8_t **planes = mp_aframe_get_data_ro(p->pending);
|
||||
int read = mp_scaletempo2_fill_input_buffer(&p->data,
|
||||
int read = mp_scaletempo2_fill_input_buffer(p->data,
|
||||
planes, frame_size, p->speed);
|
||||
mp_aframe_skip_samples(p->pending, read);
|
||||
}
|
||||
if (final && p->pending && !p->sent_final) {
|
||||
mp_scaletempo2_set_final(&p->data);
|
||||
mp_scaletempo2_set_final(p->data);
|
||||
p->sent_final = true;
|
||||
}
|
||||
|
||||
if (mp_scaletempo2_frames_available(&p->data, p->speed)) {
|
||||
if (mp_scaletempo2_frames_available(p->data, p->speed)) {
|
||||
if (eof) {
|
||||
mp_pin_out_repeat_eof(p->in_pin); // drain more next time
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ static void af_scaletempo2_process(struct mp_filter *f)
|
|||
}
|
||||
|
||||
assert(p->pending);
|
||||
if (mp_scaletempo2_frames_available(&p->data, p->speed)) {
|
||||
if (mp_scaletempo2_frames_available(p->data, p->speed)) {
|
||||
struct mp_aframe *out = mp_aframe_new_ref(p->cur_format);
|
||||
int out_samples = p->data.ola_hop_size;
|
||||
int out_samples = p->data->ola_hop_size;
|
||||
if (mp_aframe_pool_allocate(p->out_pool, out, out_samples) < 0) {
|
||||
talloc_free(out);
|
||||
goto error;
|
||||
|
@ -101,14 +101,14 @@ static void af_scaletempo2_process(struct mp_filter *f)
|
|||
|
||||
uint8_t **planes = mp_aframe_get_data_rw(out);
|
||||
assert(planes);
|
||||
assert(mp_aframe_get_planes(out) == p->data.channels);
|
||||
assert(mp_aframe_get_planes(out) == p->data->channels);
|
||||
|
||||
out_samples = mp_scaletempo2_fill_buffer(&p->data,
|
||||
out_samples = mp_scaletempo2_fill_buffer(p->data,
|
||||
(float**)planes, out_samples, p->speed);
|
||||
|
||||
double pts = mp_aframe_get_pts(p->pending);
|
||||
if (pts != MP_NOPTS_VALUE) {
|
||||
double frame_delay = mp_scaletempo2_get_latency(&p->data, p->speed)
|
||||
double frame_delay = mp_scaletempo2_get_latency(p->data, p->speed)
|
||||
+ out_samples * p->speed;
|
||||
mp_aframe_set_pts(out, pts - frame_delay / mp_aframe_get_effective_rate(out));
|
||||
|
||||
|
@ -122,7 +122,7 @@ static void af_scaletempo2_process(struct mp_filter *f)
|
|||
|
||||
// reset the filter to ensure it stops generating audio
|
||||
// and mp_scaletempo2_frames_available returns false
|
||||
mp_scaletempo2_reset(&p->data);
|
||||
mp_scaletempo2_reset(p->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ static bool init_scaletempo2(struct mp_filter *f)
|
|||
p->sent_final = false;
|
||||
mp_aframe_config_copy(p->cur_format, p->pending);
|
||||
|
||||
mp_scaletempo2_init(&p->data, mp_aframe_get_channels(p->pending),
|
||||
mp_scaletempo2_init(p->data, mp_aframe_get_channels(p->pending),
|
||||
mp_aframe_get_rate(p->pending));
|
||||
|
||||
return true;
|
||||
|
@ -172,7 +172,7 @@ static bool af_scaletempo2_command(struct mp_filter *f, struct mp_filter_command
|
|||
static void af_scaletempo2_reset(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
mp_scaletempo2_reset(&p->data);
|
||||
mp_scaletempo2_reset(p->data);
|
||||
p->initialized = false;
|
||||
TA_FREEP(&p->pending);
|
||||
}
|
||||
|
@ -180,8 +180,8 @@ static void af_scaletempo2_reset(struct mp_filter *f)
|
|||
static void af_scaletempo2_destroy(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
mp_scaletempo2_destroy(&p->data);
|
||||
talloc_free(p->pending);
|
||||
TA_FREEP(&p->data);
|
||||
TA_FREEP(&p->pending);
|
||||
}
|
||||
|
||||
static const struct mp_filter_info af_scaletempo2_filter = {
|
||||
|
@ -206,7 +206,8 @@ static struct mp_filter *af_scaletempo2_create(
|
|||
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
||||
|
||||
struct priv *p = f->priv;
|
||||
p->data.opts = talloc_steal(p, options);
|
||||
p->data = talloc_zero(p, struct mp_scaletempo2);
|
||||
p->data->opts = talloc_steal(p, options);
|
||||
p->speed = 1.0;
|
||||
p->cur_format = talloc_steal(p, mp_aframe_create());
|
||||
p->out_pool = mp_aframe_pool_create(p);
|
||||
|
|
|
@ -41,19 +41,15 @@ static bool in_interval(int n, struct interval q)
|
|||
return n >= q.lo && n <= q.hi;
|
||||
}
|
||||
|
||||
static float **realloc_2d(float **p, int x, int y)
|
||||
static void alloc_sample_buffer(struct mp_scaletempo2 *p, float ***ptr, size_t size)
|
||||
{
|
||||
float **array = realloc(p, sizeof(float*) * x + sizeof(float) * x * y);
|
||||
float* data = (float*) (array + x);
|
||||
for (int i = 0; i < x; ++i) {
|
||||
array[i] = data + i * y;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
talloc_free(*ptr);
|
||||
|
||||
static void zero_2d(float **a, int x, int y)
|
||||
{
|
||||
memset(a + x, 0, sizeof(float) * x * y);
|
||||
float **buff = talloc_array(p, float*, p->channels);
|
||||
for (int i = 0; i < p->channels; ++i) {
|
||||
buff[i] = talloc_array(buff, float, size);
|
||||
}
|
||||
*ptr = buff;
|
||||
}
|
||||
|
||||
static void zero_2d_partial(float **a, int x, int y)
|
||||
|
@ -480,12 +476,6 @@ static bool can_perform_wsola(struct mp_scaletempo2 *p, double playback_rate)
|
|||
return frames_needed(p, playback_rate) <= 0;
|
||||
}
|
||||
|
||||
static void resize_input_buffer(struct mp_scaletempo2 *p, int size)
|
||||
{
|
||||
p->input_buffer_size = size;
|
||||
p->input_buffer = realloc_2d(p->input_buffer, p->channels, size);
|
||||
}
|
||||
|
||||
// pad end with silence until a wsola iteration can be performed
|
||||
static void add_input_buffer_final_silence(struct mp_scaletempo2 *p, double playback_rate)
|
||||
{
|
||||
|
@ -493,11 +483,9 @@ static void add_input_buffer_final_silence(struct mp_scaletempo2 *p, double play
|
|||
if (needed <= 0)
|
||||
return; // no silence needed for iteration
|
||||
|
||||
int required_size = needed + p->input_buffer_frames;
|
||||
if (required_size > p->input_buffer_size)
|
||||
resize_input_buffer(p, required_size);
|
||||
|
||||
int last_index = needed + p->input_buffer_frames - 1;
|
||||
for (int i = 0; i < p->channels; ++i) {
|
||||
MP_TARRAY_GROW(p, p->input_buffer[i], last_index);
|
||||
float *ch_input = p->input_buffer[i];
|
||||
for (int j = 0; j < needed; ++j) {
|
||||
ch_input[p->input_buffer_frames + j] = 0.0f;
|
||||
|
@ -523,11 +511,9 @@ int mp_scaletempo2_fill_input_buffer(struct mp_scaletempo2 *p,
|
|||
if (read == 0)
|
||||
return 0;
|
||||
|
||||
int required_size = read + p->input_buffer_frames;
|
||||
if (required_size > p->input_buffer_size)
|
||||
resize_input_buffer(p, required_size);
|
||||
|
||||
int last_index = read + p->input_buffer_frames - 1;
|
||||
for (int i = 0; i < p->channels; ++i) {
|
||||
MP_TARRAY_GROW(p, p->input_buffer[i], last_index);
|
||||
memcpy(p->input_buffer[i] + p->input_buffer_frames,
|
||||
planes[i], read * sizeof(float));
|
||||
}
|
||||
|
@ -771,18 +757,6 @@ bool mp_scaletempo2_frames_available(struct mp_scaletempo2 *p, double playback_r
|
|||
|| p->num_complete_frames > 0;
|
||||
}
|
||||
|
||||
void mp_scaletempo2_destroy(struct mp_scaletempo2 *p)
|
||||
{
|
||||
free(p->ola_window);
|
||||
free(p->transition_window);
|
||||
free(p->wsola_output);
|
||||
free(p->optimal_block);
|
||||
free(p->search_block);
|
||||
free(p->target_block);
|
||||
free(p->input_buffer);
|
||||
free(p->energy_candidate_blocks);
|
||||
}
|
||||
|
||||
void mp_scaletempo2_reset(struct mp_scaletempo2 *p)
|
||||
{
|
||||
p->input_buffer_frames = 0;
|
||||
|
@ -791,8 +765,6 @@ void mp_scaletempo2_reset(struct mp_scaletempo2 *p)
|
|||
p->output_time = 0.0;
|
||||
p->search_block_index = 0;
|
||||
p->target_block_index = 0;
|
||||
// Clear the queue of decoded packets.
|
||||
zero_2d(p->wsola_output, p->channels, p->wsola_output_size);
|
||||
p->num_complete_frames = 0;
|
||||
p->wsola_output_started = false;
|
||||
}
|
||||
|
@ -847,28 +819,26 @@ void mp_scaletempo2_init(struct mp_scaletempo2 *p, int channels, int rate)
|
|||
// 1, ... |num_candidate_blocks|
|
||||
p->search_block_center_offset = p->num_candidate_blocks / 2
|
||||
+ (p->ola_window_size / 2 - 1);
|
||||
p->ola_window = realloc(p->ola_window, sizeof(float) * p->ola_window_size);
|
||||
MP_RESIZE_ARRAY(p, p->ola_window, p->ola_window_size);
|
||||
get_symmetric_hanning_window(p->ola_window_size, p->ola_window);
|
||||
p->transition_window = realloc(p->transition_window,
|
||||
sizeof(float) * p->ola_window_size * 2);
|
||||
MP_RESIZE_ARRAY(p, p->transition_window, p->ola_window_size * 2);
|
||||
get_symmetric_hanning_window(2 * p->ola_window_size, p->transition_window);
|
||||
|
||||
p->wsola_output_size = p->ola_window_size + p->ola_hop_size;
|
||||
p->wsola_output = realloc_2d(p->wsola_output, p->channels, p->wsola_output_size);
|
||||
// Initialize for overlap-and-add of the first block.
|
||||
zero_2d(p->wsola_output, p->channels, p->wsola_output_size);
|
||||
alloc_sample_buffer(p, &p->wsola_output, p->wsola_output_size);
|
||||
|
||||
// Auxiliary containers.
|
||||
p->optimal_block = realloc_2d(p->optimal_block, p->channels, p->ola_window_size);
|
||||
alloc_sample_buffer(p, &p->optimal_block, p->ola_window_size);
|
||||
p->search_block_size = p->num_candidate_blocks + (p->ola_window_size - 1);
|
||||
p->search_block = realloc_2d(p->search_block, p->channels, p->search_block_size);
|
||||
p->target_block = realloc_2d(p->target_block, p->channels, p->ola_window_size);
|
||||
alloc_sample_buffer(p, &p->search_block, p->search_block_size);
|
||||
alloc_sample_buffer(p, &p->target_block, p->ola_window_size);
|
||||
|
||||
resize_input_buffer(p, 4 * MPMAX(p->ola_window_size, p->search_block_size));
|
||||
p->input_buffer_frames = 0;
|
||||
p->input_buffer_final_frames = 0;
|
||||
p->input_buffer_added_silence = 0;
|
||||
size_t initial_size = 4 * MPMAX(p->ola_window_size, p->search_block_size);
|
||||
alloc_sample_buffer(p, &p->input_buffer, initial_size);
|
||||
|
||||
p->energy_candidate_blocks = realloc(p->energy_candidate_blocks,
|
||||
sizeof(float) * p->channels * p->num_candidate_blocks);
|
||||
MP_RESIZE_ARRAY(p, p->energy_candidate_blocks,
|
||||
p->channels * p->num_candidate_blocks);
|
||||
}
|
||||
|
|
|
@ -110,7 +110,6 @@ struct mp_scaletempo2 {
|
|||
float **target_block;
|
||||
// Buffered audio data.
|
||||
float **input_buffer;
|
||||
int input_buffer_size;
|
||||
int input_buffer_frames;
|
||||
// How many frames in |input_buffer| need to be flushed by padding with
|
||||
// silence to process the final packet. While this is nonzero, the filter
|
||||
|
|
|
@ -193,17 +193,19 @@ static void thread_resume(struct ao *ao)
|
|||
thread_unpause(ao);
|
||||
}
|
||||
|
||||
static void set_state_and_wakeup_thread(struct ao *ao,
|
||||
enum wasapi_thread_state thread_state)
|
||||
static void thread_wakeup(void *ptr)
|
||||
{
|
||||
struct ao *ao = ptr;
|
||||
struct wasapi_state *state = ao->priv;
|
||||
SetEvent(state->hUserWake);
|
||||
}
|
||||
|
||||
static void set_thread_state(struct ao *ao,
|
||||
enum wasapi_thread_state thread_state)
|
||||
{
|
||||
struct wasapi_state *state = ao->priv;
|
||||
atomic_store(&state->thread_state, thread_state);
|
||||
SetEvent(state->hWake);
|
||||
}
|
||||
|
||||
static void thread_process_dispatch(void *ptr)
|
||||
{
|
||||
set_state_and_wakeup_thread(ptr, WASAPI_THREAD_DISPATCH);
|
||||
thread_wakeup(ao);
|
||||
}
|
||||
|
||||
static DWORD __stdcall AudioThread(void *lpParameter)
|
||||
|
@ -220,18 +222,25 @@ static DWORD __stdcall AudioThread(void *lpParameter)
|
|||
|
||||
MP_DBG(ao, "Entering dispatch loop\n");
|
||||
while (true) {
|
||||
if (WaitForSingleObject(state->hWake, INFINITE) != WAIT_OBJECT_0)
|
||||
MP_ERR(ao, "Unexpected return value from WaitForSingleObject\n");
|
||||
HANDLE handles[] = {state->hWake, state->hUserWake};
|
||||
switch (WaitForMultipleObjects(MP_ARRAY_SIZE(handles), handles, FALSE, INFINITE)) {
|
||||
case WAIT_OBJECT_0:
|
||||
// fill twice on under-full buffer (see comment in thread_feed)
|
||||
if (thread_feed(ao) && thread_feed(ao))
|
||||
MP_ERR(ao, "Unable to fill buffer fast enough\n");
|
||||
continue;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
break;
|
||||
default:
|
||||
MP_ERR(ao, "Unexpected return value from WaitForMultipleObjects\n");
|
||||
break;
|
||||
}
|
||||
|
||||
mp_dispatch_queue_process(state->dispatch, 0);
|
||||
|
||||
int thread_state = atomic_load(&state->thread_state);
|
||||
switch (thread_state) {
|
||||
case WASAPI_THREAD_FEED:
|
||||
// fill twice on under-full buffer (see comment in thread_feed)
|
||||
if (thread_feed(ao) && thread_feed(ao))
|
||||
MP_ERR(ao, "Unable to fill buffer fast enough\n");
|
||||
break;
|
||||
case WASAPI_THREAD_DISPATCH:
|
||||
mp_dispatch_queue_process(state->dispatch, 0);
|
||||
break;
|
||||
case WASAPI_THREAD_RESET:
|
||||
thread_reset(ao);
|
||||
|
@ -267,8 +276,8 @@ static void uninit(struct ao *ao)
|
|||
{
|
||||
MP_DBG(ao, "Uninit wasapi\n");
|
||||
struct wasapi_state *state = ao->priv;
|
||||
if (state->hWake)
|
||||
set_state_and_wakeup_thread(ao, WASAPI_THREAD_SHUTDOWN);
|
||||
if (state->hWake && state->hUserWake)
|
||||
set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
|
||||
|
||||
if (state->hAudioThread &&
|
||||
WaitForSingleObject(state->hAudioThread, INFINITE) != WAIT_OBJECT_0)
|
||||
|
@ -279,6 +288,7 @@ static void uninit(struct ao *ao)
|
|||
|
||||
SAFE_DESTROY(state->hInitDone, CloseHandle(state->hInitDone));
|
||||
SAFE_DESTROY(state->hWake, CloseHandle(state->hWake));
|
||||
SAFE_DESTROY(state->hUserWake, CloseHandle(state->hUserWake));
|
||||
SAFE_DESTROY(state->hAudioThread,CloseHandle(state->hAudioThread));
|
||||
|
||||
wasapi_change_uninit(ao);
|
||||
|
@ -312,14 +322,15 @@ static int init(struct ao *ao)
|
|||
|
||||
state->hInitDone = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
state->hWake = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
if (!state->hInitDone || !state->hWake) {
|
||||
state->hUserWake = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
if (!state->hInitDone || !state->hWake || !state->hUserWake) {
|
||||
MP_FATAL(ao, "Error creating events\n");
|
||||
uninit(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->dispatch = mp_dispatch_create(state);
|
||||
mp_dispatch_set_wakeup_fn(state->dispatch, thread_process_dispatch, ao);
|
||||
mp_dispatch_set_wakeup_fn(state->dispatch, thread_wakeup, ao);
|
||||
|
||||
state->init_ok = false;
|
||||
state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL);
|
||||
|
@ -474,17 +485,17 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
|||
|
||||
static void audio_reset(struct ao *ao)
|
||||
{
|
||||
set_state_and_wakeup_thread(ao, WASAPI_THREAD_RESET);
|
||||
set_thread_state(ao, WASAPI_THREAD_RESET);
|
||||
}
|
||||
|
||||
static void audio_resume(struct ao *ao)
|
||||
{
|
||||
set_state_and_wakeup_thread(ao, WASAPI_THREAD_RESUME);
|
||||
set_thread_state(ao, WASAPI_THREAD_RESUME);
|
||||
}
|
||||
|
||||
static bool audio_set_pause(struct ao *ao, bool paused)
|
||||
{
|
||||
set_state_and_wakeup_thread(ao, paused ? WASAPI_THREAD_PAUSE : WASAPI_THREAD_UNPAUSE);
|
||||
set_thread_state(ao, paused ? WASAPI_THREAD_PAUSE : WASAPI_THREAD_UNPAUSE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ void wasapi_change_uninit(struct ao* ao);
|
|||
|
||||
enum wasapi_thread_state {
|
||||
WASAPI_THREAD_FEED = 0,
|
||||
WASAPI_THREAD_DISPATCH,
|
||||
WASAPI_THREAD_RESUME,
|
||||
WASAPI_THREAD_RESET,
|
||||
WASAPI_THREAD_SHUTDOWN,
|
||||
|
@ -66,6 +65,7 @@ typedef struct wasapi_state {
|
|||
HANDLE hWake; // thread wakeup event
|
||||
atomic_int thread_state; // enum wasapi_thread_state (what to do on wakeup)
|
||||
struct mp_dispatch_queue *dispatch; // for volume/mute/session display
|
||||
HANDLE hUserWake; // mpv-requested wakeup event
|
||||
|
||||
// for setting the audio thread priority
|
||||
HANDLE hTask;
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
#
|
||||
# This file is part of mpv.
|
||||
#
|
||||
# mpv 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.
|
||||
#
|
||||
# mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# PowerShell command line completion for the mpv media player.
|
||||
|
||||
# It can be installed by dot sourcing it in the PowerShell profile.
|
||||
|
||||
$Options = New-Object Collections.Generic.List[Object]
|
||||
|
||||
$DynamicOptions = @(
|
||||
@{ name = 'vaapi-device'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'd3d11-adapter'; pattern = 'description: (.+)' },
|
||||
@{ name = 'vulkan-device'; pattern = "^\s*('.+?')" },
|
||||
@{ name = 'audio-device'; pattern = "^\s*('\S+')" },
|
||||
@{ name = 'hwdec'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'error-diffusion'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'scale'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'cscale'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'dscale'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'tscale'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'profile'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'ao'; pattern = '^\s*([-\w]+)' },
|
||||
@{ name = 'vo'; pattern = '^\s*([-\w]+)' }
|
||||
)
|
||||
|
||||
Function SetOptions
|
||||
{
|
||||
try
|
||||
{
|
||||
$optionContent = mpv --no-config --list-options
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw
|
||||
}
|
||||
|
||||
foreach ($line in $optionContent)
|
||||
{
|
||||
$line = $line.Trim()
|
||||
|
||||
if (-not $line.StartsWith('--'))
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
$name = ''; $value = ''; $type = ''; $choices = $null;
|
||||
|
||||
if ($line.Contains(' '))
|
||||
{
|
||||
$name = $line.Substring(2, $line.IndexOf(' ') - 2)
|
||||
$value = $line.Substring($line.IndexOf(' ') + 1).Trim()
|
||||
|
||||
if ($value.Contains('('))
|
||||
{
|
||||
$value = $value.Substring(0, $value.IndexOf('(')).TrimEnd()
|
||||
}
|
||||
|
||||
$value = $value
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = $line.Substring(2)
|
||||
}
|
||||
|
||||
if ($value.StartsWith('Choices:'))
|
||||
{
|
||||
$type = 'choice'
|
||||
$choices = $value.Substring(8).TrimStart() -split ' '
|
||||
}
|
||||
|
||||
if ($value.StartsWith('Flag'))
|
||||
{
|
||||
$type = 'flag'
|
||||
}
|
||||
|
||||
if ($value.Contains('[file]') -or $name.Contains('-file'))
|
||||
{
|
||||
$type = 'file'
|
||||
}
|
||||
|
||||
$table = @{ name = $name; value = $value; type = $type; choices = $choices }
|
||||
|
||||
if ($type -eq 'flag')
|
||||
{
|
||||
$noTable = @{ name = 'no-' + $name; value = $value; type = ''; choices = $null }
|
||||
$Options.Add($table)
|
||||
$Options.Add($noTable)
|
||||
}
|
||||
else
|
||||
{
|
||||
$Options.Add($table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Function Update-Option($name)
|
||||
{
|
||||
foreach ($it in $Options)
|
||||
{
|
||||
if ($name -eq $it.name)
|
||||
{
|
||||
$option = $it
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $option)
|
||||
{
|
||||
Write-Error "Option $name is unknown."
|
||||
return
|
||||
}
|
||||
|
||||
if ($null -ne $option.choices)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
foreach ($opt in $DynamicOptions)
|
||||
{
|
||||
if ($name -eq $opt.name)
|
||||
{
|
||||
$output = mpv ('--' + $opt.name + '=help') | Select-Object -Skip 1 |
|
||||
Select-String ($opt.pattern) -AllMatches |
|
||||
ForEach-Object { $_.matches.Groups[1].Value } |
|
||||
Select-Object -Unique | Sort-Object
|
||||
|
||||
$output = $output | foreach { if ($_ -match "'\w+'") { $_ -replace "'", '' } else { $_ } }
|
||||
$output = $output | foreach { if ($_ -match "^'.+'$") { $_ -replace "'", '' } else { $_ } }
|
||||
$output = $output | foreach { if ($_.Contains(' ') -or $_.Contains('{')) { '"' + $_ + '"' } else { $_ } }
|
||||
|
||||
if ($output -is [string])
|
||||
{
|
||||
$output = @($output)
|
||||
}
|
||||
|
||||
$output += @('help')
|
||||
$option.choices = $output
|
||||
$option.type = 'choice'
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-Completion($cursorPosition, $wordToComplete, $commandName)
|
||||
{
|
||||
if ($Options.Count -eq 0)
|
||||
{
|
||||
SetOptions
|
||||
}
|
||||
|
||||
if ($commandName.StartsWith('--'))
|
||||
{
|
||||
if ($commandName -like '--*-file*=')
|
||||
{
|
||||
return (Get-ChildItem -file).FullName | Resolve-Path -Relative |
|
||||
ForEach-Object { if ($_.Contains(' ')) { $commandName + "'$_'" } else { $commandName + $_ } }
|
||||
}
|
||||
|
||||
if ($commandName -match '(--.+-file.*=)(.+)')
|
||||
{
|
||||
return (Get-ChildItem -file).FullName | Resolve-Path -Relative |
|
||||
Where-Object { $_.ToLower().Contains($Matches[2].ToLower()) } |
|
||||
ForEach-Object { if ($_.Contains(' ')) { $Matches[1] + "'$_'" } else { $Matches[1] + $_ } }
|
||||
}
|
||||
|
||||
$shortCommandName = $commandName.Substring(2)
|
||||
|
||||
$argName = ''
|
||||
|
||||
if ($commandName.EndsWith('='))
|
||||
{
|
||||
$shortCommandName = $shortCommandName.Substring(0, $shortCommandName.Length -1)
|
||||
}
|
||||
elseif ($commandName.Contains('='))
|
||||
{
|
||||
$shortCommandName = $shortCommandName.Substring(0, $shortCommandName.IndexOf('='))
|
||||
$argName = $commandName.Substring($commandName.IndexOf('=') + 1)
|
||||
}
|
||||
|
||||
foreach ($it in $DynamicOptions)
|
||||
{
|
||||
if ($shortCommandName -eq $it.name)
|
||||
{
|
||||
Update-Option $it.name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
$results = New-Object Collections.Generic.List[Object]
|
||||
|
||||
$exactMatches = $Options | Where-Object { $_.name -eq $shortCommandName }
|
||||
|
||||
foreach ($it in $exactMatches)
|
||||
{
|
||||
if (-not $commandName.Contains('='))
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
$arguments = $null
|
||||
|
||||
if ($it.type -eq 'flag')
|
||||
{
|
||||
$arguments = 'yes', 'no'
|
||||
}
|
||||
|
||||
if ($it.type -eq 'choice' -and $null -ne $it.choices)
|
||||
{
|
||||
$arguments = $it.choices
|
||||
}
|
||||
|
||||
if ($null -ne $arguments)
|
||||
{
|
||||
foreach ($arg in $arguments)
|
||||
{
|
||||
if ($argName -ne '')
|
||||
{
|
||||
if ($arg.StartsWith($argName))
|
||||
{
|
||||
$results.Add('--' + $it.name + '=' + $arg)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$results.Add('--' + $it.name + '=' + $arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $commandName.Contains('='))
|
||||
{
|
||||
$partlyMatches = $Options | Where-Object { $_.name.StartsWith($shortCommandName) }
|
||||
|
||||
foreach ($it in $partlyMatches)
|
||||
{
|
||||
if ($it.name -eq $shortCommandName)
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
$results.Add('--' + $it.name)
|
||||
}
|
||||
}
|
||||
|
||||
return $results
|
||||
}
|
||||
elseif ($commandName -eq '')
|
||||
{
|
||||
return (Get-ChildItem).FullName | Resolve-Path -Relative
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Get-ChildItem).FullName | Resolve-Path -Relative |
|
||||
Where-Object { $_.ToLower().Contains($commandName.ToLower()) }
|
||||
}
|
||||
}
|
||||
|
||||
Register-ArgumentCompleter -Native -CommandName mpv -ScriptBlock {
|
||||
param($commandName, $wordToComplete, $cursorPosition)
|
||||
|
||||
Get-Completion $cursorPosition "$wordToComplete" "$commandName" | ForEach-Object {
|
||||
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
||||
}
|
||||
}
|
||||
|
||||
Register-ArgumentCompleter -Native -CommandName mpvnet -ScriptBlock {
|
||||
param($commandName, $wordToComplete, $cursorPosition)
|
||||
|
||||
Get-Completion $cursorPosition "$wordToComplete" "$commandName" | ForEach-Object {
|
||||
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
||||
}
|
||||
}
|
|
@ -99,12 +99,17 @@ struct config_cache {
|
|||
void *wakeup_cb_ctx;
|
||||
};
|
||||
|
||||
struct force_update {
|
||||
char *name;
|
||||
uint64_t ts;
|
||||
};
|
||||
|
||||
// Per m_config_data state for each m_config_group.
|
||||
struct m_group_data {
|
||||
char *udata; // pointer to group user option struct
|
||||
uint64_t ts; // timestamp of the data copy
|
||||
char **force_updates; // track if any opt in group was written with force_update
|
||||
int force_updates_len;
|
||||
char *udata; // pointer to group user option struct
|
||||
uint64_t ts; // timestamp of the data copy
|
||||
struct force_update **force_update; // tracks opts that are written with force update
|
||||
int force_update_len;
|
||||
};
|
||||
|
||||
static void add_sub_group(struct m_config_shadow *shadow, const char *name_prefix,
|
||||
|
@ -590,23 +595,30 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
|
|||
return m_config_cache_from_shadow(ta_parent, global->config, group);
|
||||
}
|
||||
|
||||
static void clear_force_update_list(struct m_group_data *gsrc)
|
||||
static void append_force_update(struct m_config_cache *cache, struct m_group_data *gdata,
|
||||
const char *opt_name)
|
||||
{
|
||||
int index = 0;
|
||||
while (index < gsrc->force_updates_len) {
|
||||
TA_FREEP(&gsrc->force_updates[index]);
|
||||
++index;
|
||||
for (int i = 0; i < gdata->force_update_len; ++i) {
|
||||
if (strcmp(opt_name, gdata->force_update[i]->name) == 0) {
|
||||
gdata->force_update[i]->ts = gdata->ts;
|
||||
return;
|
||||
}
|
||||
}
|
||||
gsrc->force_updates_len = 0;
|
||||
struct force_update *new_update = talloc_zero(cache, struct force_update);
|
||||
new_update->name = talloc_strdup(cache, opt_name);
|
||||
new_update->ts = gdata->ts;
|
||||
MP_TARRAY_APPEND(cache, gdata->force_update, gdata->force_update_len, new_update);
|
||||
}
|
||||
|
||||
static bool check_force_update_list(struct m_group_data *gsrc, const char *opt_name)
|
||||
static bool check_force_update(struct m_group_data *gdata, const char *opt_name,
|
||||
uint64_t timestamp)
|
||||
{
|
||||
int index = 0;
|
||||
while (index < gsrc->force_updates_len) {
|
||||
if (strcmp(opt_name, gsrc->force_updates[index]) == 0)
|
||||
for (int i = 0; i < gdata->force_update_len; ++i) {
|
||||
if ((strcmp(opt_name, gdata->force_update[i]->name) == 0) &&
|
||||
gdata->force_update[i]->ts == timestamp)
|
||||
{
|
||||
return true;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -637,8 +649,8 @@ static void update_next_option(struct m_config_cache *cache, void **p_opt)
|
|||
|
||||
if (opt->offset >= 0 && opt->type->size) {
|
||||
bool opt_equal = m_option_equal(opt, ddst, dsrc);
|
||||
bool force_update = opt->force_update && gsrc->force_updates_len &&
|
||||
check_force_update_list(gsrc, opt->name);
|
||||
bool force_update = opt->force_update &&
|
||||
check_force_update(gsrc, opt->name, in->ts);
|
||||
if (!opt_equal || force_update) {
|
||||
uint64_t ch = get_opt_change_mask(dst->shadow,
|
||||
in->upd_group, dst->group_index, opt);
|
||||
|
@ -666,8 +678,6 @@ static void update_next_option(struct m_config_cache *cache, void **p_opt)
|
|||
}
|
||||
|
||||
gdst->ts = gsrc->ts;
|
||||
} else {
|
||||
clear_force_update_list(gsrc);
|
||||
}
|
||||
|
||||
in->upd_group++;
|
||||
|
@ -791,10 +801,8 @@ bool m_config_cache_write_opt(struct m_config_cache *cache, void *ptr)
|
|||
}
|
||||
}
|
||||
|
||||
if (opt->force_update) {
|
||||
MP_TARRAY_APPEND(shadow, gsrc->force_updates, gsrc->force_updates_len,
|
||||
talloc_strdup(shadow, opt->name));
|
||||
}
|
||||
if (opt->force_update)
|
||||
append_force_update(cache, gsrc, opt->name);
|
||||
|
||||
mp_mutex_unlock(&shadow->lock);
|
||||
|
||||
|
|
Loading…
Reference in New Issue