#include "avcodec.h"
#include "bytestream.h"
#include "decode.h"
+#include "hwaccel.h"
#include "internal.h"
#include "thread.h"
av_assert0(avci->compat_decode_consumed == 0);
+ if (avci->draining_done && pkt && pkt->size != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Got unexpected packet after EOF\n");
+ avcodec_flush_buffers(avctx);
+ }
+
*got_frame = 0;
avci->compat_decode = 1;
return ret;
}
-static int is_hwaccel_pix_fmt(enum AVPixelFormat pix_fmt)
+enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *avctx,
+ const enum AVPixelFormat *fmt)
{
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
- return desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
-}
+ const AVPixFmtDescriptor *desc;
+ const AVCodecHWConfig *config;
+ int i, n;
+
+ // If a device was supplied when the codec was opened, assume that the
+ // user wants to use it.
+ if (avctx->hw_device_ctx && avctx->codec->hw_configs) {
+ AVHWDeviceContext *device_ctx =
+ (AVHWDeviceContext*)avctx->hw_device_ctx->data;
+ for (i = 0;; i++) {
+ config = &avctx->codec->hw_configs[i]->public;
+ if (!config)
+ break;
+ if (!(config->methods &
+ AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
+ continue;
+ if (device_ctx->type != config->device_type)
+ continue;
+ for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
+ if (config->pix_fmt == fmt[n])
+ return fmt[n];
+ }
+ }
+ }
+ // No device or other setup, so we have to choose from things which
+ // don't any other external information.
+
+ // If the last element of the list is a software format, choose it
+ // (this should be best software format if any exist).
+ for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++);
+ desc = av_pix_fmt_desc_get(fmt[n - 1]);
+ if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
+ return fmt[n - 1];
+
+ // Finally, traverse the list in order and choose the first entry
+ // with no external dependencies (if there is no hardware configuration
+ // information available then this just picks the first entry).
+ for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
+ for (i = 0;; i++) {
+ config = avcodec_get_hw_config(avctx->codec, i);
+ if (!config)
+ break;
+ if (config->pix_fmt == fmt[n])
+ break;
+ }
+ if (!config) {
+ // No specific config available, so the decoder must be able
+ // to handle this format without any additional setup.
+ return fmt[n];
+ }
+ if (config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL) {
+ // Usable with only internal setup.
+ return fmt[n];
+ }
+ }
-enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum AVPixelFormat *fmt)
-{
- while (*fmt != AV_PIX_FMT_NONE && is_hwaccel_pix_fmt(*fmt))
- ++fmt;
- return fmt[0];
+ // Nothing is usable, give up.
+ return AV_PIX_FMT_NONE;
}
-static AVHWAccel *find_hwaccel(AVCodecContext *avctx,
- enum AVPixelFormat pix_fmt)
+int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx,
+ enum AVHWDeviceType dev_type)
{
- AVHWAccel *hwaccel = NULL;
- const AVClass *av_class =
- (avctx->codec->caps_internal & FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS)
- ? avctx->codec->priv_class : NULL;
+ AVHWDeviceContext *device_ctx;
+ AVHWFramesContext *frames_ctx;
+ int ret;
+
+ if (!avctx->hwaccel)
+ return AVERROR(ENOSYS);
+
+ if (avctx->hw_frames_ctx)
+ return 0;
+ if (!avctx->hw_device_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "A hardware frames or device context is "
+ "required for hardware accelerated decoding.\n");
+ return AVERROR(EINVAL);
+ }
- while ((hwaccel = av_hwaccel_next(hwaccel))) {
- if (hwaccel->decoder_class == av_class && hwaccel->id == avctx->codec_id
- && hwaccel->pix_fmt == pix_fmt)
- return hwaccel;
+ device_ctx = (AVHWDeviceContext *)avctx->hw_device_ctx->data;
+ if (device_ctx->type != dev_type) {
+ av_log(avctx, AV_LOG_ERROR, "Device type %s expected for hardware "
+ "decoding, but got %s.\n", av_hwdevice_get_type_name(dev_type),
+ av_hwdevice_get_type_name(device_ctx->type));
+ return AVERROR(EINVAL);
}
- return NULL;
+
+ ret = avcodec_get_hw_frames_parameters(avctx,
+ avctx->hw_device_ctx,
+ avctx->hwaccel->pix_fmt,
+ &avctx->hw_frames_ctx);
+ if (ret < 0)
+ return ret;
+
+ frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
+
+
+ if (frames_ctx->initial_pool_size) {
+ // We guarantee 4 base work surfaces. The function above guarantees 1
+ // (the absolute minimum), so add the missing count.
+ frames_ctx->initial_pool_size += 3;
+
+ // Add an additional surface per thread is frame threading is enabled.
+ if (avctx->active_thread_type & FF_THREAD_FRAME)
+ frames_ctx->initial_pool_size += avctx->thread_count;
+ }
+
+ ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
+ if (ret < 0) {
+ av_buffer_unref(&avctx->hw_frames_ctx);
+ return ret;
+ }
+
+ return 0;
}
-static int setup_hwaccel(AVCodecContext *avctx,
- const enum AVPixelFormat fmt,
- const char *name)
+int avcodec_get_hw_frames_parameters(AVCodecContext *avctx,
+ AVBufferRef *device_ref,
+ enum AVPixelFormat hw_pix_fmt,
+ AVBufferRef **out_frames_ref)
{
- AVHWAccel *hwa = find_hwaccel(avctx, fmt);
- int ret = 0;
+ AVBufferRef *frames_ref = NULL;
+ const AVCodecHWConfigInternal *hw_config;
+ const AVHWAccel *hwa;
+ int i, ret;
- if (!hwa) {
- av_log(avctx, AV_LOG_ERROR,
- "Could not find an AVHWAccel for the pixel format: %s\n",
- name);
+ for (i = 0;; i++) {
+ hw_config = avctx->codec->hw_configs[i];
+ if (!hw_config)
+ return AVERROR(ENOENT);
+ if (hw_config->public.pix_fmt == hw_pix_fmt)
+ break;
+ }
+
+ hwa = hw_config->hwaccel;
+ if (!hwa || !hwa->frame_params)
return AVERROR(ENOENT);
+
+ frames_ref = av_hwframe_ctx_alloc(device_ref);
+ if (!frames_ref)
+ return AVERROR(ENOMEM);
+
+ ret = hwa->frame_params(avctx, frames_ref);
+ if (ret >= 0) {
+ *out_frames_ref = frames_ref;
+ } else {
+ av_buffer_unref(&frames_ref);
}
+ return ret;
+}
- if (hwa->capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL &&
+static int hwaccel_init(AVCodecContext *avctx,
+ const AVCodecHWConfigInternal *hw_config)
+{
+ const AVHWAccel *hwaccel;
+ int err;
+
+ hwaccel = hw_config->hwaccel;
+ if (hwaccel->capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL &&
avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
av_log(avctx, AV_LOG_WARNING, "Ignoring experimental hwaccel: %s\n",
- hwa->name);
+ hwaccel->name);
return AVERROR_PATCHWELCOME;
}
- if (hwa->priv_data_size) {
- avctx->internal->hwaccel_priv_data = av_mallocz(hwa->priv_data_size);
+ if (hwaccel->priv_data_size) {
+ avctx->internal->hwaccel_priv_data =
+ av_mallocz(hwaccel->priv_data_size);
if (!avctx->internal->hwaccel_priv_data)
return AVERROR(ENOMEM);
}
- avctx->hwaccel = hwa;
- if (hwa->init) {
- ret = hwa->init(avctx);
- if (ret < 0) {
+ avctx->hwaccel = hwaccel;
+ if (hwaccel->init) {
+ err = hwaccel->init(avctx);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed setup for format %s: "
+ "hwaccel initialisation returned error.\n",
+ av_get_pix_fmt_name(hw_config->public.pix_fmt));
av_freep(&avctx->internal->hwaccel_priv_data);
avctx->hwaccel = NULL;
- return ret;
+ return err;
}
}
return 0;
}
+static void hwaccel_uninit(AVCodecContext *avctx)
+{
+ if (avctx->hwaccel && avctx->hwaccel->uninit)
+ avctx->hwaccel->uninit(avctx);
+
+ av_freep(&avctx->internal->hwaccel_priv_data);
+
+ avctx->hwaccel = NULL;
+
+ av_buffer_unref(&avctx->hw_frames_ctx);
+}
+
int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt)
{
const AVPixFmtDescriptor *desc;
enum AVPixelFormat *choices;
- enum AVPixelFormat ret;
- unsigned n = 0;
-
- while (fmt[n] != AV_PIX_FMT_NONE)
- ++n;
-
+ enum AVPixelFormat ret, user_choice;
+ const AVCodecHWConfigInternal *hw_config;
+ const AVCodecHWConfig *config;
+ int i, n, err;
+
+ // Find end of list.
+ for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++);
+ // Must contain at least one entry.
av_assert0(n >= 1);
- avctx->sw_pix_fmt = fmt[n - 1];
- av_assert2(!is_hwaccel_pix_fmt(avctx->sw_pix_fmt));
+ // If a software format is available, it must be the last entry.
+ desc = av_pix_fmt_desc_get(fmt[n - 1]);
+ if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
+ // No software format is available.
+ } else {
+ avctx->sw_pix_fmt = fmt[n - 1];
+ }
choices = av_malloc_array(n + 1, sizeof(*choices));
if (!choices)
memcpy(choices, fmt, (n + 1) * sizeof(*choices));
for (;;) {
- if (avctx->hwaccel && avctx->hwaccel->uninit)
- avctx->hwaccel->uninit(avctx);
- av_freep(&avctx->internal->hwaccel_priv_data);
- avctx->hwaccel = NULL;
-
- av_buffer_unref(&avctx->hw_frames_ctx);
+ // Remove the previous hwaccel, if there was one.
+ hwaccel_uninit(avctx);
- ret = avctx->get_format(avctx, choices);
+ user_choice = avctx->get_format(avctx, choices);
+ if (user_choice == AV_PIX_FMT_NONE) {
+ // Explicitly chose nothing, give up.
+ ret = AV_PIX_FMT_NONE;
+ break;
+ }
- desc = av_pix_fmt_desc_get(ret);
+ desc = av_pix_fmt_desc_get(user_choice);
if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid format returned by "
+ "get_format() callback.\n");
ret = AV_PIX_FMT_NONE;
break;
}
+ av_log(avctx, AV_LOG_DEBUG, "Format %s chosen by get_format().\n",
+ desc->name);
- if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
+ for (i = 0; i < n; i++) {
+ if (choices[i] == user_choice)
+ break;
+ }
+ if (i == n) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid return from get_format(): "
+ "%s not in possible list.\n", desc->name);
break;
+ }
- if (avctx->hw_frames_ctx) {
- AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
- if (hw_frames_ctx->format != ret) {
- av_log(avctx, AV_LOG_ERROR, "Format returned from get_buffer() "
- "does not match the format of provided AVHWFramesContext\n");
- ret = AV_PIX_FMT_NONE;
- break;
+ if (avctx->codec->hw_configs) {
+ for (i = 0;; i++) {
+ hw_config = avctx->codec->hw_configs[i];
+ if (!hw_config)
+ break;
+ if (hw_config->public.pix_fmt == user_choice)
+ break;
}
+ } else {
+ hw_config = NULL;
}
- if (!setup_hwaccel(avctx, ret, desc->name))
+ if (!hw_config) {
+ // No config available, so no extra setup required.
+ ret = user_choice;
break;
+ }
+ config = &hw_config->public;
+
+ if (config->methods &
+ AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
+ avctx->hw_frames_ctx) {
+ const AVHWFramesContext *frames_ctx =
+ (AVHWFramesContext*)avctx->hw_frames_ctx->data;
+ if (frames_ctx->format != user_choice) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid setup for format %s: "
+ "does not match the format of the provided frames "
+ "context.\n", desc->name);
+ goto try_again;
+ }
+ } else if (config->methods &
+ AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
+ avctx->hw_device_ctx) {
+ const AVHWDeviceContext *device_ctx =
+ (AVHWDeviceContext*)avctx->hw_device_ctx->data;
+ if (device_ctx->type != config->device_type) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid setup for format %s: "
+ "does not match the type of the provided device "
+ "context.\n", desc->name);
+ goto try_again;
+ }
+ } else if (config->methods &
+ AV_CODEC_HW_CONFIG_METHOD_INTERNAL) {
+ // Internal-only setup, no additional configuration.
+ } else if (config->methods &
+ AV_CODEC_HW_CONFIG_METHOD_AD_HOC) {
+ // Some ad-hoc configuration we can't see and can't check.
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Invalid setup for format %s: "
+ "missing configuration.\n", desc->name);
+ goto try_again;
+ }
+ if (hw_config->hwaccel) {
+ av_log(avctx, AV_LOG_DEBUG, "Format %s requires hwaccel "
+ "initialisation.\n", desc->name);
+ err = hwaccel_init(avctx, hw_config);
+ if (err < 0)
+ goto try_again;
+ }
+ ret = user_choice;
+ break;
- /* Remove failed hwaccel from choices */
- for (n = 0; choices[n] != ret; n++)
- av_assert0(choices[n] != AV_PIX_FMT_NONE);
-
- do
- choices[n] = choices[n + 1];
- while (choices[n++] != AV_PIX_FMT_NONE);
+ try_again:
+ av_log(avctx, AV_LOG_DEBUG, "Format %s not usable, retrying "
+ "get_format() without it.\n", desc->name);
+ for (i = 0; i < n; i++) {
+ if (choices[i] == user_choice)
+ break;
+ }
+ for (; i + 1 < n; i++)
+ choices[i] = choices[i + 1];
+ --n;
}
av_freep(&choices);