1 /*****************************************************************************
2 * vt_utils.c: videotoolbox/cvpx utility functions
3 *****************************************************************************
4 * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <vlc_atomic.h>
29 CFMutableDictionaryRef
30 cfdict_create(CFIndex capacity
)
32 return CFDictionaryCreateMutable(kCFAllocatorDefault
, capacity
,
33 &kCFTypeDictionaryKeyCallBacks
,
34 &kCFTypeDictionaryValueCallBacks
);
38 cfdict_set_int32(CFMutableDictionaryRef dict
, CFStringRef key
, int value
)
40 CFNumberRef number
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &value
);
41 CFDictionarySetValue(dict
, key
, number
);
48 CVPixelBufferRef cvpx
;
50 atomic_uint ref_count
;
51 void (*on_released_cb
)(void *);
52 void *on_released_data
;
56 cvpxpic_destroy_cb(picture_context_t
*opaque
)
58 struct cvpxpic_ctx
*ctx
= (struct cvpxpic_ctx
*)opaque
;
60 if (atomic_fetch_sub(&ctx
->ref_count
, 1) == 1)
63 if (ctx
->on_released_cb
)
64 ctx
->on_released_cb(ctx
->on_released_data
);
69 static picture_context_t
*
70 cvpxpic_copy_cb(struct picture_context_t
*opaque
)
72 struct cvpxpic_ctx
*ctx
= (struct cvpxpic_ctx
*)opaque
;
73 atomic_fetch_add(&ctx
->ref_count
, 1);
78 cvpxpic_attach_common(picture_t
*p_pic
, CVPixelBufferRef cvpx
,
79 void (*pf_destroy
)(picture_context_t
*),
80 void (*on_released_cb
)(void *), void *on_released_data
)
82 struct cvpxpic_ctx
*ctx
= malloc(sizeof(struct cvpxpic_ctx
));
85 picture_Release(p_pic
);
88 ctx
->s
.destroy
= pf_destroy
;
89 ctx
->s
.copy
= cvpxpic_copy_cb
;
90 ctx
->cvpx
= CVPixelBufferRetain(cvpx
);
91 atomic_init(&ctx
->ref_count
, 1);
93 ctx
->on_released_cb
= on_released_cb
;
94 ctx
->on_released_data
= on_released_data
;
96 p_pic
->context
= &ctx
->s
;
102 cvpxpic_attach(picture_t
*p_pic
, CVPixelBufferRef cvpx
)
104 return cvpxpic_attach_common(p_pic
, cvpx
, cvpxpic_destroy_cb
, NULL
, NULL
);
107 int cvpxpic_attach_with_cb(picture_t
*p_pic
, CVPixelBufferRef cvpx
,
108 void (*on_released_cb
)(void *),
109 void *on_released_data
)
111 return cvpxpic_attach_common(p_pic
, cvpx
, cvpxpic_destroy_cb
, on_released_cb
,
116 cvpxpic_get_ref(picture_t
*pic
)
118 assert(pic
->context
!= NULL
);
119 return ((struct cvpxpic_ctx
*)pic
->context
)->cvpx
;
123 cvpxpic_destroy_mapped_ro_cb(picture_context_t
*opaque
)
125 struct cvpxpic_ctx
*ctx
= (struct cvpxpic_ctx
*)opaque
;
127 CVPixelBufferUnlockBaseAddress(ctx
->cvpx
, kCVPixelBufferLock_ReadOnly
);
128 cvpxpic_destroy_cb(opaque
);
132 cvpxpic_destroy_mapped_rw_cb(picture_context_t
*opaque
)
134 struct cvpxpic_ctx
*ctx
= (struct cvpxpic_ctx
*)opaque
;
136 CVPixelBufferUnlockBaseAddress(ctx
->cvpx
, 0);
137 cvpxpic_destroy_cb(opaque
);
141 cvpxpic_create_mapped(const video_format_t
*fmt
, CVPixelBufferRef cvpx
,
144 unsigned planes_count
;
145 switch (fmt
->i_chroma
)
148 case VLC_CODEC_UYVY
: planes_count
= 0; break;
149 case VLC_CODEC_NV12
: planes_count
= 2; break;
150 case VLC_CODEC_I420
: planes_count
= 3; break;
151 default: return NULL
;
154 CVPixelBufferLockFlags lock
= readonly
? kCVPixelBufferLock_ReadOnly
: 0;
155 CVPixelBufferLockBaseAddress(cvpx
, lock
);
156 picture_resource_t rsc
= { };
159 assert(CVPixelBufferGetPlaneCount(cvpx
) == planes_count
);
162 if (planes_count
== 0)
164 rsc
.p
[0].p_pixels
= CVPixelBufferGetBaseAddress(cvpx
);
165 rsc
.p
[0].i_lines
= CVPixelBufferGetHeight(cvpx
);
166 rsc
.p
[0].i_pitch
= CVPixelBufferGetBytesPerRow(cvpx
);
170 for (unsigned i
= 0; i
< planes_count
; ++i
)
172 rsc
.p
[i
].p_pixels
= CVPixelBufferGetBaseAddressOfPlane(cvpx
, i
);
173 rsc
.p
[i
].i_lines
= CVPixelBufferGetHeightOfPlane(cvpx
, i
);
174 rsc
.p
[i
].i_pitch
= CVPixelBufferGetBytesPerRowOfPlane(cvpx
, i
);
178 void (*pf_destroy
)(picture_context_t
*) = readonly
?
179 cvpxpic_destroy_mapped_ro_cb
: cvpxpic_destroy_mapped_rw_cb
;
181 picture_t
*pic
= picture_NewFromResource(fmt
, &rsc
);
183 || cvpxpic_attach_common(pic
, cvpx
, pf_destroy
, NULL
, NULL
) != VLC_SUCCESS
)
185 CVPixelBufferUnlockBaseAddress(cvpx
, lock
);
192 cvpxpic_unmap(picture_t
*mapped_pic
)
194 video_format_t fmt
= mapped_pic
->format
;
195 switch (fmt
.i_chroma
)
197 case VLC_CODEC_UYVY
: fmt
.i_chroma
= VLC_CODEC_CVPX_UYVY
; break;
198 case VLC_CODEC_NV12
: fmt
.i_chroma
= VLC_CODEC_CVPX_NV12
; break;
199 case VLC_CODEC_I420
: fmt
.i_chroma
= VLC_CODEC_CVPX_I420
; break;
200 case VLC_CODEC_BGRA
: fmt
.i_chroma
= VLC_CODEC_CVPX_BGRA
; break;
202 assert(!"invalid mapped_pic fmt");
203 picture_Release(mapped_pic
);
206 assert(mapped_pic
->context
!= NULL
);
208 picture_t
*hw_pic
= picture_NewFromFormat(&fmt
);
211 picture_Release(mapped_pic
);
215 cvpxpic_attach(hw_pic
, cvpxpic_get_ref(mapped_pic
));
216 picture_CopyProperties(hw_pic
, mapped_pic
);
217 picture_Release(mapped_pic
);
222 cvpxpool_create(const video_format_t
*fmt
, unsigned count
)
225 switch (fmt
->i_chroma
)
227 case VLC_CODEC_CVPX_UYVY
:
228 cvpx_format
= kCVPixelFormatType_422YpCbCr8
;
230 case VLC_CODEC_CVPX_NV12
:
231 cvpx_format
= kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
;
233 case VLC_CODEC_CVPX_I420
:
234 cvpx_format
= kCVPixelFormatType_420YpCbCr8Planar
;
236 case VLC_CODEC_CVPX_BGRA
:
237 cvpx_format
= kCVPixelFormatType_32BGRA
;
243 /* destination pixel buffer attributes */
244 CFMutableDictionaryRef cvpx_attrs_dict
= cfdict_create(5);
245 if (unlikely(cvpx_attrs_dict
== NULL
))
247 CFMutableDictionaryRef pool_dict
= cfdict_create(2);
248 if (unlikely(pool_dict
== NULL
))
250 CFRelease(cvpx_attrs_dict
);
254 CFMutableDictionaryRef io_dict
= cfdict_create(0);
255 if (unlikely(io_dict
== NULL
))
257 CFRelease(cvpx_attrs_dict
);
258 CFRelease(pool_dict
);
261 CFDictionarySetValue(cvpx_attrs_dict
,
262 kCVPixelBufferIOSurfacePropertiesKey
, io_dict
);
265 cfdict_set_int32(cvpx_attrs_dict
, kCVPixelBufferBytesPerRowAlignmentKey
,
267 cfdict_set_int32(cvpx_attrs_dict
, kCVPixelBufferPixelFormatTypeKey
,
269 cfdict_set_int32(cvpx_attrs_dict
, kCVPixelBufferWidthKey
, fmt
->i_width
);
270 cfdict_set_int32(cvpx_attrs_dict
, kCVPixelBufferHeightKey
, fmt
->i_height
);
271 /* Required by CIFilter to render IOSurface */
272 cfdict_set_int32(cvpx_attrs_dict
, kCVPixelBufferBytesPerRowAlignmentKey
, 16);
274 cfdict_set_int32(pool_dict
, kCVPixelBufferPoolMinimumBufferCountKey
, count
);
275 cfdict_set_int32(pool_dict
, kCVPixelBufferPoolMaximumBufferAgeKey
, 0);
277 CVPixelBufferPoolRef pool
;
279 CVPixelBufferPoolCreate(NULL
, pool_dict
, cvpx_attrs_dict
, &pool
);
280 CFRelease(pool_dict
);
281 CFRelease(cvpx_attrs_dict
);
282 if (err
!= kCVReturnSuccess
)
285 CVPixelBufferRef cvpxs
[count
];
286 for (unsigned i
= 0; i
< count
; ++i
)
288 err
= CVPixelBufferPoolCreatePixelBuffer(NULL
, pool
, &cvpxs
[i
]);
289 if (err
!= kCVReturnSuccess
)
291 CVPixelBufferPoolRelease(pool
);
297 for (unsigned i
= 0; i
< count
; ++i
)
304 cvpxpool_new_cvpx(CVPixelBufferPoolRef pool
)
306 CVPixelBufferRef cvpx
;
307 CVReturn err
= CVPixelBufferPoolCreatePixelBuffer(NULL
, pool
, &cvpx
);
309 if (err
!= kCVReturnSuccess
)