mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-13 18:40:03 +01:00
lavc: Use hardware config information in ff_get_format()
This removes the dependency that hardware pixel formats previously had on AVHWAccel instances, meaning only those which actually do something need exist after this patch. Also updates avcodec_default_get_format() to be able to choose hardware formats if either a matching device has been supplied or no additional external configuration is required, and avcodec_get_hw_frames_parameters() to use the hardware config rather than searching the old hwaccel list.
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
#include "avcodec.h"
|
#include "avcodec.h"
|
||||||
#include "bytestream.h"
|
#include "bytestream.h"
|
||||||
#include "decode.h"
|
#include "decode.h"
|
||||||
|
#include "hwaccel.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
@@ -644,29 +645,67 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
|
|||||||
return ret;
|
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);
|
const AVPixFmtDescriptor *desc;
|
||||||
return desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
|
const AVCodecHWConfig *config;
|
||||||
}
|
int i, n;
|
||||||
|
|
||||||
enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum AVPixelFormat *fmt)
|
// If a device was supplied when the codec was opened, assume that the
|
||||||
{
|
// user wants to use it.
|
||||||
while (*fmt != AV_PIX_FMT_NONE && is_hwaccel_pix_fmt(*fmt))
|
if (avctx->hw_device_ctx && avctx->codec->hw_configs) {
|
||||||
++fmt;
|
AVHWDeviceContext *device_ctx =
|
||||||
return fmt[0];
|
(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.
|
||||||
|
|
||||||
static AVHWAccel *find_hwaccel(enum AVCodecID codec_id,
|
// If the last element of the list is a software format, choose it
|
||||||
enum AVPixelFormat pix_fmt)
|
// (this should be best software format if any exist).
|
||||||
{
|
for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++);
|
||||||
AVHWAccel *hwaccel = NULL;
|
desc = av_pix_fmt_desc_get(fmt[n - 1]);
|
||||||
|
if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
|
||||||
|
return fmt[n - 1];
|
||||||
|
|
||||||
while ((hwaccel = av_hwaccel_next(hwaccel)))
|
// Finally, traverse the list in order and choose the first entry
|
||||||
if (hwaccel->id == codec_id
|
// with no external dependencies (if there is no hardware configuration
|
||||||
&& hwaccel->pix_fmt == pix_fmt)
|
// information available then this just picks the first entry).
|
||||||
return hwaccel;
|
for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
|
||||||
return NULL;
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing is usable, give up.
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx,
|
int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx,
|
||||||
@@ -730,9 +769,19 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx,
|
|||||||
AVBufferRef **out_frames_ref)
|
AVBufferRef **out_frames_ref)
|
||||||
{
|
{
|
||||||
AVBufferRef *frames_ref = NULL;
|
AVBufferRef *frames_ref = NULL;
|
||||||
AVHWAccel *hwa = find_hwaccel(avctx->codec_id, hw_pix_fmt);
|
const AVCodecHWConfigInternal *hw_config;
|
||||||
int ret;
|
const AVHWAccel *hwa;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
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)
|
if (!hwa || !hwa->frame_params)
|
||||||
return AVERROR(ENOENT);
|
return AVERROR(ENOENT);
|
||||||
|
|
||||||
@@ -749,52 +798,66 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_hwaccel(AVCodecContext *avctx,
|
static int hwaccel_init(AVCodecContext *avctx,
|
||||||
const enum AVPixelFormat fmt,
|
const AVCodecHWConfigInternal *hw_config)
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
AVHWAccel *hwa = find_hwaccel(avctx->codec_id, fmt);
|
const AVHWAccel *hwaccel;
|
||||||
int ret = 0;
|
int err;
|
||||||
|
|
||||||
if (!hwa) {
|
hwaccel = hw_config->hwaccel;
|
||||||
av_log(avctx, AV_LOG_ERROR,
|
if (hwaccel->priv_data_size) {
|
||||||
"Could not find an AVHWAccel for the pixel format: %s",
|
avctx->internal->hwaccel_priv_data =
|
||||||
name);
|
av_mallocz(hwaccel->priv_data_size);
|
||||||
return AVERROR(ENOENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwa->priv_data_size) {
|
|
||||||
avctx->internal->hwaccel_priv_data = av_mallocz(hwa->priv_data_size);
|
|
||||||
if (!avctx->internal->hwaccel_priv_data)
|
if (!avctx->internal->hwaccel_priv_data)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
avctx->hwaccel = hwa;
|
avctx->hwaccel = (AVHWAccel*)hwaccel;
|
||||||
if (hwa->init) {
|
err = hwaccel->init(avctx);
|
||||||
ret = hwa->init(avctx);
|
if (err < 0) {
|
||||||
if (ret < 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);
|
av_freep(&avctx->internal->hwaccel_priv_data);
|
||||||
avctx->hwaccel = NULL;
|
avctx->hwaccel = NULL;
|
||||||
return ret;
|
return err;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt)
|
||||||
{
|
{
|
||||||
const AVPixFmtDescriptor *desc;
|
const AVPixFmtDescriptor *desc;
|
||||||
enum AVPixelFormat *choices;
|
enum AVPixelFormat *choices;
|
||||||
enum AVPixelFormat ret;
|
enum AVPixelFormat ret, user_choice;
|
||||||
unsigned n = 0;
|
const AVCodecHWConfigInternal *hw_config;
|
||||||
|
const AVCodecHWConfig *config;
|
||||||
while (fmt[n] != AV_PIX_FMT_NONE)
|
int i, n, err;
|
||||||
++n;
|
|
||||||
|
|
||||||
|
// Find end of list.
|
||||||
|
for (n = 0; fmt[n] != AV_PIX_FMT_NONE; n++);
|
||||||
|
// Must contain at least one entry.
|
||||||
av_assert0(n >= 1);
|
av_assert0(n >= 1);
|
||||||
|
// 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];
|
avctx->sw_pix_fmt = fmt[n - 1];
|
||||||
av_assert2(!is_hwaccel_pix_fmt(avctx->sw_pix_fmt));
|
}
|
||||||
|
|
||||||
choices = av_malloc_array(n + 1, sizeof(*choices));
|
choices = av_malloc_array(n + 1, sizeof(*choices));
|
||||||
if (!choices)
|
if (!choices)
|
||||||
@@ -803,44 +866,108 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt)
|
|||||||
memcpy(choices, fmt, (n + 1) * sizeof(*choices));
|
memcpy(choices, fmt, (n + 1) * sizeof(*choices));
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (avctx->hwaccel && avctx->hwaccel->uninit)
|
// Remove the previous hwaccel, if there was one.
|
||||||
avctx->hwaccel->uninit(avctx);
|
hwaccel_uninit(avctx);
|
||||||
av_freep(&avctx->internal->hwaccel_priv_data);
|
|
||||||
avctx->hwaccel = NULL;
|
|
||||||
|
|
||||||
av_buffer_unref(&avctx->hw_frames_ctx);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
ret = avctx->get_format(avctx, choices);
|
desc = av_pix_fmt_desc_get(user_choice);
|
||||||
|
|
||||||
desc = av_pix_fmt_desc_get(ret);
|
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Invalid format returned by "
|
||||||
|
"get_format() callback.\n");
|
||||||
ret = AV_PIX_FMT_NONE;
|
ret = AV_PIX_FMT_NONE;
|
||||||
break;
|
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++) {
|
||||||
break;
|
if (choices[i] == user_choice)
|
||||||
|
|
||||||
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;
|
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 (!setup_hwaccel(avctx, ret, desc->name))
|
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 (!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;
|
break;
|
||||||
|
|
||||||
/* Remove failed hwaccel from choices */
|
try_again:
|
||||||
for (n = 0; choices[n] != ret; n++)
|
av_log(avctx, AV_LOG_DEBUG, "Format %s not usable, retrying "
|
||||||
av_assert0(choices[n] != AV_PIX_FMT_NONE);
|
"get_format() without it.\n", desc->name);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
do
|
if (choices[i] == user_choice)
|
||||||
choices[n] = choices[n + 1];
|
break;
|
||||||
while (choices[n++] != AV_PIX_FMT_NONE);
|
}
|
||||||
|
for (; i + 1 < n; i++)
|
||||||
|
choices[i] = choices[i + 1];
|
||||||
|
--n;
|
||||||
}
|
}
|
||||||
|
|
||||||
av_freep(&choices);
|
av_freep(&choices);
|
||||||
|
|||||||
@@ -272,6 +272,12 @@ int ff_side_data_update_matrix_encoding(AVFrame *frame,
|
|||||||
* Select the (possibly hardware accelerated) pixel format.
|
* Select the (possibly hardware accelerated) pixel format.
|
||||||
* This is a wrapper around AVCodecContext.get_format() and should be used
|
* This is a wrapper around AVCodecContext.get_format() and should be used
|
||||||
* instead of calling get_format() directly.
|
* instead of calling get_format() directly.
|
||||||
|
*
|
||||||
|
* The list of pixel formats must contain at least one valid entry, and is
|
||||||
|
* terminated with AV_PIX_FMT_NONE. If it is possible to decode to software,
|
||||||
|
* the last entry in the list must be the most accurate software format.
|
||||||
|
* If it is not possible to decode to software, AVCodecContext.sw_pix_fmt
|
||||||
|
* must be set before calling this function.
|
||||||
*/
|
*/
|
||||||
int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt);
|
int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user