modules/sceJpegEncUser, codec: Implement stubbed functions (#2608)

- MJpeg and sceJpegEncoderSetValidRegion functions are not yet supported.
- Ignores the CompressionRatio parameter.
This commit is contained in:
Saturnsky 2023-05-28 23:07:31 +09:00 committed by GitHub
parent a197d981e0
commit d16f87c337
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 294 additions and 19 deletions

2
external/ffmpeg vendored

@ -1 +1 @@
Subproject commit 44845469c918c07d8470df2330711ed28174c468
Subproject commit f643d93999c4e69bef287a7af6b6656ce8f8a257

View File

@ -2,6 +2,7 @@ add_library(
codec
STATIC
include/codec/state.h
include/codec/types.h
src/atrac9.cpp
src/decoder.cpp
src/aac.cpp

View File

@ -236,6 +236,8 @@ struct PlayerState {
~PlayerState();
};
void convert_rgb_to_yuv(const uint8_t *rgba, uint8_t *yuv, uint32_t width, uint32_t height, const bool is_yuv420);
void convert_yuv_to_rgb(const uint8_t *yuv, uint8_t *rgba, uint32_t width, uint32_t height, const bool is_yuv420);
int convert_yuv_to_jpeg(const uint8_t *yuv, uint8_t *jpeg, uint32_t width, uint32_t height, uint32_t max_size, bool is_yuv420);
void copy_yuv_data_from_frame(AVFrame *frame, uint8_t *dest);
std::string codec_error_name(int error);

View File

@ -0,0 +1,60 @@
// Vita3K emulator project
// Copyright (C) 2023 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <mem/ptr.h>
#include <util/types.h>
enum SceJpegEncErrorCode {
SCE_JPEGENC_ERROR_IMAGE_SIZE = 0x80650200,
SCE_JPEGENC_ERROR_INSUFFICIENT_BUFFER = 0x80650201,
SCE_JPEGENC_ERROR_INVALID_COMPRATIO = 0x80650202,
SCE_JPEGENC_ERROR_INVALID_PIXELFORMAT = 0x80650203,
SCE_JPEGENC_ERROR_INVALID_HEADER_MODE = 0x80650204,
SCE_JPEGENC_ERROR_INVALID_POINTER = 0x80650205,
SCE_JPEGENC_ERROR_NOT_PHY_CONTINUOUS_MEMORY = 0x80650206
};
enum SceJpegEncoderPixelFormat : int32_t {
SCE_JPEGENC_PIXEL_RGBA8888 = 0,
SCE_JPEGENC_PIXEL_BGRA8888 = 4,
SCE_JPEGENC_PIXEL_YCBCR420 = 8,
SCE_JPEGENC_PIXEL_YCBCR422 = 9,
SCE_JPEGENC_PITCH_HW_CSC = 16,
};
enum SceJpegEncoderHeaderMode : int32_t {
SCE_JPEGENC_HEADER_MODE_JPEG = 0,
SCE_JPEGENC_HEADER_MODE_MJPEG = 1
};
enum SceJpegEncoderInitParamOption : int32_t {
SCE_JPEGENC_INIT_PARAM_OPTION_NONE = 0,
SCE_JPEGENC_INIT_PARAM_OPTION_LPDDR2_MEMORY = 1
};
struct SceJpegEncoderInitParam {
uint32_t size;
int32_t inWidth;
int32_t inHeight;
int32_t pixelFormat;
Ptr<uint8_t> outBuffer;
uint32_t outSize;
SceJpegEncoderInitParamOption option;
};

View File

@ -19,6 +19,7 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
@ -56,6 +57,116 @@ void convert_yuv_to_rgb(const uint8_t *yuv, uint8_t *rgba, uint32_t width, uint3
sws_freeContext(context);
}
void convert_rgb_to_yuv(const uint8_t *rgba, uint8_t *yuv, uint32_t width, uint32_t height, const bool is_yuv420) {
SwsContext *context = sws_getContext(width, height, AV_PIX_FMT_RGBA, width, height, is_yuv420 ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_YUV422P,
0, nullptr, nullptr, nullptr);
assert(context);
const uint8_t *slices[] = {
rgba,
};
int strides[] = {
static_cast<int>(width * 4),
};
uint8_t *dst_slices[] = {
&yuv[0], // Y Slice
&yuv[width * height], // U Slice
&yuv[static_cast<uint32_t>(width * height * (is_yuv420 ? 1.25 : 1.5))], // V Slice
};
const int dst_strides[] = {
static_cast<int>(width),
static_cast<int>(width) / (is_yuv420 ? 2 : 1),
static_cast<int>(width) / (is_yuv420 ? 2 : 1),
};
int error = sws_scale(context, slices, strides, 0, height, dst_slices, dst_strides);
sws_freeContext(context);
assert(error == height);
}
void convert_yuv420_to_yuv444(const uint8_t *yuv420, uint8_t *yuv444, uint32_t width, uint32_t height) {
SwsContext *context = sws_getContext(width, height, AV_PIX_FMT_YUV420P, width, height, AV_PIX_FMT_YUV444P,
0, nullptr, nullptr, nullptr);
assert(context);
const uint8_t *src_slices[] = {
&yuv420[0], // Y Slice
&yuv420[width * height], // U Slice
&yuv420[static_cast<uint32_t>(width * height * 1.25)], // V Slice
};
int src_strides[] = {
static_cast<int>(width),
static_cast<int>(width) / 2,
static_cast<int>(width) / 2,
};
uint8_t *dst_slices[] = {
&yuv444[0], // Y Slice
&yuv444[width * height], // U Slice
&yuv444[2 * width * height], // V Slice
};
const int dst_strides[] = {
static_cast<int>(width),
static_cast<int>(width),
static_cast<int>(width),
};
int error = sws_scale(context, src_slices, src_strides, 0, height, dst_slices, dst_strides);
sws_freeContext(context);
assert(error == height);
}
int convert_yuv_to_jpeg(const uint8_t *yuv, uint8_t *jpeg, uint32_t width, uint32_t height, uint32_t max_size, bool is_yuv420) {
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
assert(codec);
AVCodecContext *context = avcodec_alloc_context3(codec);
assert(context);
context->width = width;
context->height = height;
context->pix_fmt = is_yuv420 ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUVJ422P;
context->time_base.num = 1;
context->time_base.den = 25;
int ret = avcodec_open2(context, codec, NULL);
assert(ret >= 0);
AVFrame *frame = av_frame_alloc();
assert(frame);
frame->format = context->pix_fmt;
frame->width = context->width;
frame->height = context->height;
ret = av_image_fill_arrays(frame->data, frame->linesize, yuv, context->pix_fmt, context->width, context->height, 1);
assert(ret >= 0);
AVPacket *pkt = av_packet_alloc();
ret = avcodec_send_frame(context, frame);
assert(ret >= 0);
ret = avcodec_receive_packet(context, pkt);
uint32_t size = pkt->size;
if (ret == 0 && size <= max_size) {
memcpy(jpeg, pkt->data, pkt->size);
}
av_packet_unref(pkt);
av_frame_free(&frame);
avcodec_free_context(&context);
if (size > max_size) {
return -1;
}
return size;
}
bool MjpegDecoderState::send(const uint8_t *data, uint32_t size) {
std::vector<uint8_t> jpeg_buffer(size + AV_INPUT_BUFFER_PADDING_SIZE);
std::memcpy(jpeg_buffer.data(), data, size);

View File

@ -128,6 +128,7 @@ public:
"SceHttp",
"SceIme",
"SceIofilemgr",
"SceJpegEncUser",
"SceJpegUser",
"SceKernelForMono",
"SceKernelForVM",

View File

@ -17,43 +17,143 @@
#include "SceJpegEncUser.h"
EXPORT(int, sceJpegEncoderCsc) {
return UNIMPLEMENTED();
#include <codec/state.h>
#include <codec/types.h>
#include <util/tracy.h>
TRACY_MODULE_NAME(SceJpegEncUser);
struct SceJpegEncoderContext {
int32_t inWidth;
int32_t inHeight;
int32_t pixelFormat;
Ptr<uint8_t> outBuffer;
uint32_t outSize;
SceJpegEncoderInitParamOption option;
int32_t compressRatio;
int32_t headerMode;
};
int sceJpegEncoderInitImpl(SceJpegEncoderContext *context, int32_t inWidth, int32_t inHeight, int32_t pixelFormat, Ptr<uint8_t> outBuffer, uint32_t outSize, SceJpegEncoderInitParamOption option = SCE_JPEGENC_INIT_PARAM_OPTION_NONE) {
context->inWidth = inWidth;
context->inHeight = inHeight;
context->pixelFormat = pixelFormat;
context->outBuffer = outBuffer;
context->outSize = outSize;
context->option = option;
context->compressRatio = 255;
context->headerMode = SCE_JPEGENC_HEADER_MODE_JPEG;
return 0;
}
EXPORT(int, sceJpegEncoderEncode) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderCsc, SceJpegEncoderContext *context, Ptr<uint8_t> outBuffer, Ptr<uint8_t> inBuffer, int32_t inPitch, int32_t inPixelFormat) {
TRACY_FUNC(sceJpegEncoderCsc, context, outBuffer, inBuffer, inPitch, inPixelFormat);
auto inBufferData = inBuffer.get(emuenv.mem);
auto outBufferData = outBuffer.get(emuenv.mem);
bool is_yuv420 = false;
if ((context->pixelFormat & SCE_JPEGENC_PIXEL_YCBCR422) == SCE_JPEGENC_PIXEL_YCBCR422) {
is_yuv420 = false;
} else if ((context->pixelFormat & SCE_JPEGENC_PIXEL_YCBCR420) == SCE_JPEGENC_PIXEL_YCBCR420) {
is_yuv420 = true;
} else {
return SCE_JPEGENC_ERROR_INVALID_PIXELFORMAT;
}
if (inPixelFormat != SCE_JPEGENC_PIXEL_RGBA8888) {
return STUBBED("Only RGBA8888 to YCbCr is implemented.");
}
int32_t width = inPitch;
int32_t height = context->inHeight * inPitch / context->inWidth;
convert_rgb_to_yuv(inBufferData, outBufferData, context->inWidth, context->inHeight, is_yuv420);
return 0;
}
EXPORT(int, sceJpegEncoderEnd) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderEncode, SceJpegEncoderContext *context, Ptr<uint8_t> inBuffer) {
TRACY_FUNC(sceJpegEncoderEncode, context, inBuffer);
auto inBufferData = inBuffer.get(emuenv.mem);
auto outBufferData = context->outBuffer.get(emuenv.mem);
int width = context->inWidth;
int height = context->inHeight;
bool is_yuv420 = false;
if ((context->pixelFormat & SCE_JPEGENC_PIXEL_YCBCR422) == SCE_JPEGENC_PIXEL_YCBCR422) {
is_yuv420 = false;
} else if ((context->pixelFormat & SCE_JPEGENC_PIXEL_YCBCR420) == SCE_JPEGENC_PIXEL_YCBCR420) {
is_yuv420 = true;
} else {
return SCE_JPEGENC_ERROR_INVALID_PIXELFORMAT;
}
uint32_t size = convert_yuv_to_jpeg(inBufferData, outBufferData, width, height, context->outSize, is_yuv420);
if (size == -1) {
return SCE_JPEGENC_ERROR_INSUFFICIENT_BUFFER;
}
return size;
}
EXPORT(int, sceJpegEncoderEnd, SceJpegEncoderContext *context) {
TRACY_FUNC(sceJpegEncoderEnd, context);
return 0;
}
EXPORT(int, sceJpegEncoderGetContextSize) {
return UNIMPLEMENTED();
TRACY_FUNC(sceJpegEncoderGetContextSize);
return sizeof(SceJpegEncoderContext);
}
EXPORT(int, sceJpegEncoderInit) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderInit, SceJpegEncoderContext *context, int32_t inWidth, int32_t inHeight, int32_t pixelFormat, Ptr<uint8_t> outBuffer, uint32_t outSize) {
TRACY_FUNC(sceJpegEncoderInit, context, inWidth, inHeight, pixelFormat, outBuffer, outSize);
return sceJpegEncoderInitImpl(context, inWidth, inHeight, pixelFormat, outBuffer, outSize, SCE_JPEGENC_INIT_PARAM_OPTION_NONE);
}
EXPORT(int, sceJpegEncoderInitWithParam) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderInitWithParam, SceJpegEncoderContext *context, SceJpegEncoderInitParam *initParam) {
TRACY_FUNC(sceJpegEncoderInitWithParam, context, initParam);
return sceJpegEncoderInitImpl(context, initParam->inWidth, initParam->inHeight, initParam->pixelFormat, initParam->outBuffer, initParam->outSize, initParam->option);
}
EXPORT(int, sceJpegEncoderSetCompressionRatio) {
return UNIMPLEMENTED();
// TODO: CompressionRatio is ignored for the time being.
EXPORT(int, sceJpegEncoderSetCompressionRatio, SceJpegEncoderContext *context, int32_t ratio) {
TRACY_FUNC(sceJpegEncoderSetCompressionRatio, context, ratio);
if (ratio < 0 || ratio > 255) {
return SCE_JPEGENC_ERROR_INVALID_COMPRATIO;
}
context->compressRatio = ratio;
return 0;
}
EXPORT(int, sceJpegEncoderSetHeaderMode) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderSetHeaderMode, SceJpegEncoderContext *context, int32_t mode) {
TRACY_FUNC(sceJpegEncoderSetHeaderMode, context, mode);
if (mode != SCE_JPEGENC_HEADER_MODE_JPEG && mode != SCE_JPEGENC_HEADER_MODE_MJPEG) {
return SCE_JPEGENC_ERROR_INVALID_HEADER_MODE;
} else if (mode == SCE_JPEGENC_HEADER_MODE_MJPEG) {
return STUBBED("SCE_JPEGENC_HEADER_MODE_MJPEG is not supported");
}
context->headerMode = mode;
return 0;
}
EXPORT(int, sceJpegEncoderSetOutputAddr) {
return UNIMPLEMENTED();
EXPORT(int, sceJpegEncoderSetOutputAddr, SceJpegEncoderContext *context, Ptr<uint8_t> outBuffer, uint32_t outSize) {
TRACY_FUNC(sceJpegEncoderSetOutputAddr, context, outBuffer, outSize);
context->outBuffer = outBuffer;
context->outSize = outSize;
return 0;
}
EXPORT(int, sceJpegEncoderSetValidRegion) {
EXPORT(int, sceJpegEncoderSetValidRegion, SceJpegEncoderContext *context, int32_t inWidth, int32_t inHeight) {
TRACY_FUNC(sceJpegEncoderSetValidRegion, context, inWidth, inHeight);
return UNIMPLEMENTED();
}