win-dshow, obs-ffmpeg: Add hardware decoding support
Fixes hardware decoding support and updates it to FFmpeg 4.0.
This commit is contained in:
155
deps/media-playback/media-playback/decode.c
vendored
155
deps/media-playback/media-playback/decode.c
vendored
@@ -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
|
||||
|
3
deps/media-playback/media-playback/decode.h
vendored
3
deps/media-playback/media-playback/decode.h
vendored
@@ -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;
|
||||
|
Reference in New Issue
Block a user