USB: Swap jpge for libjpeg

This commit is contained in:
Stenzek 2024-03-28 20:53:26 +10:00 committed by Connor McLaughlin
parent ebf0cf91b6
commit 431b2c5b83
7 changed files with 284 additions and 103 deletions

View File

@ -385,6 +385,7 @@ set(pcsx2USBSources
USB/qemu-usb/input-keymap-qcode-to-qnum.cpp
USB/qemu-usb/usb-ohci.cpp
USB/shared/ringbuffer.cpp
USB/usb-eyetoy/cam-jpeg.cpp
USB/usb-eyetoy/jo_mpeg.cpp
USB/usb-eyetoy/usb-eyetoy-webcam.cpp
USB/usb-hid/usb-hid.cpp
@ -414,6 +415,7 @@ set(pcsx2USBHeaders
USB/qemu-usb/queue.h
USB/qemu-usb/qusb.h
USB/shared/ringbuffer.h
USB/usb-eyetoy/cam-jpeg.h
USB/usb-eyetoy/jo_mpeg.h
USB/usb-eyetoy/ov519.h
USB/usb-eyetoy/usb-eyetoy-webcam.h

View File

@ -0,0 +1,165 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "cam-jpeg.h"
#include "common/Console.h"
#include <jpeglib.h>
#include <csetjmp>
namespace
{
struct JPEGErrorHandler
{
jpeg_error_mgr err;
jmp_buf jbuf;
};
} // namespace
static bool HandleJPEGError(JPEGErrorHandler* eh)
{
jpeg_std_error(&eh->err);
eh->err.error_exit = [](j_common_ptr cinfo) {
JPEGErrorHandler* eh = (JPEGErrorHandler*)cinfo->err;
char msg[JMSG_LENGTH_MAX];
eh->err.format_message(cinfo, msg);
Console.ErrorFmt("libjpeg fatal error: {}", msg);
longjmp(eh->jbuf, 1);
};
if (setjmp(eh->jbuf) == 0)
return true;
return false;
}
bool CompressCamJPEG(std::vector<u8>* buffer, const u8* image, u32 width, u32 height, int quality)
{
struct MemCallback
{
jpeg_destination_mgr mgr;
std::vector<u8>* buffer;
size_t buffer_used;
};
JPEGErrorHandler err;
if (!HandleJPEGError(&err))
return false;
MemCallback cb;
cb.buffer = buffer;
cb.buffer_used = 0;
cb.mgr.next_output_byte = buffer->data();
cb.mgr.free_in_buffer = buffer->size();
cb.mgr.init_destination = [](j_compress_ptr cinfo) {};
cb.mgr.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
MemCallback* cb = (MemCallback*)cinfo->dest;
// double size
cb->buffer_used = cb->buffer->size();
cb->buffer->resize(cb->buffer->size() * 2);
cb->mgr.next_output_byte = cb->buffer->data() + cb->buffer_used;
cb->mgr.free_in_buffer = cb->buffer->size() - cb->buffer_used;
return TRUE;
};
cb.mgr.term_destination = [](j_compress_ptr cinfo) {
MemCallback* cb = (MemCallback*)cinfo->dest;
// get final size
cb->buffer->resize(cb->buffer->size() - cb->mgr.free_in_buffer);
};
jpeg_compress_struct info;
info.err = &err.err;
jpeg_create_compress(&info);
info.dest = &cb.mgr;
info.image_width = width;
info.image_height = height;
info.in_color_space = JCS_RGB;
info.input_components = 3;
jpeg_set_defaults(&info);
jpeg_set_quality(&info, quality, TRUE);
// H2V1
info.comp_info[0].h_samp_factor = 2;
info.comp_info[0].v_samp_factor = 1;
info.comp_info[1].h_samp_factor = 1;
info.comp_info[1].v_samp_factor = 1;
info.comp_info[2].h_samp_factor = 1;
info.comp_info[2].v_samp_factor = 1;
jpeg_start_compress(&info, TRUE);
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{
u8* scanline_buffer[1] = { const_cast<u8*>(image + (y * width * 3)) };
if (jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
{
Console.ErrorFmt("jpeg_write_scanlines() failed at row {}", y);
result = false;
break;
}
}
jpeg_finish_compress(&info);
jpeg_destroy_compress(&info);
return result;
}
bool DecompressCamJPEG(std::vector<u8>* buffer, u32* width, u32* height, const u8* data, size_t data_size)
{
JPEGErrorHandler err;
if (!HandleJPEGError(&err))
return false;
jpeg_decompress_struct info;
info.err = &err.err;
jpeg_create_decompress(&info);
jpeg_mem_src(&info, static_cast<const unsigned char*>(data), data_size);
const int herr = jpeg_read_header(&info, TRUE);
if (herr != JPEG_HEADER_OK)
{
Console.ErrorFmt("jpeg_read_header() returned {}", herr);
return false;
}
if (info.image_width == 0 || info.image_height == 0 || info.num_components < 3)
{
Console.ErrorFmt("Invalid image dimensions: {}x{}x{}", info.image_width, info.image_height, info.num_components);
return false;
}
info.out_color_space = JCS_RGB;
info.out_color_components = 3;
if (!jpeg_start_decompress(&info))
{
Console.ErrorFmt("jpeg_start_decompress() returned failure");
return false;
}
buffer->resize(info.image_width * info.image_height * 3);
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{
u8* scanline_buffer[1] = { buffer->data() + (y * info.image_width * 3) };
if (jpeg_read_scanlines(&info, scanline_buffer, 1) != 1)
{
Console.ErrorFmt("jpeg_read_scanlines() failed at row {}", y);
result = false;
break;
}
}
jpeg_finish_decompress(&info);
jpeg_destroy_decompress(&info);
return result;
}

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "common/Pcsx2Defs.h"
#include <vector>
bool CompressCamJPEG(std::vector<u8>* buffer, const u8* image, u32 width, u32 height, int quality);
bool DecompressCamJPEG(std::vector<u8>* buffer, u32* width, u32* height, const u8* data, size_t data_size);

View File

@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "cam-linux.h"
#include "cam-jpeg.h"
#include "usb-eyetoy-webcam.h"
#include "jpgd.h"
#include "jpge.h"
#include "jo_mpeg.h"
#include "common/Console.h"
@ -58,22 +57,23 @@ namespace usb_eyetoy
static void store_mpeg_frame(const unsigned char* data, const unsigned int len)
{
mpeg_mutex.lock();
memcpy(mpeg_buffer.start, data, len);
if (len > 0)
memcpy(mpeg_buffer.start, data, len);
mpeg_buffer.length = len;
mpeg_mutex.unlock();
}
static void process_image(const unsigned char* data, int size)
{
const int bytesPerPixel = 3;
int comprBufSize = frame_width * frame_height * bytesPerPixel;
constexpr int bytesPerPixel = 3;
const size_t comprBufSize = frame_width * frame_height * bytesPerPixel;
if (pixelformat == V4L2_PIX_FMT_YUYV)
{
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = 0;
std::vector<u8> comprBuf(comprBufSize);
if (frame_format == format_mpeg)
{
comprLen = jo_write_mpeg(comprBuf, data, frame_width, frame_height, JO_YUYV, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_NONE);
const size_t comprLen = jo_write_mpeg(comprBuf.data(), data, frame_width, frame_height, JO_YUYV, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_NONE);
comprBuf.resize(comprLen);
}
else if (frame_format == format_jpeg)
{
@ -105,14 +105,10 @@ namespace usb_eyetoy
dst[5] = (b > 255) ? 255 : ((b < 0) ? 0 : b);
}
}
jpge::params params;
params.m_quality = 80;
params.m_subsampling = jpge::H2V1;
comprLen = comprBufSize;
if (!jpge::compress_image_to_jpeg_file_in_memory(comprBuf, comprLen, frame_width, frame_height, 3, data2, params))
{
comprLen = 0;
}
if (!CompressCamJPEG(&comprBuf, data2, frame_width, frame_height, 80))
comprBuf.clear();
free(data2);
}
else if (frame_format == format_yuv400)
@ -135,22 +131,26 @@ namespace usb_eyetoy
comprBuf[in_pos++] = src[0];//Y
}
}
comprLen = 80 * 64;
comprBuf.resize(80 * 64);
}
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
else
{
comprBuf.clear();
}
store_mpeg_frame(comprBuf.data(), static_cast<unsigned int>(comprBuf.size()));
}
else if (pixelformat == V4L2_PIX_FMT_JPEG)
{
if (frame_format == format_mpeg)
{
int width, height, actual_comps;
unsigned char* rgbData = jpgd::decompress_jpeg_image_from_memory(data, size, &width, &height, &actual_comps, 3);
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = jo_write_mpeg(comprBuf, rgbData, frame_width, frame_height, JO_RGB24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_NONE);
free(rgbData);
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
std::vector<u8> rgbData;
u32 width, height;
if (DecompressCamJPEG(&rgbData, &width, &height, data, size))
{
std::vector<u8> comprBuf(comprBufSize);
const size_t comprLen = jo_write_mpeg(comprBuf.data(), rgbData.data(), frame_width, frame_height, JO_RGB24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_NONE);
store_mpeg_frame(comprBuf.data(), comprLen);
}
}
else if (frame_format == format_jpeg)
{
@ -158,35 +158,35 @@ namespace usb_eyetoy
}
else if (frame_format == format_yuv400)
{
int width, height, actual_comps;
unsigned char* rgbData = jpgd::decompress_jpeg_image_from_memory(data, size, &width, &height, &actual_comps, 3);
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = 0;
int in_pos = 0;
for (int my = 0; my < 8; my++)
for (int mx = 0; mx < 10; mx++)
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
{
int srcx = 4* (8*mx + x);
int srcy = 4* (8*my + y);
unsigned char* src = rgbData + (srcy * frame_width + srcx) * bytesPerPixel;
if (srcy >= frame_height)
std::vector<u8> rgbData;
u32 width, height;
if (DecompressCamJPEG(&rgbData, &width, &height, data, size))
{
const size_t comprLen = 80 * 64;
std::vector<u8> comprBuf(comprLen);
int in_pos = 0;
for (int my = 0; my < 8; my++)
for (int mx = 0; mx < 10; mx++)
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
{
comprBuf[in_pos++] = 0x01;
int srcx = 4* (8*mx + x);
int srcy = 4* (8*my + y);
unsigned char* src = rgbData.data() + (srcy * frame_width + srcx) * bytesPerPixel;
if (srcy >= frame_height)
{
comprBuf[in_pos++] = 0x01;
}
else
{
float r = src[0];
float g = src[1];
float b = src[2];
comprBuf[in_pos++] = 0.299f * r + 0.587f * g + 0.114f * b;
}
}
else
{
float r = src[0];
float g = src[1];
float b = src[2];
comprBuf[in_pos++] = 0.299f * r + 0.587f * g + 0.114f * b;
}
}
comprLen = 80 * 64;
free(rgbData);
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
store_mpeg_frame(comprBuf.data(), comprLen);
}
}
}
else
@ -536,8 +536,8 @@ namespace usb_eyetoy
void create_dummy_frame_eyetoy()
{
const int bytesPerPixel = 3;
int comprBufSize = frame_width * frame_height * bytesPerPixel;
constexpr int bytesPerPixel = 3;
const int comprBufSize = frame_width * frame_height * bytesPerPixel;
unsigned char* rgbData = (unsigned char*)calloc(1, comprBufSize);
for (int y = 0; y < frame_height; y++)
{
@ -549,27 +549,25 @@ namespace usb_eyetoy
ptr[2] = 255 * y / frame_height;
}
}
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = 0;
std::vector<u8> comprBuf(comprBufSize);
if (frame_format == format_mpeg)
{
comprLen = jo_write_mpeg(comprBuf, rgbData, frame_width, frame_height, JO_RGB24, JO_NONE, JO_NONE);
const size_t comprLen = jo_write_mpeg(comprBuf.data(), rgbData, frame_width, frame_height, JO_RGB24, JO_NONE, JO_NONE);
comprBuf.resize(comprLen);
}
else if (frame_format == format_jpeg)
{
jpge::params params;
params.m_quality = 80;
params.m_subsampling = jpge::H2V1;
comprLen = comprBufSize;
if (!jpge::compress_image_to_jpeg_file_in_memory(comprBuf, comprLen, frame_width, frame_height, 3, rgbData, params))
{
comprLen = 0;
}
if (!CompressCamJPEG(&comprBuf, rgbData, frame_width, frame_height, 80))
comprBuf.clear();
}
else
{
comprBuf.clear();
}
free(rgbData);
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
store_mpeg_frame(comprBuf.data(), static_cast<unsigned int>(comprBuf.size()));
}
void create_dummy_frame_ov511p()

View File

@ -1,16 +1,18 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#include "common/Console.h"
#include "common/StringUtil.h"
#include "jpge.h"
#include "videodev.h"
#include "cam-jpeg.h"
#include "cam-windows.h"
#include "usb-eyetoy-webcam.h"
#include "jo_mpeg.h"
#include "USB/USB.h"
#include <jpeglib.h>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-goto"
@ -373,7 +375,8 @@ namespace usb_eyetoy
void store_mpeg_frame(const unsigned char* data, const unsigned int len)
{
mpeg_mutex.lock();
memcpy(mpeg_buffer.start, data, len);
if (len > 0)
memcpy(mpeg_buffer.start, data, len);
mpeg_buffer.length = len;
mpeg_mutex.unlock();
}
@ -383,13 +386,13 @@ namespace usb_eyetoy
if (bitsperpixel == 24)
{
const int bytesPerPixel = 3;
int comprBufSize = frame_width * frame_height * bytesPerPixel;
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = 0;
const size_t comprBufSize = frame_width * frame_height * bytesPerPixel;
std::vector<u8> comprBuf(comprBufSize);
if (frame_format == format_mpeg)
{
comprLen = jo_write_mpeg(
comprBuf, data, frame_width, frame_height, JO_BGR24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_FLIP_Y);
const size_t comprLen = jo_write_mpeg(
comprBuf.data(), data, frame_width, frame_height, JO_BGR24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_FLIP_Y);
comprBuf.resize(comprBufSize);
}
else if (frame_format == format_jpeg)
{
@ -407,14 +410,9 @@ namespace usb_eyetoy
}
}
jpge::params params;
params.m_quality = 80;
params.m_subsampling = jpge::H2V1;
comprLen = comprBufSize;
if (!jpge::compress_image_to_jpeg_file_in_memory(comprBuf, comprLen, frame_width, frame_height, 3, data2, params))
{
comprLen = 0;
}
if (!CompressCamJPEG(&comprBuf, data2, frame_width, frame_height, 80))
comprBuf.clear();
free(data2);
}
else if (frame_format == format_yuv400)
@ -440,10 +438,13 @@ namespace usb_eyetoy
comprBuf[in_pos++] = 0.299f * r + 0.587f * g + 0.114f * b;
}
}
comprLen = 80 * 64;
comprBuf.resize(80 * 64);
}
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
else
{
comprBuf.clear();
}
store_mpeg_frame(comprBuf.data(), static_cast<unsigned int>(comprBuf.size()));
}
else
{
@ -453,8 +454,8 @@ namespace usb_eyetoy
void create_dummy_frame_eyetoy()
{
const int bytesPerPixel = 3;
int comprBufSize = frame_width * frame_height * bytesPerPixel;
constexpr int bytesPerPixel = 3;
const int comprBufSize = frame_width * frame_height * bytesPerPixel;
unsigned char* rgbData = (unsigned char*)calloc(1, comprBufSize);
for (int y = 0; y < frame_height; y++)
{
@ -466,27 +467,25 @@ namespace usb_eyetoy
ptr[2] = 255 * y / frame_height;
}
}
unsigned char* comprBuf = (unsigned char*)calloc(1, comprBufSize);
int comprLen = 0;
std::vector<u8> comprBuf(comprBufSize);
if (frame_format == format_mpeg)
{
comprLen = jo_write_mpeg(comprBuf, rgbData, frame_width, frame_height, JO_RGB24, JO_NONE, JO_NONE);
const size_t comprLen = jo_write_mpeg(comprBuf.data(), rgbData, frame_width, frame_height, JO_RGB24, JO_NONE, JO_NONE);
comprBuf.resize(comprBufSize);
}
else if (frame_format == format_jpeg)
{
jpge::params params;
params.m_quality = 80;
params.m_subsampling = jpge::H2V1;
comprLen = comprBufSize;
if (!jpge::compress_image_to_jpeg_file_in_memory(comprBuf, comprLen, frame_width, frame_height, 3, rgbData, params))
{
comprLen = 0;
}
if (!CompressCamJPEG(&comprBuf, rgbData, frame_width, frame_height, 80))
comprBuf.clear();
}
else
{
comprBuf.clear();
}
free(rgbData);
store_mpeg_frame(comprBuf, comprLen);
free(comprBuf);
store_mpeg_frame(comprBuf.data(), static_cast<unsigned int>(comprBuf.size()));
}
void create_dummy_frame_ov511p()

View File

@ -327,6 +327,7 @@
<ClCompile Include="USB\qemu-usb\input-keymap-qcode-to-qnum.cpp" />
<ClCompile Include="USB\qemu-usb\usb-ohci.cpp" />
<ClCompile Include="USB\shared\ringbuffer.cpp" />
<ClCompile Include="USB\usb-eyetoy\cam-jpeg.cpp" />
<ClCompile Include="USB\usb-eyetoy\cam-windows.cpp" />
<ClCompile Include="USB\usb-eyetoy\jo_mpeg.cpp" />
<ClCompile Include="USB\usb-eyetoy\usb-eyetoy-webcam.cpp" />
@ -676,6 +677,7 @@
<ClInclude Include="USB\qemu-usb\qusb.h" />
<ClInclude Include="USB\qemu-usb\USBinternal.h" />
<ClInclude Include="USB\shared\ringbuffer.h" />
<ClInclude Include="USB\usb-eyetoy\cam-jpeg.h" />
<ClInclude Include="USB\usb-eyetoy\cam-windows.h" />
<ClInclude Include="USB\usb-eyetoy\jo_mpeg.h" />
<ClInclude Include="USB\usb-eyetoy\ov519.h" />

View File

@ -1407,6 +1407,9 @@
<ClCompile Include="SIO\Pad\PadPopn.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
<ClCompile Include="USB\usb-eyetoy\cam-jpeg.cpp">
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -2324,6 +2327,9 @@
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SupportURLs.h" />
<ClInclude Include="USB\usb-eyetoy\cam-jpeg.h">
<Filter>System\Ps2\USB\usb-eyetoy</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">