@@ -148,6 +148,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
148148 // do Y
149149 size_t new_width = CVPixelBufferGetWidthOfPlane (pixelBuffer, 0 );
150150 size_t new_height = CVPixelBufferGetHeightOfPlane (pixelBuffer, 0 );
151+ size_t row_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 0 );
151152
152153 if ((width[0 ] != new_width) || (height[0 ] != new_height)) {
153154 width[0 ] = new_width;
@@ -156,7 +157,15 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
156157 }
157158
158159 uint8_t *w = img_data[0 ].ptrw ();
159- memcpy (w, dataY, new_width * new_height);
160+ if (new_width == row_stride) {
161+ memcpy (w, dataY, new_width * new_height);
162+ } else {
163+ for (size_t i = 0 ; i < new_height; i++) {
164+ memcpy (w, dataY, new_width);
165+ w += new_width;
166+ dataY += row_stride;
167+ }
168+ }
160169
161170 img[0 ].instantiate ();
162171 img[0 ]->set_data (new_width, new_height, 0 , Image::FORMAT_R8, img_data[0 ]);
@@ -166,6 +175,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
166175 // do CbCr
167176 size_t new_width = CVPixelBufferGetWidthOfPlane (pixelBuffer, 1 );
168177 size_t new_height = CVPixelBufferGetHeightOfPlane (pixelBuffer, 1 );
178+ size_t row_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 1 );
169179
170180 if ((width[1 ] != new_width) || (height[1 ] != new_height)) {
171181 width[1 ] = new_width;
@@ -174,7 +184,15 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
174184 }
175185
176186 uint8_t *w = img_data[1 ].ptrw ();
177- memcpy (w, dataCbCr, 2 * new_width * new_height);
187+ if (new_width * 2 == row_stride) {
188+ memcpy (w, dataCbCr, 2 * new_width * new_height);
189+ } else {
190+ for (size_t i = 0 ; i < new_height; i++) {
191+ memcpy (w, dataCbCr, new_width * 2 );
192+ w += new_width * 2 ;
193+ dataCbCr += row_stride;
194+ }
195+ }
178196
179197 // /TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion
180198 img[1 ].instantiate ();
@@ -200,6 +218,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
200218private:
201219 AVCaptureDevice *device;
202220 MyCaptureSession *capture_session;
221+ bool device_locked;
203222
204223public:
205224 AVCaptureDevice *get_device () const ;
@@ -210,6 +229,9 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
210229
211230 bool activate_feed () override ;
212231 void deactivate_feed () override ;
232+
233+ bool set_format (int p_index, const Dictionary &p_parameters) override ;
234+ Array get_formats () const override ;
213235};
214236
215237AVCaptureDevice *CameraFeedMacOS::get_device () const {
@@ -219,6 +241,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
219241CameraFeedMacOS::CameraFeedMacOS () {
220242 device = nullptr ;
221243 capture_session = nullptr ;
244+ device_locked = false ;
222245}
223246
224247void CameraFeedMacOS::set_device (AVCaptureDevice *p_device) {
@@ -239,6 +262,15 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
239262 if (capture_session) {
240263 // Already recording!
241264 } else {
265+ // Configure device format if specified.
266+ if (selected_format != -1 ) {
267+ NSError *error;
268+ if (!device_locked) {
269+ device_locked = [device lockForConfiguration: &error];
270+ ERR_FAIL_COND_V_MSG (!device_locked, false , error.localizedFailureReason .UTF8String );
271+ }
272+ [device setActiveFormat: device.formats[selected_format]];
273+ }
242274 // Start camera capture, check permission.
243275 if (@available (macOS 10.14 , *)) {
244276 AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType: AVMediaTypeVideo];
@@ -267,6 +299,61 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CM
267299 [capture_session cleanup ];
268300 capture_session = nullptr ;
269301 };
302+ if (device_locked) {
303+ [device unlockForConfiguration ];
304+ device_locked = false ;
305+ }
306+ }
307+
308+ bool CameraFeedMacOS::set_format (int p_index, const Dictionary &p_parameters) {
309+ if (p_index == -1 ) {
310+ selected_format = p_index;
311+ if (is_active ()) {
312+ [capture_session beginConfiguration ];
313+ }
314+ if (device_locked) {
315+ [device unlockForConfiguration ];
316+ device_locked = false ;
317+ }
318+ if (is_active ()) {
319+ [capture_session commitConfiguration ];
320+ }
321+ return true ;
322+ }
323+ ERR_FAIL_INDEX_V ((unsigned int )p_index, device.formats .count , false );
324+ if (is_active ()) {
325+ if (!device_locked) {
326+ NSError *error;
327+ device_locked = [device lockForConfiguration: &error];
328+ ERR_FAIL_COND_V_MSG (!device_locked, false , error.localizedFailureReason .UTF8String );
329+ }
330+ [capture_session beginConfiguration ];
331+ [device setActiveFormat: device.formats[p_index]];
332+ }
333+ selected_format = p_index;
334+ if (is_active ()) {
335+ [capture_session commitConfiguration ];
336+ }
337+ return true ;
338+ }
339+
340+ Array CameraFeedMacOS::get_formats () const {
341+ Array result;
342+ for (AVCaptureDeviceFormat *format in device.formats ) {
343+ Dictionary dictionary;
344+ CMFormatDescriptionRef formatDescription = format.formatDescription ;
345+ CMVideoDimensions dimension = CMVideoFormatDescriptionGetDimensions (formatDescription);
346+ dictionary[" width" ] = dimension.width ;
347+ dictionary[" height" ] = dimension.height ;
348+ FourCharCode fourcc = CMFormatDescriptionGetMediaSubType (formatDescription);
349+ dictionary[" format" ] =
350+ String::chr ((char )(fourcc >> 24 ) & 0xFF ) +
351+ String::chr ((char )(fourcc >> 16 ) & 0xFF ) +
352+ String::chr ((char )(fourcc >> 8 ) & 0xFF ) +
353+ String::chr ((char )(fourcc >> 0 ) & 0xFF );
354+ result.push_back (dictionary);
355+ }
356+ return result;
270357}
271358
272359// ////////////////////////////////////////////////////////////////////////
0 commit comments