win-dshow, obs-ffmpeg: Add hardware decoding support

Fixes hardware decoding support and updates it to FFmpeg 4.0.
This commit is contained in:
jp9000
2019-07-27 21:44:30 -07:00
parent baddca2536
commit a3fface27f
5 changed files with 204 additions and 57 deletions

View File

@@ -17,29 +17,58 @@
#include "decode.h"
#include "media.h"
static AVCodec *find_hardware_decoder(enum AVCodecID id)
{
AVHWAccel *hwa = av_hwaccel_next(NULL);
AVCodec *c = NULL;
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(58, 4, 100)
#define USE_NEW_HARDWARE_CODEC_METHOD
#endif
while (hwa) {
if (hwa->id == id) {
if (hwa->pix_fmt == AV_PIX_FMT_VDTOOL ||
hwa->pix_fmt == AV_PIX_FMT_DXVA2_VLD ||
hwa->pix_fmt == AV_PIX_FMT_VAAPI_VLD) {
c = avcodec_find_decoder_by_name(hwa->name);
if (c)
break;
}
#ifdef USE_NEW_HARDWARE_CODEC_METHOD
enum AVHWDeviceType hw_priority[] = {
AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_NONE,
};
static bool has_hw_type(AVCodec *c, enum AVHWDeviceType type)
{
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(c, i);
if (!config) {
break;
}
hwa = av_hwaccel_next(hwa);
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == type)
return true;
}
return c;
return false;
}
static int mp_open_codec(struct mp_decode *d)
static void init_hw_decoder(struct mp_decode *d, AVCodecContext *c)
{
enum AVHWDeviceType *priority = hw_priority;
AVBufferRef *hw_ctx = NULL;
while (*priority != AV_HWDEVICE_TYPE_NONE) {
if (has_hw_type(d->codec, *priority)) {
int ret = av_hwdevice_ctx_create(&hw_ctx, *priority,
NULL, NULL, 0);
if (ret == 0)
break;
}
priority++;
}
if (hw_ctx) {
c->hw_device_ctx = av_buffer_ref(hw_ctx);
d->hw = true;
}
}
#endif
static int mp_open_codec(struct mp_decode *d, bool hw)
{
AVCodecContext *c;
int ret;
@@ -58,6 +87,13 @@ static int mp_open_codec(struct mp_decode *d)
c = d->stream->codec;
#endif
d->hw = false;
#ifdef USE_NEW_HARDWARE_CODEC_METHOD
if (hw)
init_hw_decoder(d, c);
#endif
if (c->thread_count == 1 && c->codec_id != AV_CODEC_ID_PNG &&
c->codec_id != AV_CODEC_ID_TIFF &&
c->codec_id != AV_CODEC_ID_JPEG2000 &&
@@ -101,35 +137,25 @@ bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw)
id = stream->codec->codec_id;
#endif
if (hw) {
d->codec = find_hardware_decoder(id);
if (d->codec) {
ret = mp_open_codec(d);
if (ret < 0)
d->codec = NULL;
}
}
if (id == AV_CODEC_ID_VP8)
d->codec = avcodec_find_decoder_by_name("libvpx");
else if (id == AV_CODEC_ID_VP9)
d->codec = avcodec_find_decoder_by_name("libvpx-vp9");
if (!d->codec)
d->codec = avcodec_find_decoder(id);
if (!d->codec) {
if (id == AV_CODEC_ID_VP8)
d->codec = avcodec_find_decoder_by_name("libvpx");
else if (id == AV_CODEC_ID_VP9)
d->codec = avcodec_find_decoder_by_name("libvpx-vp9");
blog(LOG_WARNING, "MP: Failed to find %s codec",
av_get_media_type_string(type));
return false;
}
if (!d->codec)
d->codec = avcodec_find_decoder(id);
if (!d->codec) {
blog(LOG_WARNING, "MP: Failed to find %s codec",
av_get_media_type_string(type));
return false;
}
ret = mp_open_codec(d);
if (ret < 0) {
blog(LOG_WARNING, "MP: Failed to open %s decoder: %s",
av_get_media_type_string(type), av_err2str(ret));
return false;
}
ret = mp_open_codec(d, hw);
if (ret < 0) {
blog(LOG_WARNING, "MP: Failed to open %s decoder: %s",
av_get_media_type_string(type), av_err2str(ret));
return false;
}
d->frame = av_frame_alloc();
@@ -139,6 +165,19 @@ bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw)
return false;
}
if (d->hw) {
d->hw_frame = av_frame_alloc();
if (!d->hw_frame) {
blog(LOG_WARNING, "MP: Failed to allocate %s hw frame",
av_get_media_type_string(type));
return false;
}
d->in_frame = d->hw_frame;
} else {
d->in_frame = d->frame;
}
if (d->codec->capabilities & CODEC_CAP_TRUNC)
d->decoder->flags |= CODEC_FLAG_TRUNC;
return true;
@@ -163,6 +202,10 @@ void mp_decode_free(struct mp_decode *d)
mp_decode_clear_packets(d);
circlebuf_free(&d->packets);
if (d->hw_frame) {
av_frame_unref(d->hw_frame);
av_free(d->hw_frame);
}
if (d->decoder) {
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 40, 101)
avcodec_free_context(&d->decoder);
@@ -190,8 +233,8 @@ static inline int64_t get_estimated_duration(struct mp_decode *d,
return d->frame_pts - last_pts;
if (d->audio) {
return av_rescale_q(d->frame->nb_samples,
(AVRational){1, d->frame->sample_rate},
return av_rescale_q(d->in_frame->nb_samples,
(AVRational){1, d->in_frame->sample_rate},
(AVRational){1, 1000000000});
} else {
if (d->last_duration)
@@ -209,7 +252,7 @@ static int decode_packet(struct mp_decode *d, int *got_frame)
*got_frame = 0;
#ifdef USE_NEW_FFMPEG_DECODE_API
ret = avcodec_receive_frame(d->decoder, d->frame);
ret = avcodec_receive_frame(d->decoder, d->in_frame);
if (ret != 0 && ret != AVERROR(EAGAIN)) {
if (ret == AVERROR_EOF)
ret = 0;
@@ -224,7 +267,7 @@ static int decode_packet(struct mp_decode *d, int *got_frame)
return ret;
}
ret = avcodec_receive_frame(d->decoder, d->frame);
ret = avcodec_receive_frame(d->decoder, d->in_frame);
if (ret != 0 && ret != AVERROR(EAGAIN)) {
if (ret == AVERROR_EOF)
ret = 0;
@@ -240,13 +283,23 @@ static int decode_packet(struct mp_decode *d, int *got_frame)
#else
if (d->audio) {
ret = avcodec_decode_audio4(d->decoder, d->frame, got_frame,
ret = avcodec_decode_audio4(d->decoder, d->in_frame, got_frame,
&d->pkt);
} else {
ret = avcodec_decode_video2(d->decoder, d->frame, got_frame,
ret = avcodec_decode_video2(d->decoder, d->in_frame, got_frame,
&d->pkt);
}
#endif
#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 (err != 0) {
ret = 0;
*got_frame = false;
}
}
#endif
return ret;
}
@@ -319,15 +372,15 @@ bool mp_decode_next(struct mp_decode *d)
if (d->frame_ready) {
int64_t last_pts = d->frame_pts;
if (d->frame->best_effort_timestamp == AV_NOPTS_VALUE)
if (d->in_frame->best_effort_timestamp == AV_NOPTS_VALUE)
d->frame_pts = d->next_pts;
else
d->frame_pts =
av_rescale_q(d->frame->best_effort_timestamp,
av_rescale_q(d->in_frame->best_effort_timestamp,
d->stream->time_base,
(AVRational){1, 1000000000});
int64_t duration = d->frame->pkt_duration;
int64_t duration = d->in_frame->pkt_duration;
if (!duration)
duration = get_estimated_duration(d, last_pts);
else

View File

@@ -63,10 +63,13 @@ struct mp_decode {
int64_t last_duration;
int64_t frame_pts;
int64_t next_pts;
AVFrame *in_frame;
AVFrame *hw_frame;
AVFrame *frame;
bool got_first_keyframe;
bool frame_ready;
bool eof;
bool hw;
AVPacket orig_pkt;
AVPacket pkt;