Files
packages/anda/multimedia/ffmpeg/ffmpeg-HandBrake.patch
T
2026-06-02 08:36:55 -05:00

2811 lines
117 KiB
Diff

From c4abce42f746dd95e79d7498b1a45789ecb0e61f Mon Sep 17 00:00:00 2001
From: Terra Packaging Team <terra@fyralabs.com>
Date: Mon, 1 Jun 2026 23:18:31 -0500
Subject: [PATCH] Patch for HandBrake
Patch based off a very similar patch by Negativo17 but formatted as a Git patch.
See: https://github.com/HandBrake/HandBrake/tree/2f464fcf93d411ebdd969b39d67739ed658c5e58
---
configure | 5 +
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/amfenc.c | 154 +++++++-
libavcodec/amfenc.h | 3 +
libavcodec/amfenc_av1.c | 17 +-
libavcodec/amfenc_h264.c | 11 +-
libavcodec/amfenc_hevc.c | 16 +-
libavcodec/av1dec.c | 36 +-
libavcodec/av1dec.h | 3 +-
libavcodec/bsf/hevc_mp4toannexb.c | 6 +-
libavcodec/ccaption_dec.c | 9 +-
libavcodec/dvdsubdec.c | 43 ++-
libavcodec/hwaccels.h | 1 +
libavcodec/libdav1d.c | 14 +
libavcodec/mf_utils.c | 2 +
libavcodec/mf_utils.h | 1 +
libavcodec/mfenc.c | 1 +
libavcodec/pgssubdec.c | 554 ++++++++++++++++++-----------
libavcodec/qsvenc.c | 49 +++
libavcodec/qsvenc.h | 8 +-
libavcodec/qsvenc_av1.c | 4 +
libavcodec/videotoolbox.c | 56 ++-
libavcodec/videotoolbox_av1.c | 105 ++++++
libavcodec/vt_internal.h | 4 +
libavfilter/vf_vpp_qsv.c | 6 +-
libavformat/isom.h | 3 +
libavformat/matroskaenc.c | 10 +
libavformat/mov.c | 229 +++++++++++-
libavformat/movenc.c | 213 ++++++++---
libavutil/frame.c | 1 +
libavutil/frame.h | 6 +
libavutil/hwcontext_videotoolbox.c | 12 +-
libswscale/swscale_unscaled.c | 6 +-
34 files changed, 1276 insertions(+), 314 deletions(-)
create mode 100644 libavcodec/videotoolbox_av1.c
diff --git a/configure b/configure
index 1652b0d..3a47be0 100755
--- a/configure
+++ b/configure
@@ -2464,6 +2464,7 @@ TYPES_LIST="
kCMVideoCodecType_HEVC
kCMVideoCodecType_HEVCWithAlpha
kCMVideoCodecType_VP9
+ kCMVideoCodecType_AV1
kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange
kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange
@@ -3170,6 +3171,8 @@ av1_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferAV1_bit_depth_idx"
av1_vaapi_hwaccel_select="av1_decoder"
av1_vdpau_hwaccel_deps="vdpau VdpPictureInfoAV1"
av1_vdpau_hwaccel_select="av1_decoder"
+av1_videotoolbox_hwaccel_deps="videotoolbox"
+av1_videotoolbox_hwaccel_select="av1_decoder"
av1_vulkan_hwaccel_deps="vulkan"
av1_vulkan_hwaccel_select="av1_decoder"
h263_vaapi_hwaccel_deps="vaapi"
@@ -3340,6 +3343,7 @@ av1_cuvid_decoder_deps="cuvid CUVIDAV1PICPARAMS"
av1_mediacodec_decoder_deps="mediacodec"
av1_mediacodec_encoder_deps="mediacodec"
av1_mediacodec_encoder_select="extract_extradata_bsf"
+av1_mf_encoder_deps="mediafoundation"
av1_nvenc_encoder_deps="nvenc NV_ENC_PIC_PARAMS_AV1"
av1_nvenc_encoder_select="atsc_a53"
av1_qsv_decoder_select="qsvdec"
@@ -6718,6 +6722,7 @@ enabled videotoolbox && {
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVC "-framework CoreMedia"
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVCWithAlpha "-framework CoreMedia"
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_VP9 "-framework CoreMedia"
+ check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_AV1 "-framework CoreMedia"
check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange "-framework CoreVideo"
check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange "-framework CoreVideo"
check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange "-framework CoreVideo"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 9d1c1fc..c94be5e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1008,6 +1008,7 @@ OBJS-$(CONFIG_AV1_D3D12VA_HWACCEL) += dxva2_av1.o d3d12va_av1.o
OBJS-$(CONFIG_AV1_NVDEC_HWACCEL) += nvdec_av1.o
OBJS-$(CONFIG_AV1_VAAPI_HWACCEL) += vaapi_av1.o
OBJS-$(CONFIG_AV1_VDPAU_HWACCEL) += vdpau_av1.o
+OBJS-$(CONFIG_AV1_VIDEOTOOLBOX_HWACCEL) += videotoolbox_av1.o
OBJS-$(CONFIG_AV1_VULKAN_HWACCEL) += vulkan_decode.o vulkan_av1.o
OBJS-$(CONFIG_H263_VAAPI_HWACCEL) += vaapi_mpeg4.o
OBJS-$(CONFIG_H263_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index aa0fc47..f531761 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -838,6 +838,7 @@ extern const FFCodec ff_av1_nvenc_encoder;
extern const FFCodec ff_av1_qsv_decoder;
extern const FFCodec ff_av1_qsv_encoder;
extern const FFCodec ff_av1_amf_encoder;
+extern const FFCodec ff_av1_mf_encoder;
extern const FFCodec ff_av1_vaapi_encoder;
extern const FFCodec ff_libopenh264_encoder;
extern const FFCodec ff_libopenh264_decoder;
diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index a47aea6..95fce9a 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -415,10 +415,6 @@ static int amf_init_encoder(AVCodecContext *avctx)
else
pix_fmt = avctx->pix_fmt;
- if (pix_fmt == AV_PIX_FMT_P010) {
- AMF_RETURN_IF_FALSE(ctx, ctx->version >= AMF_MAKE_FULL_VERSION(1, 4, 32, 0), AVERROR_UNKNOWN, "10-bit encoder is not supported by AMD GPU drivers versions lower than 23.30.\n");
- }
-
ctx->format = amf_av_to_amf_format(pix_fmt);
AMF_RETURN_IF_FALSE(ctx, ctx->format != AMF_SURFACE_UNKNOWN, AVERROR(EINVAL),
"Format %s is not supported\n", av_get_pix_fmt_name(pix_fmt));
@@ -766,11 +762,50 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
switch (avctx->codec->id) {
case AV_CODEC_ID_H264:
AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_INSERT_AUD, !!ctx->aud);
+ switch (frame->pict_type) {
+ case AV_PICTURE_TYPE_I:
+ if (ctx->forced_idr) {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_INSERT_SPS, 1);
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_INSERT_PPS, 1);
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_IDR);
+ } else {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_I);
+ }
+ break;
+ case AV_PICTURE_TYPE_P:
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_P);
+ break;
+ case AV_PICTURE_TYPE_B:
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_B);
+ break;
+ }
break;
case AV_CODEC_ID_HEVC:
AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, !!ctx->aud);
+ switch (frame->pict_type) {
+ case AV_PICTURE_TYPE_I:
+ if (ctx->forced_idr) {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_INSERT_HEADER, 1);
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_IDR);
+ } else {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_I);
+ }
+ break;
+ case AV_PICTURE_TYPE_P:
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_P);
+ break;
+ }
+ break;
+ case AV_CODEC_ID_AV1:
+ if (frame->pict_type == AV_PICTURE_TYPE_I) {
+ if (ctx->forced_idr) {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_AV1_FORCE_INSERT_SEQUENCE_HEADER, 1);
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE, AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_KEY);
+ } else {
+ AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE, AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_INTRA_ONLY);
+ }
+ }
break;
- //case AV_CODEC_ID_AV1 not supported
default:
break;
}
@@ -878,6 +913,115 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
return ret;
}
+int ff_amf_get_color_primaries(AVCodecContext *avctx)
+{
+ amf_int64 color_primaries = AMF_COLOR_PRIMARIES_UNDEFINED;
+ switch (avctx->color_primaries) {
+ case AVCOL_PRI_BT709:
+ color_primaries = AMF_COLOR_PRIMARIES_BT709;
+ break;
+ case AVCOL_PRI_UNSPECIFIED:
+ color_primaries = AMF_COLOR_PRIMARIES_UNSPECIFIED;
+ break;
+ case AVCOL_PRI_RESERVED:
+ color_primaries = AMF_COLOR_PRIMARIES_RESERVED;
+ break;
+ case AVCOL_PRI_BT470M:
+ color_primaries = AMF_COLOR_PRIMARIES_BT470M;
+ break;
+ case AVCOL_PRI_BT470BG:
+ color_primaries = AMF_COLOR_PRIMARIES_BT470BG;
+ break;
+ case AVCOL_PRI_SMPTE170M:
+ color_primaries = AMF_COLOR_PRIMARIES_SMPTE170M;
+ break;
+ case AVCOL_PRI_SMPTE240M:
+ color_primaries = AMF_COLOR_PRIMARIES_SMPTE240M;
+ break;
+ case AVCOL_PRI_FILM:
+ color_primaries = AMF_COLOR_PRIMARIES_FILM;
+ break;
+ case AVCOL_PRI_BT2020:
+ color_primaries = AMF_COLOR_PRIMARIES_BT2020;
+ break;
+ case AVCOL_PRI_SMPTE428:
+ color_primaries = AMF_COLOR_PRIMARIES_SMPTE428;
+ break;
+ case AVCOL_PRI_SMPTE431:
+ color_primaries = AMF_COLOR_PRIMARIES_SMPTE431;
+ break;
+ case AVCOL_PRI_SMPTE432:
+ color_primaries = AMF_COLOR_PRIMARIES_SMPTE432;
+ break;
+ case AVCOL_PRI_EBU3213:
+ color_primaries = AMF_COLOR_PRIMARIES_JEDEC_P22;
+ break;
+ }
+ return color_primaries;
+}
+
+int ff_amf_get_transfer_characteristic(AVCodecContext *avctx)
+{
+ amf_int64 transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED;
+ switch (avctx->color_trc) {
+ case AVCOL_TRC_BT709:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709;
+ break;
+ case AVCOL_TRC_UNSPECIFIED:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNSPECIFIED;
+ break;
+ case AVCOL_TRC_RESERVED:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_RESERVED;
+ break;
+ case AVCOL_TRC_GAMMA22:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22;
+ break;
+ case AVCOL_TRC_GAMMA28:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA28;
+ break;
+ case AVCOL_TRC_SMPTE170M:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M;
+ break;
+ case AVCOL_TRC_SMPTE240M:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE240M;
+ break;
+ case AVCOL_TRC_LINEAR:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_LINEAR;
+ break;
+ case AVCOL_TRC_LOG:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_LOG;
+ break;
+ case AVCOL_TRC_LOG_SQRT:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_LOG_SQRT;
+ break;
+ case AVCOL_TRC_IEC61966_2_4:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_IEC61966_2_4;
+ break;
+ case AVCOL_TRC_BT1361_ECG:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_BT1361_ECG;
+ break;
+ case AVCOL_TRC_IEC61966_2_1:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_IEC61966_2_1;
+ break;
+ case AVCOL_TRC_BT2020_10:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_BT2020_10;
+ break;
+ case AVCOL_TRC_BT2020_12:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_BT2020_12;
+ break;
+ case AVCOL_TRC_SMPTE2084:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE2084;
+ break;
+ case AVCOL_TRC_SMPTE428:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE428;
+ break;
+ case AVCOL_TRC_ARIB_STD_B67:
+ transfer_characteristic = AMF_COLOR_TRANSFER_CHARACTERISTIC_ARIB_STD_B67;
+ break;
+ }
+ return transfer_characteristic;
+}
+
int ff_amf_get_color_profile(AVCodecContext *avctx)
{
amf_int64 color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index d985d01..0c0624e 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -114,6 +114,7 @@ typedef struct AmfContext {
int max_b_frames;
int qvbr_quality_level;
int hw_high_motion_quality_boost;
+ int forced_idr;
// HEVC - specific options
@@ -173,6 +174,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
*/
extern const enum AVPixelFormat ff_amf_pix_fmts[];
+int ff_amf_get_color_primaries(AVCodecContext *avctx);
+int ff_amf_get_transfer_characteristic(AVCodecContext *avctx);
int ff_amf_get_color_profile(AVCodecContext *avctx);
/**
diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c
index 2a7a782..a947c1d 100644
--- a/libavcodec/amfenc_av1.c
+++ b/libavcodec/amfenc_av1.c
@@ -116,6 +116,7 @@ static const AVOption options[] = {
{ "none", "no adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_AQ_MODE_NONE }, 0, 0, VE, .unit = "adaptive_quantisation_mode" },
{ "caq", "context adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_AQ_MODE_CAQ }, 0, 0, VE, .unit = "adaptive_quantisation_mode" },
+ { "forced_idr", "Force I frames to be IDR frames", OFFSET(forced_idr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
{ "align", "alignment mode", OFFSET(align), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS, VE, .unit = "align" },
{ "64x16", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY }, 0, 0, VE, .unit = "align" },
@@ -186,6 +187,8 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx)
AMFRate framerate;
AMFSize framesize = AMFConstructSize(avctx->width, avctx->height);
amf_int64 color_depth;
+ amf_int64 color_primaries;
+ amf_int64 transfer_characteristic;
amf_int64 color_profile;
enum AVPixelFormat pix_fmt;
@@ -238,7 +241,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
/// Color profile
+ color_primaries = ff_amf_get_color_primaries(avctx);
+ transfer_characteristic = ff_amf_get_transfer_characteristic(avctx);
color_profile = ff_amf_get_color_profile(avctx);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PRIMARIES, color_primaries);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_TRANSFER_CHARACTERISTIC, transfer_characteristic);
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PROFILE, color_profile);
/// Color Depth
@@ -250,16 +257,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_COLOR_BIT_DEPTH, color_depth);
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PROFILE, color_profile);
- if (color_depth == AMF_COLOR_BIT_DEPTH_8) {
- /// Color Transfer Characteristics (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_TRANSFER_CHARACTERISTIC, AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709);
- /// Color Primaries (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT709);
- } else {
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_TRANSFER_CHARACTERISTIC, AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE2084);
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020);
- }
profile_level = avctx->level;
if (profile_level == AV_LEVEL_UNKNOWN) {
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
index 8edd39c..2406f74 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -133,6 +133,7 @@ static const AVOption options[] = {
{ "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE },
{ "me_quarter_pel", "Enable ME Quarter Pixel", OFFSET(me_quarter_pel),AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE },
+ { "forced_idr", "Force I frames to be IDR frames", OFFSET(forced_idr) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) , AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE },
@@ -201,6 +202,8 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
AMFRate framerate;
AMFSize framesize = AMFConstructSize(avctx->width, avctx->height);
int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0;
+ amf_int64 color_primaries;
+ amf_int64 transfer_characteristic;
amf_int64 color_profile;
enum AVPixelFormat pix_fmt;
@@ -273,7 +276,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio);
}
+ color_primaries = ff_amf_get_color_primaries(avctx);
+ transfer_characteristic = ff_amf_get_transfer_characteristic(avctx);
color_profile = ff_amf_get_color_profile(avctx);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, color_primaries);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, transfer_characteristic);
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile);
/// Color Range (Support for older Drivers)
@@ -287,10 +294,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
AMF_RETURN_IF_FALSE(ctx, pix_fmt != AV_PIX_FMT_P010, AVERROR_INVALIDDATA, "10-bit input video is not supported by AMF H264 encoder\n");
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_8);
- /// Color Transfer Characteristics (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc);
- /// Color Primaries (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries);
// autodetect rate control method
if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN) {
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
index 4898824..9d3604d 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -100,6 +100,7 @@ static const AVOption options[] = {
{ "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL,{ .i64 = -1 }, -1, 1, VE },
{ "me_quarter_pel", "Enable ME Quarter Pixel ", OFFSET(me_quarter_pel),AV_OPT_TYPE_BOOL,{ .i64 = -1 }, -1, 1, VE },
+ { "forced_idr", "Force I frames to be IDR frames", OFFSET(forced_idr) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE },
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = -1 }, -1, 1, VE },
@@ -167,6 +168,8 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
AMFSize framesize = AMFConstructSize(avctx->width, avctx->height);
int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0;
amf_int64 color_depth;
+ amf_int64 color_primaries;
+ amf_int64 transfer_characteristic;
amf_int64 color_profile;
enum AVPixelFormat pix_fmt;
@@ -241,7 +244,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio);
}
+ color_primaries = ff_amf_get_color_primaries(avctx);
+ transfer_characteristic = ff_amf_get_transfer_characteristic(avctx);
color_profile = ff_amf_get_color_profile(avctx);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, color_primaries);
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, transfer_characteristic);
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile);
/// Color Range (Support for older Drivers)
AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, !!(avctx->color_range == AVCOL_RANGE_JPEG));
@@ -253,15 +260,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
color_depth = AMF_COLOR_BIT_DEPTH_10;
}
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, color_depth);
- if (color_depth == AMF_COLOR_BIT_DEPTH_8) {
- /// Color Transfer Characteristics (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709);
- /// Color Primaries (AMF matches ISO/IEC)
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT709);
- } else {
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE2084);
- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020);
- }
// Picture control properties
AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr);
diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index d921def..86cc007 100644
--- a/libavcodec/av1dec.c
+++ b/libavcodec/av1dec.c
@@ -542,6 +542,7 @@ static int get_pixel_format(AVCodecContext *avctx)
CONFIG_AV1_NVDEC_HWACCEL + \
CONFIG_AV1_VAAPI_HWACCEL + \
CONFIG_AV1_VDPAU_HWACCEL + \
+ CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + \
CONFIG_AV1_VULKAN_HWACCEL)
enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts;
@@ -569,6 +570,9 @@ static int get_pixel_format(AVCodecContext *avctx)
#if CONFIG_AV1_VDPAU_HWACCEL
*fmtp++ = AV_PIX_FMT_VDPAU;
#endif
+#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL
+ *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX;
+#endif
#if CONFIG_AV1_VULKAN_HWACCEL
*fmtp++ = AV_PIX_FMT_VULKAN;
#endif
@@ -593,6 +597,9 @@ static int get_pixel_format(AVCodecContext *avctx)
#if CONFIG_AV1_VDPAU_HWACCEL
*fmtp++ = AV_PIX_FMT_VDPAU;
#endif
+#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL
+ *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX;
+#endif
#if CONFIG_AV1_VULKAN_HWACCEL
*fmtp++ = AV_PIX_FMT_VULKAN;
#endif
@@ -1003,6 +1010,8 @@ static int export_itut_t35(AVCodecContext *avctx, AVFrame *frame,
break;
}
case ITU_T_T35_PROVIDER_CODE_DOLBY: {
+ AVBufferRef *rpu_buf;
+ AVFrameSideData *rpu;
int provider_oriented_code = bytestream2_get_be32(&gb);
if (itut_t35->itu_t_t35_country_code != ITU_T_T35_COUNTRY_CODE_US ||
provider_oriented_code != 0x800)
@@ -1015,6 +1024,18 @@ static int export_itut_t35(AVCodecContext *avctx, AVFrame *frame,
break; // ignore
}
+ rpu_buf = av_buffer_alloc(itut_t35->payload_size);
+ if (rpu_buf) {
+ memcpy(rpu_buf->data, itut_t35->payload, itut_t35->payload_size);
+ rpu = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_RPU_BUFFER_T35, rpu_buf);
+ if (!rpu) {
+ av_buffer_unref(&rpu_buf);
+ return AVERROR(ENOMEM);
+ }
+ } else {
+ return AVERROR(ENOMEM);
+ }
+
ret = ff_dovi_attach_side_data(&s->dovi, frame);
if (ret < 0)
return ret;
@@ -1447,6 +1468,10 @@ static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
if (raw_tile_group && (s->tile_num == raw_tile_group->tg_end + 1)) {
int show_frame = s->raw_frame_header->show_frame;
+ // Set nb_unit to point at the next OBU, to indicate which
+ // OBUs have been processed for this current frame. (If this
+ // frame gets output, we set nb_unit to this value later too.)
+ s->nb_unit = i + 1;
if (avctx->hwaccel && s->cur_frame.f) {
ret = FF_HW_SIMPLE_CALL(avctx, end_frame);
if (ret < 0) {
@@ -1457,6 +1482,8 @@ static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
update_reference_list(avctx);
+ // Set start_unit to indicate the first OBU of the next frame.
+ s->start_unit = s->nb_unit;
raw_tile_group = NULL;
s->raw_frame_header = NULL;
@@ -1486,7 +1513,7 @@ end:
s->raw_frame_header = NULL;
av_packet_unref(s->pkt);
ff_cbs_fragment_reset(&s->current_obu);
- s->nb_unit = 0;
+ s->nb_unit = s->start_unit = 0;
}
if (!ret && !frame->buf[0])
ret = AVERROR(EAGAIN);
@@ -1513,7 +1540,7 @@ static int av1_receive_frame(AVCodecContext *avctx, AVFrame *frame)
return ret;
}
- s->nb_unit = 0;
+ s->nb_unit = s->start_unit = 0;
av_log(avctx, AV_LOG_DEBUG, "Total OBUs on this packet: %d.\n",
s->current_obu.nb_units);
}
@@ -1534,7 +1561,7 @@ static void av1_decode_flush(AVCodecContext *avctx)
av1_frame_unref(&s->cur_frame);
s->operating_point_idc = 0;
- s->nb_unit = 0;
+ s->nb_unit = s->start_unit = 0;
s->raw_frame_header = NULL;
s->raw_seq = NULL;
s->cll = NULL;
@@ -1602,6 +1629,9 @@ const FFCodec ff_av1_decoder = {
#if CONFIG_AV1_VDPAU_HWACCEL
HWACCEL_VDPAU(av1),
#endif
+#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL
+ HWACCEL_VIDEOTOOLBOX(av1),
+#endif
#if CONFIG_AV1_VULKAN_HWACCEL
HWACCEL_VULKAN(av1),
#endif
diff --git a/libavcodec/av1dec.h b/libavcodec/av1dec.h
index 8b2a7b0..10c807f 100644
--- a/libavcodec/av1dec.h
+++ b/libavcodec/av1dec.h
@@ -114,7 +114,8 @@ typedef struct AV1DecContext {
AV1Frame ref[AV1_NUM_REF_FRAMES];
AV1Frame cur_frame;
- int nb_unit;
+ int nb_unit; ///< The index of the next OBU to be processed.
+ int start_unit; ///< The index of the first OBU of the current frame.
// AVOptions
int operating_point;
diff --git a/libavcodec/bsf/hevc_mp4toannexb.c b/libavcodec/bsf/hevc_mp4toannexb.c
index f281185..241fa07 100644
--- a/libavcodec/bsf/hevc_mp4toannexb.c
+++ b/libavcodec/bsf/hevc_mp4toannexb.c
@@ -125,6 +125,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
AVPacket *in;
GetByteContext gb;
+ int has_sps = 0, has_pps = 0;
int got_irap = 0;
int i, ret = 0;
@@ -158,11 +159,14 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
}
nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
+ has_sps = (has_sps || nalu_type == HEVC_NAL_SPS);
+ has_pps = (has_pps || nalu_type == HEVC_NAL_PPS);
/* prepend extradata to IRAP frames */
is_irap = nalu_type >= HEVC_NAL_BLA_W_LP &&
nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
- add_extradata = is_irap && !got_irap;
+ /* ignore the extradata if IRAP frame has sps and pps */
+ add_extradata = is_irap && !got_irap && !(has_sps && has_pps);
extra_size = add_extradata * ctx->par_out->extradata_size;
got_irap |= is_irap;
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index d8b992b..4a38664 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -889,12 +889,13 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
if (ctx->buffer[bidx].str[0] || ctx->real_time) {
ff_dlog(ctx, "cdp writing data (%s)\n", ctx->buffer[bidx].str);
- start_time = ctx->buffer_time[0];
- sub->pts = start_time;
- end_time = ctx->buffer_time[1];
- if (!ctx->real_time)
+ if (!ctx->real_time) {
+ start_time = ctx->buffer_time[0];
+ sub->pts = start_time;
+ end_time = ctx->buffer_time[1];
sub->end_display_time = av_rescale_q(end_time - start_time,
AV_TIME_BASE_Q, ms_tb);
+ }
else
sub->end_display_time = -1;
ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated);
diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
index f876935..bd32b7d 100644
--- a/libavcodec/dvdsubdec.c
+++ b/libavcodec/dvdsubdec.c
@@ -45,6 +45,8 @@ typedef struct DVDSubContext
int buf_size;
int forced_subs_only;
uint8_t used_color[256];
+ int64_t pts;
+ int output_empty_rects;
} DVDSubContext;
static void yuv_a_to_rgba(const uint8_t *ycbcr, const uint8_t *alpha, uint32_t *rgba, int num_values)
@@ -230,7 +232,10 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
uint32_t size;
int64_t offset1, offset2;
- if (buf_size < 10)
+ if (buf_size < 2)
+ return AVERROR(EAGAIN);
+
+ if (buf_size == 2 && AV_RB16(buf) == 0)
return -1;
if (AV_RB16(buf) == 0) { /* HD subpicture with 4-byte offsets */
@@ -243,15 +248,22 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
cmd_pos = 2;
}
+ if (big_offsets && buf_size < 6)
+ return AVERROR(EAGAIN);
+
size = READ_OFFSET(buf + (big_offsets ? 2 : 0));
- cmd_pos = READ_OFFSET(buf + cmd_pos);
- if (cmd_pos < 0 || cmd_pos > buf_size - 2 - offset_size) {
- if (cmd_pos > size) {
- av_log(ctx, AV_LOG_ERROR, "Discarding invalid packet\n");
- return 0;
- }
+ if (size == 0)
+ return -1;
+
+ if (buf_size < size)
return AVERROR(EAGAIN);
+
+ cmd_pos = READ_OFFSET(buf + cmd_pos);
+
+ if (cmd_pos < 0 || cmd_pos > size) {
+ av_log(ctx, AV_LOG_ERROR, "Discarding invalid packet\n");
+ return AVERROR_INVALIDDATA;
}
while (cmd_pos > 0 && cmd_pos < buf_size - 2 - offset_size) {
@@ -524,10 +536,13 @@ static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
int appended = 0;
int is_menu;
+ if (ctx->pts == AV_NOPTS_VALUE && sub->pts != AV_NOPTS_VALUE)
+ ctx->pts = sub->pts;
if (ctx->buf_size) {
int ret = append_to_cached_buf(avctx, buf, buf_size);
if (ret < 0) {
*data_size = 0;
+ ctx->pts = AV_NOPTS_VALUE;
return ret;
}
buf = ctx->buf;
@@ -538,7 +553,12 @@ static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
is_menu = decode_dvd_subtitles(ctx, sub, buf, buf_size);
if (is_menu == AVERROR(EAGAIN)) {
*data_size = 0;
- return appended ? 0 : append_to_cached_buf(avctx, buf, buf_size);
+ int ret = appended ? 0 : append_to_cached_buf(avctx, buf, buf_size);
+ if (ret < 0) {
+ ctx->pts = AV_NOPTS_VALUE;
+ return ret;
+ }
+ return buf_size;
}
if (is_menu < 0) {
@@ -547,9 +567,10 @@ static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
reset_rects(sub);
*data_size = 0;
+ ctx->pts = AV_NOPTS_VALUE;
return buf_size;
}
- if (!is_menu && find_smallest_bounding_rectangle(ctx, sub) == 0)
+ if (!is_menu && !ctx->output_empty_rects && find_smallest_bounding_rectangle(ctx, sub) == 0)
goto no_subtitle;
if (ctx->forced_subs_only && !(sub->rects[0]->flags & AV_SUBTITLE_FLAG_FORCED))
@@ -557,6 +578,8 @@ static int dvdsub_decode(AVCodecContext *avctx, AVSubtitle *sub,
ctx->buf_size = 0;
*data_size = 1;
+ sub->pts = ctx->pts;
+ ctx->pts = AV_NOPTS_VALUE;
return buf_size;
}
@@ -682,6 +705,7 @@ static av_cold int dvdsub_init(AVCodecContext *avctx)
av_log(avctx, AV_LOG_DEBUG, " 0x%06"PRIx32, ctx->palette[i]);
av_log(avctx, AV_LOG_DEBUG, "\n");
}
+ ctx->pts = AV_NOPTS_VALUE;
return 1;
}
@@ -698,6 +722,7 @@ static const AVOption options[] = {
{ "palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
{ "ifo_palette", "obtain the global palette from .IFO file", OFFSET(ifo_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
{ "forced_subs_only", "Only show forced subtitles", OFFSET(forced_subs_only), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SD},
+ { "output_empty_rects", "Output subtitles with empty or fully transparent rects", OFFSET(output_empty_rects), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SD},
{ NULL }
};
static const AVClass dvdsub_class = {
diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h
index 5171e4c..2b9bdc8 100644
--- a/libavcodec/hwaccels.h
+++ b/libavcodec/hwaccels.h
@@ -26,6 +26,7 @@ extern const struct FFHWAccel ff_av1_dxva2_hwaccel;
extern const struct FFHWAccel ff_av1_nvdec_hwaccel;
extern const struct FFHWAccel ff_av1_vaapi_hwaccel;
extern const struct FFHWAccel ff_av1_vdpau_hwaccel;
+extern const struct FFHWAccel ff_av1_videotoolbox_hwaccel;
extern const struct FFHWAccel ff_av1_vulkan_hwaccel;
extern const struct FFHWAccel ff_h263_vaapi_hwaccel;
extern const struct FFHWAccel ff_h263_videotoolbox_hwaccel;
diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index ff67f44..354e9b7 100644
--- a/libavcodec/libdav1d.c
+++ b/libavcodec/libdav1d.c
@@ -563,6 +563,8 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame)
break;
}
case ITU_T_T35_PROVIDER_CODE_DOLBY: {
+ AVBufferRef *rpu_buf;
+ AVFrameSideData *rpu;
int provider_oriented_code = bytestream2_get_be32(&gb);
if (itut_t35->country_code != ITU_T_T35_COUNTRY_CODE_US ||
provider_oriented_code != 0x800)
@@ -575,6 +577,18 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame)
break; // ignore
}
+ rpu_buf = av_buffer_alloc(itut_t35->payload_size);
+ if (rpu_buf) {
+ memcpy(rpu_buf->data, itut_t35->payload, itut_t35->payload_size);
+ rpu = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_RPU_BUFFER_T35, rpu_buf);
+ if (!rpu) {
+ av_buffer_unref(&rpu_buf);
+ goto fail;
+ }
+ } else {
+ goto fail;
+ }
+
res = ff_dovi_attach_side_data(&dav1d->dovi, frame);
if (res < 0)
goto fail;
diff --git a/libavcodec/mf_utils.c b/libavcodec/mf_utils.c
index 48e3a63..ff44130 100644
--- a/libavcodec/mf_utils.c
+++ b/libavcodec/mf_utils.c
@@ -240,6 +240,7 @@ static struct GUID_Entry guid_names[] = {
GUID_ENTRY(MFMediaType_Video),
GUID_ENTRY(MFAudioFormat_PCM),
GUID_ENTRY(MFAudioFormat_Float),
+ GUID_ENTRY(ff_MFVideoFormat_AV1),
GUID_ENTRY(MFVideoFormat_H264),
GUID_ENTRY(MFVideoFormat_H264_ES),
GUID_ENTRY(ff_MFVideoFormat_HEVC),
@@ -507,6 +508,7 @@ void ff_media_type_dump(void *log, IMFMediaType *type)
const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec)
{
switch (codec) {
+ case AV_CODEC_ID_AV1: return &ff_MFVideoFormat_AV1;
case AV_CODEC_ID_H264: return &MFVideoFormat_H264;
case AV_CODEC_ID_HEVC: return &ff_MFVideoFormat_HEVC;
case AV_CODEC_ID_AC3: return &MFAudioFormat_Dolby_AC3;
diff --git a/libavcodec/mf_utils.h b/libavcodec/mf_utils.h
index 387c005..a59b36d 100644
--- a/libavcodec/mf_utils.h
+++ b/libavcodec/mf_utils.h
@@ -113,6 +113,7 @@ DEFINE_GUID(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE, 0xf5523a5, 0x1cb2,
DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_HEVC, 0x43564548); // FCC('HEVC')
DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_HEVC_ES, 0x53564548); // FCC('HEVS')
+DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_AV1, 0x31305641); // FCC('AV01')
// This enum is missing from mingw-w64's codecapi.h by v7.0.0.
diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c
index b8f8a25..c062d87 100644
--- a/libavcodec/mfenc.c
+++ b/libavcodec/mfenc.c
@@ -1315,3 +1315,4 @@ static const FFCodecDefault defaults[] = {
MF_ENCODER(VIDEO, h264, H264, venc_opts, VFMTS, VCAPS, defaults);
MF_ENCODER(VIDEO, hevc, HEVC, venc_opts, VFMTS, VCAPS, defaults);
+MF_ENCODER(VIDEO, av1, AV1, venc_opts, VFMTS, VCAPS, defaults);
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index d93bcf1..9e2e8bf 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -35,9 +35,11 @@
#include "libavutil/opt.h"
#define RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
-#define MAX_EPOCH_PALETTES 8 // Max 8 allowed per PGS epoch
-#define MAX_EPOCH_OBJECTS 64 // Max 64 allowed per PGS epoch
-#define MAX_OBJECT_REFS 2 // Max objects per display set
+#define MAX_EPOCH_PALETTES 8 // Max 8 allowed per PGS epoch
+#define MAX_EPOCH_OBJECTS 64 // Max 64 allowed per PGS epoch
+#define MAX_OBJECT_REFS 2 // Max objects per display set
+#define MAX_OBJECT_WH 4096 // Max object width/height
+
enum SegmentType {
PALETTE_SEGMENT = 0x14,
@@ -48,57 +50,80 @@ enum SegmentType {
};
typedef struct PGSSubObjectRef {
- int id;
- int window_id;
- uint8_t composition_flag;
- int x;
- int y;
- int crop_x;
- int crop_y;
- int crop_w;
- int crop_h;
+ uint16_t id;
+ uint8_t window_id;
+ uint8_t composition_flag;
+ uint16_t x;
+ uint16_t y;
+ uint16_t crop_x;
+ uint16_t crop_y;
+ uint16_t crop_w;
+ uint16_t crop_h;
} PGSSubObjectRef;
typedef struct PGSSubPresentation {
- int id_number;
- int palette_id;
- int object_count;
+ uint8_t palette_flag;
+ uint8_t palette_id;
+ uint8_t object_count;
PGSSubObjectRef objects[MAX_OBJECT_REFS];
- int64_t pts;
+ int64_t pts;
} PGSSubPresentation;
typedef struct PGSSubObject {
- int id;
- int w;
- int h;
- uint8_t *rle;
- unsigned int rle_buffer_size, rle_data_len;
- unsigned int rle_remaining_len;
+ uint16_t id;
+ uint16_t w;
+ uint16_t h;
+ uint8_t *rle;
+ uint8_t *bitmap;
+ uint32_t rle_buffer_size;
+ uint32_t rle_data_len;
+ uint32_t rle_remaining_len;
+ uint32_t bitmap_buffer_size;
+ uint32_t bitmap_size;
} PGSSubObject;
typedef struct PGSSubObjects {
- int count;
+ uint8_t count;
PGSSubObject object[MAX_EPOCH_OBJECTS];
} PGSSubObjects;
typedef struct PGSSubPalette {
- int id;
- uint32_t clut[256];
+ uint8_t id;
+ uint32_t clut[AVPALETTE_COUNT];
} PGSSubPalette;
typedef struct PGSSubPalettes {
- int count;
+ uint8_t count;
PGSSubPalette palette[MAX_EPOCH_PALETTES];
} PGSSubPalettes;
+typedef struct PGSGraphicPlane {
+ uint8_t count;
+ uint8_t writable;
+ AVSubtitleRect visible_rect[MAX_OBJECT_REFS];
+} PGSGraphicPlane;
+
typedef struct PGSSubContext {
AVClass *class;
PGSSubPresentation presentation;
PGSSubPalettes palettes;
PGSSubObjects objects;
+ PGSGraphicPlane plane;
int forced_subs_only;
} PGSSubContext;
+static void clear_graphic_plane(PGSSubContext *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->plane.count; i++) {
+ av_freep(&ctx->plane.visible_rect[i].data[0]);
+ memset(&ctx->plane.visible_rect[i], 0, sizeof(ctx->plane.visible_rect[i]));
+ }
+ ctx->plane.writable = 0;
+ ctx->plane.count = 0;
+}
+
static void flush_cache(AVCodecContext *avctx)
{
PGSSubContext *ctx = avctx->priv_data;
@@ -106,8 +131,11 @@ static void flush_cache(AVCodecContext *avctx)
for (i = 0; i < ctx->objects.count; i++) {
av_freep(&ctx->objects.object[i].rle);
- ctx->objects.object[i].rle_buffer_size = 0;
+ ctx->objects.object[i].rle_buffer_size = 0;
ctx->objects.object[i].rle_remaining_len = 0;
+ av_freep(&ctx->objects.object[i].bitmap);
+ ctx->objects.object[i].bitmap_buffer_size = 0;
+ ctx->objects.object[i].bitmap_size = 0;
}
ctx->objects.count = 0;
ctx->palettes.count = 0;
@@ -144,6 +172,7 @@ static av_cold int init_decoder(AVCodecContext *avctx)
static av_cold int close_decoder(AVCodecContext *avctx)
{
+ clear_graphic_plane((PGSSubContext *)avctx->priv_data);
flush_cache(avctx);
return 0;
@@ -159,48 +188,51 @@ static av_cold int close_decoder(AVCodecContext *avctx)
* @param buf pointer to the RLE data to process
* @param buf_size size of the RLE data to process
*/
-static int decode_rle(AVCodecContext *avctx, AVSubtitleRect *rect,
- const uint8_t *buf, unsigned int buf_size)
+static int decode_object_rle(AVCodecContext *avctx, PGSSubObject *object)
{
- const uint8_t *rle_bitmap_end;
+ const uint8_t *rle_buf;
+ const uint8_t *rle_end;
int pixel_count, line_count;
+ rle_buf = object->rle;
+ rle_end = object->rle + object->rle_data_len;
- rle_bitmap_end = buf + buf_size;
+ object->bitmap_size = object->w * object->h;
+ av_fast_padded_malloc(&object->bitmap, &object->bitmap_buffer_size,
+ object->bitmap_size);
- rect->data[0] = av_malloc_array(rect->w, rect->h);
-
- if (!rect->data[0])
+ if (!object->bitmap)
return AVERROR(ENOMEM);
pixel_count = 0;
line_count = 0;
- while (buf < rle_bitmap_end && line_count < rect->h) {
+ while (rle_buf < rle_end && line_count < object->h) {
uint8_t flags, color;
int run;
- color = bytestream_get_byte(&buf);
+ color = bytestream_get_byte(&rle_buf);
run = 1;
if (color == 0x00) {
- flags = bytestream_get_byte(&buf);
+ flags = bytestream_get_byte(&rle_buf);
run = flags & 0x3f;
if (flags & 0x40)
- run = (run << 8) + bytestream_get_byte(&buf);
- color = flags & 0x80 ? bytestream_get_byte(&buf) : 0;
+ run = (run << 8) + bytestream_get_byte(&rle_buf);
+ color = flags & 0x80 ? bytestream_get_byte(&rle_buf) : 0;
}
- if (run > 0 && pixel_count + run <= rect->w * rect->h) {
- memset(rect->data[0] + pixel_count, color, run);
+ if (run > 0 && pixel_count + run <= object->w * object->h) {
+ memset(object->bitmap + pixel_count, color, run);
pixel_count += run;
} else if (!run) {
/*
* New Line. Check if correct pixels decoded, if not display warning
* and adjust bitmap pointer to correct new line position.
*/
- if (pixel_count % rect->w > 0) {
- av_log(avctx, AV_LOG_ERROR, "Decoded %d pixels, when line should be %d pixels\n",
- pixel_count % rect->w, rect->w);
+ if (pixel_count % object->w > 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Decoded %d pixels, when object line should be %d pixels\n",
+ pixel_count % object->w, object->w);
if (avctx->err_recognition & AV_EF_EXPLODE) {
return AVERROR_INVALIDDATA;
}
@@ -209,13 +241,11 @@ static int decode_rle(AVCodecContext *avctx, AVSubtitleRect *rect,
}
}
- if (pixel_count < rect->w * rect->h) {
- av_log(avctx, AV_LOG_ERROR, "Insufficient RLE data for subtitle\n");
+ if (pixel_count < object->w * object->h) {
+ av_log(avctx, AV_LOG_ERROR, "Insufficient RLE data for object\n");
return AVERROR_INVALIDDATA;
}
-
- ff_dlog(avctx, "Pixel Count = %d, Area = %d\n", pixel_count, rect->w * rect->h);
-
+ ff_dlog(avctx, "Pixel Count = %d, Area = %d\n", pixel_count, object->w * object->h);
return 0;
}
@@ -237,7 +267,7 @@ static int parse_object_segment(AVCodecContext *avctx,
uint8_t sequence_desc;
unsigned int rle_bitmap_len, width, height;
- int id;
+ int id, ret;
if (buf_size <= 4)
return AVERROR_INVALIDDATA;
@@ -260,57 +290,71 @@ static int parse_object_segment(AVCodecContext *avctx,
/* Read the Sequence Description to determine if start of RLE data or appended to previous RLE */
sequence_desc = bytestream_get_byte(&buf);
- if (!(sequence_desc & 0x80)) {
- /* Additional RLE data */
- if (buf_size > object->rle_remaining_len)
+ /* First in sequence object definition segment */
+ if (sequence_desc & 0x80) {
+ if (buf_size <= 7)
return AVERROR_INVALIDDATA;
+ buf_size -= 7;
- memcpy(object->rle + object->rle_data_len, buf, buf_size);
- object->rle_data_len += buf_size;
- object->rle_remaining_len -= buf_size;
-
- return 0;
- }
-
- if (buf_size <= 7)
- return AVERROR_INVALIDDATA;
- buf_size -= 7;
+ /* Decode rle bitmap length, stored size includes width/height data */
+ rle_bitmap_len = bytestream_get_be24(&buf) - 2*2;
- /* Decode rle bitmap length, stored size includes width/height data */
- rle_bitmap_len = bytestream_get_be24(&buf) - 2*2;
+ if (buf_size > rle_bitmap_len) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Buffer dimension %d larger than the expected RLE data %d\n",
+ buf_size, rle_bitmap_len);
+ return AVERROR_INVALIDDATA;
+ }
- if (buf_size > rle_bitmap_len) {
- av_log(avctx, AV_LOG_ERROR,
- "Buffer dimension %d larger than the expected RLE data %d\n",
- buf_size, rle_bitmap_len);
- return AVERROR_INVALIDDATA;
- }
+ /* Get bitmap dimensions from data */
+ width = bytestream_get_be16(&buf);
+ height = bytestream_get_be16(&buf);
- /* Get bitmap dimensions from data */
- width = bytestream_get_be16(&buf);
- height = bytestream_get_be16(&buf);
+ /* Make sure the bitmap is not too large */
+ if (MAX_OBJECT_WH < width || MAX_OBJECT_WH < height || !width || !height) {
+ av_log(avctx, AV_LOG_ERROR, "Bitmap dimensions (%dx%d) invalid.\n", width, height);
+ return AVERROR_INVALIDDATA;
+ }
- /* Make sure the bitmap is not too large */
- if (avctx->width < width || avctx->height < height || !width || !height) {
- av_log(avctx, AV_LOG_ERROR, "Bitmap dimensions (%dx%d) invalid.\n", width, height);
- return AVERROR_INVALIDDATA;
- }
+ object->rle_data_len = 0;
+ object->w = width;
+ object->h = height;
+ /* Dimensions against video are checked at decode after cropping. */
+ av_fast_padded_malloc(&object->rle, &object->rle_buffer_size, rle_bitmap_len);
- object->w = width;
- object->h = height;
+ if (!object->rle) {
+ object->rle_remaining_len = 0;
+ return AVERROR(ENOMEM);
+ }
- av_fast_padded_malloc(&object->rle, &object->rle_buffer_size, rle_bitmap_len);
+ memcpy(object->rle, buf, buf_size);
+ object->rle_remaining_len = rle_bitmap_len;
+ } else {
+ /* Additional RLE data */
+ if (buf_size > object->rle_remaining_len)
+ return AVERROR_INVALIDDATA;
- if (!object->rle) {
- object->rle_data_len = 0;
- object->rle_remaining_len = 0;
- return AVERROR(ENOMEM);
+ memcpy(object->rle + object->rle_data_len, buf, buf_size);
+ }
+ object->rle_data_len += buf_size;
+ object->rle_remaining_len -= buf_size;
+
+ /* Last in sequence object definition (can be both first and last) */
+ if (sequence_desc & 0x40) {
+ /* Attempt decoding if data is valid */
+ if (0 == object->rle_remaining_len) {
+ ret = decode_object_rle(avctx, object);
+ if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE || ret == AVERROR(ENOMEM))) {
+ return ret;
+ }
+ } else {
+ av_log(avctx, AV_LOG_ERROR,
+ "RLE data length %u is %u bytes shorter than expected\n",
+ object->rle_data_len, object->rle_remaining_len);
+ if (avctx->err_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ }
}
-
- memcpy(object->rle, buf, buf_size);
- object->rle_data_len = buf_size;
- object->rle_remaining_len = rle_bitmap_len - buf_size;
-
return 0;
}
@@ -318,7 +362,7 @@ static int parse_object_segment(AVCodecContext *avctx,
* Parse the palette segment packet.
*
* The palette segment contains details of the palette,
- * a maximum of 256 colors can be defined.
+ * a maximum of 256 colors (AVPALETTE_COUNT) can be defined.
*
* @param avctx contains the current codec context
* @param buf pointer to the packet to process
@@ -391,13 +435,17 @@ static int parse_presentation_segment(AVCodecContext *avctx,
int64_t pts)
{
PGSSubContext *ctx = avctx->priv_data;
- int i, state, ret;
+ int ret;
+ uint8_t i, state;
const uint8_t *buf_end = buf + buf_size;
// Video descriptor
int w = bytestream_get_be16(&buf);
int h = bytestream_get_be16(&buf);
+ // On a new display set, reset writability of the graphic plane
+ ctx->plane.writable = 0;
+
ctx->presentation.pts = pts;
ff_dlog(avctx, "Video Dimensions %dx%d\n",
@@ -406,88 +454,121 @@ static int parse_presentation_segment(AVCodecContext *avctx,
if (ret < 0)
return ret;
- /* Skip 1 bytes of unknown, frame rate */
- buf++;
+ /* Skip 3 bytes: framerate (1), presentation id number (2) */
+ buf+=3;
- // Composition descriptor
- ctx->presentation.id_number = bytestream_get_be16(&buf);
/*
- * state is a 2 bit field that defines pgs epoch boundaries
+ * State is a 2 bit field that defines pgs epoch boundaries
* 00 - Normal, previously defined objects and palettes are still valid
* 01 - Acquisition point, previous objects and palettes can be released
* 10 - Epoch start, previous objects and palettes can be released
* 11 - Epoch continue, previous objects and palettes can be released
*
- * reserved 6 bits discarded
+ * Reserved 6 bits discarded
*/
state = bytestream_get_byte(&buf) >> 6;
if (state != 0) {
+ /* Epoch start always wipes the graphic plane. Epoch continue does only if
+ * playback is not seamless, which should not happen with a proper stream.
+ */
+ if (0b10 == state)
+ clear_graphic_plane((PGSSubContext *)avctx->priv_data);
flush_cache(avctx);
}
+ /* Reserved 7 bits discarded. */
+ ctx->presentation.palette_flag = bytestream_get_byte(&buf) & 0x80;
+ ctx->presentation.palette_id = bytestream_get_byte(&buf);
+
/*
- * skip palette_update_flag (0x80),
+ * On palette update, don't parse the compositions references,
+ * just evaluate the existing graphic plane with the new palette.
*/
- buf += 1;
- ctx->presentation.palette_id = bytestream_get_byte(&buf);
- ctx->presentation.object_count = bytestream_get_byte(&buf);
- if (ctx->presentation.object_count > MAX_OBJECT_REFS) {
- av_log(avctx, AV_LOG_ERROR,
- "Invalid number of presentation objects %d\n",
- ctx->presentation.object_count);
- ctx->presentation.object_count = 2;
- if (avctx->err_recognition & AV_EF_EXPLODE) {
- return AVERROR_INVALIDDATA;
+ if (!ctx->presentation.palette_flag) {
+ ctx->presentation.object_count = bytestream_get_byte(&buf);
+ if (ctx->presentation.object_count > MAX_OBJECT_REFS) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Invalid number of presentation objects %d\n",
+ ctx->presentation.object_count);
+ ctx->presentation.object_count = 2;
+ if (avctx->err_recognition & AV_EF_EXPLODE) {
+ return AVERROR_INVALIDDATA;
+ }
}
- }
+ for (i = 0; i < ctx->presentation.object_count; i++) {
+ PGSSubObjectRef *const object = &ctx->presentation.objects[i];
- for (i = 0; i < ctx->presentation.object_count; i++)
- {
- PGSSubObjectRef *const object = &ctx->presentation.objects[i];
+ if (buf_end - buf < 8) {
+ av_log(avctx, AV_LOG_ERROR, "Insufficent space for object\n");
+ ctx->presentation.object_count = i;
+ return AVERROR_INVALIDDATA;
+ }
- if (buf_end - buf < 8) {
- av_log(avctx, AV_LOG_ERROR, "Insufficent space for object\n");
- ctx->presentation.object_count = i;
- return AVERROR_INVALIDDATA;
- }
+ object->id = bytestream_get_be16(&buf);
+ object->window_id = bytestream_get_byte(&buf);
+ object->composition_flag = bytestream_get_byte(&buf);
- object->id = bytestream_get_be16(&buf);
- object->window_id = bytestream_get_byte(&buf);
- object->composition_flag = bytestream_get_byte(&buf);
+ object->x = bytestream_get_be16(&buf);
+ object->y = bytestream_get_be16(&buf);
- object->x = bytestream_get_be16(&buf);
- object->y = bytestream_get_be16(&buf);
+ // If cropping
+ if (object->composition_flag & 0x80) {
+ object->crop_x = bytestream_get_be16(&buf);
+ object->crop_y = bytestream_get_be16(&buf);
+ object->crop_w = bytestream_get_be16(&buf);
+ object->crop_h = bytestream_get_be16(&buf);
+ }
- // If cropping
- if (object->composition_flag & 0x80) {
- object->crop_x = bytestream_get_be16(&buf);
- object->crop_y = bytestream_get_be16(&buf);
- object->crop_w = bytestream_get_be16(&buf);
- object->crop_h = bytestream_get_be16(&buf);
+ /* Placement is checked at decode after cropping. */
+ ff_dlog(avctx, "Subtitle Placement x=%d, y=%d\n",
+ object->x, object->y);
}
+ }
+ return 0;
+}
- ff_dlog(avctx, "Subtitle Placement x=%d, y=%d\n",
- object->x, object->y);
+/**
+ * Parse the window segment packet.
+ *
+ * The window segment instructs the decoder to redraw the graphic plane
+ * with the composition references provided in the presentation segment
+ *
+ * @param avctx contains the current codec context
+ */
+static int parse_window_segment(AVCodecContext *avctx, const uint8_t *buf,
+ int buf_size)
+{
+ PGSSubContext *ctx = (PGSSubContext *)avctx->priv_data;
- if (object->x > avctx->width || object->y > avctx->height) {
- av_log(avctx, AV_LOG_ERROR, "Subtitle out of video bounds. x = %d, y = %d, video width = %d, video height = %d.\n",
- object->x, object->y,
- avctx->width, avctx->height);
- object->y = object->x = 0;
- if (avctx->err_recognition & AV_EF_EXPLODE) {
- return AVERROR_INVALIDDATA;
- }
- }
+ // 1 byte: number of windows defined
+ if (bytestream_get_byte(&buf) > MAX_OBJECT_REFS) {
+ av_log(avctx, AV_LOG_ERROR, "Too many windows defined.\n");
+ return AVERROR_INVALIDDATA;
}
+ /* TODO: mask objects with windows when transfering to the graphic plane
+ * Window Segment Structure
+ * {
+ * 1 byte : window id,
+ * 2 bytes: X position of window,
+ * 2 bytes: Y position of window,
+ * 2 bytes: Width of window,
+ * 2 bytes: Height of window.
+ * }
+ */
+ // Flush the graphic plane, it will be redrawn.
+ clear_graphic_plane(ctx);
+ ctx->plane.writable = 1;
+ ctx->plane.count = ctx->presentation.object_count;
return 0;
}
/**
* Parse the display segment packet.
*
- * The display segment controls the updating of the display.
+ * The display segment closes the display set. The inferred data is used
+ * to decide if the display should be updated.
*
* @param avctx contains the current codec context
* @param data pointer to the data pertaining the subtitle to display
@@ -500,26 +581,33 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
PGSSubContext *ctx = avctx->priv_data;
int64_t pts;
PGSSubPalette *palette;
- int i, ret;
+ int i;
pts = ctx->presentation.pts != AV_NOPTS_VALUE ? ctx->presentation.pts : sub->pts;
memset(sub, 0, sizeof(*sub));
sub->pts = pts;
ctx->presentation.pts = AV_NOPTS_VALUE;
- sub->start_display_time = 0;
// There is no explicit end time for PGS subtitles. The end time
// is defined by the start of the next sub which may contain no
// objects (i.e. clears the previous sub)
sub->end_display_time = UINT32_MAX;
- sub->format = 0;
- // Blank if last object_count was 0.
- if (!ctx->presentation.object_count)
+ // Object count is zero only on an epoch start with no WDS
+ // or the last DS with a WDS had no presentation object.
+ if (!ctx->plane.count) {
return 1;
- sub->rects = av_calloc(ctx->presentation.object_count, sizeof(*sub->rects));
- if (!sub->rects) {
- return AVERROR(ENOMEM);
}
+
+ if (!ctx->presentation.palette_flag && !ctx->plane.writable) {
+ // This display set does not perform a display update
+ // E.g. it only defines new objects or palettes for future usage.
+ return 0;
+ }
+
+ sub->rects = av_calloc(ctx->plane.count, sizeof(*sub->rects));
+ if (!sub->rects)
+ return AVERROR(ENOMEM);
+
palette = find_palette(ctx->presentation.palette_id, &ctx->palettes);
if (!palette) {
// Missing palette. Should only happen with damaged streams.
@@ -528,57 +616,128 @@ static int display_end_segment(AVCodecContext *avctx, AVSubtitle *sub,
avsubtitle_free(sub);
return AVERROR_INVALIDDATA;
}
- for (i = 0; i < ctx->presentation.object_count; i++) {
- AVSubtitleRect *const rect = av_mallocz(sizeof(*rect));
- PGSSubObject *object;
- if (!rect)
- return AVERROR(ENOMEM);
- sub->rects[sub->num_rects++] = rect;
- rect->type = SUBTITLE_BITMAP;
-
- /* Process bitmap */
- object = find_object(ctx->presentation.objects[i].id, &ctx->objects);
- if (!object) {
- // Missing object. Should only happen with damaged streams.
- av_log(avctx, AV_LOG_ERROR, "Invalid object id %d\n",
- ctx->presentation.objects[i].id);
- if (avctx->err_recognition & AV_EF_EXPLODE)
- return AVERROR_INVALIDDATA;
- // Leaves rect empty with 0 width and height.
- continue;
- }
- if (ctx->presentation.objects[i].composition_flag & 0x40)
- rect->flags |= AV_SUBTITLE_FLAG_FORCED;
+ for (i = 0; i < ctx->plane.count; i++) {
+ const PGSSubObjectRef *sub_object = &ctx->presentation.objects[i];
+ AVSubtitleRect *const gp_rect = &ctx->plane.visible_rect[i];
+ AVSubtitleRect *rect;
+ gp_rect->type = SUBTITLE_BITMAP;
+
+ // Compose the graphic plane if a window segment has been provided
+ if (ctx->plane.writable) {
+ PGSSubObject *object;
+
+ // Process bitmap
+ object = find_object(sub_object->id, &ctx->objects);
+ if (!object) {
+ // Missing object. Should only happen with damaged streams.
+ av_log(avctx, AV_LOG_ERROR, "Invalid object id %d\n", sub_object->id);
+ if (avctx->err_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ // Leaves rect empty with 0 width and height.
+ continue;
+ }
+ if (sub_object->composition_flag & 0x40)
+ gp_rect->flags |= AV_SUBTITLE_FLAG_FORCED;
+
+ gp_rect->x = sub_object->x;
+ gp_rect->y = sub_object->y;
+
+ if (object->rle) {
+ int out_of_picture = 0;
+ gp_rect->w = object->w;
+ gp_rect->h = object->h;
+
+ gp_rect->linesize[0] = object->w;
+
+ // Check for cropping.
+ if (sub_object->composition_flag & 0x80) {
+ int out_of_object = 0;
+
+ if (object->w < sub_object->crop_x + sub_object->crop_w)
+ out_of_object = 1;
+ if (object->h < sub_object->crop_y + sub_object->crop_h)
+ out_of_object = 1;
+
+ if (out_of_object) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Subtitle cropping values are out of object. "
+ "obj_w = %d, obj_h = %d, crop_x = %d, crop_y = %d, "
+ "crop_w = %d, crop_h = %d.\n",
+ object->w,
+ object->h,
+ sub_object->crop_x,
+ sub_object->crop_y,
+ sub_object->crop_w,
+ sub_object->crop_h);
+ if (avctx->err_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ } else {
+ // Replace subtitle dimensions with cropping ones.
+ gp_rect->w = sub_object->crop_w;
+ gp_rect->h = sub_object->crop_h;
+ gp_rect->linesize[0] = sub_object->crop_w;
+ }
+ }
+
+ /* Make sure the subtitle is not out of picture. */
+ if (avctx->width < gp_rect->x + gp_rect->w || !gp_rect->w)
+ out_of_picture = 1;
+ if (avctx->height < gp_rect->y + gp_rect->h || !gp_rect->h)
+ out_of_picture = 1;
+ if (out_of_picture) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Subtitle out of video bounds. "
+ "x = %d, y = %d, width = %d, height = %d.\n",
+ gp_rect->x, gp_rect->y, gp_rect->w, gp_rect->h);
+ if (avctx->err_recognition & AV_EF_EXPLODE)
+ return AVERROR_INVALIDDATA;
+ gp_rect->w = 0;
+ gp_rect->h = 0;
+ continue;
+ }
- rect->x = ctx->presentation.objects[i].x;
- rect->y = ctx->presentation.objects[i].y;
+ if (!object->bitmap_size || object->rle_remaining_len) {
+ gp_rect->w = 0;
+ gp_rect->h = 0;
+ continue;
+ }
- if (object->rle) {
- rect->w = object->w;
- rect->h = object->h;
+ gp_rect->data[0] = av_malloc_array(gp_rect->w, gp_rect->h);
+ if (!gp_rect->data[0])
+ return AVERROR(ENOMEM);
- rect->linesize[0] = object->w;
+ if (sub_object->composition_flag & 0x80) {
+ /* Copy cropped bitmap. */
+ int y;
- if (object->rle_remaining_len) {
- av_log(avctx, AV_LOG_ERROR, "RLE data length %u is %u bytes shorter than expected\n",
- object->rle_data_len, object->rle_remaining_len);
- if (avctx->err_recognition & AV_EF_EXPLODE)
- return AVERROR_INVALIDDATA;
- }
- ret = decode_rle(avctx, rect, object->rle, object->rle_data_len);
- if (ret < 0) {
- if ((avctx->err_recognition & AV_EF_EXPLODE) ||
- ret == AVERROR(ENOMEM)) {
- return ret;
+ for (y = 0; y < sub_object->crop_h; y++) {
+ memcpy(&gp_rect->data[0][y * sub_object->crop_w],
+ &object->bitmap[(sub_object->crop_y + y) *
+ object->w + sub_object->crop_x],
+ sub_object->crop_w);
+ }
+ }
+ else {
+ /* copy full object */
+ memcpy(gp_rect->data[0], object->bitmap, object->bitmap_size);
}
- rect->w = 0;
- rect->h = 0;
- continue;
}
}
- /* Allocate memory for colors */
- rect->nb_colors = 256;
+ // Export graphic plane content with latest palette
+ rect = av_memdup(gp_rect, sizeof(*gp_rect));
+ if (!rect)
+ return AVERROR(ENOMEM);
+
+ sub->rects[sub->num_rects++] = rect;
+ if (gp_rect->data[0]) {
+ rect->data[0] = av_memdup(gp_rect->data[0], rect->w*rect->h);
+ if (!rect->data[0])
+ return AVERROR(ENOMEM);
+ }
+
+ // Allocate memory for colors
+ rect->nb_colors = AVPALETTE_COUNT;
rect->data[1] = av_mallocz(AVPALETTE_SIZE);
if (!rect->data[1])
return AVERROR(ENOMEM);
@@ -641,14 +800,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
ret = parse_presentation_segment(avctx, buf, segment_length, sub->pts);
break;
case WINDOW_SEGMENT:
- /*
- * Window Segment Structure (No new information provided):
- * 2 bytes: Unknown,
- * 2 bytes: X position of subtitle,
- * 2 bytes: Y position of subtitle,
- * 2 bytes: Width of subtitle,
- * 2 bytes: Height of subtitle.
- */
+ ret = parse_window_segment(avctx, buf, segment_length);
break;
case DISPLAY_SEGMENT:
if (*got_sub_ptr) {
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 8200a14..284b608 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -494,6 +494,9 @@ static void dump_video_av1_param(AVCodecContext *avctx, QSVEncContext *q,
mfxExtAV1BitstreamParam *av1_bs_param = (mfxExtAV1BitstreamParam *)coding_opts[1];
mfxExtCodingOption2 *co2 = (mfxExtCodingOption2*)coding_opts[2];
mfxExtCodingOption3 *co3 = (mfxExtCodingOption3*)coding_opts[3];
+#if QSV_HAVE_EXT_AV1_SCC
+ mfxExtAV1ScreenContentTools *scc = (mfxExtAV1ScreenContentTools*)coding_opts[4];
+#endif
av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n",
print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel);
@@ -566,6 +569,13 @@ static void dump_video_av1_param(AVCodecContext *avctx, QSVEncContext *q,
print_threestate(av1_bs_param->WriteIVFHeaders));
av_log(avctx, AV_LOG_VERBOSE, "LowDelayBRC: %s\n", print_threestate(co3->LowDelayBRC));
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %d;\n", co2->MaxFrameSize);
+#if QSV_HAVE_EXT_AV1_SCC
+ if (scc) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "Palette: %s; IntraBlockCopy: %s\n",
+ print_threestate(scc->Palette), print_threestate(scc->IntraBlockCopy));
+ }
+#endif
}
#endif
@@ -1282,6 +1292,28 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
}
#endif
+#if QSV_HAVE_EXT_AV1_SCC
+ if (q->palette_mode || q->intrabc) {
+ if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 13)) {
+ if (q->param.mfx.CodecId != MFX_CODEC_AV1) {
+ av_log(avctx, AV_LOG_ERROR, "Not supported encoder for Screen Content Tool Encode. "
+ "Supported: av1_qsv \n");
+ return AVERROR_UNKNOWN;
+ }
+
+ q->extsccparam.Header.BufferId = MFX_EXTBUFF_AV1_SCREEN_CONTENT_TOOLS;
+ q->extsccparam.Header.BufferSz = sizeof(q->extsccparam);
+ q->extsccparam.Palette = q->palette_mode ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
+ q->extsccparam.IntraBlockCopy = q->intrabc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
+ q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extsccparam;
+ } else {
+ av_log(avctx, AV_LOG_ERROR,
+ "This version of runtime doesn't support Screen Content Tool Encode\n");
+ return AVERROR_UNKNOWN;
+ }
+ }
+#endif
+
if (!check_enc_param(avctx,q)) {
av_log(avctx, AV_LOG_ERROR,
"some encoding parameters are not supported by the QSV "
@@ -1389,11 +1421,21 @@ static int qsv_retrieve_enc_av1_params(AVCodecContext *avctx, QSVEncContext *q)
.Header.BufferSz = sizeof(co3),
};
+#if QSV_HAVE_EXT_AV1_SCC
+ mfxExtAV1ScreenContentTools scc_buf = {
+ .Header.BufferId = MFX_EXTBUFF_AV1_SCREEN_CONTENT_TOOLS,
+ .Header.BufferSz = sizeof(scc_buf),
+ };
+#endif
+
mfxExtBuffer *ext_buffers[] = {
(mfxExtBuffer*)&av1_extend_tile_buf,
(mfxExtBuffer*)&av1_bs_param,
(mfxExtBuffer*)&co2,
(mfxExtBuffer*)&co3,
+#if QSV_HAVE_EXT_AV1_SCC
+ (mfxExtBuffer*)&scc_buf,
+#endif
};
if (!QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 5)) {
@@ -1842,6 +1884,13 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
return ret;
}
+ // Update AVCodecContext with actual encoding parameters
+ mfxInfoMFX *info = &q->param.mfx;
+ avctx->has_b_frames = 0;
+ if (info->GopRefDist > 1) {
+ avctx->has_b_frames = info->GopRefDist - 1;
+ }
+
q->avctx = avctx;
return 0;
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index 4bc77f2..2e0a19b 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -38,6 +38,7 @@
#define QSV_HAVE_EXT_VP9_TILES QSV_VERSION_ATLEAST(1, 29)
#define QSV_HAVE_EXT_AV1_PARAM QSV_VERSION_ATLEAST(2, 5)
+#define QSV_HAVE_EXT_AV1_SCC QSV_VERSION_ATLEAST(2, 13)
#if defined(_WIN32) || defined(__CYGWIN__)
#define QSV_HAVE_AVBR 1
@@ -188,10 +189,13 @@ typedef struct QSVEncContext {
mfxFrameSurface1 **opaque_surfaces;
AVBufferRef *opaque_alloc_buf;
#endif
+#if QSV_HAVE_EXT_AV1_SCC
+ mfxExtAV1ScreenContentTools extsccparam;
+#endif
mfxExtVideoSignalInfo extvsi;
- mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + (QSV_HAVE_EXT_AV1_PARAM * 2) + QSV_HAVE_HE];
+ mfxExtBuffer *extparam_internal[5 + (QSV_HAVE_MF * 2) + (QSV_HAVE_EXT_AV1_PARAM * 2) + QSV_HAVE_HE + QSV_HAVE_EXT_AV1_SCC];
int nb_extparam_internal;
mfxExtBuffer **extparam_str;
@@ -319,6 +323,8 @@ typedef struct QSVEncContext {
int dual_gfx;
AVDictionary *qsv_params;
+ int palette_mode;
+ int intrabc;
} QSVEncContext;
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q);
diff --git a/libavcodec/qsvenc_av1.c b/libavcodec/qsvenc_av1.c
index a86b409..4f035f3 100644
--- a/libavcodec/qsvenc_av1.c
+++ b/libavcodec/qsvenc_av1.c
@@ -189,6 +189,10 @@ static const AVOption options[] = {
{ "tile_cols", "Number of columns for tiled encoding", OFFSET(qsv.tile_cols), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, VE },
{ "tile_rows", "Number of rows for tiled encoding", OFFSET(qsv.tile_rows), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, VE },
{ "look_ahead_depth", "Depth of look ahead in number frames, available when extbrc option is enabled", OFFSET(qsv.look_ahead_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VE },
+#if QSV_HAVE_EXT_AV1_SCC
+ { "palette_mode", "Enable palette mode of Screen Content Tool for encoding", OFFSET(qsv.palette_mode), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, VE},
+ { "intrabc", "Enable intra block copy of Screen Content Tool for encoding", OFFSET(qsv.intrabc), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, VE},
+#endif
{ NULL },
};
diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c
index 505483e..04195c4 100644
--- a/libavcodec/videotoolbox.c
+++ b/libavcodec/videotoolbox.c
@@ -56,6 +56,10 @@ enum { kCMVideoCodecType_HEVC = 'hvc1' };
enum { kCMVideoCodecType_VP9 = 'vp09' };
#endif
+#if !HAVE_KCMVIDEOCODECTYPE_AV1
+enum { kCMVideoCodecType_AV1 = 'av01' };
+#endif
+
#define VIDEOTOOLBOX_ESDS_EXTRADATA_PADDING 12
typedef struct VTHWFrame {
@@ -92,6 +96,26 @@ int ff_videotoolbox_buffer_copy(VTContext *vtctx,
return 0;
}
+int ff_videotoolbox_buffer_append(VTContext *vtctx,
+ const uint8_t *buffer,
+ uint32_t size)
+{
+ void *tmp;
+
+ tmp = av_fast_realloc(vtctx->bitstream,
+ &vtctx->allocated_size,
+ vtctx->bitstream_size + size);
+
+ if (!tmp)
+ return AVERROR(ENOMEM);
+
+ vtctx->bitstream = tmp;
+ memcpy(vtctx->bitstream + vtctx->bitstream_size, buffer, size);
+ vtctx->bitstream_size += size;
+
+ return 0;
+}
+
static int videotoolbox_postproc_frame(void *avctx, AVFrame *frame)
{
int ret;
@@ -108,9 +132,6 @@ static int videotoolbox_postproc_frame(void *avctx, AVFrame *frame)
frame->crop_top = 0;
frame->crop_bottom = 0;
- if ((ret = av_vt_pixbuf_set_attachments(avctx, ref->pixbuf, frame)) < 0)
- return ret;
-
frame->data[3] = (uint8_t*)ref->pixbuf;
if (ref->hw_frames_ctx) {
@@ -790,7 +811,7 @@ static CFDictionaryRef videotoolbox_buffer_attributes_create(int width,
#if TARGET_OS_IPHONE
CFDictionarySetValue(buffer_attributes, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue);
#else
- CFDictionarySetValue(buffer_attributes, kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey, kCFBooleanTrue);
+ CFDictionarySetValue(buffer_attributes, kCVPixelBufferMetalCompatibilityKey, kCFBooleanTrue);
#endif
CFRelease(io_surface_properties);
@@ -846,6 +867,13 @@ static CFDictionaryRef videotoolbox_decoder_config_create(CMVideoCodecType codec
if (data)
CFDictionarySetValue(avc_info, CFSTR("vpcC"), data);
break;
+#endif
+#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL
+ case kCMVideoCodecType_AV1 :
+ data = ff_videotoolbox_av1c_extradata_create(avctx);
+ if (data)
+ CFDictionarySetValue(avc_info, CFSTR("av1C"), data);
+ break;
#endif
default:
break;
@@ -912,10 +940,30 @@ static int videotoolbox_start(AVCodecContext *avctx)
case AV_CODEC_ID_VP9 :
videotoolbox->cm_codec_type = kCMVideoCodecType_VP9;
break;
+ case AV_CODEC_ID_AV1 :
+ videotoolbox->cm_codec_type = kCMVideoCodecType_AV1;
+ break;
default :
break;
}
+#if ARCH_X86_64
+ if (avctx->codec_id == AV_CODEC_ID_H264 &&
+ avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10)
+ {
+ // 10-bit H.264 is not supported on x86_64
+ return AVERROR(ENOSYS);
+ }
+#endif
+
+ if (avctx->codec_id == AV_CODEC_ID_H264 &&
+ (avctx->level == 61 || avctx->level == 62))
+ {
+ // H.264 Level 6.1 and 6.2 can't be
+ // decoded properly
+ return AVERROR(ENOSYS);
+ }
+
#if defined(MAC_OS_X_VERSION_10_9) && !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9) && AV_HAS_BUILTIN(__builtin_available)
if (avctx->codec_id == AV_CODEC_ID_PRORES) {
if (__builtin_available(macOS 10.9, *)) {
diff --git a/libavcodec/videotoolbox_av1.c b/libavcodec/videotoolbox_av1.c
new file mode 100644
index 0000000..b4d3919
--- /dev/null
+++ b/libavcodec/videotoolbox_av1.c
@@ -0,0 +1,105 @@
+/*
+ * Videotoolbox hardware acceleration for AV1
+ * Copyright (c) 2023 Jan Ekström
+ * Copyright (c) 2024 Ruslan Chernenko
+ * Copyright (c) 2024 Martin Storsjö
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/mem.h"
+
+#include "av1dec.h"
+#include "hwaccel_internal.h"
+#include "internal.h"
+#include "vt_internal.h"
+
+CFDataRef ff_videotoolbox_av1c_extradata_create(AVCodecContext *avctx)
+{
+ AV1DecContext *s = avctx->priv_data;
+ uint8_t *buf;
+ CFDataRef data;
+ if (!s->raw_seq)
+ return NULL;
+
+ buf = av_malloc(s->seq_data_ref->size + 4);
+ if (!buf)
+ return NULL;
+ buf[0] = 0x81; // version and marker (constant)
+ buf[1] = s->raw_seq->seq_profile << 5 | s->raw_seq->seq_level_idx[0];
+ buf[2] = s->raw_seq->seq_tier[0] << 7 |
+ s->raw_seq->color_config.high_bitdepth << 6 |
+ s->raw_seq->color_config.twelve_bit << 5 |
+ s->raw_seq->color_config.mono_chrome << 4 |
+ s->raw_seq->color_config.subsampling_x << 3 |
+ s->raw_seq->color_config.subsampling_y << 2 |
+ s->raw_seq->color_config.chroma_sample_position;
+
+ if (s->raw_seq->initial_display_delay_present_flag)
+ buf[3] = 0 << 5 |
+ s->raw_seq->initial_display_delay_present_flag << 4 |
+ s->raw_seq->initial_display_delay_minus_1[0];
+ else
+ buf[3] = 0x00;
+ memcpy(buf + 4, s->seq_data_ref->data, s->seq_data_ref->size);
+ data = CFDataCreate(kCFAllocatorDefault, buf, s->seq_data_ref->size + 4);
+ av_free(buf);
+ return data;
+};
+
+
+static int videotoolbox_av1_start_frame(AVCodecContext *avctx,
+ const uint8_t *buffer,
+ uint32_t size)
+{
+ return 0;
+}
+
+static int videotoolbox_av1_decode_slice(AVCodecContext *avctx,
+ const uint8_t *buffer,
+ uint32_t size)
+{
+ return 0;
+}
+
+static int videotoolbox_av1_end_frame(AVCodecContext *avctx)
+{
+ const AV1DecContext *s = avctx->priv_data;
+ VTContext *vtctx = avctx->internal->hwaccel_priv_data;
+ AVFrame *frame = s->cur_frame.f;
+
+ vtctx->bitstream_size = 0;
+ for (int i = s->start_unit; i < s->nb_unit; i++)
+ ff_videotoolbox_buffer_append(vtctx, s->current_obu.units[i].data,
+ s->current_obu.units[i].data_size);
+ return ff_videotoolbox_common_end_frame(avctx, frame);
+}
+
+const FFHWAccel ff_av1_videotoolbox_hwaccel = {
+ .p.name = "av1_videotoolbox",
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_AV1,
+ .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX,
+ .alloc_frame = ff_videotoolbox_alloc_frame,
+ .start_frame = videotoolbox_av1_start_frame,
+ .decode_slice = videotoolbox_av1_decode_slice,
+ .end_frame = videotoolbox_av1_end_frame,
+ .frame_params = ff_videotoolbox_frame_params,
+ .init = ff_videotoolbox_common_init,
+ .uninit = ff_videotoolbox_uninit,
+ .priv_data_size = sizeof(VTContext),
+};
diff --git a/libavcodec/vt_internal.h b/libavcodec/vt_internal.h
index 9502d7c..effa96f 100644
--- a/libavcodec/vt_internal.h
+++ b/libavcodec/vt_internal.h
@@ -56,6 +56,9 @@ int ff_videotoolbox_frame_params(AVCodecContext *avctx,
int ff_videotoolbox_buffer_copy(VTContext *vtctx,
const uint8_t *buffer,
uint32_t size);
+int ff_videotoolbox_buffer_append(VTContext *vtctx,
+ const uint8_t *buffer,
+ uint32_t size);
int ff_videotoolbox_uninit(AVCodecContext *avctx);
int ff_videotoolbox_h264_start_frame(AVCodecContext *avctx,
const uint8_t *buffer,
@@ -64,6 +67,7 @@ int ff_videotoolbox_h264_decode_slice(AVCodecContext *avctx,
const uint8_t *buffer,
uint32_t size);
int ff_videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame);
+CFDataRef ff_videotoolbox_av1c_extradata_create(AVCodecContext *avctx);
CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx);
CFDataRef ff_videotoolbox_hvcc_extradata_create(AVCodecContext *avctx);
CFDataRef ff_videotoolbox_vpcc_extradata_create(AVCodecContext *avctx);
diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c
index 7f05f1f..223e919 100644
--- a/libavfilter/vf_vpp_qsv.c
+++ b/libavfilter/vf_vpp_qsv.c
@@ -494,9 +494,9 @@ static int vpp_set_frame_ext_params(AVFilterContext *ctx, const AVFrame *in, AVF
outvsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_OUT;
outvsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
outvsi_conf.VideoFullRange = (out->color_range == AVCOL_RANGE_JPEG);
- outvsi_conf.ColourPrimaries = (out->color_primaries == AVCOL_PRI_UNSPECIFIED) ? AVCOL_PRI_BT709 : out->color_primaries;
- outvsi_conf.TransferCharacteristics = (out->color_trc == AVCOL_TRC_UNSPECIFIED) ? AVCOL_TRC_BT709 : out->color_trc;
- outvsi_conf.MatrixCoefficients = (out->colorspace == AVCOL_SPC_UNSPECIFIED) ? AVCOL_SPC_BT709 : out->colorspace;
+ outvsi_conf.ColourPrimaries = (out->color_primaries == AVCOL_PRI_UNSPECIFIED) ? invsi_conf.ColourPrimaries : out->color_primaries;
+ outvsi_conf.TransferCharacteristics = (out->color_trc == AVCOL_TRC_UNSPECIFIED) ? invsi_conf.TransferCharacteristics : out->color_trc;
+ outvsi_conf.MatrixCoefficients = (out->colorspace == AVCOL_SPC_UNSPECIFIED) ? invsi_conf.MatrixCoefficients : out->colorspace;
outvsi_conf.ColourDescriptionPresent = 1;
if (memcmp(&vpp->invsi_conf, &invsi_conf, sizeof(mfxExtVideoSignalInfo)) ||
diff --git a/libavformat/isom.h b/libavformat/isom.h
index f18fe54..02f8f49 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -272,6 +272,9 @@ typedef struct MOVStreamContext {
MOVEncryptionIndex *encryption_index;
} cenc;
+ int has_fallback; // Audio fallback track
+ int fallback;
+
struct IAMFDemuxContext *iamf;
int iamf_stream_offset;
} MOVStreamContext;
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 5d00b26..888e868 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -2933,6 +2933,16 @@ static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt)
case AV_CODEC_ID_AAC:
if (side_data_size && mkv->track.bc) {
int output_sample_rate = 0;
+ if (par->extradata && par->extradata_size) {
+ if (par->extradata_size != side_data_size ||
+ memcmp(par->extradata, side_data, side_data_size)) {
+ av_log(s, AV_LOG_ERROR, "Error, AAC extradata changed mid-stream.\n");
+ return AVERROR(EINVAL);
+ } else {
+ // Already written
+ break;
+ }
+ }
ret = get_aac_sample_rates(s, mkv, side_data, side_data_size,
&track->sample_rate, &output_sample_rate);
if (ret < 0)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index eb9b4ac..9657975 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -56,6 +56,7 @@
#include "libavcodec/mpegaudiodecheader.h"
#include "libavcodec/mlp_parse.h"
#include "avformat.h"
+#include "avlanguage.h"
#include "internal.h"
#include "avio_internal.h"
#include "demux.h"
@@ -131,6 +132,33 @@ static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb,
return 0;
}
+static int mov_metadata_int16_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb16(pb), 0);
+
+ return 0;
+}
+
+static int mov_metadata_int32_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb32(pb), 0);
+
+ return 0;
+}
+
+static int mov_metadata_int64_no_padding(MOVContext *c, AVIOContext *pb,
+ unsigned len, const char *key)
+{
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ av_dict_set_int(&c->fc->metadata, key, avio_rb64(pb), 0);
+
+ return 0;
+}
+
static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb,
unsigned len, const char *key)
{
@@ -342,6 +370,73 @@ static int mov_metadata_hmmt(MOVContext *c, AVIOContext *pb, unsigned len)
return 0;
}
+static int mov_read_3gp_udta_tag(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ const char *key;
+ AVDictionary *metadata;
+ uint16_t langcode = 0;
+ char key2[32], language[4] = {0};
+ uint32_t str_size, version;
+ char *str;
+ if (atom.size < 6)
+ return AVERROR_INVALIDDATA;
+ switch (atom.type) {
+ case MKTAG( 'a','l','b','m'): key = "album"; break;
+ case MKTAG( 'a','u','t','h'): key = "author"; break;
+ case MKTAG( 'c','p','r','t'): key = "copyright"; break;
+ case MKTAG( 'd','s','c','p'): key = "comment"; break;
+ case MKTAG( 'g','n','r','e'): key = "genre"; break;
+ case MKTAG( 'p','e','r','f'): key = "artist"; break;
+ case MKTAG( 't','i','t','l'): key = "title"; break;
+ case MKTAG( 'y','r','r','c'): key = "date"; break;
+ default: return 0;
+ }
+ version = avio_rb32(pb); // version + flags
+ if (version != 0)
+ av_log(c->fc, AV_LOG_WARNING, "udta %s unknown version number: %u\n", str, version);
+ if (MKTAG( 'y','r','r','c') == atom.type) {
+ int year;
+ year = avio_rb16(pb);
+ str = av_asprintf("%d", year);
+ if (!str)
+ return AVERROR(ENOMEM);
+ } else {
+ int ret;
+ const char *tmp;
+ langcode = avio_rb16(pb);
+ ff_mov_lang_to_iso639(langcode, language);
+ tmp = ff_convert_lang_to(language, AV_LANG_ISO639_2_BIBL);
+ if (!tmp)
+ av_log(c->fc, AV_LOG_WARNING, "udta %s unknown language code: %u\n", str, langcode);
+ str_size = atom.size - 6;
+ if (str_size <= 0 || str_size >= INT_MAX/2)
+ return AVERROR_INVALIDDATA;
+ str = av_mallocz(str_size + 1);
+ if (!str)
+ return AVERROR(ENOMEM);
+ ret = ffio_read_size(pb, str, str_size);
+ if (ret < 0) {
+ av_free(str);
+ return ret;
+ }
+ str[str_size] = 0;
+ }
+ if (c->trak_index < 0) {
+ metadata = c->fc->metadata;
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ }
+ else {
+ metadata = c->fc->streams[c->trak_index]->metadata;
+ }
+ av_dict_set(&metadata, key, str, 0);
+ if (*language && strcmp(language, "und")) {
+ snprintf(key2, sizeof(key2), "%s-%s", key, language);
+ av_dict_set(&metadata, key2, str, 0);
+ }
+ av_freep(&str);
+ return 0;
+}
+
static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
char tmp_key[AV_FOURCC_MAX_STRING_SIZE] = {0};
@@ -354,6 +449,7 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL;
int raw = 0;
int num = 0;
+ AVDictionary *metadata;
switch (atom.type) {
case MKTAG( '@','P','R','M'): key = "premiere_version"; raw = 1; break;
@@ -364,35 +460,71 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 'a','k','I','D'): key = "account_type";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'a','p','I','D'): key = "account_id"; break;
+ case MKTAG( 'a','t','I','D'): key = "artist_id";
+ parse = mov_metadata_int32_no_padding; break;
case MKTAG( 'c','a','t','g'): key = "category"; break;
+ case MKTAG( 'c','m','I','D'): key = "composer_id";
+ parse = mov_metadata_int32_no_padding; break;
+ case MKTAG( 'c','n','I','D'): key = "content_id";
+ parse = mov_metadata_int32_no_padding; break;
case MKTAG( 'c','p','i','l'): key = "compilation";
parse = mov_metadata_int8_no_padding; break;
- case MKTAG( 'c','p','r','t'): key = "copyright"; break;
+ case MKTAG( 'c','p','r','t'):
+ key = "copyright";
+ if (!c->itunes_metadata) {
+ int64_t pos = avio_tell(pb);
+ int ret = mov_read_3gp_udta_tag(c, pb, atom);
+ if (ret != AVERROR_INVALIDDATA)
+ return ret;
+ avio_seek(pb, pos, SEEK_SET);
+ }
+ break;
case MKTAG( 'd','e','s','c'): key = "description"; break;
case MKTAG( 'd','i','s','k'): key = "disc";
parse = mov_metadata_track_or_disc_number; break;
case MKTAG( 'e','g','i','d'): key = "episode_uid";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'F','I','R','M'): key = "firmware"; raw = 1; break;
- case MKTAG( 'g','n','r','e'): key = "genre";
- parse = mov_metadata_gnre; break;
+ case MKTAG( 'g','e','I','D'): key = "genre_id";
+ parse = mov_metadata_int32_no_padding; break;
+ case MKTAG( 'g','n','r','e'):
+ key = "genre";
+ parse = mov_metadata_gnre;
+ if (!c->itunes_metadata) {
+ int64_t pos = avio_tell(pb);
+ int ret = mov_read_3gp_udta_tag(c, pb, atom);
+ if (ret != AVERROR_INVALIDDATA)
+ return ret;
+ avio_seek(pb, pos, SEEK_SET);
+ }
+ break;
case MKTAG( 'h','d','v','d'): key = "hd_video";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'H','M','M','T'):
return mov_metadata_hmmt(c, pb, atom.size);
+ case MKTAG( 'i','t','n','u'): key = "itunes_u";
+ parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'k','e','y','w'): key = "keywords"; break;
case MKTAG( 'l','d','e','s'): key = "synopsis"; break;
case MKTAG( 'l','o','c','i'):
return mov_metadata_loci(c, pb, atom.size);
case MKTAG( 'm','a','n','u'): key = "make"; break;
case MKTAG( 'm','o','d','l'): key = "model"; break;
+ case MKTAG( 'n','a','m','e'): key = "title"; raw = 1; break;
case MKTAG( 'p','c','s','t'): key = "podcast";
parse = mov_metadata_int8_no_padding; break;
case MKTAG( 'p','g','a','p'): key = "gapless_playback";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 'p','l','I','D'): key = "playlist_id";
+ parse = mov_metadata_int64_no_padding; break;
case MKTAG( 'p','u','r','d'): key = "purchase_date"; break;
case MKTAG( 'r','t','n','g'): key = "rating";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 's','f','I','D'): key = "itunes_country";
+ parse = mov_metadata_int32_no_padding; break;
+ case MKTAG( 's','d','e','s'): key = "series_description"; break;
+ case MKTAG( 's','h','w','m'): key = "show_work_and_movement";
+ parse = mov_metadata_int8_no_padding; break;
case MKTAG( 's','o','a','a'): key = "sort_album_artist"; break;
case MKTAG( 's','o','a','l'): key = "sort_album"; break;
case MKTAG( 's','o','a','r'): key = "sort_artist"; break;
@@ -401,6 +533,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 's','o','s','n'): key = "sort_show"; break;
case MKTAG( 's','t','i','k'): key = "media_type";
parse = mov_metadata_int8_no_padding; break;
+ case MKTAG( 't','m','p','o'): key = "tmpo";
+ parse = mov_metadata_int16_no_padding; break;
case MKTAG( 't','r','k','n'): key = "track";
parse = mov_metadata_track_or_disc_number; break;
case MKTAG( 't','v','e','n'): key = "episode_id"; break;
@@ -410,17 +544,23 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG( 't','v','s','h'): key = "show"; break;
case MKTAG( 't','v','s','n'): key = "season_number";
parse = mov_metadata_int8_bypass_padding; break;
+ case MKTAG( 'x','i','d',' '): key = "xid"; break;
case MKTAG(0xa9,'A','R','T'): key = "artist"; break;
case MKTAG(0xa9,'P','R','D'): key = "producer"; break;
case MKTAG(0xa9,'a','l','b'): key = "album"; break;
- case MKTAG(0xa9,'a','u','t'): key = "artist"; break;
+ case MKTAG(0xa9,'a','r','d'): key = "art_director"; break;
+ case MKTAG(0xa9,'a','r','g'): key = "arranger"; break;
+ case MKTAG(0xa9,'a','u','t'): key = "author"; break;
+ case MKTAG(0xa9,'c','a','k'): key = "acknowledgement"; break;
case MKTAG(0xa9,'c','h','p'): key = "chapter"; break;
case MKTAG(0xa9,'c','m','t'): key = "comment"; break;
case MKTAG(0xa9,'c','o','m'): key = "composer"; break;
+ case MKTAG(0xa9,'c','o','n'): key = "conductor"; break;
case MKTAG(0xa9,'c','p','y'): key = "copyright"; break;
case MKTAG(0xa9,'d','a','y'): key = "date"; break;
case MKTAG(0xa9,'d','i','r'): key = "director"; break;
case MKTAG(0xa9,'d','i','s'): key = "disclaimer"; break;
+ case MKTAG(0xa9,'d','e','s'): key = "song_description"; break;
case MKTAG(0xa9,'e','d','1'): key = "edit_date"; break;
case MKTAG(0xa9,'e','n','c'): key = "encoder"; break;
case MKTAG(0xa9,'f','m','t'): key = "original_format"; break;
@@ -428,23 +568,45 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
case MKTAG(0xa9,'g','r','p'): key = "grouping"; break;
case MKTAG(0xa9,'h','s','t'): key = "host_computer"; break;
case MKTAG(0xa9,'i','n','f'): key = "comment"; break;
+ case MKTAG(0xa9,'l','n','t'): key = "linear_notes"; break;
case MKTAG(0xa9,'l','y','r'): key = "lyrics"; break;
case MKTAG(0xa9,'m','a','k'): key = "make"; break;
case MKTAG(0xa9,'m','o','d'): key = "model"; break;
+ case MKTAG(0xa9,'m','v','n'): key = "movement_name"; break;
+ case MKTAG(0xa9,'m','v','i'): key = "movement_number";
+ parse = mov_metadata_int16_no_padding; break;
+ case MKTAG(0xa9,'m','v','c'): key = "movement_count";
+ parse = mov_metadata_int16_no_padding; break;
case MKTAG(0xa9,'n','a','m'): key = "title"; break;
case MKTAG(0xa9,'o','p','e'): key = "original_artist"; break;
+ case MKTAG(0xa9,'p','h','g'): key = "phonogram_rights"; break;
case MKTAG(0xa9,'p','r','d'): key = "producer"; break;
case MKTAG(0xa9,'p','r','f'): key = "performers"; break;
+ case MKTAG(0xa9,'p','u','b'): key = "publisher"; break;
case MKTAG(0xa9,'r','e','q'): key = "playback_requirements"; break;
+ case MKTAG(0xa9,'s','n','e'): key = "sound_engineer"; break;
+ case MKTAG(0xa9,'s','o','l'): key = "soloist"; break;
case MKTAG(0xa9,'s','r','c'): key = "original_source"; break;
case MKTAG(0xa9,'s','t','3'): key = "subtitle"; break;
case MKTAG(0xa9,'s','w','r'): key = "encoder"; break;
+ case MKTAG(0xa9,'t','h','x'): key = "thanks"; break;
case MKTAG(0xa9,'t','o','o'): key = "encoder"; break;
case MKTAG(0xa9,'t','r','k'): key = "track"; break;
case MKTAG(0xa9,'u','r','l'): key = "URL"; break;
+ case MKTAG(0xa9,'w','r','k'): key = "work_name"; break;
case MKTAG(0xa9,'w','r','n'): key = "warning"; break;
case MKTAG(0xa9,'w','r','t'): key = "composer"; break;
+ case MKTAG(0xa9,'x','p','d'): key = "executive_producer"; break;
case MKTAG(0xa9,'x','y','z'): key = "location"; break;
+ case MKTAG( 'a','l','b','m'):
+ case MKTAG( 'a','u','t','h'):
+ case MKTAG( 'd','s','c','p'):
+ case MKTAG( 'p','e','r','f'):
+ case MKTAG( 't','i','t','l'):
+ case MKTAG( 'y','r','r','c'):
+ if (!c->itunes_metadata) {
+ return mov_read_3gp_udta_tag(c, pb, atom);
+ }
}
retry:
if (c->itunes_metadata && atom.size > 8) {
@@ -571,17 +733,23 @@ retry:
}
str[str_size] = 0;
}
- c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
- av_dict_set(&c->fc->metadata, key, str, 0);
+ if (c->trak_index < 0) {
+ metadata = c->fc->metadata;
+ c->fc->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+ if (!strcmp(key, "encoder")) {
+ int major, minor, micro;
+ if (sscanf(str, "HandBrake %d.%d.%d", &major, &minor, &micro) == 3) {
+ c->handbrake_version = 1000000*major + 1000*minor + micro;
+ }
+ }
+ }
+ else {
+ metadata = c->fc->streams[c->trak_index]->metadata;
+ }
+ av_dict_set(&metadata, key, str, 0);
if (*language && strcmp(language, "und")) {
snprintf(key2, sizeof(key2), "%s-%s", key, language);
- av_dict_set(&c->fc->metadata, key2, str, 0);
- }
- if (!strcmp(key, "encoder")) {
- int major, minor, micro;
- if (sscanf(str, "HandBrake %d.%d.%d", &major, &minor, &micro) == 3) {
- c->handbrake_version = 1000000*major + 1000*minor + micro;
- }
+ av_dict_set(&metadata, key2, str, 0);
}
}
@@ -9122,6 +9290,23 @@ fail:
return ret;
}
+static int mov_read_fall(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = st->priv_data;
+
+ sc->fallback = avio_rb32(pb);
+ sc->has_fallback = 1;
+
+ return 0;
+}
+
+
static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('A','C','L','R'), mov_read_aclr },
{ MKTAG('A','P','R','G'), mov_read_avid },
@@ -9224,6 +9409,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('v','p','c','C'), mov_read_vpcc },
{ MKTAG('m','d','c','v'), mov_read_mdcv },
{ MKTAG('c','l','l','i'), mov_read_clli },
+{ MKTAG('f','a','l','l'), mov_read_fall },
{ MKTAG('d','v','c','C'), mov_read_dvcc_dvvc },
{ MKTAG('d','v','v','C'), mov_read_dvcc_dvvc },
{ MKTAG('d','v','w','C'), mov_read_dvcc_dvvc },
@@ -10446,6 +10632,23 @@ static int mov_read_header(AVFormatContext *s)
err = ff_replaygain_export(st, s->metadata);
if (err < 0)
return err;
+ if (sc->has_fallback) {
+ for (j = 0; j < s->nb_streams; j++) {
+ if (s->streams[j]->id == sc->fallback) {
+ AVPacketSideData *sd;
+ int *fallback;
+ sd = av_packet_side_data_new(&st->codecpar->coded_side_data,
+ &st->codecpar->nb_coded_side_data,
+ AV_PKT_DATA_FALLBACK_TRACK,
+ sizeof(int), 0);
+ if (!sd)
+ return AVERROR(ENOMEM);
+ fallback = (int*)sd->data;
+ *fallback = j;
+ break;
+ }
+ }
+ }
break;
case AVMEDIA_TYPE_VIDEO:
if (sc->display_matrix) {
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 83ce396..e038d12 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -28,6 +28,7 @@
#include "movenc.h"
#include "avformat.h"
+#include "avlanguage.h"
#include "avio_internal.h"
#include "dovi_isom.h"
#include "riff.h"
@@ -4055,6 +4056,41 @@ static int mov_write_udta_sdp(AVIOContext *pb, MOVTrack *track)
return len + 24;
}
+static uint16_t language_code(const char *str)
+{
+ return (((str[0] - 0x60) & 0x1F) << 10) +
+ (((str[1] - 0x60) & 0x1F) << 5) +
+ (( str[2] - 0x60) & 0x1F);
+}
+static int mov_write_3gp_udta_tag(AVIOContext *pb, AVDictionary *metadata,
+ const char *tag, const char *str)
+{
+ int64_t pos = avio_tell(pb);
+ AVDictionaryEntry *t = av_dict_get(metadata, str, NULL, 0);
+ if (!t || !utf8len(t->value))
+ return 0;
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, tag); /* type */
+ avio_wb32(pb, 0); /* version + flags */
+ if (!strcmp(tag, "yrrc"))
+ avio_wb16(pb, atoi(t->value));
+ else {
+ int lang = 0, len;
+ len = strlen(t->key);
+ if (t->key[len - 4] == '-') {
+ lang = ff_mov_iso639_to_lang(&t->key[len - 3], 1);
+ }
+ if (!lang)
+ lang = ff_mov_iso639_to_lang("und", 1);
+ avio_wb16(pb, lang); /* language */
+ avio_write(pb, t->value, strlen(t->value) + 1); /* UTF8 string value */
+ if (!strcmp(tag, "albm") &&
+ (t = av_dict_get(metadata, "track", NULL, 0)))
+ avio_w8(pb, atoi(t->value));
+ }
+ return update_size(pb, pos);
+}
+
static int mov_write_track_metadata(AVIOContext *pb, AVStream *st,
const char *tag, const char *str)
{
@@ -4129,8 +4165,23 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
if (ret < 0)
return ret;
- if (mov->mode & (MODE_MP4|MODE_MOV))
+ if (mov->mode & (MODE_MP4|MODE_MOV)) {
+ AVDictionaryEntry *t = NULL;
+ int und = 0;
+
mov_write_track_metadata(pb_buf, st, "name", "title");
+ while ((t = av_dict_get(st->metadata, "title-", t, AV_DICT_IGNORE_SUFFIX))) {
+ int len = strlen(t->key);
+ if (len == 10 &&
+ ff_convert_lang_to(&t->key[len - 3], AV_LANG_ISO639_2_BIBL)) {
+ mov_write_3gp_udta_tag(pb_buf, st->metadata, "titl", t->key);
+ if (!strcmp("und", &t->key[len - 3]))
+ und = 1;
+ }
+ }
+ if (!und)
+ mov_write_3gp_udta_tag(pb_buf, st->metadata, "titl", "title");
+ }
if (mov->mode & MODE_MP4) {
if ((ret = mov_write_track_kinds(pb_buf, st)) < 0)
@@ -4505,20 +4556,66 @@ static int mov_write_trkn_tag(AVIOContext *pb, MOVMuxContext *mov,
return size;
}
+/* iTunes iTMF metadata */
+static int mov_write_itmf_tag(AVIOContext *pb, AVFormatContext *s,
+ const char *name, const char *tag)
+{
+ AVDictionaryEntry *t = av_dict_get(s->metadata, tag, NULL, 0);
+ uint64_t size = 0;
+ if (t) {
+ size_t name_len = strlen(name);
+ size_t value_len = strlen(t->value);
+ size = 8 + 28 + 12 + name_len + 16 + value_len;
+
+ if (size > INT_MAX) {
+ av_log(s, AV_LOG_WARNING,
+ "Unable to write %s, invalid value len %ld\n",
+ name, value_len);
+ return 0;
+ }
+
+ avio_wb32(pb, size); // size
+ ffio_wfourcc(pb, "----"); // type
+
+ avio_wb32(pb, 28); // size
+ ffio_wfourcc(pb, "mean"); // type
+ avio_wb32(pb, 0); // version + flags
+ avio_write(pb, "com.apple.iTunes", 16);
+
+ avio_wb32(pb, 12 + name_len); // size
+ ffio_wfourcc(pb, "name"); // type
+ avio_wb32(pb, 0); // version + flags
+ avio_write(pb, name, name_len);
+
+ avio_wb32(pb, 16 + value_len); // size
+ ffio_wfourcc(pb, "data"); // type
+ avio_wb16(pb, 0); // typeReserved
+ avio_w8(pb, 0); // typeSetIdentifier
+ avio_w8(pb, 1); // typeCode = UTF-8
+ avio_wb32(pb, 0); // locale
+ avio_write(pb, t->value, value_len);
+ }
+ return size;
+}
+
static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
const char *name, const char *tag,
int len)
{
AVDictionaryEntry *t = NULL;
- uint8_t num;
+ uint64_t num;
int size = 24 + len;
- if (len != 1 && len != 4)
+ if (len != 1 && len != 4 &&
+ len != 2 && len != 8)
return -1;
if (!(t = av_dict_get(s->metadata, tag, NULL, 0)))
return 0;
- num = atoi(t->value);
+ if (len <= 4)
+ num = atoi(t->value);
+ else
+ num = atol(t->value);
avio_wb32(pb, size);
ffio_wfourcc(pb, name);
@@ -4526,7 +4623,9 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
ffio_wfourcc(pb, "data");
avio_wb32(pb, 0x15);
avio_wb32(pb, 0);
- if (len==4) avio_wb32(pb, num);
+ if (len==8) avio_wb64(pb, num);
+ else if (len==4) avio_wb32(pb, num);
+ else if (len==2) avio_wb16(pb, num);
else avio_w8 (pb, num);
return size;
@@ -4582,6 +4681,8 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_string_metadata(s, pb, "\251lyr", "lyrics" , 1);
mov_write_string_metadata(s, pb, "desc", "description",1);
mov_write_string_metadata(s, pb, "ldes", "synopsis" , 1);
+ mov_write_string_metadata(s, pb, "sdes", "series_description", 1);
+ mov_write_string_metadata(s, pb, "rtng", "rating", 1);
mov_write_string_metadata(s, pb, "tvsh", "show" , 1);
mov_write_string_metadata(s, pb, "tven", "episode_id",1);
mov_write_string_metadata(s, pb, "tvnn", "network" , 1);
@@ -4592,10 +4693,67 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1);
mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1);
mov_write_int8_metadata (s, pb, "cpil", "compilation", 1);
+
+ mov_write_string_metadata(s, pb, "\251st3", "subtitle" , 1);
+ mov_write_string_metadata(s, pb, "\251des", "song_description", 1);
+ mov_write_string_metadata(s, pb, "\251dir", "director" , 1);
+ mov_write_string_metadata(s, pb, "\251ard", "art_director" , 1);
+ mov_write_string_metadata(s, pb, "\251arg", "arranger" , 1);
+ mov_write_string_metadata(s, pb, "\251aut", "author" , 1);
+ mov_write_string_metadata(s, pb, "\251cak", "acknowledgement" , 1);
+ mov_write_string_metadata(s, pb, "\251con", "conductor" , 1);
+
+ mov_write_string_metadata(s, pb, "\251wrk", "work_name" , 1);
+ mov_write_string_metadata(s, pb, "\251mvn", "movement_name" , 1);
+ mov_write_int8_metadata (s, pb, "\251mvi", "movement_number", 2);
+ mov_write_int8_metadata (s, pb, "\251mvc", "movement_count" , 2);
+ mov_write_int8_metadata (s, pb, "shwm", "show_work_and_movement", 1);
+
+ mov_write_string_metadata(s, pb, "\251lnt", "linear_notes" , 1);
+ mov_write_string_metadata(s, pb, "\251mak", "make" , 1); // Record company
+ mov_write_string_metadata(s, pb, "\251ope", "original_artist" , 1);
+ mov_write_string_metadata(s, pb, "\251phg", "phonogram_rights" , 1);
+ mov_write_string_metadata(s, pb, "\251prd", "producer" , 1);
+ mov_write_string_metadata(s, pb, "\251prf", "performers" , 1);
+ mov_write_string_metadata(s, pb, "\251pub", "publisher" , 1);
+ mov_write_string_metadata(s, pb, "\251sne", "sound_engineer" , 1);
+ mov_write_string_metadata(s, pb, "\251sol", "soloist" , 1);
+ mov_write_string_metadata(s, pb, "\251src", "original_source" , 1); // Credits
+ mov_write_string_metadata(s, pb, "\251thx", "thanks" , 1);
+ mov_write_string_metadata(s, pb, "\251url", "URL" , 1); // Online extras
+ mov_write_string_metadata(s, pb, "\251xpd", "executive_producer", 1);
+
+ mov_write_string_metadata(s, pb, "sonm", "sort_name" , 1);
+ mov_write_string_metadata(s, pb, "soar", "sort_artist" , 1);
+ mov_write_string_metadata(s, pb, "soaa", "sort_album_artist", 1);
+ mov_write_string_metadata(s, pb, "soal", "sort_album" , 1);
+ mov_write_string_metadata(s, pb, "soco", "sort_composer" , 1);
+ mov_write_string_metadata(s, pb, "sosn", "sort_show" , 1);
+
+ mov_write_string_metadata(s, pb, "\251enc", "encoder" , 1); // Encoded by
+ mov_write_string_metadata(s, pb, "purd", "purchase_date" , 1);
+
+ mov_write_int8_metadata (s, pb, "itnu", "itunes_u" , 1);
+ mov_write_int8_metadata (s, pb, "pcst", "podcast" , 1);
+ mov_write_string_metadata(s, pb, "catg", "category" , 1);
+
+ mov_write_string_metadata(s, pb, "apID", "account_id" , 1);
+ mov_write_int8_metadata (s, pb, "akID", "account_type" , 1);
+ mov_write_int8_metadata (s, pb, "sfID", "itunes_country", 4);
+ mov_write_int8_metadata (s, pb, "cnID", "content_id" , 4);
+ mov_write_int8_metadata (s, pb, "atID", "artist_id" , 4);
+ mov_write_int8_metadata (s, pb, "plID", "playlist_id" , 8);
+ mov_write_int8_metadata (s, pb, "geID", "genre_id" , 4);
+ mov_write_int8_metadata (s, pb, "cmID", "composer_id" , 4);
+ mov_write_string_metadata(s, pb, "xid ", "xid" , 1);
+
mov_write_covr(pb, s);
mov_write_trkn_tag(pb, mov, s, 0); // track number
mov_write_trkn_tag(pb, mov, s, 1); // disc number
mov_write_tmpo_tag(pb, s);
+ mov_write_itmf_tag(pb, s, "iTunEXTC", "iTunEXTC");
+ mov_write_itmf_tag(pb, s, "iTunMOVI", "iTunMOVI");
+
return update_size(pb, pos);
}
@@ -4727,35 +4885,6 @@ static int ascii_to_wc(AVIOContext *pb, const uint8_t *b)
return 0;
}
-static uint16_t language_code(const char *str)
-{
- return (((str[0] - 0x60) & 0x1F) << 10) +
- (((str[1] - 0x60) & 0x1F) << 5) +
- (( str[2] - 0x60) & 0x1F);
-}
-
-static int mov_write_3gp_udta_tag(AVIOContext *pb, AVFormatContext *s,
- const char *tag, const char *str)
-{
- int64_t pos = avio_tell(pb);
- AVDictionaryEntry *t = av_dict_get(s->metadata, str, NULL, 0);
- if (!t || !utf8len(t->value))
- return 0;
- avio_wb32(pb, 0); /* size */
- ffio_wfourcc(pb, tag); /* type */
- avio_wb32(pb, 0); /* version + flags */
- if (!strcmp(tag, "yrrc"))
- avio_wb16(pb, atoi(t->value));
- else {
- avio_wb16(pb, language_code("eng")); /* language */
- avio_write(pb, t->value, strlen(t->value) + 1); /* UTF8 string value */
- if (!strcmp(tag, "albm") &&
- (t = av_dict_get(s->metadata, "track", NULL, 0)))
- avio_w8(pb, atoi(t->value));
- }
- return update_size(pb, pos);
-}
-
static int mov_write_chpl_tag(AVIOContext *pb, AVFormatContext *s)
{
int64_t pos = avio_tell(pb);
@@ -4794,14 +4923,14 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov,
return ret;
if (mov->mode & MODE_3GP) {
- mov_write_3gp_udta_tag(pb_buf, s, "perf", "artist");
- mov_write_3gp_udta_tag(pb_buf, s, "titl", "title");
- mov_write_3gp_udta_tag(pb_buf, s, "auth", "author");
- mov_write_3gp_udta_tag(pb_buf, s, "gnre", "genre");
- mov_write_3gp_udta_tag(pb_buf, s, "dscp", "comment");
- mov_write_3gp_udta_tag(pb_buf, s, "albm", "album");
- mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright");
- mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "perf", "artist");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "titl", "title");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "auth", "author");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "gnre", "genre");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "dscp", "comment");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "albm", "album");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "cprt", "copyright");
+ mov_write_3gp_udta_tag(pb_buf, s->metadata, "yrrc", "date");
mov_write_loci_tag(s, pb_buf);
} else if (mov->mode == MODE_MOV && !(mov->flags & FF_MOV_FLAG_USE_MDTA)) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
mov_write_string_metadata(s, pb_buf, "\251ART", "artist", 0);
diff --git a/libavutil/frame.c b/libavutil/frame.c
index f0a0dba..ab48eaa 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -45,6 +45,7 @@ static const AVSideDataDescriptor sd_props[] = {
[AV_FRAME_DATA_FILM_GRAIN_PARAMS] = { "Film grain parameters" },
[AV_FRAME_DATA_DETECTION_BBOXES] = { "Bounding boxes for object detection and classification" },
[AV_FRAME_DATA_DOVI_RPU_BUFFER] = { "Dolby Vision RPU Data" },
+ [AV_FRAME_DATA_DOVI_RPU_BUFFER_T35] = { "Dolby Vision RPU ITU T35 Data" },
[AV_FRAME_DATA_DOVI_METADATA] = { "Dolby Vision Metadata" },
[AV_FRAME_DATA_LCEVC] = { "LCEVC NAL data" },
[AV_FRAME_DATA_VIEW_ID] = { "View ID" },
diff --git a/libavutil/frame.h b/libavutil/frame.h
index f780656..8f2bbfd 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -200,6 +200,12 @@ enum AVFrameSideDataType {
*/
AV_FRAME_DATA_DOVI_RPU_BUFFER,
+ /**
+ * Dolby Vision RPU ITU T35 raw data, suitable for passing to SVT-AV1
+ * or other libraries. Array of uint8_t.
+ */
+ AV_FRAME_DATA_DOVI_RPU_BUFFER_T35,
+
/**
* Parsed Dolby Vision metadata, suitable for passing to a software
* implementation. The payload is the AVDOVIMetadata struct defined in
diff --git a/libavutil/hwcontext_videotoolbox.c b/libavutil/hwcontext_videotoolbox.c
index 122a61d..04e0f0c 100644
--- a/libavutil/hwcontext_videotoolbox.c
+++ b/libavutil/hwcontext_videotoolbox.c
@@ -183,7 +183,8 @@ static int vt_pool_alloc(AVHWFramesContext *ctx)
VTFramesContext *fctx = ctx->hwctx;
AVVTFramesContext *hw_ctx = &fctx->p;
CVReturn err;
- CFNumberRef w, h, pixfmt;
+ CFNumberRef w, h, extend_w_num, extend_h_num, pixfmt;
+ int extend_w, extend_h;
uint32_t cv_pixfmt;
CFMutableDictionaryRef attributes, iosurface_properties;
@@ -216,6 +217,15 @@ static int vt_pool_alloc(AVHWFramesContext *ctx)
CFRelease(w);
CFRelease(h);
+ extend_w = FFALIGN(ctx->width, 16) - ctx->width;
+ extend_h = FFALIGN(ctx->height, 16) - ctx->height;
+ extend_w_num = CFNumberCreate(NULL, kCFNumberSInt32Type, &extend_w);
+ extend_h_num = CFNumberCreate(NULL, kCFNumberSInt32Type, &extend_h);
+ CFDictionarySetValue(attributes, kCVPixelBufferExtendedPixelsRightKey, extend_w_num);
+ CFDictionarySetValue(attributes, kCVPixelBufferExtendedPixelsBottomKey, extend_h_num);
+ CFRelease(extend_w_num);
+ CFRelease(extend_h_num);
+
err = CVPixelBufferPoolCreate(
NULL,
NULL,
diff --git a/libswscale/swscale_unscaled.c b/libswscale/swscale_unscaled.c
index 587483d..e7d7671 100644
--- a/libswscale/swscale_unscaled.c
+++ b/libswscale/swscale_unscaled.c
@@ -345,7 +345,7 @@ static int planar8ToP01xleWrapper(SwsContext *c, const uint8_t *src[],
const uint8_t *tsrc0 = src[0];
for (x = c->srcW; x > 0; x--) {
t = *tsrc0++;
- output_pixel(tdstY++, t | (t << 8));
+ output_pixel(tdstY++, (t << 8));
}
src[0] += srcStride[0];
dstY += dstStride[0] / 2;
@@ -356,9 +356,9 @@ static int planar8ToP01xleWrapper(SwsContext *c, const uint8_t *src[],
const uint8_t *tsrc2 = src[2];
for (x = c->srcW / 2; x > 0; x--) {
t = *tsrc1++;
- output_pixel(tdstUV++, t | (t << 8));
+ output_pixel(tdstUV++, (t << 8));
t = *tsrc2++;
- output_pixel(tdstUV++, t | (t << 8));
+ output_pixel(tdstUV++, (t << 8));
}
src[1] += srcStride[1];
src[2] += srcStride[2];
--
2.54.0