deps/media-playback: Fix hw accel decode crash

When hardware accelerated decoding is enabled, sometimes it can't
initialize for whatever reason, so it will fall back to software on its
own.  When this occurs, it will not use the hardware pixel format on the
frame; instead it will defer to a standard format on the frame.  So if
the frame format does not match the expected format, assume software
decoding.  (This was also what the hw-decode.c FFmpeg example did if the
format did not match the expected format)
This commit is contained in:
jp9000 2019-08-24 18:12:02 -07:00
parent 41e9dd38fa
commit 94581952b5
2 changed files with 23 additions and 10 deletions

View file

@ -29,7 +29,8 @@ enum AVHWDeviceType hw_priority[] = {
AV_HWDEVICE_TYPE_NONE,
};
static bool has_hw_type(AVCodec *c, enum AVHWDeviceType type)
static bool has_hw_type(AVCodec *c, enum AVHWDeviceType type,
enum AVPixelFormat *hw_format)
{
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(c, i);
@ -38,8 +39,10 @@ static bool has_hw_type(AVCodec *c, enum AVHWDeviceType type)
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == type)
config->device_type == type) {
*hw_format = config->pix_fmt;
return true;
}
}
return false;
@ -51,7 +54,7 @@ static void init_hw_decoder(struct mp_decode *d, AVCodecContext *c)
AVBufferRef *hw_ctx = NULL;
while (*priority != AV_HWDEVICE_TYPE_NONE) {
if (has_hw_type(d->codec, *priority)) {
if (has_hw_type(d->codec, *priority, &d->hw_format)) {
int ret = av_hwdevice_ctx_create(&hw_ctx, *priority,
NULL, NULL, 0);
if (ret == 0)
@ -63,6 +66,7 @@ static void init_hw_decoder(struct mp_decode *d, AVCodecContext *c)
if (hw_ctx) {
c->hw_device_ctx = av_buffer_ref(hw_ctx);
c->opaque = d;
d->hw = true;
}
}
@ -158,8 +162,8 @@ bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw)
return false;
}
d->frame = av_frame_alloc();
if (!d->frame) {
d->sw_frame = av_frame_alloc();
if (!d->sw_frame) {
blog(LOG_WARNING, "MP: Failed to allocate %s frame",
av_get_media_type_string(type));
return false;
@ -175,7 +179,7 @@ bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw)
d->in_frame = d->hw_frame;
} else {
d->in_frame = d->frame;
d->in_frame = d->sw_frame;
}
if (d->codec->capabilities & CODEC_CAP_TRUNC)
@ -213,9 +217,9 @@ void mp_decode_free(struct mp_decode *d)
avcodec_close(d->decoder);
#endif
}
if (d->frame) {
av_frame_unref(d->frame);
av_free(d->frame);
if (d->sw_frame) {
av_frame_unref(d->sw_frame);
av_free(d->sw_frame);
}
memset(d, 0, sizeof(*d));
@ -293,13 +297,20 @@ static int decode_packet(struct mp_decode *d, int *got_frame)
#ifdef USE_NEW_HARDWARE_CODEC_METHOD
if (*got_frame && ret && d->hw) {
int err = av_hwframe_transfer_data(d->frame, d->hw_frame, 0);
if (d->hw_frame->format != d->hw_format) {
d->frame = d->hw_frame;
return ret;
}
int err = av_hwframe_transfer_data(d->sw_frame, d->hw_frame, 0);
if (err != 0) {
ret = 0;
*got_frame = false;
}
}
#endif
d->frame = d->sw_frame;
return ret;
}

View file

@ -64,8 +64,10 @@ struct mp_decode {
int64_t frame_pts;
int64_t next_pts;
AVFrame *in_frame;
AVFrame *sw_frame;
AVFrame *hw_frame;
AVFrame *frame;
enum AVPixelFormat hw_format;
bool got_first_keyframe;
bool frame_ready;
bool eof;