|
@@ -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); |
|
|
|
|
|
return desc->flags & AV_PIX_FMT_FLAG_HWACCEL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static AVHWAccel *find_hwaccel(enum AVCodecID codec_id, |
|
|
|
|
|
enum AVPixelFormat pix_fmt) |
|
|
|
|
|
{ |
|
|
|
|
|
AVHWAccel *hwaccel = NULL; |
|
|
|
|
|
|
|
|
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]; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
while ((hwaccel = av_hwaccel_next(hwaccel))) |
|
|
|
|
|
if (hwaccel->id == codec_id |
|
|
|
|
|
&& hwaccel->pix_fmt == pix_fmt) |
|
|
|
|
|
return hwaccel; |
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
// 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); |
|
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
const AVCodecHWConfigInternal *hw_config; |
|
|
|
|
|
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, |
|
|
|
|
|
const enum AVPixelFormat fmt, |
|
|
|
|
|
const char *name) |
|
|
|
|
|
|
|
|
static int hwaccel_init(AVCodecContext *avctx, |
|
|
|
|
|
const AVCodecHWConfigInternal *hw_config) |
|
|
{ |
|
|
{ |
|
|
AVHWAccel *hwa = find_hwaccel(avctx->codec_id, fmt); |
|
|
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (!hwa) { |
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, |
|
|
|
|
|
"Could not find an AVHWAccel for the pixel format: %s", |
|
|
|
|
|
name); |
|
|
|
|
|
return AVERROR(ENOENT); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const AVHWAccel *hwaccel; |
|
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
if (hwa->priv_data_size) { |
|
|
|
|
|
avctx->internal->hwaccel_priv_data = av_mallocz(hwa->priv_data_size); |
|
|
|
|
|
|
|
|
hwaccel = hw_config->hwaccel; |
|
|
|
|
|
if (hwaccel->priv_data_size) { |
|
|
|
|
|
avctx->internal->hwaccel_priv_data = |
|
|
|
|
|
av_mallocz(hwaccel->priv_data_size); |
|
|
if (!avctx->internal->hwaccel_priv_data) |
|
|
if (!avctx->internal->hwaccel_priv_data) |
|
|
return AVERROR(ENOMEM); |
|
|
return AVERROR(ENOMEM); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
avctx->hwaccel = hwa; |
|
|
|
|
|
if (hwa->init) { |
|
|
|
|
|
ret = hwa->init(avctx); |
|
|
|
|
|
if (ret < 0) { |
|
|
|
|
|
av_freep(&avctx->internal->hwaccel_priv_data); |
|
|
|
|
|
avctx->hwaccel = NULL; |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
avctx->hwaccel = (AVHWAccel*)hwaccel; |
|
|
|
|
|
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 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; |
|
|
|
|
|
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); |
|
|
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)); |
|
|
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) |
|
|
|
|
|
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) { |
|
|
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++) { |
|
|
|
|
|
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; |
|
|
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; |
|
|
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); |
|
|
av_freep(&choices); |
|
|