Added support for Motion JPEG camera capture
Fixes https://github.com/libsdl-org/SDL/issues/12183
This commit is contained in:
@@ -76,6 +76,7 @@ SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_UYVY, FCC('UYVY'));
|
||||
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YVYU, FCC('YVYU'));
|
||||
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV12, FCC('NV12'));
|
||||
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV21, FCC('NV21'));
|
||||
SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_MJPG, FCC('MJPG'));
|
||||
#undef SDL_DEFINE_MEDIATYPE_GUID
|
||||
|
||||
#ifdef __GNUC__
|
||||
@@ -102,7 +103,8 @@ static const struct
|
||||
{ &SDL_MFVideoFormat_UYVY, SDL_PIXELFORMAT_UYVY, SDL_COLORSPACE_BT709_LIMITED },
|
||||
{ &SDL_MFVideoFormat_YVYU, SDL_PIXELFORMAT_YVYU, SDL_COLORSPACE_BT709_LIMITED },
|
||||
{ &SDL_MFVideoFormat_NV12, SDL_PIXELFORMAT_NV12, SDL_COLORSPACE_BT709_LIMITED },
|
||||
{ &SDL_MFVideoFormat_NV21, SDL_PIXELFORMAT_NV21, SDL_COLORSPACE_BT709_LIMITED }
|
||||
{ &SDL_MFVideoFormat_NV21, SDL_PIXELFORMAT_NV21, SDL_COLORSPACE_BT709_LIMITED },
|
||||
{ &SDL_MFVideoFormat_MJPG, SDL_PIXELFORMAT_MJPG, SDL_COLORSPACE_SRGB }
|
||||
};
|
||||
|
||||
static SDL_Colorspace GetMediaTypeColorspace(IMFMediaType *mediatype, SDL_Colorspace default_colorspace)
|
||||
@@ -296,6 +298,13 @@ static void MediaTypeToSDLFmt(IMFMediaType *mediatype, SDL_PixelFormat *format,
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG_CAMERA
|
||||
SDL_Log("Unknown media type: 0x%x (%c%c%c%c)", type.Data1,
|
||||
(char)(Uint8)(type.Data1 >> 0),
|
||||
(char)(Uint8)(type.Data1 >> 8),
|
||||
(char)(Uint8)(type.Data1 >> 16),
|
||||
(char)(Uint8)(type.Data1 >> 24));
|
||||
#endif
|
||||
*format = SDL_PIXELFORMAT_UNKNOWN;
|
||||
*colorspace = SDL_COLORSPACE_UNKNOWN;
|
||||
}
|
||||
@@ -467,7 +476,11 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SD
|
||||
CleanupIMF2DBuffer2(NULL, objs);
|
||||
} else {
|
||||
frame->pixels = pixels;
|
||||
frame->pitch = (int) pitch;
|
||||
if (frame->format == SDL_PIXELFORMAT_MJPG) {
|
||||
frame->pitch = (int)buflen;
|
||||
} else {
|
||||
frame->pitch = (int)pitch;
|
||||
}
|
||||
if (!SDL_SetPointerPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer2, NULL)) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
}
|
||||
@@ -491,9 +504,13 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SD
|
||||
CleanupIMFMediaBuffer(NULL, objs);
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
} else {
|
||||
pitch = (LONG) device->hidden->pitch;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
pixels += -pitch * (frame->h - 1);
|
||||
if (frame->format == SDL_PIXELFORMAT_MJPG) {
|
||||
pitch = (LONG)currentlen;
|
||||
} else {
|
||||
pitch = (LONG)device->hidden->pitch;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
pixels += -pitch * (frame->h - 1);
|
||||
}
|
||||
}
|
||||
frame->pixels = pixels;
|
||||
frame->pitch = (int) pitch;
|
||||
@@ -522,6 +539,18 @@ static void MEDIAFOUNDATION_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
|
||||
|
||||
#else
|
||||
|
||||
static SDL_CameraFrameResult MEDIAFOUNDATION_CopyFrame(SDL_Surface *frame, const void *pixels, LONG pitch, DWORD buflen)
|
||||
{
|
||||
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
||||
if (!frame->pixels) {
|
||||
return SDL_CAMERA_FRAME_ERROR;
|
||||
}
|
||||
|
||||
SDL_memcpy(frame->pixels, pixels, buflen);
|
||||
frame->pitch = (int)pitch;
|
||||
return SDL_CAMERA_FRAME_READY;
|
||||
}
|
||||
|
||||
static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS)
|
||||
{
|
||||
SDL_assert(device->hidden->current_sample != NULL);
|
||||
@@ -563,12 +592,10 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SD
|
||||
if (FAILED(ret)) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
} else {
|
||||
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
||||
if (frame->pixels == NULL) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
if (frame->format == SDL_PIXELFORMAT_MJPG) {
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, pixels, (LONG)buflen, buflen);
|
||||
} else {
|
||||
SDL_memcpy(frame->pixels, pixels, buflen);
|
||||
frame->pitch = (int)pitch;
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, pixels, pitch, buflen);
|
||||
}
|
||||
IMF2DBuffer2_Unlock2D(buffer2d2);
|
||||
}
|
||||
@@ -578,17 +605,17 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SD
|
||||
if (FAILED(ret)) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
} else {
|
||||
BYTE *bufstart = pixels;
|
||||
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
bufstart += -pitch * (frame->h - 1);
|
||||
}
|
||||
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
||||
if (frame->pixels == NULL) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
if (frame->format == SDL_PIXELFORMAT_MJPG) {
|
||||
// FIXME: How big is this frame?
|
||||
const DWORD buflen = 0;
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, pixels, pitch, buflen);
|
||||
} else {
|
||||
SDL_memcpy(frame->pixels, bufstart, buflen);
|
||||
frame->pitch = (int)pitch;
|
||||
BYTE *bufstart = pixels;
|
||||
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
bufstart += -pitch * (frame->h - 1);
|
||||
}
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, bufstart, pitch, buflen);
|
||||
}
|
||||
IMF2DBuffer_Unlock2D(buffer2d);
|
||||
}
|
||||
@@ -599,18 +626,16 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SD
|
||||
if (FAILED(ret)) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
} else {
|
||||
BYTE *bufstart = pixels;
|
||||
pitch = (LONG)device->hidden->pitch;
|
||||
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
bufstart += -pitch * (frame->h - 1);
|
||||
}
|
||||
frame->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), buflen);
|
||||
if (frame->pixels == NULL) {
|
||||
result = SDL_CAMERA_FRAME_ERROR;
|
||||
if (frame->format == SDL_PIXELFORMAT_MJPG) {
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, pixels, (LONG)currentlen, currentlen);
|
||||
} else {
|
||||
SDL_memcpy(frame->pixels, bufstart, buflen);
|
||||
frame->pitch = (int)pitch;
|
||||
BYTE *bufstart = pixels;
|
||||
pitch = (LONG)device->hidden->pitch;
|
||||
const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h;
|
||||
if (pitch < 0) { // image rows are reversed.
|
||||
bufstart += -pitch * (frame->h - 1);
|
||||
}
|
||||
result = MEDIAFOUNDATION_CopyFrame(frame, bufstart, pitch, buflen);
|
||||
}
|
||||
IMFMediaBuffer_Unlock(buffer);
|
||||
}
|
||||
|
||||
@@ -128,10 +128,11 @@ static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *
|
||||
const io_method io = device->hidden->io;
|
||||
size_t size = device->hidden->buffers[0].length;
|
||||
struct v4l2_buffer buf;
|
||||
ssize_t amount;
|
||||
|
||||
switch (io) {
|
||||
case IO_METHOD_READ:
|
||||
if (read(fd, device->hidden->buffers[0].start, size) == -1) {
|
||||
if ((amount = read(fd, device->hidden->buffers[0].start, size)) == -1) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return SDL_CAMERA_FRAME_SKIP;
|
||||
@@ -148,7 +149,11 @@ static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *
|
||||
|
||||
*timestampNS = SDL_GetTicksNS(); // oh well, close enough.
|
||||
frame->pixels = device->hidden->buffers[0].start;
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
if (device->hidden->driver_pitch) {
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
} else {
|
||||
frame->pitch = (int)amount;
|
||||
}
|
||||
break;
|
||||
|
||||
case IO_METHOD_MMAP:
|
||||
@@ -178,7 +183,11 @@ static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *
|
||||
}
|
||||
|
||||
frame->pixels = device->hidden->buffers[buf.index].start;
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
if (device->hidden->driver_pitch) {
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
} else {
|
||||
frame->pitch = buf.bytesused;
|
||||
}
|
||||
device->hidden->buffers[buf.index].available = 1;
|
||||
|
||||
*timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec);
|
||||
@@ -222,7 +231,11 @@ static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *
|
||||
}
|
||||
|
||||
frame->pixels = (void*)buf.m.userptr;
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
if (device->hidden->driver_pitch) {
|
||||
frame->pitch = device->hidden->driver_pitch;
|
||||
} else {
|
||||
frame->pitch = buf.bytesused;
|
||||
}
|
||||
device->hidden->buffers[i].available = 1;
|
||||
|
||||
*timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec);
|
||||
@@ -404,10 +417,15 @@ static void format_v4l2_to_sdl(Uint32 fmt, SDL_PixelFormat *format, SDL_Colorspa
|
||||
switch (fmt) {
|
||||
#define CASE(x, y, z) case x: *format = y; *colorspace = z; return
|
||||
CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2, SDL_COLORSPACE_BT709_LIMITED);
|
||||
CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_MJPG, SDL_COLORSPACE_SRGB);
|
||||
#undef CASE
|
||||
default:
|
||||
#if DEBUG_CAMERA
|
||||
SDL_Log("CAMERA: Unknown format V4L2_PIX_FORMAT '%d'", fmt);
|
||||
SDL_Log("CAMERA: Unknown format V4L2_PIX_FORMAT '%c%c%c%c' (0x%x)",
|
||||
(char)(Uint8)(fmt >> 0),
|
||||
(char)(Uint8)(fmt >> 8),
|
||||
(char)(Uint8)(fmt >> 16),
|
||||
(char)(Uint8)(fmt >> 24), fmt);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user