From 5ffd6a9e9b7a0d894d7513ad20c24c2727426ecd Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 8 Mar 2014 23:38:53 +0100 Subject: [PATCH] encode: add locking Since the AO will run in a thread, and there's lots of shared state with encoding, we have to add locking. One case this doesn't handle correctly are the encode_lavc_available() calls in ao_lavc.c and vo_lavc.c. They don't do much (and usually only to protect against doing --ao=lavc with normal playback), and changing it would be a bit messy. So just leave them. --- audio/out/ao_lavc.c | 23 +++++++++++++++---- common/encode_lavc.c | 40 +++++++++++++++++++++++++++++---- common/encode_lavc.h | 7 ++++++ video/out/vo_lavc.c | 53 +++++++++++++++++++++++++++++++------------- 4 files changed, 99 insertions(+), 24 deletions(-) diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index 7d31f63ec9..afb021f7a0 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -102,12 +102,14 @@ static int init(struct ao *ao) return -1; } + pthread_mutex_lock(&ao->encode_lavc_ctx->lock); + ac->stream = encode_lavc_alloc_stream(ao->encode_lavc_ctx, AVMEDIA_TYPE_AUDIO); if (!ac->stream) { MP_ERR(ao, "could not get a new audio stream\n"); - return -1; + goto fail; } codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream); @@ -126,7 +128,7 @@ static int init(struct ao *ao) struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_any(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) - return -1; + goto fail; mp_chmap_reorder_to_lavc(&ao->channels); ac->stream->codec->channels = ao->channels.num; ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels); @@ -140,7 +142,7 @@ static int init(struct ao *ao) ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8; if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0) - return -1; + goto fail; ac->pcmhack = 0; if (ac->stream->codec->frame_size <= 1) @@ -169,7 +171,12 @@ static int init(struct ao *ao) ao->untimed = true; + pthread_mutex_unlock(&ao->encode_lavc_ctx->lock); return 0; + +fail: + pthread_mutex_unlock(&ao->encode_lavc_ctx->lock); + return -1; } // close audio device @@ -179,8 +186,11 @@ static void uninit(struct ao *ao, bool cut_audio) struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; + pthread_mutex_lock(&ectx->lock); + if (!encode_lavc_start(ectx)) { MP_WARN(ao, "not even ready to encode audio at end -> dropped"); + pthread_mutex_unlock(&ectx->lock); return; } @@ -192,7 +202,7 @@ static void uninit(struct ao *ao, bool cut_audio) while (encode(ao, outpts, NULL) > 0) ; } - ao->priv = NULL; + pthread_mutex_unlock(&ectx->lock); } // return: how many bytes can be played without blocking @@ -323,8 +333,11 @@ static int play(struct ao *ao, void **data, int samples, int flags) double nextpts; double outpts; + pthread_mutex_lock(&ectx->lock); + if (!encode_lavc_start(ectx)) { MP_WARN(ao, "not ready yet for encoding audio\n"); + pthread_mutex_unlock(&ectx->lock); return 0; } @@ -354,6 +367,7 @@ static int play(struct ao *ao, void **data, int samples, int flags) talloc_free(tmp); } + pthread_mutex_unlock(&ectx->lock); return FFMIN(written, samples); } @@ -444,6 +458,7 @@ static int play(struct ao *ao, void **data, int samples, int flags) ectx->next_in_pts = nextpts; } + pthread_mutex_unlock(&ectx->lock); return bufpos; } diff --git a/common/encode_lavc.c b/common/encode_lavc.c index 5f9e2a0390..5d9853673b 100644 --- a/common/encode_lavc.c +++ b/common/encode_lavc.c @@ -103,6 +103,15 @@ static bool value_has_flag(const char *value, const char *flag) return val; \ } +#define CHECK_FAIL_UNLOCK(ctx, val) \ + if (ctx && (ctx->failed || ctx->finished)) { \ + MP_ERR(ctx, \ + "Called a function on a %s encoding context. Bailing out.\n", \ + ctx->failed ? "failed" : "finished"); \ + pthread_mutex_unlock(&ctx->lock); \ + return val; \ + } + int encode_lavc_available(struct encode_lavc_context *ctx) { CHECK_FAIL(ctx, 0); @@ -134,6 +143,7 @@ struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options, mp_msg_force_stderr(global, true); ctx = talloc_zero(NULL, struct encode_lavc_context); + pthread_mutex_init(&ctx->lock, NULL); ctx->log = mp_log_new(ctx, global->log, "encode-lavc"); ctx->global = global; encode_lavc_discontinuity(ctx); @@ -309,6 +319,7 @@ void encode_lavc_free(struct encode_lavc_context *ctx) encode_lavc_fail(ctx, "called encode_lavc_free without encode_lavc_finish\n"); + pthread_mutex_destroy(&ctx->lock); talloc_free(ctx); } @@ -383,14 +394,18 @@ void encode_lavc_finish(struct encode_lavc_context *ctx) void encode_lavc_set_video_fps(struct encode_lavc_context *ctx, float fps) { + pthread_mutex_lock(&ctx->lock); ctx->vo_fps = fps; + pthread_mutex_unlock(&ctx->lock); } void encode_lavc_set_audio_pts(struct encode_lavc_context *ctx, double pts) { if (ctx) { + pthread_mutex_lock(&ctx->lock); ctx->last_audio_in_pts = pts; ctx->samples_since_last_pts = 0; + pthread_mutex_unlock(&ctx->lock); } } @@ -780,11 +795,15 @@ void encode_lavc_discontinuity(struct encode_lavc_context *ctx) if (!ctx) return; - CHECK_FAIL(ctx, ); + pthread_mutex_lock(&ctx->lock); + + CHECK_FAIL_UNLOCK(ctx, ); ctx->audio_pts_offset = MP_NOPTS_VALUE; ctx->last_video_in_pts = MP_NOPTS_VALUE; ctx->discontinuity_pts_offset = MP_NOPTS_VALUE; + + pthread_mutex_unlock(&ctx->lock); } static void encode_lavc_printoptions(struct mp_log *log, void *obj, @@ -1013,7 +1032,9 @@ int encode_lavc_getstatus(struct encode_lavc_context *ctx, if (!ctx) return -1; - CHECK_FAIL(ctx, -1); + pthread_mutex_lock(&ctx->lock); + + CHECK_FAIL_UNLOCK(ctx, -1); minutes = (now - ctx->t0) / 60.0 * (1 - f) / f; megabytes = ctx->avc->pb ? (avio_size(ctx->avc->pb) / 1048576.0 / f) : 0; @@ -1029,12 +1050,16 @@ int encode_lavc_getstatus(struct encode_lavc_context *ctx, snprintf(buf, bufsize, "{%.1fmin %.1fMB}", minutes, megabytes); buf[bufsize - 1] = 0; + + pthread_mutex_unlock(&ctx->lock); return 0; } void encode_lavc_expect_stream(struct encode_lavc_context *ctx, int mt) { - CHECK_FAIL(ctx, ); + pthread_mutex_lock(&ctx->lock); + + CHECK_FAIL_UNLOCK(ctx, ); switch (mt) { case AVMEDIA_TYPE_VIDEO: @@ -1044,11 +1069,18 @@ void encode_lavc_expect_stream(struct encode_lavc_context *ctx, int mt) ctx->expect_audio = true; break; } + + pthread_mutex_unlock(&ctx->lock); } bool encode_lavc_didfail(struct encode_lavc_context *ctx) { - return ctx && ctx->failed; + if (!ctx) + return false; + pthread_mutex_lock(&ctx->lock); + bool fail = ctx && ctx->failed; + pthread_mutex_unlock(&ctx->lock); + return fail; } void encode_lavc_fail(struct encode_lavc_context *ctx, const char *format, ...) diff --git a/common/encode_lavc.h b/common/encode_lavc.h index deaf42b684..af7f4fba3d 100644 --- a/common/encode_lavc.h +++ b/common/encode_lavc.h @@ -22,6 +22,8 @@ #ifndef MPLAYER_ENCODE_LAVC_H #define MPLAYER_ENCODE_LAVC_H +#include + #include #include #include @@ -37,6 +39,11 @@ struct encode_lavc_context { struct encode_output_conf *options; struct mp_log *log; + // All entry points must be guarded with the lock. Functions called by + // the playback core lock this automatically, but ao_lavc.c and vo_lavc.c + // must lock manually before accessing state. + pthread_mutex_t lock; + float vo_fps; // these are processed from the options diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index e96556b1e8..b5176ebcb4 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -74,19 +74,21 @@ static int preinit(struct vo *vo) return 0; } -static void draw_image(struct vo *vo, mp_image_t *mpi); +static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi); static void uninit(struct vo *vo) { struct priv *vc = vo->priv; if (!vc) return; + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); + if (vc->lastipts >= 0 && vc->stream) - draw_image(vo, NULL); + draw_image_unlocked(vo, NULL); mp_image_unrefp(&vc->lastimg); - vo->priv = NULL; + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); } static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) @@ -101,6 +103,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) if (!vc) return -1; + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); + display_aspect_ratio.num = params->d_w; display_aspect_ratio.den = params->d_h; image_aspect_ratio.num = width; @@ -123,7 +127,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) vc->stream->codec->sample_aspect_ratio.den, aspect.num, aspect.den); } - return 0; + goto done; } /* FIXME Is it possible with raw video? */ @@ -168,9 +172,12 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) mp_image_unrefp(&vc->lastimg); +done: + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); return 0; error: + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); uninit(vo); return -1; } @@ -182,14 +189,12 @@ static int query_format(struct vo *vo, uint32_t format) if (!vo->encode_lavc_ctx) return 0; - if (!encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt)) - return 0; - - return - VFCAP_CSP_SUPPORTED | - // we can do it - VFCAP_CSP_SUPPORTED_BY_HW; - // we don't convert colorspaces here + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); + int flags = 0; + if (encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt)) + flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW; + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); + return flags; } static void write_packet(struct vo *vo, int size, AVPacket *packet) @@ -272,7 +277,7 @@ static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet) } } -static void draw_image(struct vo *vo, mp_image_t *mpi) +static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) { struct priv *vc = vo->priv; struct encode_lavc_context *ectx = vo->encode_lavc_ctx; @@ -479,6 +484,13 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) } } +static void draw_image(struct vo *vo, mp_image_t *mpi) +{ + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); + draw_image_unlocked(vo, mpi); + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); +} + static void flip_page_timed(struct vo *vo, int64_t pts_us, int duration) { } @@ -487,6 +499,8 @@ static void draw_osd(struct vo *vo, struct osd_state *osd) { struct priv *vc = vo->priv; + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); + if (vc->lastimg && vc->lastimg_wants_osd && vo->params) { struct mp_osd_res dim = osd_res_from_image_params(vo->params); @@ -495,11 +509,15 @@ static void draw_osd(struct vo *vo, struct osd_state *osd) osd_draw_on_image(osd, dim, osd_get_vo_pts(osd), OSD_DRAW_SUB_ONLY, vc->lastimg); } + + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); } static int control(struct vo *vo, uint32_t request, void *data) { struct priv *vc = vo->priv; + int r = VO_NOTIMPL; + pthread_mutex_lock(&vo->encode_lavc_ctx->lock); switch (request) { case VOCTRL_SET_YUV_COLORSPACE: vc->colorspace = *(struct mp_csp_details *)data; @@ -509,12 +527,15 @@ static int control(struct vo *vo, uint32_t request, void *data) vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream); vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream); } - return 1; + r = 1; + break; case VOCTRL_GET_YUV_COLORSPACE: *(struct mp_csp_details *)data = vc->colorspace; - return 1; + r = 1; + break; } - return VO_NOTIMPL; + pthread_mutex_unlock(&vo->encode_lavc_ctx->lock); + return r; } const struct vo_driver video_out_lavc = {