You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-15 14:13:16 +02:00
lavc/videotoolboxenc: Use shared pixel buffer pool
This reduces the chance of a memcpy in the media server. Signed-off-by: Rick Kern <kernrj@gmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
#include "libavutil/atomic.h"
|
#include "libavutil/atomic.h"
|
||||||
#include "libavutil/avstring.h"
|
#include "libavutil/avstring.h"
|
||||||
#include "libavcodec/avcodec.h"
|
#include "libavcodec/avcodec.h"
|
||||||
|
#include "libavutil/pixdesc.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
@@ -74,6 +75,18 @@ typedef struct VTEncContext {
|
|||||||
bool warned_color_range;
|
bool warned_color_range;
|
||||||
} VTEncContext;
|
} VTEncContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NULL-safe release of *refPtr, and sets value to NULL.
|
||||||
|
*/
|
||||||
|
static void vt_release_num(CFNumberRef* refPtr){
|
||||||
|
if (!*refPtr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(*refPtr);
|
||||||
|
*refPtr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void set_async_error(VTEncContext *vtctx, int err)
|
static void set_async_error(VTEncContext *vtctx, int err)
|
||||||
{
|
{
|
||||||
BufNode *info;
|
BufNode *info;
|
||||||
@@ -477,9 +490,100 @@ static bool get_vt_profile_level(AVCodecContext *avctx,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_cv_pixel_format(AVCodecContext* avctx,
|
||||||
|
enum AVPixelFormat fmt,
|
||||||
|
enum AVColorRange range,
|
||||||
|
int* av_pixel_format,
|
||||||
|
int* range_guessed)
|
||||||
|
{
|
||||||
|
if (range_guessed) *range_guessed = range != AVCOL_RANGE_MPEG &&
|
||||||
|
range != AVCOL_RANGE_JPEG;
|
||||||
|
|
||||||
|
//MPEG range is used when no range is set
|
||||||
|
if (fmt == AV_PIX_FMT_NV12) {
|
||||||
|
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
||||||
|
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange :
|
||||||
|
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
||||||
|
} else if (fmt == AV_PIX_FMT_YUV420P) {
|
||||||
|
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
||||||
|
kCVPixelFormatType_420YpCbCr8PlanarFullRange :
|
||||||
|
kCVPixelFormatType_420YpCbCr8Planar;
|
||||||
|
} else {
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_cv_pixel_buffer_info(AVCodecContext* avctx,
|
||||||
|
CFMutableDictionaryRef* dict)
|
||||||
|
{
|
||||||
|
CFNumberRef cv_color_format_num = NULL;
|
||||||
|
CFNumberRef width_num = NULL;
|
||||||
|
CFNumberRef height_num = NULL;
|
||||||
|
CFMutableDictionaryRef pixel_buffer_info = NULL;
|
||||||
|
int cv_color_format;
|
||||||
|
int status = get_cv_pixel_format(avctx,
|
||||||
|
avctx->pix_fmt,
|
||||||
|
avctx->color_range,
|
||||||
|
&cv_color_format,
|
||||||
|
NULL);
|
||||||
|
if (status) return status;
|
||||||
|
|
||||||
|
pixel_buffer_info = CFDictionaryCreateMutable(
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
20,
|
||||||
|
&kCFCopyStringDictionaryKeyCallBacks,
|
||||||
|
&kCFTypeDictionaryValueCallBacks);
|
||||||
|
|
||||||
|
if (!pixel_buffer_info) goto pbinfo_nomem;
|
||||||
|
|
||||||
|
cv_color_format_num = CFNumberCreate(kCFAllocatorDefault,
|
||||||
|
kCFNumberSInt32Type,
|
||||||
|
&cv_color_format);
|
||||||
|
if (!cv_color_format_num) goto pbinfo_nomem;
|
||||||
|
|
||||||
|
CFDictionarySetValue(pixel_buffer_info,
|
||||||
|
kCVPixelBufferPixelFormatTypeKey,
|
||||||
|
cv_color_format_num);
|
||||||
|
vt_release_num(&cv_color_format_num);
|
||||||
|
|
||||||
|
width_num = CFNumberCreate(kCFAllocatorDefault,
|
||||||
|
kCFNumberSInt32Type,
|
||||||
|
&avctx->width);
|
||||||
|
if (!width_num) return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
CFDictionarySetValue(pixel_buffer_info,
|
||||||
|
kCVPixelBufferWidthKey,
|
||||||
|
width_num);
|
||||||
|
vt_release_num(&width_num);
|
||||||
|
|
||||||
|
height_num = CFNumberCreate(kCFAllocatorDefault,
|
||||||
|
kCFNumberSInt32Type,
|
||||||
|
&avctx->height);
|
||||||
|
if (!height_num) goto pbinfo_nomem;
|
||||||
|
|
||||||
|
CFDictionarySetValue(pixel_buffer_info,
|
||||||
|
kCVPixelBufferHeightKey,
|
||||||
|
height_num);
|
||||||
|
vt_release_num(&height_num);
|
||||||
|
|
||||||
|
*dict = pixel_buffer_info;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pbinfo_nomem:
|
||||||
|
vt_release_num(&cv_color_format_num);
|
||||||
|
vt_release_num(&width_num);
|
||||||
|
vt_release_num(&height_num);
|
||||||
|
if (pixel_buffer_info) CFRelease(pixel_buffer_info);
|
||||||
|
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
static av_cold int vtenc_init(AVCodecContext *avctx)
|
static av_cold int vtenc_init(AVCodecContext *avctx)
|
||||||
{
|
{
|
||||||
CFMutableDictionaryRef enc_info;
|
CFMutableDictionaryRef enc_info;
|
||||||
|
CFMutableDictionaryRef pixel_buffer_info;
|
||||||
CMVideoCodecType codec_type;
|
CMVideoCodecType codec_type;
|
||||||
VTEncContext *vtctx = avctx->priv_data;
|
VTEncContext *vtctx = avctx->priv_data;
|
||||||
CFStringRef profile_level;
|
CFStringRef profile_level;
|
||||||
@@ -517,13 +621,19 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
|
|||||||
CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
status = create_cv_pixel_buffer_info(avctx, &pixel_buffer_info);
|
||||||
|
if (status) {
|
||||||
|
CFRelease(enc_info);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
status = VTCompressionSessionCreate(
|
status = VTCompressionSessionCreate(
|
||||||
kCFAllocatorDefault,
|
kCFAllocatorDefault,
|
||||||
avctx->width,
|
avctx->width,
|
||||||
avctx->height,
|
avctx->height,
|
||||||
codec_type,
|
codec_type,
|
||||||
enc_info,
|
enc_info,
|
||||||
NULL,
|
pixel_buffer_info,
|
||||||
kCFAllocatorDefault,
|
kCFAllocatorDefault,
|
||||||
vtenc_output_callback,
|
vtenc_output_callback,
|
||||||
avctx,
|
avctx,
|
||||||
@@ -549,6 +659,7 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CFRelease(pixel_buffer_info);
|
||||||
CFRelease(enc_info);
|
CFRelease(enc_info);
|
||||||
|
|
||||||
if (status || !vtctx->session) {
|
if (status || !vtctx->session) {
|
||||||
@@ -912,26 +1023,37 @@ static int get_cv_pixel_info(
|
|||||||
int av_format = frame->format;
|
int av_format = frame->format;
|
||||||
int av_color_range = av_frame_get_color_range(frame);
|
int av_color_range = av_frame_get_color_range(frame);
|
||||||
int i;
|
int i;
|
||||||
|
int range_guessed;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = get_cv_pixel_format(avctx, av_format, av_color_range, color, &range_guessed);
|
||||||
|
if (status) {
|
||||||
|
av_log(avctx,
|
||||||
|
AV_LOG_ERROR,
|
||||||
|
"Could not get pixel format for color format '%s' range '%s'.\n",
|
||||||
|
av_get_pix_fmt_name(av_format),
|
||||||
|
av_color_range > AVCOL_RANGE_UNSPECIFIED &&
|
||||||
|
av_color_range < AVCOL_RANGE_NB ?
|
||||||
|
av_color_range_name(av_color_range) :
|
||||||
|
"Unknown");
|
||||||
|
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
av_log(avctx, AV_LOG_WARNING, "");
|
||||||
|
}
|
||||||
|
|
||||||
switch (av_format) {
|
switch (av_format) {
|
||||||
case AV_PIX_FMT_NV12:
|
case AV_PIX_FMT_NV12:
|
||||||
switch (av_color_range) {
|
|
||||||
case AVCOL_RANGE_MPEG:
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVCOL_RANGE_JPEG:
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (!vtctx->warned_color_range) {
|
|
||||||
vtctx->warned_color_range = true;
|
|
||||||
av_log(avctx, AV_LOG_WARNING, "Color range not set for NV12. Using MPEG range.\n");
|
|
||||||
}
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
*plane_count = 2;
|
*plane_count = 2;
|
||||||
|
|
||||||
widths [0] = avctx->width;
|
widths [0] = avctx->width;
|
||||||
@@ -944,23 +1066,6 @@ static int get_cv_pixel_info(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case AV_PIX_FMT_YUV420P:
|
case AV_PIX_FMT_YUV420P:
|
||||||
switch (av_color_range) {
|
|
||||||
case AVCOL_RANGE_MPEG:
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8Planar;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVCOL_RANGE_JPEG:
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8PlanarFullRange;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (!vtctx->warned_color_range) {
|
|
||||||
vtctx->warned_color_range = true;
|
|
||||||
av_log(avctx, AV_LOG_WARNING, "Color range not set for YUV 4:2:0. Using MPEG range.\n");
|
|
||||||
}
|
|
||||||
*color = kCVPixelFormatType_420YpCbCr8Planar;
|
|
||||||
}
|
|
||||||
|
|
||||||
*plane_count = 3;
|
*plane_count = 3;
|
||||||
|
|
||||||
widths [0] = avctx->width;
|
widths [0] = avctx->width;
|
||||||
@@ -976,7 +1081,15 @@ static int get_cv_pixel_info(
|
|||||||
strides[2] = frame ? frame->linesize[2] : (avctx->width + 1) / 2;
|
strides[2] = frame ? frame->linesize[2] : (avctx->width + 1) / 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: return AVERROR(EINVAL);
|
default:
|
||||||
|
av_log(
|
||||||
|
avctx,
|
||||||
|
AV_LOG_ERROR,
|
||||||
|
"Could not get frame format info for color %d range %d.\n",
|
||||||
|
av_format,
|
||||||
|
av_color_range);
|
||||||
|
|
||||||
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
*contiguous_buf_size = 0;
|
*contiguous_buf_size = 0;
|
||||||
@@ -1111,6 +1224,8 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx,
|
|||||||
size_t strides[AV_NUM_DATA_POINTERS];
|
size_t strides[AV_NUM_DATA_POINTERS];
|
||||||
int status;
|
int status;
|
||||||
size_t contiguous_buf_size;
|
size_t contiguous_buf_size;
|
||||||
|
CVPixelBufferPoolRef pix_buf_pool;
|
||||||
|
VTEncContext* vtctx = avctx->priv_data;
|
||||||
|
|
||||||
memset(widths, 0, sizeof(widths));
|
memset(widths, 0, sizeof(widths));
|
||||||
memset(heights, 0, sizeof(heights));
|
memset(heights, 0, sizeof(heights));
|
||||||
@@ -1141,16 +1256,19 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
status = CVPixelBufferCreate(
|
pix_buf_pool = VTCompressionSessionGetPixelBufferPool(vtctx->session);
|
||||||
kCFAllocatorDefault,
|
if (!pix_buf_pool) {
|
||||||
frame->width,
|
av_log(avctx, AV_LOG_ERROR, "Could not get pixel buffer pool.\n");
|
||||||
frame->height,
|
return AVERROR_EXTERNAL;
|
||||||
color,
|
}
|
||||||
NULL,
|
|
||||||
cv_img
|
status = CVPixelBufferPoolCreatePixelBuffer(NULL,
|
||||||
);
|
pix_buf_pool,
|
||||||
|
cv_img);
|
||||||
|
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Could not create pixel buffer from pool: %d.\n", status);
|
||||||
return AVERROR_EXTERNAL;
|
return AVERROR_EXTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1306,9 +1424,7 @@ static av_cold int vtenc_close(AVCodecContext *avctx)
|
|||||||
|
|
||||||
static const enum AVPixelFormat pix_fmts[] = {
|
static const enum AVPixelFormat pix_fmts[] = {
|
||||||
AV_PIX_FMT_NV12,
|
AV_PIX_FMT_NV12,
|
||||||
#if !TARGET_OS_IPHONE
|
|
||||||
AV_PIX_FMT_YUV420P,
|
AV_PIX_FMT_YUV420P,
|
||||||
#endif
|
|
||||||
AV_PIX_FMT_NONE
|
AV_PIX_FMT_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user