summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/decode/ad_lavc.c24
-rw-r--r--filters/f_decoder_wrapper.c24
-rw-r--r--filters/f_decoder_wrapper.h13
-rw-r--r--input/sdl_gamepad.c4
-rw-r--r--player/client.c239
-rw-r--r--video/decode/vd_lavc.c59
-rw-r--r--video/out/vo_drm.c1
7 files changed, 232 insertions, 132 deletions
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index fbd6ad32ac..a420021f4a 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -48,7 +48,7 @@ struct priv {
bool preroll_done;
double next_pts;
AVRational codec_timebase;
- bool eof_returned;
+ struct lavc_state state;
struct mp_decoder public;
};
@@ -159,10 +159,10 @@ static void reset(struct mp_filter *da)
ctx->trim_samples = 0;
ctx->preroll_done = false;
ctx->next_pts = MP_NOPTS_VALUE;
- ctx->eof_returned = false;
+ ctx->state = (struct lavc_state){0};
}
-static bool send_packet(struct mp_filter *da, struct demux_packet *mpkt)
+static int send_packet(struct mp_filter *da, struct demux_packet *mpkt)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
@@ -177,16 +177,12 @@ static bool send_packet(struct mp_filter *da, struct demux_packet *mpkt)
mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase);
int ret = avcodec_send_packet(avctx, mpkt ? &pkt : NULL);
-
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- return false;
-
if (ret < 0)
MP_ERR(da, "Error decoding audio.\n");
- return true;
+ return ret;
}
-static bool receive_frame(struct mp_filter *da, struct mp_frame *out)
+static int receive_frame(struct mp_filter *da, struct mp_frame *out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
@@ -198,7 +194,7 @@ static bool receive_frame(struct mp_filter *da, struct mp_frame *out)
// over in case we get new packets at some point in the future.
// (Dont' reset the filter itself, we want to keep other state.)
avcodec_flush_buffers(priv->avctx);
- return false;
+ return ret;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
MP_ERR(da, "Error decoding audio.\n");
}
@@ -209,14 +205,14 @@ static bool receive_frame(struct mp_filter *da, struct mp_frame *out)
#endif
if (!priv->avframe->buf[0])
- return true;
+ return ret;
double out_pts = mp_pts_from_av(priv->avframe->pts, &priv->codec_timebase);
struct mp_aframe *mpframe = mp_aframe_from_avframe(priv->avframe);
if (!mpframe) {
MP_ERR(da, "Converting libavcodec frame to mpv frame failed.\n");
- return true;
+ return ret;
}
if (priv->force_channel_map.num)
@@ -264,14 +260,14 @@ static bool receive_frame(struct mp_filter *da, struct mp_frame *out)
av_frame_unref(priv->avframe);
- return true;
+ return ret;
}
static void process(struct mp_filter *ad)
{
struct priv *priv = ad->priv;
- lavc_process(ad, &priv->eof_returned, send_packet, receive_frame);
+ lavc_process(ad, &priv->state, send_packet, receive_frame);
}
static const struct mp_filter_info ad_lavc_filter = {
diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c
index f18247abaf..4347917152 100644
--- a/filters/f_decoder_wrapper.c
+++ b/filters/f_decoder_wrapper.c
@@ -22,6 +22,7 @@
#include <assert.h>
#include <libavutil/buffer.h>
+#include <libavutil/common.h>
#include <libavutil/rational.h>
#include "config.h"
@@ -814,22 +815,23 @@ error:
return NULL;
}
-void lavc_process(struct mp_filter *f, bool *eof_flag,
- bool (*send)(struct mp_filter *f, struct demux_packet *pkt),
- bool (*receive)(struct mp_filter *f, struct mp_frame *res))
+void lavc_process(struct mp_filter *f, struct lavc_state *state,
+ int (*send)(struct mp_filter *f, struct demux_packet *pkt),
+ int (*receive)(struct mp_filter *f, struct mp_frame *res))
{
if (!mp_pin_in_needs_data(f->ppins[1]))
return;
struct mp_frame frame = {0};
- if (!receive(f, &frame)) {
- if (!*eof_flag)
+ int ret_recv = receive(f, &frame);
+ if (ret_recv == AVERROR_EOF) {
+ if (!state->eof_returned)
mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
- *eof_flag = true;
+ state->eof_returned = true;
} else if (frame.type) {
- *eof_flag = false;
+ state->eof_returned = false;
mp_pin_in_write(f->ppins[1], frame);
- } else {
+ } else if (ret_recv == AVERROR(EAGAIN)) {
// Need to feed a packet.
frame = mp_pin_out_read(f->ppins[0]);
struct demux_packet *pkt = NULL;
@@ -843,7 +845,8 @@ void lavc_process(struct mp_filter *f, bool *eof_flag,
}
return;
}
- if (!send(f, pkt)) {
+ int ret_send = send(f, pkt);
+ if (ret_send == AVERROR(EAGAIN)) {
// Should never happen, but can happen with broken decoders.
MP_WARN(f, "could not consume packet\n");
mp_pin_out_unread(f->ppins[0], frame);
@@ -852,5 +855,8 @@ void lavc_process(struct mp_filter *f, bool *eof_flag,
}
talloc_free(pkt);
mp_filter_internal_mark_progress(f);
+ } else {
+ // Just try again.
+ mp_filter_internal_mark_progress(f);
}
}
diff --git a/filters/f_decoder_wrapper.h b/filters/f_decoder_wrapper.h
index b69c0c7680..28d9b5cb7b 100644
--- a/filters/f_decoder_wrapper.h
+++ b/filters/f_decoder_wrapper.h
@@ -109,11 +109,14 @@ extern const struct mp_decoder_fns vd_lavc;
extern const struct mp_decoder_fns ad_lavc;
extern const struct mp_decoder_fns ad_spdif;
-// Convenience wrapper for lavc based decoders. eof_flag must be set to false
-// on init and resets.
-void lavc_process(struct mp_filter *f, bool *eof_flag,
- bool (*send)(struct mp_filter *f, struct demux_packet *pkt),
- bool (*receive)(struct mp_filter *f, struct mp_frame *res));
+// Convenience wrapper for lavc based decoders. Treat lavc_state as private;
+// init to all-0 on init and resets.
+struct lavc_state {
+ bool eof_returned;
+};
+void lavc_process(struct mp_filter *f, struct lavc_state *state,
+ int (*send)(struct mp_filter *f, struct demux_packet *pkt),
+ int (*receive)(struct mp_filter *f, struct mp_frame *res));
// ad_spdif.c
struct mp_decoder_list *select_spdif_codec(const char *codec, const char *pref);
diff --git a/input/sdl_gamepad.c b/input/sdl_gamepad.c
index 23997bfa07..96b8409b49 100644
--- a/input/sdl_gamepad.c
+++ b/input/sdl_gamepad.c
@@ -29,7 +29,7 @@ struct gamepad_priv {
static Uint32 gamepad_cancel_wakeup;
-static void initalize_events()
+static void initialize_events(void)
{
gamepad_cancel_wakeup = SDL_RegisterEvents(1);
}
@@ -204,7 +204,7 @@ static void read_gamepad_thread(struct mp_input_src *src, void *param)
return;
};
- pthread_once(&events_initialized, initalize_events);
+ pthread_once(&events_initialized, initialize_events);
if (gamepad_cancel_wakeup == (Uint32)-1) {
MP_ERR(src, "Can't register SDL custom events\n");
diff --git a/player/client.c b/player/client.c
index a1ce20fd74..3462845aed 100644
--- a/player/client.c
+++ b/player/client.c
@@ -39,6 +39,7 @@
#include "options/m_property.h"
#include "options/path.h"
#include "options/parse_configfile.h"
+#include "osdep/atomic.h"
#include "osdep/threads.h"
#include "osdep/timer.h"
#include "osdep/io.h"
@@ -65,6 +66,8 @@ struct mp_client_api {
pthread_mutex_t lock;
+ atomic_bool uses_vo_libmpv;
+
// -- protected by lock
struct mpv_handle **clients;
@@ -81,19 +84,23 @@ struct mp_client_api {
};
struct observe_property {
+ struct mpv_handle *owner;
char *name;
int id; // ==mp_get_property_id(name)
uint64_t event_mask; // ==mp_get_property_event_mask(name)
int64_t reply_id;
mpv_format format;
+ const struct m_option *type;
bool changed; // property change should be signaled to user
- bool need_new_value; // a new value should be retrieved
- bool updating; // a new value is being retrieved
- bool updated; // a new value was successfully retrieved
bool dead; // property unobserved while retrieving value
- bool new_value_valid, user_value_valid;
- union m_option_value new_value, user_value;
- struct mpv_handle *client;
+ bool value_valid;
+ union m_option_value value;
+ // Only if async. update is used.
+ uint64_t async_change_ts; // logical timestamp incremented on each change
+ uint64_t async_value_ts; // logical timestamp for async_value contents
+ bool async_updating; // if true, updating async_value_ts to change_ts
+ bool async_value_valid;
+ union m_option_value async_value;
};
struct mpv_handle {
@@ -129,12 +136,12 @@ struct mpv_handle {
int first_event; // events[first_event] is the first readable event
int num_events; // number of readable events
int reserved_events; // number of entries reserved for replies
+ size_t async_counter; // pending other async events
bool choked; // recovering from queue overflow
struct observe_property **properties;
int num_properties;
int lowest_changed; // attempt at making change processing incremental
- int properties_updating;
uint64_t property_event_masks; // or-ed together event masks of all properties
bool fuzzy_initialized; // see scripting.c wait_loaded()
@@ -143,7 +150,7 @@ struct mpv_handle {
};
static bool gen_log_message_event(struct mpv_handle *ctx);
-static bool gen_property_change_event(struct mpv_handle *ctx);
+static bool gen_property_change_event(struct mpv_handle *ctx, bool *unlocked);
static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask);
void mp_clients_init(struct MPContext *mpctx)
@@ -359,7 +366,7 @@ static void unlock_core(mpv_handle *ctx)
void mpv_wait_async_requests(mpv_handle *ctx)
{
pthread_mutex_lock(&ctx->lock);
- while (ctx->reserved_events || ctx->properties_updating)
+ while (ctx->reserved_events || ctx->async_counter)
wait_wakeup(ctx, INT64_MAX);
pthread_mutex_unlock(&ctx->lock);
}
@@ -838,12 +845,15 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
talloc_steal(event, event->data);
break;
}
+ bool unlocked = false;
// If there's a changed property, generate change event (never queued).
- if (gen_property_change_event(ctx))
+ if (gen_property_change_event(ctx, &unlocked))
break;
// Pop item from message queue, and return as event.
if (gen_log_message_event(ctx))
break;
+ if (unlocked)
+ continue;
int r = wait_wakeup(ctx, deadline);
if (r == ETIMEDOUT)
break;
@@ -1417,17 +1427,20 @@ int mpv_get_property_async(mpv_handle *ctx, uint64_t ud, const char *name,
static void property_free(void *p)
{
struct observe_property *prop = p;
- const struct m_option *type = get_mp_type_get(prop->format);
- if (type) {
- m_option_free(type, &prop->new_value);
- m_option_free(type, &prop->user_value);
+
+ assert(!prop->async_updating);
+
+ if (prop->type) {
+ m_option_free(prop->type, &prop->value);
+ m_option_free(prop->type, &prop->async_value);
}
}
int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
const char *name, mpv_format format)
{
- if (format != MPV_FORMAT_NONE && !get_mp_type_get(format))
+ const struct m_option *type = get_mp_type_get(format);
+ if (format != MPV_FORMAT_NONE && !type)
return MPV_ERROR_PROPERTY_FORMAT;
// Explicitly disallow this, because it would require a special code path.
if (format == MPV_FORMAT_OSD_STRING)
@@ -1437,15 +1450,14 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
struct observe_property *prop = talloc_ptrtype(ctx, prop);
talloc_set_destructor(prop, property_free);
*prop = (struct observe_property){
- .client = ctx,
+ .owner = ctx,
.name = talloc_strdup(prop, name),
.id = mp_get_property_id(ctx->mpctx, name),
.event_mask = mp_get_property_event_mask(name),
.reply_id = userdata,
.format = format,
+ .type = type,
.changed = true,
- .updating = false,
- .updated = false,
};
MP_TARRAY_APPEND(ctx, ctx->properties, ctx->num_properties, prop);
ctx->property_event_masks |= prop->event_mask;
@@ -1458,33 +1470,24 @@ static void mark_property_changed(struct mpv_handle *client, int index)
{
struct observe_property *prop = client->properties[index];
prop->changed = true;
+ prop->async_change_ts += 1;
client->lowest_changed = MPMIN(client->lowest_changed, index);
}
int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
{
pthread_mutex_lock(&ctx->lock);
- ctx->property_event_masks = 0;
int count = 0;
- for (int n = ctx->num_properties - 1; n >= 0; n--) {
+ for (int n = 0; n < ctx->num_properties; n++) {
struct observe_property *prop = ctx->properties[n];
- if (prop->reply_id == userdata) {
- if (prop->updating) {
- prop->dead = true;
- } else {
- // In case mpv_unobserve_property() is called after mpv_wait_event()
- // returned, and the mpv_event still references the name somehow,
- // make sure it's not freed while in use. The same can happen
- // with the value update mechanism.
- talloc_steal(ctx->cur_event, prop);
- }
- MP_TARRAY_REMOVE_AT(ctx->properties, ctx->num_properties, n);
+ // Perform actual removal of the property lazily to avoid creating
+ // dangling pointers and such.
+ if (prop->reply_id == userdata && !prop->dead) {
+ mark_property_changed(ctx, n);
+ prop->dead = true;
count++;
}
- if (!prop->dead)
- ctx->property_event_masks |= prop->event_mask;
}
- ctx->lowest_changed = 0;
pthread_mutex_unlock(&ctx->lock);
return count;
}
@@ -1524,13 +1527,19 @@ static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask)
wakeup_client(ctx);
}
-static void update_prop(void *p)
+static void update_prop_async(void *p)
{
struct observe_property *prop = p;
- struct mpv_handle *ctx = prop->client;
+ struct mpv_handle *ctx = prop->owner;
- const struct m_option *type = get_mp_type_get(prop->format);
union m_option_value val = {0};
+ bool val_valid = false;
+ uint64_t value_ts;
+
+ pthread_mutex_lock(&ctx->lock);
+ value_ts = prop->async_change_ts;
+ assert(prop->async_updating);
+ pthread_mutex_unlock(&ctx->lock);
struct getproperty_request req = {
.mpctx = ctx->mpctx,
@@ -1538,72 +1547,153 @@ static void update_prop(void *p)
.format = prop->format,
.data = &val,
};
-
getproperty_fn(&req);
+ val_valid = req.status >= 0;
pthread_mutex_lock(&ctx->lock);
- ctx->properties_updating--;
- prop->updating = false;
- m_option_free(type, &prop->new_value);
- prop->new_value_valid = req.status >= 0;
- if (prop->new_value_valid)
- memcpy(&prop->new_value, &val, type->type->size);
- if (prop->user_value_valid != prop->new_value_valid) {
- prop->updated = true;
- } else if (prop->user_value_valid && prop->new_value_valid) {
- if (!equal_mpv_value(&prop->user_value, &prop->new_value, prop->format))
- prop->updated = true;
- }
- if (prop->dead)
- talloc_steal(ctx->cur_event, prop);
+
+ assert(prop->async_updating);
+
+ // Move to prop->async_value
+ m_option_free(prop->type, &prop->async_value);
+ memcpy(&prop->async_value, &val, prop->type->type->size);
+ prop->async_value_valid = val_valid;
+
+ prop->async_value_ts = value_ts;
+ prop->async_updating = false;
+
+ // Cause it to re-check the property.
+ prop->changed = true;
+ ctx->lowest_changed = 0;
+
+ ctx->async_counter -= 1;
wakeup_client(ctx);
+
pthread_mutex_unlock(&ctx->lock);
}
+static bool update_prop(struct mpv_handle *ctx, struct observe_property *prop)
+{
+ if (!prop->type)
+ return true;
+
+ union m_option_value val = {0};
+ bool val_valid = false;
+
+ // With vo_libmpv, we can't lock the core for stupid reasons.
+ // Yes, that's FUCKING HORRIBLE. On the other hand, might be useful for
+ // true async. properties in the future.
+ if (atomic_load_explicit(&ctx->clients->uses_vo_libmpv, memory_order_relaxed)) {
+ if (prop->async_change_ts > prop->async_value_ts) {
+ if (!prop->async_updating) {
+ prop->async_updating = true;
+ ctx->async_counter += 1;
+ mp_dispatch_enqueue(ctx->mpctx->dispatch, update_prop_async, prop);
+ }
+ return false; // re-update later when the changed value comes in
+ }
+
+ m_option_copy(prop->type, &val, &prop->async_value);
+ val_valid = prop->async_value_valid;
+ } else {
+ pthread_mutex_unlock(&ctx->lock);
+
+ struct getproperty_request req = {
+ .mpctx = ctx->mpctx,
+ .name = prop->name,
+ .format = prop->format,
+ .data = &val,
+ };
+ run_locked(ctx, getproperty_fn, &req);
+ val_valid = req.status >= 0;
+
+ pthread_mutex_lock(&ctx->lock);
+ }
+
+ bool changed = prop->value_valid != val_valid;
+ if (prop->value_valid && val_valid)
+ changed = !equal_mpv_value(&prop->value, &val, prop->format);
+
+ if (changed) {
+ prop->value_valid = val_valid;
+ if (val_valid) {
+ // move val to prop->value
+ m_option_free(prop->type, &prop->value);
+ memcpy(&prop->value, &val, prop->type->type->size);
+ val_valid = false;
+ }
+ }
+
+ if (val_valid)
+ m_option_free(prop->type, &val);
+
+ return changed;
+}
+
// Set ctx->cur_event to a generated property change event, if there is any
// outstanding property.
-static bool gen_property_change_event(struct mpv_handle *ctx)
+static bool gen_property_change_event(struct mpv_handle *ctx, bool *unlocked)
{
if (!ctx->mpctx->initialized)
return false;
+
+ *ctx->cur_event = (struct mpv_event){
+ .event_id = MPV_EVENT_NONE,
+ };
+
+ bool need_gc = false;
int start = ctx->lowest_changed;
ctx->lowest_changed = ctx->num_properties;
for (int n = start; n < ctx->num_properties; n++) {
struct observe_property *prop = ctx->properties[n];
- if ((prop->changed || prop->updating || prop->updated) && n < ctx->lowest_changed)
+ if (prop->changed && n < ctx->lowest_changed)
ctx->lowest_changed = n;
- if (prop->changed) {
+
+ bool updated = false;
+ if (prop->changed && !prop->dead) {
prop->changed = false;
- if (prop->format != MPV_FORMAT_NONE) {
- ctx->properties_updating++;
- prop->updating = true;
- mp_dispatch_enqueue(ctx->mpctx->dispatch, update_prop, prop);
- } else {
- prop->updated = true;
- }
+ updated = update_prop(ctx, prop);
+ *unlocked = true; // not always; but good enough
}
- bool updated = prop->updated;
- prop->updated = false;
- if (updated) {
- const struct m_option *type = get_mp_type_get(prop->format);
- prop->user_value_valid = prop->new_value_valid;
- if (prop->new_value_valid)
- m_option_copy(type, &prop->user_value, &prop->new_value);
+
+ if (prop->dead) {
+ need_gc = true;
+ } else if (updated) {
ctx->cur_property_event = (struct mpv_event_property){
.name = prop->name,
- .format = prop->user_value_valid ? prop->format : 0,
+ .format = prop->value_valid ? prop->format : 0,
+ .data = prop->value_valid ? &prop->value : NULL,
};
- if (prop->user_value_valid)
- ctx->cur_property_event.data = &prop->user_value;
*ctx->cur_event = (struct mpv_event){
.event_id = MPV_EVENT_PROPERTY_CHANGE,
.reply_userdata = prop->reply_id,
.data = &ctx->cur_property_event,
};
- return true;
+ break;
}
}
- return false;
+
+ if (need_gc) {
+ // Remove entries which have the .dead flag set. The point of doing this
+ // here is to ensure that this does not conflict with update_prop(),
+ // and that a previously returned mpv_event struct pointing to removed
+ // property entries does not result in dangling pointers.
+ ctx->property_event_masks = 0;
+ ctx->lowest_changed = 0;
+ for (int n = ctx->num_properties - 1; n >= 0; n--) {
+ struct observe_property *prop = ctx->properties[n];
+ if (prop->dead) {
+ if (!prop->async_updating) {
+ MP_TARRAY_REMOVE_AT(ctx->properties, ctx->num_properties, n);
+ talloc_free(prop);
+ }
+ } else {
+ ctx->property_event_masks |= prop->event_mask;
+ }
+ }
+ }
+
+ return !!ctx->cur_event->event_id;
}
int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
@@ -1828,6 +1918,7 @@ bool mp_set_main_render_context(struct mp_client_api *client_api,
if (res)
client_api->render_context = active ? ctx : NULL;
pthread_mutex_unlock(&client_api->lock);
+ atomic_store(&client_api->uses_vo_libmpv, active);
return res;
}
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 6c356e2e01..93cb261766 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -172,7 +172,7 @@ typedef struct lavc_ctx {
AVRational codec_timebase;
enum AVDiscard skip_frame;
bool flushing;
- bool eof_returned;
+ struct lavc_state state;
const char *decoder;
bool hwdec_requested;
bool hwdec_failed;
@@ -974,17 +974,17 @@ static bool do_send_packet(struct mp_filter *vd, struct demux_packet *pkt)
AVCodecContext *avctx = ctx->avctx;
if (!prepare_decoding(vd))
- return false;
+ return AVERROR_UNKNOWN;
if (avctx->skip_frame == AVDISCARD_ALL)
- return true;
+ return AVERROR(EAGAIN);
AVPacket avpkt;
mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase);
int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- return false;
+ return ret;
if (ctx->hw_probing && ctx->num_sent_packets < 32) {
pkt = pkt ? demux_copy_packet(pkt) : NULL;
@@ -993,38 +993,41 @@ static bool do_send_packet(struct mp_filter *vd, struct demux_packet *pkt)
if (ret < 0)
handle_err(vd);
- return true;
+ return ret;
}
-static bool send_queued(struct mp_filter *vd)
+static int send_queued(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- while (ctx->num_requeue_packets && do_send_packet(vd, ctx->requeue_packets[0]))
- {
+ while (ctx->num_requeue_packets) {
+ int ret = do_send_packet(vd, ctx->requeue_packets[0]);
+ if (ret < 0)
+ return ret;
talloc_free(ctx->requeue_packets[0]);
MP_TARRAY_REMOVE_AT(ctx->requeue_packets, ctx->num_requeue_packets, 0);
}
- return ctx->num_requeue_packets == 0;
+ return 0;
}
-static bool send_packet(struct mp_filter *vd, struct demux_packet *pkt)
+static int send_packet(struct mp_filter *vd, struct demux_packet *pkt)
{
- if (!send_queued(vd))
+ int ret = send_queued(vd);
+ if (ret < 0)
return false;
return do_send_packet(vd, pkt);
}
// Returns whether decoder is still active (!EOF state).
-static bool decode_frame(struct mp_filter *vd)
+static int decode_frame(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
if (!prepare_decoding(vd))
- return true;
+ return AVERROR(EAGAIN);
int ret = avcodec_receive_frame(avctx, ctx->pic);
if (ret == AVERROR_EOF) {
@@ -1034,20 +1037,20 @@ static bool decode_frame(struct mp_filter *vd)
// the delay queue has been drained.
if (!ctx->num_delay_queue)
reset_avctx(vd);
- return false;
+ return ret;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
handle_err(vd);
}
if (!ctx->pic->buf[0])
- return true;
+ return ret;
ctx->hwdec_fail_count = 0;
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
if (!mpi) {
av_frame_unref(ctx->pic);
- return true;
+ return ret;
}
assert(mpi->planes[0] || mpi->planes[3]);
mpi->pts = mp_pts_from_av(ctx->pic->pts, &ctx->codec_timebase);
@@ -1061,14 +1064,14 @@ static bool decode_frame(struct mp_filter *vd)
av_frame_unref(ctx->pic);
MP_TARRAY_APPEND(ctx, ctx->delay_queue, ctx->num_delay_queue, mpi);
- return true;
+ return ret;
}
-static bool receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
+static int receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- bool progress = decode_frame(vd);
+ int ret = decode_frame(vd);
if (ctx->hwdec_failed) {
// Failed hardware decoding? Try again in software.
@@ -1083,21 +1086,21 @@ static bool receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
ctx->num_requeue_packets = num_pkts;
send_queued(vd);
- progress = decode_frame(vd);
+ ret = decode_frame(vd);
}
if (!ctx->num_delay_queue)
- return progress;
+ return ret;
- if (ctx->num_delay_queue <= ctx->max_delay_queue && progress)
- return true;
+ if (ctx->num_delay_queue <= ctx->max_delay_queue && ret >= 0)
+ return AVERROR(EAGAIN);
struct mp_image *res = ctx->delay_queue[0];
MP_TARRAY_REMOVE_AT(ctx->delay_queue, ctx->num_delay_queue, 0);
res = res ? mp_img_swap_to_native(res) : NULL;
if (!res)
- return progress;
+ return ret;
if (ctx->use_hwdec && ctx->hwdec.copying && res->hwctx) {
struct mp_image *sw = mp_image_hw_download(res, ctx->hwdec_swpool);
@@ -1107,7 +1110,7 @@ static bool receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
MP_ERR(vd, "Could not copy back hardware decoded frame.\n");
ctx->hwdec_fail_count = INT_MAX - 1; // force fallback
handle_err(vd);
- return true;
+ return ret;
}
}
@@ -1129,7 +1132,7 @@ static bool receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
}
*out_frame = MAKE_FRAME(MP_FRAME_VIDEO, res);
- return true;
+ return ret;
}
static int control(struct mp_filter *vd, enum dec_ctrl cmd, void *arg)
@@ -1169,7 +1172,7 @@ static void process(struct mp_filter *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- lavc_process(vd, &ctx->eof_returned, send_packet, receive_frame);
+ lavc_process(vd, &ctx->state, send_packet, receive_frame);
}
static void reset(struct mp_filter *vd)
@@ -1178,7 +1181,7 @@ static void reset(struct mp_filter *vd)
flush_all(vd);
- ctx->eof_returned = false;
+ ctx->state = (struct lavc_state){0};
ctx->framedrop_flags = 0;
}
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
index ce48779a82..83dede468c 100644
--- a/video/out/vo_drm.c
+++ b/video/out/vo_drm.c
@@ -304,6 +304,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
int h = p->dst.y1 - p->dst.y0;
mp_sws_set_from_cmdline(p->sws, vo->global);
+ p->sws->allow_zimg = true;
p->sws->src = *params;
p->sws->dst = (struct mp_image_params) {
.imgfmt = p->imgfmt,