}
}
-AVPixelFormat get_vaapi_hw_format(AVCodecContext *ctx, const AVPixelFormat *fmt)
+template<AVHWDeviceType type>
+AVPixelFormat get_hw_format(AVCodecContext *ctx, const AVPixelFormat *fmt)
{
for (const AVPixelFormat *fmt_ptr = fmt; *fmt_ptr != -1; ++fmt_ptr) {
for (int i = 0;; ++i) { // Termination condition inside loop.
break;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
- config->device_type == AV_HWDEVICE_TYPE_VAAPI &&
+ config->device_type == type &&
config->pix_fmt == *fmt_ptr) {
return config->pix_fmt;
}
}
// Seemingly, it's not too easy to make something that just initializes
- // “whatever goes”, so we don't get VDPAU or CUDA here without enumerating
- // through several different types. VA-API will do for now.
+ // “whatever goes”, so we don't get CUDA or VULKAN or whatever here
+ // without enumerating through several different types.
+ // VA-API and VDPAU will do for now. We prioritize VDPAU for the
+ // simple reason that there's a VA-API-via-VDPAU emulation for NVidia
+ // cards that seems to work, but just hangs when trying to transfer the frame.
+ //
+ // Note that we don't actually check codec support beforehand,
+ // so if you have a low-end VDPAU device but a high-end VA-API device,
+ // you lose out on the extra codec support from the latter.
AVBufferRef *hw_device_ctx = nullptr;
- if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0) < 0) {
- fprintf(stderr, "Failed to initialize VA-API for FFmpeg acceleration. Decoding video in software.\n");
- } else {
+ if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VDPAU, nullptr, nullptr, 0) >= 0) {
+ video_codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
+ video_codec_ctx->get_format = get_hw_format<AV_HWDEVICE_TYPE_VDPAU>;
+ } else if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0) >= 0) {
video_codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
- video_codec_ctx->get_format = get_vaapi_hw_format;
+ video_codec_ctx->get_format = get_hw_format<AV_HWDEVICE_TYPE_VAAPI>;
+ } else {
+ fprintf(stderr, "Failed to initialize VA-API or VDPAU for FFmpeg acceleration. Decoding video in software.\n");
}
if (avcodec_open2(video_codec_ctx.get(), video_codec, nullptr) < 0) {
// Main loop.
bool first_frame = true;
+ int consecutive_errors = 0;
while (!producer_thread_should_quit.should_quit()) {
if (process_queued_commands(format_ctx.get(), pathname, last_modified, /*rewound=*/nullptr)) {
return true;
AVFrameWithDeleter frame = decode_frame(format_ctx.get(), video_codec_ctx.get(), audio_codec_ctx.get(),
pathname, video_stream_index, audio_stream_index, subtitle_stream_index, audio_frame.get(), &audio_format, &audio_pts, &error);
if (error) {
- return false;
+ if (++consecutive_errors >= 100) {
+ fprintf(stderr, "More than 100 consecutive video frames, aborting playback.\n");
+ return false;
+ } else {
+ continue;
+ }
+ } else {
+ consecutive_errors = 0;
}
if (frame == nullptr) {
// EOF. Loop back to the start if we can.
// Decode video, if we have a frame.
int err = avcodec_receive_frame(video_codec_ctx, video_avframe.get());
if (err == 0) {
- if (video_avframe->format == AV_PIX_FMT_VAAPI) {
+ if (video_avframe->format == AV_PIX_FMT_VAAPI ||
+ video_avframe->format == AV_PIX_FMT_VDPAU) {
// Get the frame down to the CPU. (TODO: See if we can keep it
// on the GPU all the way, since it will be going up again later.
// However, this only works if the OpenGL GPU is the same one.)