video: properly pass through ICC data

The same should happen with any other side data that matters to mpv,
otherwise filters will drop it.

(No, don't try to argue that mpv should use AVFrame. That won't work.)

ffmpeg_garbage() is copy&paste from frame_new_side_data() in FFmpeg
(roughly feed201849b8f91), because it's not public API. The name
reflects my opinion about FFmpeg's API.

In mp_image_to_av_frame(), change the too-fragile

  *new_ref = (struct mp_image){0};

into explicitly zeroing out the fields that are "transferred" to the
created AVFrame.
This commit is contained in:
wm4 2017-10-16 16:36:21 +02:00
parent a7464c4ed8
commit 0a7c5a130e
4 changed files with 62 additions and 8 deletions

View File

@ -352,3 +352,38 @@ int mp_set_avopts(struct mp_log *log, void *avobj, char **kv)
}
return success;
}
AVFrameSideData *ffmpeg_garbage(AVFrame *frame,
enum AVFrameSideDataType type,
AVBufferRef *buf)
{
AVFrameSideData *ret, **tmp;
if (!buf)
return NULL;
if (frame->nb_side_data > INT_MAX / sizeof(*frame->side_data) - 1)
goto fail;
tmp = av_realloc(frame->side_data,
(frame->nb_side_data + 1) * sizeof(*frame->side_data));
if (!tmp)
goto fail;
frame->side_data = tmp;
ret = av_mallocz(sizeof(*ret));
if (!ret)
goto fail;
ret->buf = buf;
ret->data = ret->buf->data;
ret->size = buf->size;
ret->type = type;
frame->side_data[frame->nb_side_data++] = ret;
return ret;
fail:
av_buffer_unref(&buf);
return NULL;
}

View File

@ -46,5 +46,8 @@ const char *mp_codec_from_av_codec_id(int codec_id);
void mp_set_avdict(struct AVDictionary **dict, char **kv);
void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d);
int mp_set_avopts(struct mp_log *log, void *avobj, char **kv);
AVFrameSideData *ffmpeg_garbage(AVFrame *frame,
enum AVFrameSideDataType type,
AVBufferRef *buf);
#endif

View File

@ -1167,12 +1167,6 @@ static bool decode_frame(struct dec_video *vd)
mp_pts_from_av(av_frame_get_pkt_duration(ctx->pic), &ctx->codec_timebase);
#endif
#if HAVE_AVUTIL_ICC_PROFILE
sd = av_frame_get_side_data(ctx->pic, AV_FRAME_DATA_ICC_PROFILE);
if (sd)
mpi->icc_profile = av_buffer_ref(sd->buf);
#endif
update_image_params(vd, ctx->pic, &mpi->params);
av_frame_unref(ctx->pic);

View File

@ -29,6 +29,7 @@
#include "mpv_talloc.h"
#include "config.h"
#include "common/av_common.h"
#include "common/common.h"
#include "hwdec.h"
#include "mp_image.h"
@ -826,6 +827,7 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
struct mp_image *mp_image_from_av_frame(struct AVFrame *src)
{
struct mp_image *dst = &(struct mp_image){0};
AVFrameSideData *sd;
for (int p = 0; p < MP_MAX_PLANES; p++)
dst->bufs[p] = src->buf[p];
@ -880,9 +882,16 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *src)
}
#endif
#if HAVE_AVUTIL_ICC_PROFILE
sd = av_frame_get_side_data(src, AV_FRAME_DATA_ICC_PROFILE);
if (sd)
dst->icc_profile = av_buffer_ref(sd->buf);
#endif
return mp_image_new_ref(dst);
}
// Convert the mp_image reference to a AVFrame reference.
struct AVFrame *mp_image_to_av_frame(struct mp_image *src)
{
@ -894,10 +903,13 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src)
return NULL;
}
for (int p = 0; p < MP_MAX_PLANES; p++)
for (int p = 0; p < MP_MAX_PLANES; p++) {
dst->buf[p] = new_ref->bufs[p];
new_ref->bufs[p] = NULL;
}
dst->hw_frames_ctx = new_ref->hwctx;
new_ref->hwctx = NULL;
dst->format = imgfmt2pixfmt(src->imgfmt);
dst->width = src->w;
@ -935,8 +947,18 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src)
*(struct mp_image_params *)dst->opaque_ref->data = src->params;
#endif
*new_ref = (struct mp_image){0};
#if HAVE_AVUTIL_ICC_PROFILE
if (src->icc_profile) {
AVFrameSideData *sd =
ffmpeg_garbage(dst, AV_FRAME_DATA_ICC_PROFILE, new_ref->icc_profile);
if (!sd)
abort();
new_ref->icc_profile = NULL;
}
#endif
talloc_free(new_ref);
if (dst->format == AV_PIX_FMT_NONE)
av_frame_free(&dst);
return dst;