You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

316 lines
9.4 KiB

/*****************************************************************************
* vt_utils.c: videotoolbox/cvpx utility functions
*****************************************************************************
* Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdatomic.h>
#include "vt_utils.h"
CFMutableDictionaryRef
cfdict_create(CFIndex capacity)
{
return CFDictionaryCreateMutable(kCFAllocatorDefault, capacity,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
void
cfdict_set_int32(CFMutableDictionaryRef dict, CFStringRef key, int value)
{
CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
CFDictionarySetValue(dict, key, number);
CFRelease(number);
}
struct cvpxpic_ctx
{
picture_context_t s;
CVPixelBufferRef cvpx;
unsigned nb_fields;
atomic_uint ref_count;
void (*on_released_cb)(CVPixelBufferRef, void *, unsigned);
void *on_released_data;
};
static void
cvpxpic_destroy_cb(picture_context_t *opaque)
{
struct cvpxpic_ctx *ctx = (struct cvpxpic_ctx *)opaque;
if (atomic_fetch_sub(&ctx->ref_count, 1) == 1)
{
CFRelease(ctx->cvpx);
if (ctx->on_released_cb)
ctx->on_released_cb(ctx->cvpx, ctx->on_released_data, ctx->nb_fields);
free(opaque);
}
}
static picture_context_t *
cvpxpic_copy_cb(struct picture_context_t *opaque)
{
struct cvpxpic_ctx *ctx = (struct cvpxpic_ctx *)opaque;
atomic_fetch_add(&ctx->ref_count, 1);
return opaque;
}
static int
cvpxpic_attach_common(picture_t *p_pic, CVPixelBufferRef cvpx,
void (*pf_destroy)(picture_context_t *),
void (*on_released_cb)(CVPixelBufferRef, void *, unsigned),
void *on_released_data)
{
struct cvpxpic_ctx *ctx = malloc(sizeof(struct cvpxpic_ctx));
if (ctx == NULL)
{
picture_Release(p_pic);
return VLC_ENOMEM;
}
ctx->s.destroy = pf_destroy;
ctx->s.copy = cvpxpic_copy_cb;
ctx->cvpx = CVPixelBufferRetain(cvpx);
ctx->nb_fields = p_pic->i_nb_fields;
atomic_init(&ctx->ref_count, 1);
ctx->on_released_cb = on_released_cb;
ctx->on_released_data = on_released_data;
p_pic->context = &ctx->s;
return VLC_SUCCESS;
}
int
cvpxpic_attach(picture_t *p_pic, CVPixelBufferRef cvpx)
{
return cvpxpic_attach_common(p_pic, cvpx, cvpxpic_destroy_cb, NULL, NULL);
}
int cvpxpic_attach_with_cb(picture_t *p_pic, CVPixelBufferRef cvpx,
void (*on_released_cb)(CVPixelBufferRef, void *, unsigned),
void *on_released_data)
{
return cvpxpic_attach_common(p_pic, cvpx, cvpxpic_destroy_cb, on_released_cb,
on_released_data);
}
CVPixelBufferRef
cvpxpic_get_ref(picture_t *pic)
{
assert(pic->context != NULL);
return ((struct cvpxpic_ctx *)pic->context)->cvpx;
}
static void
cvpxpic_destroy_mapped_ro_cb(picture_context_t *opaque)
{
struct cvpxpic_ctx *ctx = (struct cvpxpic_ctx *)opaque;
CVPixelBufferUnlockBaseAddress(ctx->cvpx, kCVPixelBufferLock_ReadOnly);
cvpxpic_destroy_cb(opaque);
}
static void
cvpxpic_destroy_mapped_rw_cb(picture_context_t *opaque)
{
struct cvpxpic_ctx *ctx = (struct cvpxpic_ctx *)opaque;
CVPixelBufferUnlockBaseAddress(ctx->cvpx, 0);
cvpxpic_destroy_cb(opaque);
}
picture_t *
cvpxpic_create_mapped(const video_format_t *fmt, CVPixelBufferRef cvpx,
bool readonly)
{
unsigned planes_count;
switch (fmt->i_chroma)
{
case VLC_CODEC_BGRA:
case VLC_CODEC_UYVY: planes_count = 0; break;
case VLC_CODEC_NV12: planes_count = 2; break;
case VLC_CODEC_I420: planes_count = 3; break;
default: return NULL;
}
CVPixelBufferLockFlags lock = readonly ? kCVPixelBufferLock_ReadOnly : 0;
CVPixelBufferLockBaseAddress(cvpx, lock);
picture_resource_t rsc = { };
#ifndef NDEBUG
assert(CVPixelBufferGetPlaneCount(cvpx) == planes_count);
#endif
if (planes_count == 0)
{
rsc.p[0].p_pixels = CVPixelBufferGetBaseAddress(cvpx);
rsc.p[0].i_lines = CVPixelBufferGetHeight(cvpx);
rsc.p[0].i_pitch = CVPixelBufferGetBytesPerRow(cvpx);
}
else
{
for (unsigned i = 0; i < planes_count; ++i)
{
rsc.p[i].p_pixels = CVPixelBufferGetBaseAddressOfPlane(cvpx, i);
rsc.p[i].i_lines = CVPixelBufferGetHeightOfPlane(cvpx, i);
rsc.p[i].i_pitch = CVPixelBufferGetBytesPerRowOfPlane(cvpx, i);
}
}
void (*pf_destroy)(picture_context_t *) = readonly ?
cvpxpic_destroy_mapped_ro_cb : cvpxpic_destroy_mapped_rw_cb;
picture_t *pic = picture_NewFromResource(fmt, &rsc);
if (pic == NULL
|| cvpxpic_attach_common(pic, cvpx, pf_destroy, NULL, NULL) != VLC_SUCCESS)
{
CVPixelBufferUnlockBaseAddress(cvpx, lock);
return NULL;
}
return pic;
}
picture_t *
cvpxpic_unmap(picture_t *mapped_pic)
{
video_format_t fmt = mapped_pic->format;
switch (fmt.i_chroma)
{
case VLC_CODEC_UYVY: fmt.i_chroma = VLC_CODEC_CVPX_UYVY; break;
case VLC_CODEC_NV12: fmt.i_chroma = VLC_CODEC_CVPX_NV12; break;
case VLC_CODEC_I420: fmt.i_chroma = VLC_CODEC_CVPX_I420; break;
case VLC_CODEC_BGRA: fmt.i_chroma = VLC_CODEC_CVPX_BGRA; break;
default:
assert(!"invalid mapped_pic fmt");
picture_Release(mapped_pic);
return NULL;
}
assert(mapped_pic->context != NULL);
picture_t *hw_pic = picture_NewFromFormat(&fmt);
if (hw_pic == NULL)
{
picture_Release(mapped_pic);
return NULL;
}
cvpxpic_attach(hw_pic, cvpxpic_get_ref(mapped_pic));
picture_CopyProperties(hw_pic, mapped_pic);
picture_Release(mapped_pic);
return hw_pic;
}
CVPixelBufferPoolRef
cvpxpool_create(const video_format_t *fmt, unsigned count)
{
int cvpx_format;
switch (fmt->i_chroma)
{
case VLC_CODEC_CVPX_UYVY:
cvpx_format = kCVPixelFormatType_422YpCbCr8;
break;
case VLC_CODEC_CVPX_NV12:
cvpx_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
break;
case VLC_CODEC_CVPX_I420:
cvpx_format = kCVPixelFormatType_420YpCbCr8Planar;
break;
case VLC_CODEC_CVPX_BGRA:
cvpx_format = kCVPixelFormatType_32BGRA;
break;
default:
return NULL;
}
/* destination pixel buffer attributes */
CFMutableDictionaryRef cvpx_attrs_dict = cfdict_create(5);
if (unlikely(cvpx_attrs_dict == NULL))
return NULL;
CFMutableDictionaryRef pool_dict = cfdict_create(2);
if (unlikely(pool_dict == NULL))
{
CFRelease(cvpx_attrs_dict);
return NULL;
}
CFMutableDictionaryRef io_dict = cfdict_create(0);
if (unlikely(io_dict == NULL))
{
CFRelease(cvpx_attrs_dict);
CFRelease(pool_dict);
return NULL;
}
CFDictionarySetValue(cvpx_attrs_dict,
kCVPixelBufferIOSurfacePropertiesKey, io_dict);
CFRelease(io_dict);
cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferBytesPerRowAlignmentKey,
fmt->i_width);
cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferPixelFormatTypeKey,
cvpx_format);
cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferWidthKey, fmt->i_width);
cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferHeightKey, fmt->i_height);
/* Required by CIFilter to render IOSurface */
cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferBytesPerRowAlignmentKey, 16);
cfdict_set_int32(pool_dict, kCVPixelBufferPoolMinimumBufferCountKey, count);
cfdict_set_int32(pool_dict, kCVPixelBufferPoolMaximumBufferAgeKey, 0);
CVPixelBufferPoolRef pool;
CVReturn err =
CVPixelBufferPoolCreate(NULL, pool_dict, cvpx_attrs_dict, &pool);
CFRelease(pool_dict);
CFRelease(cvpx_attrs_dict);
if (err != kCVReturnSuccess)
return NULL;
CVPixelBufferRef cvpxs[count];
for (unsigned i = 0; i < count; ++i)
{
err = CVPixelBufferPoolCreatePixelBuffer(NULL, pool, &cvpxs[i]);
if (err != kCVReturnSuccess)
{
CVPixelBufferPoolRelease(pool);
pool = NULL;
count = i;
break;
}
}
for (unsigned i = 0; i < count; ++i)
CFRelease(cvpxs[i]);
return pool;
}
CVPixelBufferRef
cvpxpool_new_cvpx(CVPixelBufferPoolRef pool)
{
CVPixelBufferRef cvpx;
CVReturn err = CVPixelBufferPoolCreatePixelBuffer(NULL, pool, &cvpx);
if (err != kCVReturnSuccess)
return NULL;
return cvpx;
}