Compare commits

...

7 Commits

Author SHA1 Message Date
Zhao Zhili 0ae8df5f2c avutil/common: cast GET_BYTE/GET_16BIT returned value
In case of GET_BYTE/GET_16BIT return signed value.
2025-11-19 17:46:06 +00:00
Zhao Zhili a5cc0e5c9e avfilter/vf_drawtext: fix call GET_UTF8 with invalid argument
For GET_UTF8(val, GET_BYTE, ERROR), val has type of uint32_t,
GET_BYTE must return an unsigned integer, otherwise signed
extension happened due to val= (GET_BYTE), and GET_UTF8 went to
the error path.

This bug incidentally cancelled the bug where hb_buffer_add_utf8
was being called with incorrect argument, allowing drawtext to
function correctly on x86 and macOS ARM, which defined char as
signed. However, on Linux and Android ARM environments, because
char is unsigned by default, GET_UTF8 now returns the correct
return, which unexpectedly revealed issue #20906.
2025-11-19 17:46:06 +00:00
Zhao Zhili 9bc3c572ea avfilter/vf_drawtext: fix incorrect text length
From the doc of HarfBuzz, what hb_buffer_add_utf8 needs is the
number of bytes, not Unicode character:
hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text));

Fix issue #20906.
2025-11-19 17:46:06 +00:00
Zhao Zhili 551e964e8a avcodec/videotoolboxenc: remove redundant "Error: " in error message 2025-11-20 00:56:12 +08:00
Zhao Zhili d4031984db avcodec/videotoolboxenc: reorder and cleanup headers 2025-11-20 00:56:12 +08:00
Zhao Zhili 7049df14c8 avcodec/videotoolboxenc: fix crash with negative linesize 2025-11-20 00:56:05 +08:00
Zhao Zhili 0da15c93c8 avcodec/videotoolboxenc: improve Lock/Unlock BaseAddress error handling
1. Fix continue after CVPixelBufferLockBaseAddress.
2. Remove redundant "Error: " in error message.
2025-11-20 00:55:32 +08:00
3 changed files with 59 additions and 201 deletions

View File

@ -18,28 +18,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <VideoToolbox/VideoToolbox.h>
#include <CoreVideo/CoreVideo.h>
#include <CoreMedia/CoreMedia.h>
#include <TargetConditionals.h>
#include <Availability.h>
#include "avcodec.h"
#include <CoreMedia/CoreMedia.h>
#include <CoreVideo/CoreVideo.h>
#include <dlfcn.h>
#include <pthread.h>
#include <TargetConditionals.h>
#include <VideoToolbox/VideoToolbox.h>
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavcodec/avcodec.h"
#include "libavutil/pixdesc.h"
#include "libavutil/hwcontext_videotoolbox.h"
#include "codec_internal.h"
#include "internal.h"
#include <pthread.h>
#include "atsc_a53.h"
#include "codec_internal.h"
#include "encode.h"
#include "h264.h"
#include "h264_sei.h"
#include "hwconfig.h"
#include <dlfcn.h>
#include "internal.h"
#if !HAVE_KCMVIDEOCODECTYPE_HEVC
enum { kCMVideoCodecType_HEVC = 'hvc1' };
@ -667,7 +667,7 @@ static int copy_param_sets(
next_offset = offset + sizeof(start_code) + ps_size;
if (dst_size < next_offset) {
av_log(avctx, AV_LOG_ERROR, "Error: buffer too small for parameter sets.\n");
av_log(avctx, AV_LOG_ERROR, "Buffer too small for parameter sets.\n");
return AVERROR_BUFFER_TOO_SMALL;
}
@ -1205,7 +1205,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
session);
if (status || !vtctx->session) {
av_log(avctx, AV_LOG_ERROR, "Error: cannot create compression session: %d\n", status);
av_log(avctx, AV_LOG_ERROR, "Cannot create compression session: %d\n", status);
#if !TARGET_OS_IPHONE
if (!vtctx->allow_sw) {
@ -1241,7 +1241,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
return status;
if (avctx->flags & AV_CODEC_FLAG_QSCALE && !vtenc_qscale_enabled()) {
av_log(avctx, AV_LOG_ERROR, "Error: -q:v qscale not available for encoder. Use -b:v bitrate instead.\n");
av_log(avctx, AV_LOG_ERROR, "-q:v qscale not available for encoder. Use -b:v bitrate instead.\n");
return AVERROR_EXTERNAL;
}
@ -1269,7 +1269,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
compat_keys.kVTCompressionPropertyKey_ConstantBitRate,
bit_rate_num);
if (status == kVTPropertyNotSupportedErr) {
av_log(avctx, AV_LOG_ERROR, "Error: -constant_bit_rate true is not supported by the encoder.\n");
av_log(avctx, AV_LOG_ERROR, "-constant_bit_rate true is not supported by the encoder.\n");
return AVERROR_EXTERNAL;
}
} else {
@ -1625,7 +1625,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
status = VTCompressionSessionPrepareToEncodeFrames(vtctx->session);
if (status) {
av_log(avctx, AV_LOG_ERROR, "Error: cannot prepare encoder: %d\n", status);
av_log(avctx, AV_LOG_ERROR, "Cannot prepare encoder: %d\n", status);
return AVERROR_EXTERNAL;
}
@ -1644,7 +1644,7 @@ static int vtenc_configure_encoder(AVCodecContext *avctx)
codec_type = get_cm_codec_type(avctx, vtctx->profile, vtctx->alpha_quality);
if (!codec_type) {
av_log(avctx, AV_LOG_ERROR, "Error: no mapping for AVCodecID %d\n", avctx->codec_id);
av_log(avctx, AV_LOG_ERROR, "No mapping for AVCodecID %d\n", avctx->codec_id);
return AVERROR(EINVAL);
}
@ -2328,160 +2328,39 @@ static int vtenc_cm_to_avpacket(
return 0;
}
/*
* contiguous_buf_size is 0 if not contiguous, and the size of the buffer
* containing all planes if so.
*/
static int get_cv_pixel_info(
AVCodecContext *avctx,
const AVFrame *frame,
int *color,
int *plane_count,
size_t *widths,
size_t *heights,
size_t *strides,
size_t *contiguous_buf_size)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
VTEncContext *vtctx = avctx->priv_data;
int av_format = frame->format;
int av_color_range = avctx->color_range;
int i;
int range_guessed;
int status;
if (!desc)
return AVERROR(EINVAL);
status = get_cv_pixel_format(avctx, av_format, av_color_range, color, &range_guessed);
if (status)
return status;
if (range_guessed) {
if (!vtctx->warned_color_range) {
vtctx->warned_color_range = true;
av_log(avctx,
AV_LOG_WARNING,
"Color range not set for %s. Using MPEG range.\n",
av_get_pix_fmt_name(av_format));
}
}
*plane_count = av_pix_fmt_count_planes(avctx->pix_fmt);
for (i = 0; i < desc->nb_components; i++) {
int p = desc->comp[i].plane;
bool hasAlpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA);
bool isAlpha = hasAlpha && (p + 1 == *plane_count);
bool isChroma = (p != 0) && !isAlpha;
int shiftw = isChroma ? desc->log2_chroma_w : 0;
int shifth = isChroma ? desc->log2_chroma_h : 0;
widths[p] = (avctx->width + ((1 << shiftw) >> 1)) >> shiftw;
heights[p] = (avctx->height + ((1 << shifth) >> 1)) >> shifth;
strides[p] = frame->linesize[p];
}
*contiguous_buf_size = 0;
for (i = 0; i < *plane_count; i++) {
if (i < *plane_count - 1 &&
frame->data[i] + strides[i] * heights[i] != frame->data[i + 1]) {
*contiguous_buf_size = 0;
break;
}
*contiguous_buf_size += strides[i] * heights[i];
}
return 0;
}
//Not used on OSX - frame is never copied.
static int copy_avframe_to_pixel_buffer(AVCodecContext *avctx,
const AVFrame *frame,
CVPixelBufferRef cv_img,
const size_t *plane_strides,
const size_t *plane_rows)
CVPixelBufferRef cv_img)
{
int i, j;
size_t plane_count;
int status;
int rows;
int src_stride;
int dst_stride;
uint8_t *src_addr;
uint8_t *dst_addr;
size_t copy_bytes;
int num_planes = av_pix_fmt_count_planes(frame->format);
size_t num_cv_plane = CVPixelBufferIsPlanar(cv_img) ?
CVPixelBufferGetPlaneCount(cv_img) : 1;
if (num_planes != num_cv_plane) {
av_log(avctx, AV_LOG_ERROR,
"Different number of planes in AVFrame and CVPixelBuffer.\n");
return AVERROR_BUG;
}
status = CVPixelBufferLockBaseAddress(cv_img, 0);
if (status) {
av_log(
avctx,
AV_LOG_ERROR,
"Error: Could not lock base address of CVPixelBuffer: %d.\n",
status
);
}
if (CVPixelBufferIsPlanar(cv_img)) {
plane_count = CVPixelBufferGetPlaneCount(cv_img);
for (i = 0; frame->data[i]; i++) {
if (i == plane_count) {
CVPixelBufferUnlockBaseAddress(cv_img, 0);
av_log(avctx,
AV_LOG_ERROR,
"Error: different number of planes in AVFrame and CVPixelBuffer.\n"
);
av_log(avctx, AV_LOG_ERROR, "Could not lock base address of CVPixelBuffer: %d.\n", status);
return AVERROR_EXTERNAL;
}
dst_addr = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_img, i);
src_addr = (uint8_t*)frame->data[i];
dst_stride = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i);
src_stride = plane_strides[i];
rows = plane_rows[i];
if (dst_stride == src_stride) {
memcpy(dst_addr, src_addr, src_stride * rows);
} else {
copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
for (j = 0; j < rows; j++) {
memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes);
}
}
}
} else {
if (frame->data[1]) {
CVPixelBufferUnlockBaseAddress(cv_img, 0);
av_log(avctx,
AV_LOG_ERROR,
"Error: different number of planes in AVFrame and non-planar CVPixelBuffer.\n"
);
return AVERROR_EXTERNAL;
}
dst_addr = (uint8_t*)CVPixelBufferGetBaseAddress(cv_img);
src_addr = (uint8_t*)frame->data[0];
dst_stride = CVPixelBufferGetBytesPerRow(cv_img);
src_stride = plane_strides[0];
rows = plane_rows[0];
if (dst_stride == src_stride) {
memcpy(dst_addr, src_addr, src_stride * rows);
} else {
copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
for (j = 0; j < rows; j++) {
memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes);
}
}
int dst_stride[4] = {0};
uint8_t *dst_addr[4] = {0};
for (int i = 0; i < num_planes; i++) {
dst_addr[i] = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_img, i);
dst_stride[i] = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i);
}
av_image_copy2(dst_addr, dst_stride, frame->data, frame->linesize,
frame->format, frame->width, frame->height);
status = CVPixelBufferUnlockBaseAddress(cv_img, 0);
if (status) {
av_log(avctx, AV_LOG_ERROR, "Error: Could not unlock CVPixelBuffer base address: %d.\n", status);
av_log(avctx, AV_LOG_ERROR, "Could not unlock CVPixelBuffer base address: %d.\n", status);
return AVERROR_EXTERNAL;
}
@ -2493,13 +2372,7 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx,
CVPixelBufferRef *cv_img,
BufNode *node)
{
int plane_count;
int color;
size_t widths [AV_NUM_DATA_POINTERS];
size_t heights[AV_NUM_DATA_POINTERS];
size_t strides[AV_NUM_DATA_POINTERS];
int status;
size_t contiguous_buf_size;
CVPixelBufferPoolRef pix_buf_pool;
VTEncContext* vtctx = avctx->priv_data;
@ -2519,33 +2392,21 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx,
return 0;
}
memset(widths, 0, sizeof(widths));
memset(heights, 0, sizeof(heights));
memset(strides, 0, sizeof(strides));
status = get_cv_pixel_info(
avctx,
frame,
&color,
&plane_count,
widths,
heights,
strides,
&contiguous_buf_size
);
int range_guessed;
status = get_cv_pixel_format(avctx, frame->format, avctx->color_range,
&(int) {0}, &range_guessed);
if (status) {
av_log(
avctx,
AV_LOG_ERROR,
"Error: Cannot convert format %d color_range %d: %d\n",
frame->format,
frame->color_range,
status
);
av_log(avctx, AV_LOG_ERROR, "Cannot convert format %d color_range %d: %d\n",
frame->format, frame->color_range, status);
return status;
}
if (range_guessed) {
if (!vtctx->warned_color_range) {
vtctx->warned_color_range = true;
av_log(avctx, AV_LOG_WARNING, "Color range not set for %s. Using MPEG range.\n",
av_get_pix_fmt_name(frame->format));
}
}
pix_buf_pool = VTCompressionSessionGetPixelBufferPool(vtctx->session);
if (!pix_buf_pool) {
@ -2582,7 +2443,7 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx,
return AVERROR_EXTERNAL;
}
status = copy_avframe_to_pixel_buffer(avctx, frame, *cv_img, strides, heights);
status = copy_avframe_to_pixel_buffer(avctx, frame, *cv_img);
if (status) {
CFRelease(*cv_img);
*cv_img = NULL;
@ -2652,7 +2513,7 @@ static int vtenc_send_frame(AVCodecContext *avctx,
);
if (status) {
av_log(avctx, AV_LOG_ERROR, "Error: cannot encode frame: %d\n", status);
av_log(avctx, AV_LOG_ERROR, "Cannot encode frame: %d\n", status);
status = AVERROR_EXTERNAL;
// Not necessary, just in case new code put after here
goto out;

View File

@ -1395,8 +1395,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics)
{
DrawTextContext *s = ctx->priv;
char *text = s->expanded_text.str;
char *textdup = NULL, *start = NULL;
int num_chars = 0;
char *textdup = NULL;
int width64 = 0, w64 = 0;
int cur_min_y64 = 0, first_max_y64 = -32000;
int first_min_x64 = 32000, last_max_x64 = -32000;
@ -1406,7 +1405,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics)
Glyph *glyph = NULL;
int i, tab_idx = 0, last_tab_idx = 0, line_offset = 0;
char* p;
uint8_t *start, *p;
int ret = 0;
// Count the lines and the tab characters
@ -1459,7 +1458,7 @@ continue_on_failed2:
TextLine *cur_line = &s->lines[line_count];
HarfbuzzData *hb = &cur_line->hb_data;
cur_line->cluster_offset = line_offset;
ret = shape_text_hb(s, hb, start, num_chars);
ret = shape_text_hb(s, hb, start, p - start);
if (ret != 0) {
goto done;
}
@ -1517,14 +1516,12 @@ continue_on_failed2:
if (w64 > width64) {
width64 = w64;
}
num_chars = -1;
start = p;
++line_count;
line_offset = i + 1;
}
if (code == 0) break;
++num_chars;
}
metrics->line_height64 = s->face->size->metrics.height;

View File

@ -486,13 +486,13 @@ static av_always_inline av_const int av_parity_c(uint32_t v)
* to prevent undefined results.
*/
#define GET_UTF8(val, GET_BYTE, ERROR)\
val= (GET_BYTE);\
val= (uint8_t)(GET_BYTE);\
{\
uint32_t top = (val & 128) >> 1;\
if ((val & 0xc0) == 0x80 || val >= 0xFE)\
{ERROR}\
while (val & top) {\
unsigned int tmp = (GET_BYTE) - 128;\
unsigned int tmp = (uint8_t)(GET_BYTE) - 128;\
if(tmp>>6)\
{ERROR}\
val= (val<<6) + tmp;\
@ -511,11 +511,11 @@ static av_always_inline av_const int av_parity_c(uint32_t v)
* typically a goto statement.
*/
#define GET_UTF16(val, GET_16BIT, ERROR)\
val = (GET_16BIT);\
val = (uint16_t)(GET_16BIT);\
{\
unsigned int hi = val - 0xD800;\
if (hi < 0x800) {\
val = (GET_16BIT) - 0xDC00;\
val = (uint16_t)(GET_16BIT) - 0xDC00;\
if (val > 0x3FFU || hi > 0x3FFU)\
{ERROR}\
val += (hi<<10) + 0x10000;\