8 Commits

Author SHA1 Message Date
Adrian Siekierka
49871fa899 psxavenc: fixes 2025-02-16 15:59:00 +01:00
Adrian Siekierka
325a73c08f psxavenc: fix EDC not being calculated in 2336-byte sector mode 2025-02-16 13:59:11 +01:00
Adrian Siekierka
bf5cd4eeb8 psxavenc: refactor str2/str2cd output to match xa/xacd output cleanup 2025-02-16 13:59:11 +01:00
Adrian Siekierka
f623e56035 libpsxav: fix EOF flag being misapplied in xa/xacd output 2025-02-16 13:59:11 +01:00
Adrian Siekierka
bea15ca01f increase permissible frame rate range to 1..60 FPS 2025-02-15 22:56:57 +01:00
Adrian Siekierka
f8e44a59c3 re-enable colorspace conversion 2025-02-15 22:52:17 +01:00
Adrian Siekierka
703bb67393 chore: update ffmpeg in Windows build to 7.1 2025-02-15 22:45:53 +01:00
Adrian Siekierka
ac8280cd85 chore: fix GitHub CI 2025-02-15 22:44:33 +01:00
10 changed files with 107 additions and 60 deletions

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
ROOT_DIR="$(pwd)" ROOT_DIR="$(pwd)"
FFMPEG_VERSION="6.0" FFMPEG_VERSION="7.1"
NUM_JOBS="4" NUM_JOBS="4"
if [ $# -eq 1 ]; then if [ $# -eq 1 ]; then

View File

@@ -23,7 +23,7 @@ jobs:
psxavenc/.github/scripts/build.sh psxavenc-windows x86_64-w64-mingw32 psxavenc/.github/scripts/mingw-cross.txt psxavenc/.github/scripts/build.sh psxavenc-windows x86_64-w64-mingw32 psxavenc/.github/scripts/mingw-cross.txt
- name: Upload Windows build artifacts - name: Upload Windows build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: psxavenc-windows name: psxavenc-windows
path: psxavenc-windows.zip path: psxavenc-windows.zip
@@ -33,7 +33,7 @@ jobs:
psxavenc/.github/scripts/build.sh psxavenc-linux psxavenc/.github/scripts/build.sh psxavenc-linux
- name: Upload Linux build artifacts - name: Upload Linux build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: psxavenc-linux name: psxavenc-linux
path: psxavenc-linux.zip path: psxavenc-linux.zip

View File

@@ -232,23 +232,30 @@ uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings) {
return interleave; return interleave;
} }
static void psx_audio_xa_encode_init_sector(uint8_t *buffer, psx_audio_xa_settings_t settings) { static inline void psx_audio_xa_sync_subheader_copy(psx_cdrom_sector_mode2_t *buffer) {
memcpy(buffer->subheader + 1, buffer->subheader, sizeof(psx_cdrom_sector_xa_subheader_t));
}
static void psx_audio_xa_encode_init_sector(psx_cdrom_sector_mode2_t *buffer, psx_audio_xa_settings_t settings) {
if (settings.format == PSX_AUDIO_XA_FORMAT_XACD) { if (settings.format == PSX_AUDIO_XA_FORMAT_XACD) {
memset(buffer, 0, 2352); memset(buffer, 0, PSX_CDROM_SECTOR_SIZE);
memset(buffer+0x001, 0xFF, 10); memset(buffer->sync + 1, 0xFF, 10);
buffer[0x00F] = 0x02; buffer->header.mode = 0x02;
} else { } else {
memset(buffer + 0x10, 0, 2336); memset(buffer->subheader, 0, PSX_CDROM_SECTOR_SIZE - 16);
} }
buffer[0x010] = settings.file_number; buffer->subheader[0].file = settings.file_number;
buffer[0x011] = settings.channel_number & 0x1F; buffer->subheader[0].channel = settings.channel_number & PSX_CDROM_SECTOR_XA_CHANNEL_MASK;
buffer[0x012] = 0x24 | 0x40; buffer->subheader[0].submode =
buffer[0x013] = PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO
(settings.stereo ? 1 : 0) | PSX_CDROM_SECTOR_XA_SUBMODE_FORM2
| (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? 0 : 4) | PSX_CDROM_SECTOR_XA_SUBMODE_RT;
| (settings.bits_per_sample >= 8 ? 16 : 0); buffer->subheader[0].coding =
memcpy(buffer + 0x014, buffer + 0x010, 4); (settings.stereo ? PSX_CDROM_SECTOR_XA_CODING_STEREO : PSX_CDROM_SECTOR_XA_CODING_MONO)
| (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE : PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE)
| (settings.bits_per_sample >= 8 ? PSX_CDROM_SECTOR_XA_CODING_BITS_8 : PSX_CDROM_SECTOR_XA_CODING_BITS_4);
psx_audio_xa_sync_subheader_copy(buffer);
} }
int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) {
@@ -261,8 +268,8 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat
if (settings.stereo) { sample_count <<= 1; } if (settings.stereo) { sample_count <<= 1; }
for (i = 0, j = 0; i < sample_count || ((j % 18) != 0); i += sample_jump, j++) { for (i = 0, j = 0; i < sample_count || ((j % 18) != 0); i += sample_jump, j++) {
uint8_t *sector_data = output + ((j/18) * xa_sector_size) - xa_offset; psx_cdrom_sector_mode2_t *sector_data = (psx_cdrom_sector_mode2_t*) (output + ((j/18) * xa_sector_size) - xa_offset);
uint8_t *block_data = sector_data + 0x18 + ((j%18) * 0x80); uint8_t *block_data = sector_data->data + ((j%18) * 0x80);
if (init_sector) { if (init_sector) {
psx_audio_xa_encode_init_sector(sector_data, settings); psx_audio_xa_encode_init_sector(sector_data, settings);
@@ -275,7 +282,7 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat
memcpy(block_data + 12, block_data + 8, 4); memcpy(block_data + 12, block_data + 8, 4);
if ((j+1)%18 == 0) { if ((j+1)%18 == 0) {
psx_cdrom_calculate_checksums(sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); psx_cdrom_calculate_checksums((uint8_t*) sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2);
init_sector = 1; init_sector = 1;
} }
} }
@@ -285,8 +292,9 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat
void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length) { void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length) {
if (output_length >= 2336) { if (output_length >= 2336) {
output[output_length - 2352 + 0x12] |= 0x80; psx_cdrom_sector_mode2_t *sector = (psx_cdrom_sector_mode2_t*) &output[output_length - 2352];
output[output_length - 2352 + 0x18] |= 0x80; sector->subheader[0].submode |= PSX_CDROM_SECTOR_XA_SUBMODE_EOF;
psx_audio_xa_sync_subheader_copy(sector);
} }
} }

View File

@@ -79,6 +79,58 @@ void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int fla
#define PSX_CDROM_SECTOR_SIZE 2352 #define PSX_CDROM_SECTOR_SIZE 2352
typedef struct {
uint8_t minute;
uint8_t second;
uint8_t sector;
uint8_t mode;
} psx_cdrom_sector_header_t;
typedef struct {
uint8_t file;
uint8_t channel;
uint8_t submode;
uint8_t coding;
} psx_cdrom_sector_xa_subheader_t;
typedef struct {
uint8_t sync[12];
psx_cdrom_sector_header_t header;
uint8_t data[0x920];
} psx_cdrom_sector_mode1_t;
typedef struct {
uint8_t sync[12];
psx_cdrom_sector_header_t header;
psx_cdrom_sector_xa_subheader_t subheader[2];
uint8_t data[0x918];
} psx_cdrom_sector_mode2_t;
_Static_assert(sizeof(psx_cdrom_sector_mode1_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode1 sector size");
_Static_assert(sizeof(psx_cdrom_sector_mode2_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode2 sector size");
#define PSX_CDROM_SECTOR_XA_CHANNEL_MASK 0x1F
#define PSX_CDROM_SECTOR_XA_SUBMODE_EOR 0x01
#define PSX_CDROM_SECTOR_XA_SUBMODE_VIDEO 0x02
#define PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO 0x04
#define PSX_CDROM_SECTOR_XA_SUBMODE_DATA 0x08
#define PSX_CDROM_SECTOR_XA_SUBMODE_TRIGGER 0x10
#define PSX_CDROM_SECTOR_XA_SUBMODE_FORM2 0x20
#define PSX_CDROM_SECTOR_XA_SUBMODE_RT 0x40
#define PSX_CDROM_SECTOR_XA_SUBMODE_EOF 0x80
#define PSX_CDROM_SECTOR_XA_CODING_MONO 0x00
#define PSX_CDROM_SECTOR_XA_CODING_STEREO 0x01
#define PSX_CDROM_SECTOR_XA_CODING_CHANNEL_MASK 0x03
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE 0x00
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE 0x04
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_MASK 0x0C
#define PSX_CDROM_SECTOR_XA_CODING_BITS_4 0x00
#define PSX_CDROM_SECTOR_XA_CODING_BITS_8 0x10
#define PSX_CDROM_SECTOR_XA_CODING_BITS_MASK 0x30
#define PSX_CDROM_SECTOR_XA_CODING_EMPHASIS 0x40
typedef enum { typedef enum {
PSX_CDROM_SECTOR_TYPE_MODE1, PSX_CDROM_SECTOR_TYPE_MODE1,
PSX_CDROM_SECTOR_TYPE_MODE2_FORM1, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1,
@@ -87,4 +139,4 @@ typedef enum {
void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type);
#endif /* __LIBPSXAV_H__ */ #endif /* __LIBPSXAV_H__ */

View File

@@ -23,23 +23,22 @@ freely, subject to the following restrictions:
#include "common.h" #include "common.h"
void init_sector_buffer_video(uint8_t *buffer, settings_t *settings) { void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings) {
int offset;
if (settings->format == FORMAT_STR2CD) { if (settings->format == FORMAT_STR2CD) {
memset(buffer, 0, 2352); memset(buffer, 0, PSX_CDROM_SECTOR_SIZE);
memset(buffer+0x001, 0xFF, 10); memset(buffer->sync + 1, 0xFF, 10);
buffer[0x00F] = 0x02; buffer->header.mode = 0x02;
offset = 0x10;
} else { } else {
memset(buffer, 0, 2336); memset(buffer->subheader, 0, PSX_CDROM_SECTOR_SIZE - 16);
offset = 0;
} }
buffer[offset+0] = settings->file_number; buffer->subheader[0].file = settings->file_number;
buffer[offset+1] = settings->channel_number & 0x1F; buffer->subheader[0].channel = settings->channel_number & PSX_CDROM_SECTOR_XA_CHANNEL_MASK;
buffer[offset+2] = 0x08 | 0x40; buffer->subheader[0].submode =
buffer[offset+3] = 0x00; PSX_CDROM_SECTOR_XA_SUBMODE_DATA
memcpy(buffer + offset + 4, buffer + offset, 4); | PSX_CDROM_SECTOR_XA_SUBMODE_RT;
buffer->subheader[0].coding = 0;
memcpy(buffer->subheader + 1, buffer->subheader, sizeof(psx_cdrom_sector_xa_subheader_t));
} }
void calculate_edc_data(uint8_t *buffer) void calculate_edc_data(uint8_t *buffer)

View File

@@ -124,7 +124,7 @@ typedef struct {
} settings_t; } settings_t;
// cdrom.c // cdrom.c
void init_sector_buffer_video(uint8_t *buffer, settings_t *settings); void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings);
void calculate_edc_data(uint8_t *buffer); void calculate_edc_data(uint8_t *buffer);
// decoding.c // decoding.c

View File

@@ -204,22 +204,18 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
if (av->scaler == NULL) { if (av->scaler == NULL) {
return false; return false;
} }
#if 0
// FIXME: if this is uncommented libswscale may produce completely black
// frames for whatever reason...
if (sws_setColorspaceDetails( if (sws_setColorspaceDetails(
av->scaler, av->scaler,
sws_getCoefficients(av->video_codec_context->colorspace), sws_getCoefficients(av->video_codec_context->colorspace),
(av->video_codec_context->color_range == AVCOL_RANGE_JPEG), av->video_codec_context->color_range == AVCOL_RANGE_JPEG,
sws_getCoefficients(SWS_CS_ITU601), sws_getCoefficients(SWS_CS_ITU601),
true, true,
0, 0,
0, 1 << 16,
0 1 << 16
) < 0) { ) < 0) {
return false; return false;
} }
#endif
if (settings->swscale_options) { if (settings->swscale_options) {
if (av_opt_set_from_string(av->scaler, settings->swscale_options, NULL, "=", ":,") < 0) { if (av_opt_set_from_string(av->scaler, settings->swscale_options, NULL, "=", ":,") < 0) {
return false; return false;

View File

@@ -306,7 +306,7 @@ void encode_file_str(settings_t *settings, FILE *output) {
if ((j%interleave) < video_sectors_per_block) { if ((j%interleave) < video_sectors_per_block) {
// Video sector // Video sector
init_sector_buffer_video(buffer, settings); init_sector_buffer_video((psx_cdrom_sector_mode2_t*) buffer, settings);
encode_sector_str(settings->video_frames, buffer, settings); encode_sector_str(settings->video_frames, buffer, settings);
} else { } else {
// Audio sector // Audio sector
@@ -333,16 +333,13 @@ void encode_file_str(settings_t *settings, FILE *output) {
buffer[0x00C] = ((t/75/60)%10)|(((t/75/60)/10)<<4); buffer[0x00C] = ((t/75/60)%10)|(((t/75/60)/10)<<4);
buffer[0x00D] = (((t/75)%60)%10)|((((t/75)%60)/10)<<4); buffer[0x00D] = (((t/75)%60)%10)|((((t/75)%60)/10)<<4);
buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4); buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4);
// FIXME: EDC is not calculated in 2336-byte sector mode (shouldn't
// matter anyway, any CD image builder will have to recalculate it
// due to the sector's MSF changing)
if((j%interleave) < video_sectors_per_block) {
calculate_edc_data(buffer);
}
} }
fwrite(buffer, sector_size, 1, output); if((j%interleave) < video_sectors_per_block) {
calculate_edc_data(buffer);
}
fwrite(buffer + 2352 - sector_size, sector_size, 1, output);
time_t t = get_elapsed_time(settings); time_t t = get_elapsed_time(settings);
if (t) { if (t) {

View File

@@ -614,13 +614,8 @@ void encode_sector_str(uint8_t *video_frames, uint8_t *output, settings_t *setti
header[0x00E] = (uint8_t)(settings->state_vid.bytes_used>>16); header[0x00E] = (uint8_t)(settings->state_vid.bytes_used>>16);
header[0x00F] = (uint8_t)(settings->state_vid.bytes_used>>24); header[0x00F] = (uint8_t)(settings->state_vid.bytes_used>>24);
if (settings->format == FORMAT_STR2CD) { memcpy(output + 0x018, header, sizeof(header));
memcpy(output + 0x018, header, sizeof(header)); memcpy(output + 0x018 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016);
memcpy(output + 0x018 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016);
} else {
memcpy(output + 0x008, header, sizeof(header));
memcpy(output + 0x008 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016);
}
settings->state_vid.frame_data_offset += 2016; settings->state_vid.frame_data_offset += 2016;
} }

View File

@@ -189,7 +189,7 @@ int parse_args(settings_t* settings, int argc, char** argv) {
return -1; return -1;
} }
i = settings->video_fps_num / settings->video_fps_den; i = settings->video_fps_num / settings->video_fps_den;
if (i < 1 || i > 30) { if (i < 1 || i > 60) {
fprintf(stderr, "Invalid frame rate: %d/%d\n", settings->video_fps_num, settings->video_fps_den); fprintf(stderr, "Invalid frame rate: %d/%d\n", settings->video_fps_num, settings->video_fps_den);
return -1; return -1;
} }