mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-30 15:08:31 +00:00
Fixed camera capture on iOS
My phone captured 1920x1080 images even though the highest reported format was higher resolution, so I adjusted testcamera to be able to handle different sized images than expected. Fixes https://github.com/libsdl-org/SDL/issues/9930
This commit is contained in:
@@ -852,6 +852,8 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device)
|
|||||||
#if DEBUG_CAMERA
|
#if DEBUG_CAMERA
|
||||||
SDL_Log("CAMERA: Frame is going through without conversion!");
|
SDL_Log("CAMERA: Frame is going through without conversion!");
|
||||||
#endif
|
#endif
|
||||||
|
output_surface->w = acquired->w;
|
||||||
|
output_surface->h = acquired->h;
|
||||||
output_surface->pixels = acquired->pixels;
|
output_surface->pixels = acquired->pixels;
|
||||||
output_surface->pitch = acquired->pitch;
|
output_surface->pitch = acquired->pitch;
|
||||||
} else { // convert/scale into a different surface.
|
} else { // convert/scale into a different surface.
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
* <key>com.apple.security.device.camera</key> <true/>
|
* <key>com.apple.security.device.camera</key> <true/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static Uint32 CoreMediaFormatToSDL(FourCharCode fmt)
|
static SDL_PixelFormatEnum CoreMediaFormatToSDL(FourCharCode fmt)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
#define CASE(x, y) case x: return y
|
#define CASE(x, y) case x: return y
|
||||||
@@ -55,6 +55,10 @@ static Uint32 CoreMediaFormatToSDL(FourCharCode fmt)
|
|||||||
CASE(kCMPixelFormat_32BGRA, SDL_PIXELFORMAT_BGRA32);
|
CASE(kCMPixelFormat_32BGRA, SDL_PIXELFORMAT_BGRA32);
|
||||||
CASE(kCMPixelFormat_422YpCbCr8, SDL_PIXELFORMAT_YUY2);
|
CASE(kCMPixelFormat_422YpCbCr8, SDL_PIXELFORMAT_YUY2);
|
||||||
CASE(kCMPixelFormat_422YpCbCr8_yuvs, SDL_PIXELFORMAT_UYVY);
|
CASE(kCMPixelFormat_422YpCbCr8_yuvs, SDL_PIXELFORMAT_UYVY);
|
||||||
|
CASE(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, SDL_PIXELFORMAT_NV12);
|
||||||
|
CASE(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, SDL_PIXELFORMAT_NV12);
|
||||||
|
CASE(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, SDL_PIXELFORMAT_P010);
|
||||||
|
CASE(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange, SDL_PIXELFORMAT_P010);
|
||||||
#undef CASE
|
#undef CASE
|
||||||
default:
|
default:
|
||||||
#if DEBUG_CAMERA
|
#if DEBUG_CAMERA
|
||||||
@@ -172,6 +176,9 @@ static int COREMEDIA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame,
|
|||||||
// !!! FIXME: this currently copies the data to the surface (see FIXME about non-contiguous planar surfaces, but in theory we could just keep this locked until ReleaseFrame...
|
// !!! FIXME: this currently copies the data to the surface (see FIXME about non-contiguous planar surfaces, but in theory we could just keep this locked until ReleaseFrame...
|
||||||
CVPixelBufferLockBaseAddress(image, 0);
|
CVPixelBufferLockBaseAddress(image, 0);
|
||||||
|
|
||||||
|
frame->w = (int)CVPixelBufferGetWidth(image);
|
||||||
|
frame->h = (int)CVPixelBufferGetHeight(image);
|
||||||
|
|
||||||
if ((planar == 0) && (numPlanes == 0)) {
|
if ((planar == 0) && (numPlanes == 0)) {
|
||||||
const int pitch = (int) CVPixelBufferGetBytesPerRow(image);
|
const int pitch = (int) CVPixelBufferGetBytesPerRow(image);
|
||||||
const size_t buflen = pitch * frame->h;
|
const size_t buflen = pitch * frame->h;
|
||||||
@@ -185,22 +192,26 @@ static int COREMEDIA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame,
|
|||||||
} else {
|
} else {
|
||||||
// !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet.
|
// !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet.
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
for (int i = 0; (i < numPlanes) && (i < 3); i++) {
|
for (int i = 0; i < numPlanes; i++) {
|
||||||
buflen += CVPixelBufferGetBytesPerRowOfPlane(image, i);
|
size_t plane_height = CVPixelBufferGetHeightOfPlane(image, i);
|
||||||
|
size_t plane_pitch = CVPixelBufferGetBytesPerRowOfPlane(image, i);
|
||||||
|
size_t plane_size = (plane_pitch * plane_height);
|
||||||
|
buflen += plane_size;
|
||||||
}
|
}
|
||||||
buflen *= frame->h;
|
|
||||||
|
|
||||||
|
frame->pitch = (int)CVPixelBufferGetBytesPerRowOfPlane(image, 0); // this is what SDL3 currently expects
|
||||||
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
||||||
if (frame->pixels == NULL) {
|
if (frame->pixels == NULL) {
|
||||||
retval = -1;
|
retval = -1;
|
||||||
} else {
|
} else {
|
||||||
Uint8 *dst = frame->pixels;
|
Uint8 *dst = frame->pixels;
|
||||||
frame->pitch = (int) CVPixelBufferGetBytesPerRowOfPlane(image, 0); // this is what SDL3 currently expects, probably incorrectly.
|
for (int i = 0; i < numPlanes; i++) {
|
||||||
for (int i = 0; (i < numPlanes) && (i < 3); i++) {
|
|
||||||
const void *src = CVPixelBufferGetBaseAddressOfPlane(image, i);
|
const void *src = CVPixelBufferGetBaseAddressOfPlane(image, i);
|
||||||
const size_t pitch = CVPixelBufferGetBytesPerRowOfPlane(image, i);
|
size_t plane_height = CVPixelBufferGetHeightOfPlane(image, i);
|
||||||
SDL_memcpy(dst, src, pitch * frame->h);
|
size_t plane_pitch = CVPixelBufferGetBytesPerRowOfPlane(image, i);
|
||||||
dst += pitch * frame->h;
|
size_t plane_size = (plane_pitch * plane_height);
|
||||||
|
SDL_memcpy(dst, src, plane_size);
|
||||||
|
dst += plane_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +252,7 @@ static int COREMEDIA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *
|
|||||||
AVCaptureDevice *avdevice = (__bridge AVCaptureDevice *) device->handle;
|
AVCaptureDevice *avdevice = (__bridge AVCaptureDevice *) device->handle;
|
||||||
|
|
||||||
// Pick format that matches the spec
|
// Pick format that matches the spec
|
||||||
const Uint32 sdlfmt = spec->format;
|
const SDL_PixelFormatEnum sdlfmt = spec->format;
|
||||||
const int w = spec->width;
|
const int w = spec->width;
|
||||||
const int h = spec->height;
|
const int h = spec->height;
|
||||||
const int rate = spec->interval_denominator;
|
const int rate = spec->interval_denominator;
|
||||||
@@ -356,7 +367,7 @@ static void GatherCameraSpecs(AVCaptureDevice *device, CameraFormatAddData *add_
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Uint32 sdlfmt = CoreMediaFormatToSDL(CMFormatDescriptionGetMediaSubType(fmt.formatDescription));
|
const SDL_PixelFormatEnum sdlfmt = CoreMediaFormatToSDL(CMFormatDescriptionGetMediaSubType(fmt.formatDescription));
|
||||||
if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) {
|
if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -180,21 +180,6 @@ int SDL_AppEvent(void *appstate, const SDL_Event *event)
|
|||||||
|
|
||||||
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
|
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
|
||||||
SDL_Log("Camera approved!");
|
SDL_Log("Camera approved!");
|
||||||
if (SDL_GetCameraFormat(camera, &spec) < 0) {
|
|
||||||
SDL_Log("Couldn't get camera spec: %s", SDL_GetError());
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resize the window to match */
|
|
||||||
SDL_SetWindowSize(window, spec.width, spec.height);
|
|
||||||
|
|
||||||
/* Create texture with appropriate format */
|
|
||||||
SDL_assert(texture == NULL);
|
|
||||||
texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STREAMING, spec.width, spec.height);
|
|
||||||
if (!texture) {
|
|
||||||
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_CAMERA_DEVICE_DENIED:
|
case SDL_EVENT_CAMERA_DEVICE_DENIED:
|
||||||
@@ -213,30 +198,48 @@ int SDL_AppIterate(void *appstate)
|
|||||||
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
if (texture) { /* if not NULL, camera is ready to go. */
|
int win_w, win_h, tw, th;
|
||||||
int win_w, win_h, tw, th;
|
SDL_FRect d;
|
||||||
SDL_FRect d;
|
Uint64 timestampNS = 0;
|
||||||
Uint64 timestampNS = 0;
|
SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL;
|
||||||
SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL;
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (frame_next) {
|
if (frame_next) {
|
||||||
SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS);
|
SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (frame_next) {
|
||||||
|
if (frame_current) {
|
||||||
|
if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) {
|
||||||
|
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (frame_next) {
|
/* It's not needed to keep the frame once updated the texture is updated.
|
||||||
if (frame_current) {
|
* But in case of 0-copy, it's needed to have the frame while using the texture.
|
||||||
if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) {
|
*/
|
||||||
SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError());
|
frame_current = frame_next;
|
||||||
}
|
texture_updated = SDL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_current) {
|
||||||
|
if (!texture ||
|
||||||
|
SDL_QueryTexture(texture, NULL, NULL, &tw, &th) < 0 ||
|
||||||
|
tw != frame_current->w || th != frame_current->h) {
|
||||||
|
/* Resize the window to match */
|
||||||
|
SDL_SetWindowSize(window, frame_current->w, frame_current->h);
|
||||||
|
|
||||||
|
if (texture) {
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's not needed to keep the frame once updated the texture is updated.
|
/* Create texture with appropriate format */
|
||||||
* But in case of 0-copy, it's needed to have the frame while using the texture.
|
texture = SDL_CreateTexture(renderer, frame_current->format->format, SDL_TEXTUREACCESS_STREAMING, frame_current->w, frame_current->h);
|
||||||
*/
|
if (!texture) {
|
||||||
frame_current = frame_next;
|
SDL_Log("Couldn't create texture: %s", SDL_GetError());
|
||||||
texture_updated = SDL_FALSE;
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update SDL_Texture with last video frame (only once per new frame) */
|
/* Update SDL_Texture with last video frame (only once per new frame) */
|
||||||
|
Reference in New Issue
Block a user