@@ -69,6 +69,20 @@ XprsResult CVideoDecoder::init(bool disableHwAcceleration) {
6969}
7070
7171#ifdef __APPLE__
72+
73+ // kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange and
74+ // kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange were introduced in macOS 15.0 / iOS 18.0 SDK
75+ #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000) || \
76+ (defined (__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 ) || \
77+ (defined (__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 180000 ) || \
78+ (defined (__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED >= 110000 )
79+ #define XPRS_HAS_422_BIPLANAR_FORMATS 1
80+ #define XPRS_HAS_444_BIPLANAR_FORMATS 1
81+ #else
82+ #define XPRS_HAS_422_BIPLANAR_FORMATS 0
83+ #define XPRS_HAS_444_BIPLANAR_FORMATS 0
84+ #endif
85+
7286static PixelFormat convertVideoToolboxPixelFormat (OSType videotoolboxFormat) {
7387 switch (videotoolboxFormat) {
7488 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange :
@@ -79,12 +93,16 @@ static PixelFormat convertVideoToolboxPixelFormat(OSType videotoolboxFormat) {
7993 case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange :
8094 case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange :
8195 return PixelFormat::NV1210LE;
96+ #if XPRS_HAS_422_BIPLANAR_FORMATS
8297 case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange :
8398 case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange :
8499 return PixelFormat::YUV422P;
100+ #endif
101+ #if XPRS_HAS_444_BIPLANAR_FORMATS
85102 case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange :
86103 case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange :
87104 return PixelFormat::YUV444P;
105+ #endif
88106 default :
89107 return PixelFormat::UNKNOWN;
90108 }
@@ -113,11 +131,16 @@ void CVideoDecoder::convertAVFrame(const AVFrame* avframe, Frame& frameOut) {
113131 if (pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange &&
114132 pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarFullRange &&
115133 pixelFormat != kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange &&
116- pixelFormat != kCVPixelFormatType_420YpCbCr10BiPlanarFullRange &&
117- pixelFormat != kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange &&
118- pixelFormat != kCVPixelFormatType_422YpCbCr8BiPlanarFullRange &&
119- pixelFormat != kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange &&
120- pixelFormat != kCVPixelFormatType_444YpCbCr8BiPlanarFullRange ) {
134+ pixelFormat != kCVPixelFormatType_420YpCbCr10BiPlanarFullRange
135+ #if XPRS_HAS_422_BIPLANAR_FORMATS
136+ && pixelFormat != kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange &&
137+ pixelFormat != kCVPixelFormatType_422YpCbCr8BiPlanarFullRange
138+ #endif
139+ #if XPRS_HAS_444_BIPLANAR_FORMATS
140+ && pixelFormat != kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange &&
141+ pixelFormat != kCVPixelFormatType_444YpCbCr8BiPlanarFullRange
142+ #endif
143+ ) {
121144 return ;
122145 }
123146
@@ -135,17 +158,24 @@ void CVideoDecoder::convertAVFrame(const AVFrame* avframe, Frame& frameOut) {
135158 // Default to 420 format
136159 size_t uvHeight = height / 2 ;
137160 size_t frameSize = width * height * 3 / 2 ;
161+ #if XPRS_HAS_422_BIPLANAR_FORMATS
138162 if (pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange ||
139163 pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarFullRange ) {
140164 // 422 doubles vertical chroma samples compared to 420
141165 frameSize = width * height * 2 ;
142166 uvHeight = height;
143- } else if (
144- pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange ||
145- pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarFullRange ) {
167+ } else
168+ #endif
169+ #if XPRS_HAS_444_BIPLANAR_FORMATS
170+ if (pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange ||
171+ pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarFullRange ) {
146172 // 444 doubles both vertical and horizontal chroma samples compared to 420
147173 frameSize = width * height * 3 ;
148174 uvHeight = height;
175+ } else
176+ #endif
177+ {
178+ // 420 format - use default values above
149179 }
150180 frameSize *= bytes;
151181 if (_buffer.size () < frameSize) {
@@ -160,6 +190,7 @@ void CVideoDecoder::convertAVFrame(const AVFrame* avframe, Frame& frameOut) {
160190 }
161191
162192 // Convert biplanar 422 and 444 to triplanar
193+ #if XPRS_HAS_422_BIPLANAR_FORMATS
163194 if (pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange ||
164195 pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarFullRange ) {
165196 uint8_t * uvPlane = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane (pixelBuffer, 1 );
@@ -179,9 +210,11 @@ void CVideoDecoder::convertAVFrame(const AVFrame* avframe, Frame& frameOut) {
179210 frameOut.planes [2 ] = vPlane;
180211 frameOut.stride [2 ] = width / 2 ;
181212
182- } else if (
183- pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange ||
184- pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarFullRange ) {
213+ } else
214+ #endif
215+ #if XPRS_HAS_444_BIPLANAR_FORMATS
216+ if (pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange ||
217+ pixelFormat == kCVPixelFormatType_444YpCbCr8BiPlanarFullRange ) {
185218 uint8_t * uvPlane = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane (pixelBuffer, 1 );
186219 uint8_t * uPlane = _buffer.data () + width * height;
187220 uint8_t * vPlane = uPlane + (width * height);
@@ -197,7 +230,9 @@ void CVideoDecoder::convertAVFrame(const AVFrame* avframe, Frame& frameOut) {
197230 frameOut.stride [1 ] = width;
198231 frameOut.planes [2 ] = vPlane;
199232 frameOut.stride [2 ] = width;
200- } else {
233+ } else
234+ #endif
235+ {
201236 // Copy the UV plane for NV12
202237 uint8_t * uvPlane = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane (pixelBuffer, 1 );
203238 for (size_t y = 0 ; y < uvHeight; y++) {
0 commit comments