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.
This commit is contained in:
wm4 2014-03-08 23:38:53 +01:00
parent b48d09a89d
commit 5ffd6a9e9b
4 changed files with 99 additions and 24 deletions

View File

@ -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;
}

View File

@ -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, ...)

View File

@ -22,6 +22,8 @@
#ifndef MPLAYER_ENCODE_LAVC_H
#define MPLAYER_ENCODE_LAVC_H
#include <pthread.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avstring.h>
@ -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

View File

@ -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 = {