Refactor and get rid of common.h

This commit is contained in:
spicyjpeg 2025-02-28 02:15:21 +01:00
parent 7b5953322f
commit a39f159aaf
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
15 changed files with 1396 additions and 1526 deletions

View File

@ -266,7 +266,7 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat
uint8_t init_sector = 1; uint8_t init_sector = 1;
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++) {
psx_cdrom_sector_mode2_t *sector_data = (psx_cdrom_sector_mode2_t*) (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->data + ((j%18) * 0x80); uint8_t *block_data = sector_data->data + ((j%18) * 0x80);
@ -282,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((uint8_t*) sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); psx_cdrom_calculate_checksums((psx_cdrom_sector_t *)sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2);
init_sector = 1; init_sector = 1;
} }
} }

View File

@ -21,49 +21,88 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#include <stdint.h>
#include <string.h> #include <string.h>
#include "libpsxav.h" #include "libpsxav.h"
static uint32_t psx_cdrom_calculate_edc(uint8_t *sector, uint32_t offset, uint32_t size) #define EDC_CRC32_POLYNOMIAL 0xD8018001
{
static uint32_t edc_crc32(uint8_t *data, int length) {
uint32_t edc = 0; uint32_t edc = 0;
for (int i = offset; i < offset+size; i++) {
edc ^= 0xFF&(uint32_t)sector[i]; for (int i = 0; i < length; i++) {
for (int ibit = 0; ibit < 8; ibit++) { edc ^= 0xFF & (uint32_t)data[i];
edc = (edc>>1)^(0xD8018001*(edc&0x1));
} for (int j = 0; j < 8; j++)
edc = (edc >> 1) ^ (EDC_CRC32_POLYNOMIAL * (edc & 0x1));
} }
return edc; return edc;
} }
void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type) #define TO_BCD(x) ((x) + ((x) / 10) * 6)
{
switch (type) {
case PSX_CDROM_SECTOR_TYPE_MODE1: {
uint32_t edc = psx_cdrom_calculate_edc(sector, 0x0, 0x810);
sector[0x810] = (uint8_t)(edc);
sector[0x811] = (uint8_t)(edc >> 8);
sector[0x812] = (uint8_t)(edc >> 16);
sector[0x813] = (uint8_t)(edc >> 24);
void psx_cdrom_init_sector(psx_cdrom_sector_t *sector, int lba, psx_cdrom_sector_type_t type) {
// Sync sequence
memset(sector->mode1.sync + 1, 0xff, 10);
sector->mode1.sync[0x0] = 0x00;
sector->mode1.sync[0xb] = 0x00;
// Timecode
lba += 150;
sector->mode1.header.minute = TO_BCD(lba / 4500);
sector->mode1.header.second = TO_BCD((lba / 75) % 60);
sector->mode1.header.sector = TO_BCD(lba % 75);
// Mode
if (type == PSX_CDROM_SECTOR_TYPE_MODE1) {
sector->mode1.header.mode = 0x01;
} else {
sector->mode2.header.mode = 0x02;
memset(sector->mode2.subheader, 0, sizeof(psx_cdrom_sector_xa_subheader_t));
sector->mode2.subheader[0].submode = PSX_CDROM_SECTOR_XA_SUBMODE_DATA;
if (type == PSX_CDROM_SECTOR_TYPE_MODE2_FORM2)
sector->mode2.subheader[0].submode |= PSX_CDROM_SECTOR_XA_SUBMODE_FORM2;
memcpy(sector->mode2.subheader + 1, sector->mode2.subheader, sizeof(psx_cdrom_sector_xa_subheader_t));
}
}
void psx_cdrom_calculate_checksums(psx_cdrom_sector_t *sector, psx_cdrom_sector_type_t type) {
uint8_t *data = (uint8_t *)sector;
uint32_t edc;
switch (type) {
case PSX_CDROM_SECTOR_TYPE_MODE1:
edc = edc_crc32(data, 0x810);
data[0x810] = (uint8_t)(edc);
data[0x811] = (uint8_t)(edc >> 8);
data[0x812] = (uint8_t)(edc >> 16);
data[0x813] = (uint8_t)(edc >> 24);
memset(sector + 0x814, 0, 8); memset(sector + 0x814, 0, 8);
// TODO: ECC // TODO: ECC
} break; break;
case PSX_CDROM_SECTOR_TYPE_MODE2_FORM1: {
uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x808);
sector[0x818] = (uint8_t)(edc);
sector[0x819] = (uint8_t)(edc >> 8);
sector[0x81A] = (uint8_t)(edc >> 16);
sector[0x81B] = (uint8_t)(edc >> 24);
case PSX_CDROM_SECTOR_TYPE_MODE2_FORM1:
edc = edc_crc32(data + 0x10, 0x808);
data[0x818] = (uint8_t)(edc);
data[0x819] = (uint8_t)(edc >> 8);
data[0x81A] = (uint8_t)(edc >> 16);
data[0x81B] = (uint8_t)(edc >> 24);
// TODO: ECC // TODO: ECC
} break; break;
case PSX_CDROM_SECTOR_TYPE_MODE2_FORM2: {
uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x91C); case PSX_CDROM_SECTOR_TYPE_MODE2_FORM2:
sector[0x92C] = (uint8_t)(edc); edc = edc_crc32(data + 0x10, 0x91C);
sector[0x92D] = (uint8_t)(edc >> 8);
sector[0x92E] = (uint8_t)(edc >> 16); data[0x92C] = (uint8_t)(edc);
sector[0x92F] = (uint8_t)(edc >> 24); data[0x92D] = (uint8_t)(edc >> 8);
} break; data[0x92E] = (uint8_t)(edc >> 16);
data[0x92F] = (uint8_t)(edc >> 24);
break;
} }
} }

View File

@ -21,8 +21,7 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#ifndef __LIBPSXAV_H__ #pragma once
#define __LIBPSXAV_H__
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -106,6 +105,11 @@ typedef struct {
uint8_t data[0x918]; uint8_t data[0x918];
} psx_cdrom_sector_mode2_t; } psx_cdrom_sector_mode2_t;
typedef union {
psx_cdrom_sector_mode1_t mode1;
psx_cdrom_sector_mode2_t mode2;
} psx_cdrom_sector_t;
_Static_assert(sizeof(psx_cdrom_sector_mode1_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode1 sector size"); _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"); _Static_assert(sizeof(psx_cdrom_sector_mode2_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode2 sector size");
@ -137,6 +141,5 @@ typedef enum {
PSX_CDROM_SECTOR_TYPE_MODE2_FORM2 PSX_CDROM_SECTOR_TYPE_MODE2_FORM2
} psx_cdrom_sector_type_t; } psx_cdrom_sector_type_t;
void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); void psx_cdrom_init_sector(psx_cdrom_sector_t *sector, int lba, psx_cdrom_sector_type_t type);
void psx_cdrom_calculate_checksums(psx_cdrom_sector_t *sector, psx_cdrom_sector_type_t type);
#endif /* __LIBPSXAV_H__ */

View File

@ -25,9 +25,8 @@ libpsxav_dep = declare_dependency(include_directories: include_directories('libp
executable('psxavenc', [ executable('psxavenc', [
'psxavenc/args.c', 'psxavenc/args.c',
'psxavenc/cdrom.c',
'psxavenc/decoding.c', 'psxavenc/decoding.c',
'psxavenc/filefmt.c', 'psxavenc/filefmt.c',
'psxavenc/mdec.c', 'psxavenc/main.c',
'psxavenc/psxavenc.c' 'psxavenc/mdec.c'
], dependencies: [libm_dep, ffmpeg, libpsxav_dep], install: true) ], dependencies: [libm_dep, ffmpeg, libpsxav_dep], install: true)

View File

@ -27,6 +27,7 @@ freely, subject to the following restrictions:
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "args.h" #include "args.h"
#include "config.h"
#define INVALID_PARAM -1 #define INVALID_PARAM -1
@ -146,13 +147,6 @@ static const char *const format_names[NUM_FORMATS] = {
}; };
static void init_default_args(args_t *args) { static void init_default_args(args_t *args) {
args->flags = 0;
args->input_file = NULL;
args->output_file = NULL;
args->swresample_options = NULL;
args->swscale_options = NULL;
if ( if (
args->format == FORMAT_XA || args->format == FORMAT_XACD || args->format == FORMAT_XA || args->format == FORMAT_XACD ||
args->format == FORMAT_STR || args->format == FORMAT_STRCD args->format == FORMAT_STR || args->format == FORMAT_STRCD
@ -694,6 +688,10 @@ bool parse_args(args_t *args, const char *const *options, int count) {
print_help(args->format); print_help(args->format);
return false; return false;
} }
if (args->flags & FLAG_PRINT_VERSION) {
printf("psxavenc " VERSION "\n");
return false;
}
if (args->format == FORMAT_INVALID || args->input_file == NULL || args->output_file == NULL) { if (args->format == FORMAT_INVALID || args->input_file == NULL || args->output_file == NULL) {
fprintf( fprintf(
stderr, stderr,

View File

@ -1,61 +0,0 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "common.h"
void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings) {
if (settings->format == FORMAT_STR2CD) {
memset(buffer, 0, PSX_CDROM_SECTOR_SIZE);
memset(buffer->sync + 1, 0xFF, 10);
buffer->header.mode = 0x02;
} else if (settings->format == FORMAT_STR2V) {
memset(buffer->data, 0, 2048);
} else {
memset(buffer->subheader, 0, PSX_CDROM_SECTOR_SIZE - 16);
}
buffer->subheader[0].file = settings->file_number;
buffer->subheader[0].channel = settings->channel_number & PSX_CDROM_SECTOR_XA_CHANNEL_MASK;
buffer->subheader[0].submode =
PSX_CDROM_SECTOR_XA_SUBMODE_DATA
| 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)
{
uint32_t edc = 0;
for (int i = 0x010; i < 0x818; i++) {
edc ^= 0xFF&(uint32_t)buffer[i];
for (int ibit = 0; ibit < 8; ibit++) {
edc = (edc>>1)^(0xD8018001*(edc&0x1));
}
}
buffer[0x818] = (uint8_t)(edc);
buffer[0x819] = (uint8_t)(edc >> 8);
buffer[0x81A] = (uint8_t)(edc >> 16);
buffer[0x81B] = (uint8_t)(edc >> 24);
// TODO: ECC
}

View File

@ -1,156 +0,0 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <assert.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/avdct.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libpsxav.h>
typedef enum {
FORMAT_XA,
FORMAT_XACD,
FORMAT_SPU,
FORMAT_SPUI,
FORMAT_VAG,
FORMAT_VAGI,
FORMAT_STR2,
FORMAT_STR2CD,
FORMAT_STR2V,
FORMAT_SBS2,
NUM_FORMATS
} psxavenc_format_t;
typedef struct {
int frame_index;
int frame_data_offset;
int frame_max_size;
int frame_block_base_overflow;
int frame_block_overflow_num;
int frame_block_overflow_den;
uint16_t bits_value;
int bits_left;
uint8_t *frame_output;
int bytes_used;
int blocks_used;
int uncomp_hwords_used;
int quant_scale;
int quant_scale_sum;
uint32_t *huffman_encoding_map;
int16_t *coeff_clamp_map;
int16_t *dct_block_lists[6];
AVDCT *dct_context;
} vid_encoder_state_t;
typedef struct {
int video_frame_dst_size;
int audio_stream_index;
int video_stream_index;
AVFormatContext* format;
AVStream* audio_stream;
AVStream* video_stream;
AVCodecContext* audio_codec_context;
AVCodecContext* video_codec_context;
struct SwrContext* resampler;
struct SwsContext* scaler;
AVFrame* frame;
int sample_count_mul;
double video_next_pts;
} av_decoder_state_t;
typedef struct {
bool quiet;
bool show_progress;
int format; // FORMAT_*
int channels;
int cd_speed; // 1 or 2
int frequency; // 18900 or 37800 Hz
int bits_per_sample; // 4 or 8
int file_number; // 00-FF
int channel_number; // 00-1F
int interleave;
int alignment;
bool loop;
int video_width;
int video_height;
int video_fps_num; // FPS numerator
int video_fps_den; // FPS denominator
bool ignore_aspect_ratio;
char *swresample_options;
char *swscale_options;
int16_t *audio_samples;
int audio_sample_count;
uint8_t *video_frames;
int video_frame_count;
av_decoder_state_t decoder_state_av;
vid_encoder_state_t state_vid;
bool end_of_input;
time_t start_time;
time_t last_progress_update;
} settings_t;
// cdrom.c
void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings);
void calculate_edc_data(uint8_t *buffer);
// decoding.c
bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bool use_video, bool audio_required, bool video_required);
bool poll_av_data(settings_t *settings);
bool ensure_av_data(settings_t *settings, int needed_audio_samples, int needed_video_frames);
void retire_av_data(settings_t *settings, int retired_audio_samples, int retired_video_frames);
void close_av_data(settings_t *settings);
// filefmt.c
void encode_file_spu(settings_t *settings, FILE *output);
void encode_file_spu_interleaved(settings_t *settings, FILE *output);
void encode_file_xa(settings_t *settings, FILE *output);
void encode_file_str(settings_t *settings, FILE *output);
void encode_file_sbs(settings_t *settings, FILE *output);
// mdec.c
bool init_encoder_state(settings_t *settings);
void destroy_encoder_state(settings_t *settings);
void encode_frame_bs(uint8_t *video_frame, settings_t *settings);
void encode_sector_str(uint8_t *video_frames, uint8_t *output, settings_t *settings);

View File

@ -22,30 +22,57 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#include "common.h" #include <assert.h>
#include <stdbool.h>
int decode_frame(AVCodecContext *codec, AVFrame *frame, int *frame_size, AVPacket *packet) { #include <stdio.h>
int ret; #include <stdlib.h>
#include <string.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/avdct.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include "args.h"
#include "decoding.h"
static int decode_frame(
AVCodecContext *codec,
AVFrame *frame,
int *frame_size,
AVPacket *packet
) {
if (packet != NULL) { if (packet != NULL) {
ret = avcodec_send_packet(codec, packet); if (avcodec_send_packet(codec, packet) != 0)
if (ret != 0) {
return 0; return 0;
}
} }
ret = avcodec_receive_frame(codec, frame); int ret = avcodec_receive_frame(codec, frame);
if (ret >= 0) { if (ret >= 0) {
*frame_size = ret; *frame_size = ret;
return 1; return 1;
} else if (ret == AVERROR(EAGAIN)) {
return 1;
} else { } else {
return ret == AVERROR(EAGAIN) ? 1 : 0; return 0;
} }
} }
bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bool use_video, bool audio_required, bool video_required) bool open_av_data(decoder_t *decoder, const args_t *args, int flags) {
{ decoder->audio_samples = NULL;
av_decoder_state_t* av = &(settings->decoder_state_av); decoder->audio_sample_count = 0;
decoder->video_frames = NULL;
decoder->video_frame_count = 0;
decoder->video_width = args->video_width;
decoder->video_height = args->video_height;
decoder->video_fps_num = args->str_fps_num;
decoder->video_fps_den = args->str_fps_den;
decoder->end_of_input = false;
decoder_state_t *av = &(decoder->state);
av->video_next_pts = 0.0; av->video_next_pts = 0.0;
av->frame = NULL; av->frame = NULL;
av->video_frame_dst_size = 0; av->video_frame_dst_size = 0;
@ -59,19 +86,17 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
av->resampler = NULL; av->resampler = NULL;
av->scaler = NULL; av->scaler = NULL;
if (settings->quiet) { if (args->flags & FLAG_QUIET)
av_log_set_level(AV_LOG_QUIET); av_log_set_level(AV_LOG_QUIET);
}
av->format = avformat_alloc_context(); av->format = avformat_alloc_context();
if (avformat_open_input(&(av->format), filename, NULL, NULL)) {
return false;
}
if (avformat_find_stream_info(av->format, NULL) < 0) {
return false;
}
if (use_audio) { if (avformat_open_input(&(av->format), args->input_file, NULL, NULL))
return false;
if (avformat_find_stream_info(av->format, NULL) < 0)
return false;
if (flags & DECODER_USE_AUDIO) {
for (int i = 0; i < av->format->nb_streams; i++) { for (int i = 0; i < av->format->nb_streams; i++) {
if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
if (av->audio_stream_index >= 0) { if (av->audio_stream_index >= 0) {
@ -81,13 +106,14 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
av->audio_stream_index = i; av->audio_stream_index = i;
} }
} }
if (audio_required && av->audio_stream_index == -1) {
if ((flags & DECODER_AUDIO_REQUIRED) && av->audio_stream_index == -1) {
fprintf(stderr, "Input file has no audio data\n"); fprintf(stderr, "Input file has no audio data\n");
return false; return false;
} }
} }
if (use_video) { if (flags & DECODER_USE_VIDEO) {
for (int i = 0; i < av->format->nb_streams; i++) { for (int i = 0; i < av->format->nb_streams; i++) {
if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (av->video_stream_index >= 0) { if (av->video_stream_index >= 0) {
@ -97,7 +123,8 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
av->video_stream_index = i; av->video_stream_index = i;
} }
} }
if (video_required && av->video_stream_index == -1) {
if ((flags & DECODER_VIDEO_REQUIRED) && av->video_stream_index == -1) {
fprintf(stderr, "Input file has no video data\n"); fprintf(stderr, "Input file has no video data\n");
return false; return false;
} }
@ -109,34 +136,39 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
if (av->audio_stream != NULL) { if (av->audio_stream != NULL) {
const AVCodec *codec = avcodec_find_decoder(av->audio_stream->codecpar->codec_id); const AVCodec *codec = avcodec_find_decoder(av->audio_stream->codecpar->codec_id);
av->audio_codec_context = avcodec_alloc_context3(codec); av->audio_codec_context = avcodec_alloc_context3(codec);
if (av->audio_codec_context == NULL) {
if (av->audio_codec_context == NULL)
return false; return false;
} if (avcodec_parameters_to_context(av->audio_codec_context, av->audio_stream->codecpar) < 0)
if (avcodec_parameters_to_context(av->audio_codec_context, av->audio_stream->codecpar) < 0) {
return false; return false;
} if (avcodec_open2(av->audio_codec_context, codec, NULL) < 0)
if (avcodec_open2(av->audio_codec_context, codec, NULL) < 0) {
return false; return false;
}
AVChannelLayout layout; AVChannelLayout layout;
layout.nb_channels = settings->channels; layout.nb_channels = args->audio_channels;
if (settings->channels <= 2) {
if (args->audio_channels == 1) {
layout.order = AV_CHANNEL_ORDER_NATIVE; layout.order = AV_CHANNEL_ORDER_NATIVE;
layout.u.mask = (settings->channels == 2) ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; layout.u.mask = AV_CH_LAYOUT_MONO;
} else if (args->audio_channels == 2) {
layout.order = AV_CHANNEL_ORDER_NATIVE;
layout.u.mask = AV_CH_LAYOUT_STEREO;
} else { } else {
layout.order = AV_CHANNEL_ORDER_UNSPEC; layout.order = AV_CHANNEL_ORDER_UNSPEC;
} }
if (!settings->quiet && settings->channels > av->audio_codec_context->ch_layout.nb_channels) {
fprintf(stderr, "Warning: input file has less than %d channels\n", settings->channels); if (!(args->flags & FLAG_QUIET)) {
if (args->audio_channels > av->audio_codec_context->ch_layout.nb_channels)
fprintf(stderr, "Warning: input file has less than %d channels\n", args->audio_channels);
} }
av->sample_count_mul = settings->channels; av->sample_count_mul = args->audio_channels;
if (swr_alloc_set_opts2( if (swr_alloc_set_opts2(
&av->resampler, &av->resampler,
&layout, &layout,
AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16,
settings->frequency, args->audio_frequency,
&av->audio_codec_context->ch_layout, &av->audio_codec_context->ch_layout,
av->audio_codec_context->sample_fmt, av->audio_codec_context->sample_fmt,
av->audio_codec_context->sample_rate, av->audio_codec_context->sample_rate,
@ -145,47 +177,43 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
) < 0) { ) < 0) {
return false; return false;
} }
if (settings->swresample_options) { if (args->swresample_options) {
if (av_opt_set_from_string(av->resampler, settings->swresample_options, NULL, "=", ":,") < 0) { if (av_opt_set_from_string(av->resampler, args->swresample_options, NULL, "=", ":,") < 0)
return false; return false;
}
} }
if (swr_init(av->resampler) < 0)
if (swr_init(av->resampler) < 0) {
return false; return false;
}
} }
if (av->video_stream != NULL) { if (av->video_stream != NULL) {
const AVCodec *codec = avcodec_find_decoder(av->video_stream->codecpar->codec_id); const AVCodec *codec = avcodec_find_decoder(av->video_stream->codecpar->codec_id);
av->video_codec_context = avcodec_alloc_context3(codec); av->video_codec_context = avcodec_alloc_context3(codec);
if(av->video_codec_context == NULL) {
if (av->video_codec_context == NULL)
return false; return false;
} if (avcodec_parameters_to_context(av->video_codec_context, av->video_stream->codecpar) < 0)
if (avcodec_parameters_to_context(av->video_codec_context, av->video_stream->codecpar) < 0) {
return false; return false;
} if (avcodec_open2(av->video_codec_context, codec, NULL) < 0)
if (avcodec_open2(av->video_codec_context, codec, NULL) < 0) {
return false; return false;
if (!(args->flags & FLAG_QUIET)) {
if (
decoder->video_width > av->video_codec_context->width ||
decoder->video_height > av->video_codec_context->height
)
fprintf(stderr, "Warning: input file has resolution lower than %dx%d\n", decoder->video_width, decoder->video_height);
} }
if (!settings->quiet && ( if (!(args->flags & FLAG_BS_IGNORE_ASPECT)) {
settings->video_width > av->video_codec_context->width ||
settings->video_height > av->video_codec_context->height
)) {
fprintf(stderr, "Warning: input file has resolution lower than %dx%d\n",
settings->video_width, settings->video_height
);
}
if (!settings->ignore_aspect_ratio) {
// Reduce the provided size so that it matches the input file's // Reduce the provided size so that it matches the input file's
// aspect ratio. // aspect ratio.
double src_ratio = (double)av->video_codec_context->width / (double)av->video_codec_context->height; double src_ratio = (double)av->video_codec_context->width / (double)av->video_codec_context->height;
double dst_ratio = (double)settings->video_width / (double)settings->video_height; double dst_ratio = (double)decoder->video_width / (double)decoder->video_height;
if (src_ratio < dst_ratio) { if (src_ratio < dst_ratio) {
settings->video_width = (int)((double)settings->video_height * src_ratio + 15.0) & ~15; decoder->video_width = (int)((double)decoder->video_height * src_ratio + 15.0) & ~15;
} else { } else {
settings->video_height = (int)((double)settings->video_width / src_ratio + 15.0) & ~15; decoder->video_height = (int)((double)decoder->video_width / src_ratio + 15.0) & ~15;
} }
} }
@ -193,17 +221,16 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
av->video_codec_context->width, av->video_codec_context->width,
av->video_codec_context->height, av->video_codec_context->height,
av->video_codec_context->pix_fmt, av->video_codec_context->pix_fmt,
settings->video_width, decoder->video_width,
settings->video_height, decoder->video_height,
AV_PIX_FMT_NV21, AV_PIX_FMT_NV21,
SWS_BICUBIC, SWS_BICUBIC,
NULL, NULL,
NULL, NULL,
NULL NULL
); );
if (av->scaler == NULL) { if (av->scaler == NULL)
return false; return false;
}
if (sws_setColorspaceDetails( if (sws_setColorspaceDetails(
av->scaler, av->scaler,
sws_getCoefficients(av->video_codec_context->colorspace), sws_getCoefficients(av->video_codec_context->colorspace),
@ -213,189 +240,211 @@ bool open_av_data(const char *filename, settings_t *settings, bool use_audio, bo
0, 0,
1 << 16, 1 << 16,
1 << 16 1 << 16
) < 0) { ) < 0)
return false; return false;
} if (args->swscale_options) {
if (settings->swscale_options) { if (av_opt_set_from_string(av->scaler, args->swscale_options, NULL, "=", ":,") < 0)
if (av_opt_set_from_string(av->scaler, settings->swscale_options, NULL, "=", ":,") < 0) {
return false; return false;
}
} }
av->video_frame_dst_size = 3*settings->video_width*settings->video_height/2; av->video_frame_dst_size = 3 * decoder->video_width * decoder->video_height / 2;
} }
av->frame = av_frame_alloc(); av->frame = av_frame_alloc();
if (av->frame == NULL) {
return false;
}
settings->audio_samples = NULL; if (av->frame == NULL)
settings->audio_sample_count = 0; return false;
settings->video_frames = NULL;
settings->video_frame_count = 0;
settings->end_of_input = false;
return true; return true;
} }
static void poll_av_packet_audio(settings_t *settings, AVPacket *packet) static void poll_av_packet_audio(decoder_t *decoder, AVPacket *packet) {
{ decoder_state_t *av = &(decoder->state);
av_decoder_state_t* av = &(settings->decoder_state_av);
int frame_size, frame_sample_count; int frame_size, frame_sample_count;
uint8_t *buffer[1]; uint8_t *buffer[1];
if (decode_frame(av->audio_codec_context, av->frame, &frame_size, packet)) { if (decode_frame(av->audio_codec_context, av->frame, &frame_size, packet)) {
size_t buffer_size = sizeof(int16_t) * av->sample_count_mul * swr_get_out_samples(av->resampler, av->frame->nb_samples); size_t buffer_size = sizeof(int16_t) * av->sample_count_mul * swr_get_out_samples(av->resampler, av->frame->nb_samples);
buffer[0] = malloc(buffer_size); buffer[0] = malloc(buffer_size);
memset(buffer[0], 0, buffer_size); memset(buffer[0], 0, buffer_size);
frame_sample_count = swr_convert(av->resampler, buffer, av->frame->nb_samples, (const uint8_t**)av->frame->data, av->frame->nb_samples);
settings->audio_samples = realloc(settings->audio_samples, (settings->audio_sample_count + ((frame_sample_count + 4032) * av->sample_count_mul)) * sizeof(int16_t)); frame_sample_count = swr_convert(
memmove(&(settings->audio_samples[settings->audio_sample_count]), buffer[0], sizeof(int16_t) * frame_sample_count * av->sample_count_mul); av->resampler,
settings->audio_sample_count += frame_sample_count * av->sample_count_mul; buffer,
av->frame->nb_samples,
(const uint8_t**)av->frame->data,
av->frame->nb_samples
);
decoder->audio_samples = realloc(
decoder->audio_samples,
(decoder->audio_sample_count + ((frame_sample_count + 4032) * av->sample_count_mul)) * sizeof(int16_t)
);
memmove(
&(decoder->audio_samples[decoder->audio_sample_count]),
buffer[0],
sizeof(int16_t) * frame_sample_count * av->sample_count_mul
);
decoder->audio_sample_count += frame_sample_count * av->sample_count_mul;
free(buffer[0]); free(buffer[0]);
} }
} }
static void poll_av_packet_video(settings_t *settings, AVPacket *packet) static void poll_av_packet_video(decoder_t *decoder, AVPacket *packet) {
{ decoder_state_t *av = &(decoder->state);
av_decoder_state_t* av = &(settings->decoder_state_av);
int frame_size; int frame_size;
double pts_step = ((double)1.0*(double)settings->video_fps_den)/(double)settings->video_fps_num; double pts_step = (double)decoder->video_fps_den / (double)decoder->video_fps_num;
int plane_size = settings->video_width*settings->video_height; int plane_size = decoder->video_width * decoder->video_height;
int dst_strides[2] = { int dst_strides[2] = {
settings->video_width, settings->video_width decoder->video_width, decoder->video_width
}; };
if (decode_frame(av->video_codec_context, av->frame, &frame_size, packet)) { if (decode_frame(av->video_codec_context, av->frame, &frame_size, packet)) {
if (!av->frame->width || !av->frame->height || !av->frame->data[0]) { if (!av->frame->width || !av->frame->height || !av->frame->data[0])
return; return;
}
// Some files seem to have timestamps starting from a negative value // Some files seem to have timestamps starting from a negative value
// (but otherwise valid) for whatever reason. // (but otherwise valid) for whatever reason.
double pts = (((double)av->frame->pts)*(double)av->video_stream->time_base.num)/av->video_stream->time_base.den; double pts =
//if (pts < 0.0) { ((double)av->frame->pts * (double)av->video_stream->time_base.num)
//return; / av->video_stream->time_base.den;
//} #if 0
if (settings->video_frame_count >= 1 && pts < av->video_next_pts) { if (pts < 0.0)
return; return;
} #endif
if ((settings->video_frame_count) < 1) { if (decoder->video_frame_count >= 1 && pts < av->video_next_pts)
return;
if (decoder->video_frame_count < 1)
av->video_next_pts = pts; av->video_next_pts = pts;
} else { else
av->video_next_pts += pts_step; av->video_next_pts += pts_step;
}
//fprintf(stderr, "%d %f %f %f\n", (settings->video_frame_count), pts, av->video_next_pts, pts_step); //fprintf(stderr, "%d %f %f %f\n", decoder->video_frame_count, pts, av->video_next_pts, pts_step);
// Insert duplicate frames if the frame rate of the input stream is // Insert duplicate frames if the frame rate of the input stream is
// lower than the target frame rate. // lower than the target frame rate.
int dupe_frames = (int) ceil((pts - av->video_next_pts) / pts_step); int dupe_frames = (int) ceil((pts - av->video_next_pts) / pts_step);
if (dupe_frames < 0) dupe_frames = 0; if (dupe_frames < 0) dupe_frames = 0;
settings->video_frames = realloc( decoder->video_frames = realloc(
settings->video_frames, decoder->video_frames,
(settings->video_frame_count + dupe_frames + 1) * av->video_frame_dst_size (decoder->video_frame_count + dupe_frames + 1) * av->video_frame_dst_size
); );
for (; dupe_frames; dupe_frames--) { for (; dupe_frames; dupe_frames--) {
memcpy( memcpy(
(settings->video_frames) + av->video_frame_dst_size*(settings->video_frame_count), (decoder->video_frames) + av->video_frame_dst_size * decoder->video_frame_count,
(settings->video_frames) + av->video_frame_dst_size*(settings->video_frame_count-1), (decoder->video_frames) + av->video_frame_dst_size * (decoder->video_frame_count - 1),
av->video_frame_dst_size av->video_frame_dst_size
); );
settings->video_frame_count += 1; decoder->video_frame_count += 1;
av->video_next_pts += pts_step; av->video_next_pts += pts_step;
} }
uint8_t *dst_frame = (settings->video_frames) + av->video_frame_dst_size*(settings->video_frame_count); uint8_t *dst_frame = decoder->video_frames + av->video_frame_dst_size * decoder->video_frame_count;
uint8_t *dst_pointers[2] = { uint8_t *dst_pointers[2] = {
dst_frame, dst_frame + plane_size dst_frame, dst_frame + plane_size
}; };
sws_scale(av->scaler, (const uint8_t *const *) av->frame->data, av->frame->linesize, 0, av->frame->height, dst_pointers, dst_strides); sws_scale(
av->scaler,
(const uint8_t *const *) av->frame->data,
av->frame->linesize,
0,
av->frame->height,
dst_pointers,
dst_strides
);
settings->video_frame_count += 1; decoder->video_frame_count += 1;
} }
} }
bool poll_av_data(settings_t *settings) bool poll_av_data(decoder_t *decoder) {
{ decoder_state_t *av = &(decoder->state);
av_decoder_state_t* av = &(settings->decoder_state_av);
if (decoder->end_of_input)
return false;
AVPacket packet; AVPacket packet;
if (settings->end_of_input) {
return false;
}
if (av_read_frame(av->format, &packet) >= 0) { if (av_read_frame(av->format, &packet) >= 0) {
if (packet.stream_index == av->audio_stream_index) { if (packet.stream_index == av->audio_stream_index)
poll_av_packet_audio(settings, &packet); poll_av_packet_audio(decoder, &packet);
} else if (packet.stream_index == av->video_stream_index) { else if (packet.stream_index == av->video_stream_index)
poll_av_packet_video(settings, &packet); poll_av_packet_video(decoder, &packet);
}
av_packet_unref(&packet); av_packet_unref(&packet);
return true; return true;
} else { } else {
// out is always padded out with 4032 "0" samples, this makes calculations elsewhere easier // out is always padded out with 4032 "0" samples, this makes calculations elsewhere easier
if (av->audio_stream) { if (av->audio_stream)
memset((settings->audio_samples) + (settings->audio_sample_count), 0, 4032 * av->sample_count_mul * sizeof(int16_t)); memset(
} decoder->audio_samples + decoder->audio_sample_count,
0,
4032 * av->sample_count_mul * sizeof(int16_t)
);
settings->end_of_input = true; decoder->end_of_input = true;
return false; return false;
} }
} }
bool ensure_av_data(settings_t *settings, int needed_audio_samples, int needed_video_frames) bool ensure_av_data(decoder_t *decoder, int needed_audio_samples, int needed_video_frames) {
{ // HACK: in order to update decoder->end_of_input as soon as all data has
// HACK: in order to update settings->end_of_input as soon as all data has
// been read from the input file, this loop waits for more data than // been read from the input file, this loop waits for more data than
// strictly needed. // strictly needed.
//while (settings->audio_sample_count < needed_audio_samples || settings->video_frame_count < needed_video_frames) { #if 0
while (decoder->audio_sample_count < needed_audio_samples || decoder->video_frame_count < needed_video_frames) {
#else
while ( while (
(needed_audio_samples && settings->audio_sample_count <= needed_audio_samples) || (needed_audio_samples && decoder->audio_sample_count <= needed_audio_samples) ||
(needed_video_frames && settings->video_frame_count <= needed_video_frames) (needed_video_frames && decoder->video_frame_count <= needed_video_frames)
) { ) {
//fprintf(stderr, "ensure %d -> %d, %d -> %d\n", settings->audio_sample_count, needed_audio_samples, settings->video_frame_count, needed_video_frames); #endif
if (!poll_av_data(settings)) { //fprintf(stderr, "ensure %d -> %d, %d -> %d\n", decoder->audio_sample_count, needed_audio_samples, decoder->video_frame_count, needed_video_frames);
if (!poll_av_data(decoder)) {
// Keep returning true even if the end of the input file has been // Keep returning true even if the end of the input file has been
// reached, if the buffer is not yet completely empty. // reached, if the buffer is not yet completely empty.
return (settings->audio_sample_count || !needed_audio_samples) return
&& (settings->video_frame_count || !needed_video_frames); (decoder->audio_sample_count || !needed_audio_samples) &&
(decoder->video_frame_count || !needed_video_frames);
} }
} }
//fprintf(stderr, "ensure %d -> %d, %d -> %d\n", settings->audio_sample_count, needed_audio_samples, settings->video_frame_count, needed_video_frames); //fprintf(stderr, "ensure %d -> %d, %d -> %d\n", decoder->audio_sample_count, needed_audio_samples, decoder->video_frame_count, needed_video_frames);
return true; return true;
} }
void retire_av_data(settings_t *settings, int retired_audio_samples, int retired_video_frames) void retire_av_data(decoder_t *decoder, int retired_audio_samples, int retired_video_frames) {
{ //fprintf(stderr, "retire %d -> %d, %d -> %d\n", decoder->audio_sample_count, retired_audio_samples, decoder->video_frame_count, retired_video_frames);
av_decoder_state_t* av = &(settings->decoder_state_av); assert(retired_audio_samples <= decoder->audio_sample_count);
assert(retired_video_frames <= decoder->video_frame_count);
//fprintf(stderr, "retire %d -> %d, %d -> %d\n", settings->audio_sample_count, retired_audio_samples, settings->video_frame_count, retired_video_frames);
assert(retired_audio_samples <= settings->audio_sample_count);
assert(retired_video_frames <= settings->video_frame_count);
int sample_size = sizeof(int16_t); int sample_size = sizeof(int16_t);
if (settings->audio_sample_count > retired_audio_samples) { int frame_size = decoder->state.video_frame_dst_size;
memmove(settings->audio_samples, settings->audio_samples + retired_audio_samples, (settings->audio_sample_count - retired_audio_samples)*sample_size);
}
settings->audio_sample_count -= retired_audio_samples;
int frame_size = av->video_frame_dst_size; if (decoder->audio_sample_count > retired_audio_samples)
if (settings->video_frame_count > retired_video_frames) { memmove(
memmove(settings->video_frames, settings->video_frames + retired_video_frames*frame_size, (settings->video_frame_count - retired_video_frames)*frame_size); decoder->audio_samples,
} decoder->audio_samples + retired_audio_samples,
settings->video_frame_count -= retired_video_frames; (decoder->audio_sample_count - retired_audio_samples) * sample_size
);
if (decoder->video_frame_count > retired_video_frames)
memmove(
decoder->video_frames,
decoder->video_frames + retired_video_frames * frame_size,
(decoder->video_frame_count - retired_video_frames) * frame_size
);
decoder->audio_sample_count -= retired_audio_samples;
decoder->video_frame_count -= retired_video_frames;
} }
void close_av_data(settings_t *settings) void close_av_data(decoder_t *decoder) {
{ decoder_state_t *av = &(decoder->state);
av_decoder_state_t* av = &(settings->decoder_state_av);
av_frame_free(&(av->frame)); av_frame_free(&(av->frame));
swr_free(&(av->resampler)); swr_free(&(av->resampler));
@ -404,12 +453,12 @@ void close_av_data(settings_t *settings)
avcodec_free_context(&(av->audio_codec_context)); avcodec_free_context(&(av->audio_codec_context));
avformat_free_context(av->format); avformat_free_context(av->format);
if(settings->audio_samples != NULL) { if(decoder->audio_samples != NULL) {
free(settings->audio_samples); free(decoder->audio_samples);
settings->audio_samples = NULL; decoder->audio_samples = NULL;
} }
if(settings->video_frames != NULL) { if(decoder->video_frames != NULL) {
free(settings->video_frames); free(decoder->video_frames);
settings->video_frames = NULL; decoder->video_frames = NULL;
} }
} }

80
psxavenc/decoding.h Normal file
View File

@ -0,0 +1,80 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023, 2025 spicyjpeg
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stdbool.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/avdct.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include "args.h"
typedef struct {
int video_frame_dst_size;
int audio_stream_index;
int video_stream_index;
AVFormatContext* format;
AVStream* audio_stream;
AVStream* video_stream;
AVCodecContext* audio_codec_context;
AVCodecContext* video_codec_context;
struct SwrContext* resampler;
struct SwsContext* scaler;
AVFrame* frame;
int sample_count_mul;
double video_next_pts;
} decoder_state_t;
typedef struct {
int16_t *audio_samples;
int audio_sample_count;
uint8_t *video_frames;
int video_frame_count;
int video_width;
int video_height;
int video_fps_num;
int video_fps_den;
bool end_of_input;
decoder_state_t state;
} decoder_t;
enum {
DECODER_USE_AUDIO = 1 << 0,
DECODER_USE_VIDEO = 1 << 1,
DECODER_AUDIO_REQUIRED = 1 << 2,
DECODER_VIDEO_REQUIRED = 1 << 3
};
bool open_av_data(decoder_t *decoder, const args_t *args, int flags);
bool poll_av_data(decoder_t *decoder);
bool ensure_av_data(decoder_t *decoder, int needed_audio_samples, int needed_video_frames);
void retire_av_data(decoder_t *decoder, int retired_audio_samples, int retired_video_frames);
void close_av_data(decoder_t *decoder);

View File

@ -3,7 +3,7 @@ psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023 spicyjpeg Copyright (c) 2023, 2025 spicyjpeg
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -22,48 +22,77 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#include "common.h" #include <stdint.h>
#include "libpsxav.h" #include <stdio.h>
#include <string.h>
#include <time.h>
#include <libpsxav.h>
#include "args.h"
#include "decoding.h"
#include "mdec.h"
static time_t get_elapsed_time(settings_t *settings) { static time_t start_time = 0;
if (!settings->show_progress) { static time_t last_progress_update = 0;
return 0;
static time_t get_elapsed_time(void) {
time_t t;
if (start_time > 0) {
t = time(NULL) - start_time;
} else {
t = 0;
start_time = time(NULL);
} }
time_t t = time(NULL) - settings->start_time;
if (t <= settings->last_progress_update) { if (t <= last_progress_update)
return 0; return 0;
}
settings->last_progress_update = t; last_progress_update = t;
return t; return t;
} }
static psx_audio_xa_settings_t settings_to_libpsxav_xa_audio(settings_t *settings) { static psx_audio_xa_settings_t args_to_libpsxav_xa_audio(const args_t *args) {
psx_audio_xa_settings_t new_settings; psx_audio_xa_settings_t settings;
new_settings.bits_per_sample = settings->bits_per_sample;
new_settings.frequency = settings->frequency;
new_settings.stereo = settings->channels == 2;
new_settings.file_number = settings->file_number;
new_settings.channel_number = settings->channel_number;
switch (settings->format) { settings.bits_per_sample = args->audio_bit_depth;
case FORMAT_XA: settings.frequency = args->audio_frequency;
case FORMAT_STR2: settings.stereo = (args->audio_channels == 2);
new_settings.format = PSX_AUDIO_XA_FORMAT_XA; settings.file_number = args->audio_xa_file;
break; settings.channel_number = args->audio_xa_channel;
default:
new_settings.format = PSX_AUDIO_XA_FORMAT_XACD;
break;
}
return new_settings; if (args->format == FORMAT_XACD || args->format == FORMAT_STRCD)
settings.format = PSX_AUDIO_XA_FORMAT_XACD;
else
settings.format = PSX_AUDIO_XA_FORMAT_XA;
return settings;
}; };
void write_vag_header(int size_per_channel, uint8_t *header, settings_t *settings) { static void init_sector_buffer_video(const args_t *args, psx_cdrom_sector_mode2_t *sector, int lba) {
psx_cdrom_init_sector((psx_cdrom_sector_t *)sector, lba, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1);
sector->subheader[0].file = args->audio_xa_file;
sector->subheader[0].channel = args->audio_xa_channel & PSX_CDROM_SECTOR_XA_CHANNEL_MASK;
sector->subheader[0].submode = PSX_CDROM_SECTOR_XA_SUBMODE_DATA | PSX_CDROM_SECTOR_XA_SUBMODE_RT;
sector->subheader[0].coding = 0;
memcpy(sector->subheader + 1, sector->subheader, sizeof(psx_cdrom_sector_xa_subheader_t));
}
#define VAG_HEADER_SIZE 0x30
static void write_vag_header(const args_t *args, int size_per_channel, uint8_t *header) {
memset(header, 0, VAG_HEADER_SIZE);
// Magic // Magic
header[0x00] = 'V'; header[0x00] = 'V';
header[0x01] = 'A'; header[0x01] = 'A';
header[0x02] = 'G'; header[0x02] = 'G';
header[0x03] = settings->interleave ? 'i' : 'p';
if (args->format == FORMAT_VAGI)
header[0x03] = 'i';
else
header[0x03] = 'p';
// Version (big-endian) // Version (big-endian)
header[0x04] = 0x00; header[0x04] = 0x00;
@ -72,150 +101,228 @@ void write_vag_header(int size_per_channel, uint8_t *header, settings_t *setting
header[0x07] = 0x20; header[0x07] = 0x20;
// Interleave (little-endian) // Interleave (little-endian)
header[0x08] = (uint8_t)settings->interleave; if (args->format == FORMAT_VAGI) {
header[0x09] = (uint8_t)(settings->interleave>>8); header[0x08] = (uint8_t)args->audio_interleave;
header[0x0a] = (uint8_t)(settings->interleave>>16); header[0x09] = (uint8_t)(args->audio_interleave >> 8);
header[0x0b] = (uint8_t)(settings->interleave>>24); header[0x0a] = (uint8_t)(args->audio_interleave >> 16);
header[0x0b] = (uint8_t)(args->audio_interleave >> 24);
}
// Length of data for each channel (big-endian) // Length of data for each channel (big-endian)
header[0x0c] = (uint8_t)(size_per_channel>>24); header[0x0c] = (uint8_t)(size_per_channel >> 24);
header[0x0d] = (uint8_t)(size_per_channel>>16); header[0x0d] = (uint8_t)(size_per_channel >> 16);
header[0x0e] = (uint8_t)(size_per_channel>>8); header[0x0e] = (uint8_t)(size_per_channel >> 8);
header[0x0f] = (uint8_t)size_per_channel; header[0x0f] = (uint8_t)size_per_channel;
// Sample rate (big-endian) // Sample rate (big-endian)
header[0x10] = (uint8_t)(settings->frequency>>24); header[0x10] = (uint8_t)(args->audio_frequency >> 24);
header[0x11] = (uint8_t)(settings->frequency>>16); header[0x11] = (uint8_t)(args->audio_frequency >> 16);
header[0x12] = (uint8_t)(settings->frequency>>8); header[0x12] = (uint8_t)(args->audio_frequency >> 8);
header[0x13] = (uint8_t)settings->frequency; header[0x13] = (uint8_t)args->audio_frequency;
// Number of channels (little-endian) // Number of channels (little-endian)
header[0x1e] = (uint8_t)settings->channels; header[0x1e] = (uint8_t)args->audio_channels;
header[0x1f] = 0x00; header[0x1f] = 0x00;
// Filename // Filename
//strncpy(header + 0x20, "psxavenc", 16); int name_offset = strlen(args->output_file);
memset(header + 0x20, 0, 16); while (
name_offset > 0 &&
args->output_file[name_offset - 1] != '/' &&
args->output_file[name_offset - 1] != '\\'
)
name_offset--;
strncpy((char*)(header + 0x20), &args->output_file[name_offset], 16);
} }
void encode_file_spu(settings_t *settings, FILE *output) { void encode_file_xa(args_t *args, decoder_t *decoder, FILE *output) {
psx_audio_encoder_channel_state_t audio_state; psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
psx_audio_encoder_state_t audio_state;
memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t));
for (int j = 0; ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, 0); j++) {
int samples_length = decoder->audio_sample_count / args->audio_channels;
if (samples_length > audio_samples_per_sector)
samples_length = audio_samples_per_sector;
uint8_t buffer[PSX_CDROM_SECTOR_SIZE];
int length = psx_audio_xa_encode(
xa_settings,
&audio_state,
decoder->audio_samples,
samples_length,
buffer
);
if (decoder->end_of_input)
psx_audio_xa_encode_finalize(xa_settings, buffer, length);
if (args->format == FORMAT_XACD) {
int t = j + 75*2;
// Put the time in
buffer[0x00C] = ((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);
}
retire_av_data(decoder, samples_length * args->audio_channels, 0);
fwrite(buffer, length, 1, output);
time_t t = get_elapsed_time();
if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rLBA: %6d | Encoding speed: %5.2fx",
j,
(double)(j * audio_samples_per_sector) / (double)(args->audio_frequency * t)
);
}
}
}
void encode_file_spu(args_t *args, decoder_t *decoder, FILE *output) {
psx_audio_encoder_channel_state_t audio_state;
memset(&audio_state, 0, sizeof(psx_audio_encoder_channel_state_t));
int audio_samples_per_block = psx_audio_spu_get_samples_per_block(); int audio_samples_per_block = psx_audio_spu_get_samples_per_block();
int block_size = psx_audio_spu_get_buffer_size_per_block(); int block_size = psx_audio_spu_get_buffer_size_per_block();
uint8_t buffer[16];
int block_count; int block_count;
memset(&audio_state, 0, sizeof(psx_audio_encoder_channel_state_t));
// The header must be written after the data as we don't yet know the // The header must be written after the data as we don't yet know the
// number of audio samples. // number of audio samples.
if (settings->format == FORMAT_VAG) { if (args->format == FORMAT_VAG)
fseek(output, 48, SEEK_SET); fseek(output, VAG_HEADER_SIZE, SEEK_SET);
}
for (block_count = 0; ensure_av_data(settings, audio_samples_per_block, 0); block_count++) { for (block_count = 0; ensure_av_data(decoder, audio_samples_per_block, 0); block_count++) {
int samples_length = settings->audio_sample_count; int samples_length = decoder->audio_sample_count;
if (samples_length > audio_samples_per_block) samples_length = audio_samples_per_block;
int length = psx_audio_spu_encode(&audio_state, settings->audio_samples, samples_length, 1, buffer); if (samples_length > audio_samples_per_block)
if (!block_count) { samples_length = audio_samples_per_block;
// This flag is not required as the SPU already resets the loop
// address when starting playback of a sample. uint8_t buffer[16];
//buffer[1] |= PSX_AUDIO_SPU_LOOP_START; int length = psx_audio_spu_encode(
} &audio_state,
if (settings->end_of_input) { decoder->audio_samples,
buffer[1] |= settings->loop ? PSX_AUDIO_SPU_LOOP_REPEAT : PSX_AUDIO_SPU_LOOP_END; samples_length,
1,
buffer
);
// TODO: implement proper loop flag support
if (false)
buffer[1] |= PSX_AUDIO_SPU_LOOP_START;
if (decoder->end_of_input) {
if (args->flags & FLAG_SPU_LOOP_END)
buffer[1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
else
buffer[1] |= PSX_AUDIO_SPU_LOOP_END;
} }
retire_av_data(settings, samples_length, 0); retire_av_data(decoder, samples_length, 0);
fwrite(buffer, length, 1, output); fwrite(buffer, length, 1, output);
time_t t = get_elapsed_time(settings); time_t t = get_elapsed_time();
if (t) {
fprintf(stderr, "\rBlock: %6d | Encoding speed: %5.2fx", if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rBlock: %6d | Encoding speed: %5.2fx",
block_count, block_count,
(double)(block_count*audio_samples_per_block) / (double)(settings->frequency*t) (double)(block_count * audio_samples_per_block) / (double)(args->audio_frequency * t)
); );
} }
} }
int padding_size = (block_count*block_size) % settings->alignment; int overflow = (block_count * block_size) % args->alignment;
if (padding_size) {
padding_size = settings->alignment - padding_size; if (overflow) {
uint8_t *padding = malloc(padding_size); for (int i = 0; i < (args->alignment - overflow); i++)
memset(padding, 0, padding_size); fputc(0, output);
fwrite(padding, padding_size, 1, output); }
free(padding); if (args->format == FORMAT_VAG) {
} uint8_t header[VAG_HEADER_SIZE];
write_vag_header(args, block_count * block_size, header);
if (settings->format == FORMAT_VAG) {
uint8_t header[48];
memset(header, 0, 48);
write_vag_header(block_count*block_size, header, settings);
fseek(output, 0, SEEK_SET); fseek(output, 0, SEEK_SET);
fwrite(header, 48, 1, output); fwrite(header, VAG_HEADER_SIZE, 1, output);
} }
} }
void encode_file_spu_interleaved(settings_t *settings, FILE *output) { void encode_file_spui(args_t *args, decoder_t *decoder, FILE *output) {
int audio_state_size = sizeof(psx_audio_encoder_channel_state_t) * settings->channels; int audio_state_size = sizeof(psx_audio_encoder_channel_state_t) * args->audio_channels;
// NOTE: since the interleaved .vag format is not standardized, some tools // NOTE: since the interleaved .vag format is not standardized, some tools
// (such as vgmstream) will not properly play files with interleave < 2048, // (such as vgmstream) will not properly play files with interleave < 2048,
// alignment != 2048 or channels != 2. // alignment != 2048 or channels != 2.
int buffer_size = settings->interleave + settings->alignment - 1; int buffer_size = args->audio_interleave + args->alignment - 1;
buffer_size -= buffer_size % settings->alignment; buffer_size -= buffer_size % args->alignment;
int header_size = 48 + settings->alignment - 1;
header_size -= header_size % settings->alignment; int header_size = VAG_HEADER_SIZE + args->alignment - 1;
header_size -= header_size % args->alignment;
int audio_samples_per_block = psx_audio_spu_get_samples_per_block();
int block_size = psx_audio_spu_get_buffer_size_per_block();
int audio_samples_per_chunk = args->audio_interleave / block_size * audio_samples_per_block;
int chunk_count;
if (args->format == FORMAT_VAGI)
fseek(output, header_size, SEEK_SET);
psx_audio_encoder_channel_state_t *audio_state = malloc(audio_state_size); psx_audio_encoder_channel_state_t *audio_state = malloc(audio_state_size);
uint8_t *buffer = malloc(buffer_size); uint8_t *buffer = malloc(buffer_size);
int audio_samples_per_block = psx_audio_spu_get_samples_per_block();
int block_size = psx_audio_spu_get_buffer_size_per_block();
int audio_samples_per_chunk = settings->interleave / block_size * audio_samples_per_block;
int chunk_count;
memset(audio_state, 0, audio_state_size); memset(audio_state, 0, audio_state_size);
if (settings->format == FORMAT_VAGI) { for (chunk_count = 0; ensure_av_data(decoder, audio_samples_per_chunk * args->audio_channels, 0); chunk_count++) {
fseek(output, header_size, SEEK_SET); int samples_length = decoder->audio_sample_count / args->audio_channels;
}
for (chunk_count = 0; ensure_av_data(settings, audio_samples_per_chunk*settings->channels, 0); chunk_count++) {
int samples_length = settings->audio_sample_count / settings->channels;
if (samples_length > audio_samples_per_chunk) samples_length = audio_samples_per_chunk; if (samples_length > audio_samples_per_chunk) samples_length = audio_samples_per_chunk;
for (int ch = 0; ch < settings->channels; ch++) { for (int ch = 0; ch < args->audio_channels; ch++) {
memset(buffer, 0, buffer_size); memset(buffer, 0, buffer_size);
int length = psx_audio_spu_encode(audio_state + ch, settings->audio_samples + ch, samples_length, settings->channels, buffer); int length = psx_audio_spu_encode(
audio_state + ch,
decoder->audio_samples + ch,
samples_length,
args->audio_channels,
buffer
);
if (length) { if (length) {
//buffer[1] |= PSX_AUDIO_SPU_LOOP_START; // TODO: implement proper loop flag support
if (settings->loop) { if (args->flags & FLAG_SPU_LOOP_END)
buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_REPEAT; buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
} else if (decoder->end_of_input)
if (settings->end_of_input) {
buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_END; buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_END;
}
} }
fwrite(buffer, buffer_size, 1, output); fwrite(buffer, buffer_size, 1, output);
time_t t = get_elapsed_time(settings); time_t t = get_elapsed_time();
if (t) {
fprintf(stderr, "\rChunk: %6d | Encoding speed: %5.2fx", if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rChunk: %6d | Encoding speed: %5.2fx",
chunk_count, chunk_count,
(double)(chunk_count*audio_samples_per_chunk) / (double)(settings->frequency*t) (double)(chunk_count * audio_samples_per_chunk) / (double)(args->audio_frequency * t)
); );
} }
} }
retire_av_data(settings, samples_length*settings->channels, 0); retire_av_data(decoder, samples_length * args->audio_channels, 0);
} }
if (settings->format == FORMAT_VAGI) { if (args->format == FORMAT_VAGI) {
uint8_t *header = malloc(header_size); uint8_t *header = malloc(header_size);
memset(header, 0, header_size); memset(header, 0, header_size);
write_vag_header(chunk_count*settings->interleave, header, settings); write_vag_header(args, chunk_count * args->audio_interleave, header);
fseek(output, 0, SEEK_SET); fseek(output, 0, SEEK_SET);
fwrite(header, header_size, 1, output); fwrite(header, header_size, 1, output);
free(header); free(header);
@ -225,52 +332,14 @@ void encode_file_spu_interleaved(settings_t *settings, FILE *output) {
free(buffer); free(buffer);
} }
void encode_file_xa(settings_t *settings, FILE *output) { void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
psx_audio_xa_settings_t xa_settings = settings_to_libpsxav_xa_audio(settings); psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
psx_audio_encoder_state_t audio_state;
int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
uint8_t buffer[PSX_CDROM_SECTOR_SIZE];
memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t));
for (int j = 0; ensure_av_data(settings, audio_samples_per_sector*settings->channels, 0); j++) {
int samples_length = settings->audio_sample_count / settings->channels;
if (samples_length > audio_samples_per_sector) samples_length = audio_samples_per_sector;
int length = psx_audio_xa_encode(xa_settings, &audio_state, settings->audio_samples, samples_length, buffer);
if (settings->end_of_input) {
psx_audio_xa_encode_finalize(xa_settings, buffer, length);
}
if (settings->format == FORMAT_XACD) {
int t = j + 75*2;
// Put the time in
buffer[0x00C] = ((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);
}
retire_av_data(settings, samples_length*settings->channels, 0);
fwrite(buffer, length, 1, output);
time_t t = get_elapsed_time(settings);
if (t) {
fprintf(stderr, "\rLBA: %6d | Encoding speed: %5.2fx",
j,
(double)(j*audio_samples_per_sector) / (double)(settings->frequency*t)
);
}
}
}
void encode_file_str(settings_t *settings, FILE *output) {
psx_audio_xa_settings_t xa_settings = settings_to_libpsxav_xa_audio(settings);
psx_audio_encoder_state_t audio_state;
int audio_samples_per_sector; int audio_samples_per_sector;
uint8_t buffer[PSX_CDROM_SECTOR_SIZE]; uint8_t buffer[PSX_CDROM_SECTOR_SIZE];
int offset, sector_size; int offset, sector_size;
if (settings->format == FORMAT_STR2V) {
if (args->format == FORMAT_STRV) {
sector_size = 2048; sector_size = 2048;
offset = 0x18; offset = 0x18;
} else { } else {
@ -280,16 +349,21 @@ void encode_file_str(settings_t *settings, FILE *output) {
int interleave; int interleave;
int video_sectors_per_block; int video_sectors_per_block;
if (settings->decoder_state_av.audio_stream) { if (decoder->state.audio_stream) {
// 1/N audio, (N-1)/N video // 1/N audio, (N-1)/N video
audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings); audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
interleave = psx_audio_xa_get_sector_interleave(xa_settings) * settings->cd_speed; interleave = psx_audio_xa_get_sector_interleave(xa_settings) * args->str_cd_speed;
video_sectors_per_block = interleave - 1; video_sectors_per_block = interleave - 1;
if (!settings->quiet) { if (!(args->flags & FLAG_QUIET))
fprintf(stderr, "Interleave: %d/%d audio, %d/%d video\n", fprintf(
interleave - video_sectors_per_block, interleave, video_sectors_per_block, interleave); stderr,
} "Interleave: %d/%d audio, %d/%d video\n",
interleave - video_sectors_per_block,
interleave,
video_sectors_per_block,
interleave
);
} else { } else {
// 0/1 audio, 1/1 video // 0/1 audio, 1/1 video
audio_samples_per_sector = 0; audio_samples_per_sector = 0;
@ -297,54 +371,65 @@ void encode_file_str(settings_t *settings, FILE *output) {
video_sectors_per_block = 1; video_sectors_per_block = 1;
} }
psx_audio_encoder_state_t audio_state;
memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t)); memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t));
// e.g. 15fps = (150*7/8/15) = 8.75 blocks per frame mdec_encoder_t encoder;
settings->state_vid.frame_block_base_overflow = (75*settings->cd_speed) * video_sectors_per_block * settings->video_fps_den; init_mdec_encoder(&encoder, args->video_width, args->video_height);
settings->state_vid.frame_block_overflow_den = interleave * settings->video_fps_num;
double frame_size = (double)settings->state_vid.frame_block_base_overflow / (double)settings->state_vid.frame_block_overflow_den;
if (!settings->quiet) {
fprintf(stderr, "Frame size: %.2f sectors\n", frame_size);
}
init_encoder_state(settings); // e.g. 15fps = (150*7/8/15) = 8.75 blocks per frame
settings->state_vid.frame_output = malloc(2016 * (int)ceil(frame_size)); encoder.state.frame_block_base_overflow = (75 * args->str_cd_speed) * video_sectors_per_block * args->str_fps_den;
settings->state_vid.frame_index = 0; encoder.state.frame_block_overflow_den = interleave * args->str_fps_num;
settings->state_vid.frame_data_offset = 0; double frame_size = (double)encoder.state.frame_block_base_overflow / (double)encoder.state.frame_block_overflow_den;
settings->state_vid.frame_max_size = 0;
settings->state_vid.frame_block_overflow_num = 0; if (!(args->flags & FLAG_QUIET))
settings->state_vid.quant_scale_sum = 0; fprintf(stderr, "Frame size: %.2f sectors\n", frame_size);
encoder.state.frame_output = malloc(2016 * (int)ceil(frame_size));
encoder.state.frame_index = 0;
encoder.state.frame_data_offset = 0;
encoder.state.frame_max_size = 0;
encoder.state.frame_block_overflow_num = 0;
encoder.state.quant_scale_sum = 0;
// FIXME: this needs an extra frame to prevent A/V desync // FIXME: this needs an extra frame to prevent A/V desync
int frames_needed = (int) ceil((double)video_sectors_per_block / frame_size); int frames_needed = (int) ceil((double)video_sectors_per_block / frame_size);
if (frames_needed < 2) frames_needed = 2; if (frames_needed < 2) frames_needed = 2;
for (int j = 0; !settings->end_of_input || settings->state_vid.frame_data_offset < settings->state_vid.frame_max_size; j++) { for (int j = 0; !decoder->end_of_input || encoder.state.frame_data_offset < encoder.state.frame_max_size; j++) {
ensure_av_data(settings, audio_samples_per_sector*settings->channels, frames_needed); ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, frames_needed);
if ((j%interleave) < video_sectors_per_block) { if ((j%interleave) < video_sectors_per_block) {
// Video sector // Video sector
init_sector_buffer_video((psx_cdrom_sector_mode2_t*) buffer, settings); init_sector_buffer_video(args, (psx_cdrom_sector_mode2_t*) buffer, j);
encode_sector_str(settings->video_frames, buffer, settings);
int frames_used = encode_sector_str(&encoder, decoder->video_frames, buffer);
retire_av_data(decoder, 0, frames_used);
} else { } else {
// Audio sector // Audio sector
int samples_length = settings->audio_sample_count / settings->channels; int samples_length = decoder->audio_sample_count / args->audio_channels;
if (samples_length > audio_samples_per_sector) samples_length = audio_samples_per_sector; if (samples_length > audio_samples_per_sector) samples_length = audio_samples_per_sector;
// FIXME: this is an extremely hacky way to handle audio tracks // FIXME: this is an extremely hacky way to handle audio tracks
// shorter than the video track // shorter than the video track
if (!samples_length) { if (!samples_length)
video_sectors_per_block++; video_sectors_per_block++;
}
int length = psx_audio_xa_encode(xa_settings, &audio_state, settings->audio_samples, samples_length, buffer); int length = psx_audio_xa_encode(
if (settings->end_of_input) { xa_settings,
&audio_state,
decoder->audio_samples,
samples_length,
buffer
);
if (decoder->end_of_input)
psx_audio_xa_encode_finalize(xa_settings, buffer, length); psx_audio_xa_encode_finalize(xa_settings, buffer, length);
}
retire_av_data(settings, samples_length*settings->channels, 0); retire_av_data(decoder, samples_length * args->audio_channels, 0);
} }
if (settings->format == FORMAT_STR2CD) { if (args->format == FORMAT_STRCD) {
int t = j + 75*2; int t = j + 75*2;
// Put the time in // Put the time in
@ -353,48 +438,57 @@ void encode_file_str(settings_t *settings, FILE *output) {
buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4); buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4);
} }
if((j%interleave) < video_sectors_per_block) { if((j%interleave) < video_sectors_per_block)
calculate_edc_data(buffer); psx_cdrom_calculate_checksums((psx_cdrom_sector_t *)buffer, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1);
}
fwrite(buffer + offset, sector_size, 1, output); fwrite(buffer + offset, sector_size, 1, output);
time_t t = get_elapsed_time(settings); time_t t = get_elapsed_time();
if (t) {
fprintf(stderr, "\rFrame: %4d | LBA: %6d | Avg. q. scale: %5.2f | Encoding speed: %5.2fx", if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
settings->state_vid.frame_index, fprintf(
stderr,
"\rFrame: %4d | LBA: %6d | Avg. q. scale: %5.2f | Encoding speed: %5.2fx",
encoder.state.frame_index,
j, j,
(double)settings->state_vid.quant_scale_sum / (double)settings->state_vid.frame_index, (double)encoder.state.quant_scale_sum / (double)encoder.state.frame_index,
(double)(settings->state_vid.frame_index*settings->video_fps_den) / (double)(t*settings->video_fps_num) (double)(encoder.state.frame_index * args->str_fps_den) / (double)(t * args->str_fps_num)
); );
} }
} }
free(settings->state_vid.frame_output); free(encoder.state.frame_output);
destroy_encoder_state(settings); destroy_mdec_encoder(&encoder);
} }
void encode_file_sbs(settings_t *settings, FILE *output) { void encode_file_sbs(args_t *args, decoder_t *decoder, FILE *output) {
init_encoder_state(settings); mdec_encoder_t encoder;
settings->state_vid.frame_output = malloc(settings->alignment); init_mdec_encoder(&encoder, args->video_width, args->video_height);
settings->state_vid.frame_data_offset = 0;
settings->state_vid.frame_max_size = settings->alignment;
settings->state_vid.quant_scale_sum = 0;
for (int j = 0; ensure_av_data(settings, 0, 1); j++) { encoder.state.frame_output = malloc(args->alignment);
encode_frame_bs(settings->video_frames, settings); encoder.state.frame_data_offset = 0;
fwrite(settings->state_vid.frame_output, settings->alignment, 1, output); encoder.state.frame_max_size = args->alignment;
encoder.state.quant_scale_sum = 0;
time_t t = get_elapsed_time(settings); for (int j = 0; ensure_av_data(decoder, 0, 1); j++) {
if (t) { encode_frame_bs(&encoder, decoder->video_frames);
fprintf(stderr, "\rFrame: %4d | Avg. q. scale: %5.2f | Encoding speed: %5.2fx",
retire_av_data(decoder, 0, 1);
fwrite(encoder.state.frame_output, args->alignment, 1, output);
time_t t = get_elapsed_time();
if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rFrame: %4d | Avg. q. scale: %5.2f | Encoding speed: %5.2fx",
j, j,
(double)settings->state_vid.quant_scale_sum / (double)j, (double)encoder.state.quant_scale_sum / (double)j,
(double)(j*settings->video_fps_den) / (double)(t*settings->video_fps_num) (double)(j * args->str_fps_den) / (double)(t * args->str_fps_num)
); );
} }
} }
free(settings->state_vid.frame_output); free(encoder.state.frame_output);
destroy_encoder_state(settings); destroy_mdec_encoder(&encoder);
} }

35
psxavenc/filefmt.h Normal file
View File

@ -0,0 +1,35 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023, 2025 spicyjpeg
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stdio.h>
#include "args.h"
#include "decoding.h"
void encode_file_xa(const args_t *args, decoder_t *decoder, FILE *output);
void encode_file_spu(const args_t *args, decoder_t *decoder, FILE *output);
void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output);
void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output);
void encode_file_sbs(const args_t *args, decoder_t *decoder, FILE *output);

174
psxavenc/main.c Normal file
View File

@ -0,0 +1,174 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023 spicyjpeg
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <stdint.h>
#include <stdio.h>
#include "args.h"
#include "decoding.h"
#include "filefmt.h"
static const char *const bs_codec_names[NUM_BS_CODECS] = {
"BS v2",
"BS v3",
"BS v3 (with DC wrapping)"
};
static const uint8_t decoder_flags[NUM_FORMATS] = {
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // xa
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // xacd
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // spu
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // vag
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // spui
DECODER_USE_AUDIO | DECODER_AUDIO_REQUIRED, // vagi
DECODER_USE_AUDIO | DECODER_USE_VIDEO | DECODER_VIDEO_REQUIRED, // str
DECODER_USE_AUDIO | DECODER_USE_VIDEO | DECODER_VIDEO_REQUIRED, // strcd
DECODER_USE_AUDIO | DECODER_USE_VIDEO | DECODER_VIDEO_REQUIRED, // strspu
DECODER_USE_VIDEO | DECODER_VIDEO_REQUIRED, // strv
DECODER_USE_VIDEO | DECODER_VIDEO_REQUIRED // sbs
};
int main(int argc, const char **argv) {
args_t args;
decoder_t decoder;
FILE *output;
args.flags = 0;
args.format = FORMAT_INVALID;
args.input_file = NULL;
args.output_file = NULL;
args.swresample_options = NULL;
args.swscale_options = NULL;
if (!parse_args(&args, argv + 1, argc - 1))
return 1;
if (!open_av_data(&decoder, &args, decoder_flags[args.format])) {
fprintf(stderr, "Failed to open input file: %s\n", args.input_file);
return 1;
}
output = fopen(args.output_file, "wb");
if (output == NULL) {
fprintf(stderr, "Failed to open output file: %s\n", args.output_file);
return 1;
}
switch (args.format) {
case FORMAT_XA:
case FORMAT_XACD:
if (!(args.flags & FLAG_QUIET))
fprintf(
stderr,
"Audio format: XA-ADPCM, %d Hz %d-bit %s, F=%d C=%d\n",
args.audio_frequency,
args.audio_bit_depth,
(args.audio_channels == 2) ? "stereo" : "mono",
args.audio_xa_file,
args.audio_xa_channel
);
encode_file_xa(&args, &decoder, output);
break;
case FORMAT_SPU:
case FORMAT_VAG:
if (!(args.flags & FLAG_QUIET))
fprintf(
stderr,
"Audio format: SPU-ADPCM, %d Hz mono\n",
args.audio_frequency
);
encode_file_spu(&args, &decoder, output);
break;
case FORMAT_SPUI:
case FORMAT_VAGI:
if (!(args.flags & FLAG_QUIET))
fprintf(
stderr,
"Audio format: SPU-ADPCM, %d Hz %d channels, interleave=%d\n",
args.audio_frequency,
args.audio_channels,
args.audio_interleave
);
encode_file_spui(&args, &decoder, output);
break;
case FORMAT_STR:
case FORMAT_STRCD:
case FORMAT_STRSPU:
case FORMAT_STRV:
if (!(args.flags & FLAG_QUIET)) {
if (decoder.state.audio_stream)
fprintf(
stderr,
"Audio format: XA-ADPCM, %d Hz %d-bit %s, F=%d C=%d\n",
args.audio_frequency,
args.audio_bit_depth,
(args.audio_channels == 2) ? "stereo" : "mono",
args.audio_xa_file,
args.audio_xa_channel
);
fprintf(
stderr,
"Video format: %s, %dx%d, %.2f fps\n",
bs_codec_names[args.video_codec],
args.video_width,
args.video_height,
(double)args.str_fps_num / (double)args.str_fps_den
);
}
encode_file_str(&args, &decoder, output);
break;
case FORMAT_SBS:
if (!(args.flags & FLAG_QUIET))
fprintf(
stderr,
"Video format: %s, %dx%d, %.2f fps\n",
bs_codec_names[args.video_codec],
args.video_width,
args.video_height,
(double)args.str_fps_num / (double)args.str_fps_den
);
encode_file_sbs(&args, &decoder, output);
break;
default:
;
}
if (!(args.flags & FLAG_HIDE_PROGRESS))
fprintf(stderr, "\nDone.\n");
fclose(output);
close_av_data(&decoder);
return 0;
}

File diff suppressed because it is too large Load Diff

67
psxavenc/mdec.h Normal file
View File

@ -0,0 +1,67 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023, 2025 spicyjpeg
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <libavcodec/avdct.h>
typedef struct {
int frame_index;
int frame_data_offset;
int frame_max_size;
int frame_block_base_overflow;
int frame_block_overflow_num;
int frame_block_overflow_den;
int block_type;
int16_t last_dc_values[3];
uint16_t bits_value;
int bits_left;
uint8_t *frame_output;
int bytes_used;
int blocks_used;
int uncomp_hwords_used;
int quant_scale;
int quant_scale_sum;
AVDCT *dct_context;
uint32_t *ac_huffman_map;
uint32_t *dc_huffman_map;
int16_t *coeff_clamp_map;
int16_t *delta_clamp_map;
int16_t *dct_block_lists[6];
} mdec_encoder_state_t;
typedef struct {
int video_width;
int video_height;
mdec_encoder_state_t state;
} mdec_encoder_t;
bool init_mdec_encoder(mdec_encoder_t *encoder, int video_width, int video_height);
void destroy_mdec_encoder(mdec_encoder_t *encoder);
void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame);
int encode_sector_str(mdec_encoder_t *encoder, uint8_t *video_frames, uint8_t *output);

View File

@ -1,495 +0,0 @@
/*
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
Copyright (c) 2019 Ben "GreaseMonkey" Russell
Copyright (c) 2023 spicyjpeg
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "common.h"
#include "config.h"
const char *format_names[NUM_FORMATS] = {
"xa", "xacd",
"spu", "spui",
"vag", "vagi",
"str2", "str2cd", "str2v",
"sbs2"
};
void print_help(void) {
fprintf(stderr,
"Usage:\n"
" psxavenc -t <xa|xacd> [-f 18900|37800] [-b 4|8] [-c 1|2] [-F 0-255] [-C 0-31] <in> <out.xa>\n"
" psxavenc -t <str2|str2cd> [-f 18900|37800] [-b 4|8] [-c 1|2] [-F 0-255] [-C 0-31] [-s WxH] [-I] [-r num/den] [-x 1|2] <in> <out.str>\n"
" psxavenc -t str2v [-s WxH] [-I] [-r num/den] [-x 1|2] <in> <out.str>\n"
" psxavenc -t sbs2 [-s WxH] [-I] [-r num/den] [-a size] <in> <out.str>\n"
" psxavenc -t <spu|vag> [-f freq] [-L] [-a size] <in> <out.vag>\n"
" psxavenc -t <spui|vagi> [-f freq] [-c 1-24] [-L] [-i size] [-a size] <in> <out.vag>\n"
"\nTool options:\n"
" -h Show this help message and exit\n"
" -V Show version information and exit\n"
" -q Suppress all non-error messages\n"
"\n"
"Output options:\n"
" -t format Use specified output type\n"
" xa [A.] XA-ADPCM, 2336-byte sectors\n"
" xacd [A.] XA-ADPCM, 2352-byte sectors\n"
" spu [A.] raw SPU-ADPCM mono data\n"
" spui [A.] raw SPU-ADPCM interleaved data\n"
" vag [A.] .vag SPU-ADPCM mono\n"
" vagi [A.] .vag SPU-ADPCM interleaved\n"
" str2 [AV] v2 .str video, 2336-byte sectors\n"
" str2cd [AV] v2 .str video, 2352-byte sectors\n"
" str2v [.V] v2 .str video file\n"
" sbs2 [.V] v2 .sbs video, 2048-byte sectors\n"
" -F num xa/str2: Set the XA file number\n"
" 0-255, default 0\n"
" -C num xa/str2: Set the XA channel number\n"
" 0-31, default 0\n"
"\n"
"Audio options:\n"
" -f freq Use specified sample rate\n"
" xa/str2: 18900 or 37800, default 37800\n"
" spu/vag: any value, default 44100\n"
" spui/vagi: any value, default 44100\n"
" -b bitdepth Use specified bit depth\n"
" xa/str2: 4 or 8, default 4\n"
" spu/vag: must be 4\n"
" spui/vagi: must be 4\n"
" -c channels Use specified channel count\n"
" xa/str2: 1 or 2, default 2\n"
" spu/vag: must be 1\n"
" spui/vagi: any value, default 2\n"
" -R key=value,... Pass custom options to libswresample (see FFmpeg docs)\n"
"\n"
"SPU-ADPCM options (spu/spui/vag/vagi formats):\n"
" -L spu/vag: Add a loop marker at the end of sample data\n"
" spui/vagi: Add a loop marker at the end of each chunk\n"
" -i size spui/vagi: Use specified channel interleave\n"
" Any multiple of 16, default 2048\n"
" -a size spu/vag: Pad sample data to multiple of specified size\n"
" Any value >= 16, default 64\n"
" spui/vagi: Pad header and each chunk to multiple of specified size\n"
" Any value >= 16, default 2048\n"
"\n"
"Video options:\n"
" -s WxH Rescale input file to fit within specified size\n"
" 16x16-320x256 in 16-pixel increments, default 320x240\n"
" -I Force stretching to given size without preserving aspect ratio\n"
" -r num[/den] Set frame rate to specified integer or fraction\n"
" 1-30, default 15\n"
" -x speed str2: Set the CD-ROM speed the file is meant to played at\n"
" 1 or 2, default 2\n"
" -a size sbs2: Set the size of each frame\n"
" Any value >= 256, default 8192\n"
" -S key=value,... Pass custom options to libswscale (see FFmpeg docs)\n"
"\n"
);
}
void print_version(void) {
printf("psxavenc " VERSION "\n");
}
int parse_args_old(settings_t* settings, int argc, char** argv) {
int c, i;
char *next;
while ((c = getopt(argc, argv, "?hVqt:F:C:f:b:c:LR:i:a:s:IS:r:x:")) != -1) {
switch (c) {
case '?':
case 'h': {
print_help();
return -1;
} break;
case 'V': {
print_version();
return -1;
} break;
case 'q': {
settings->quiet = true;
settings->show_progress = false;
} break;
case 't': {
settings->format = -1;
for (i = 0; i < NUM_FORMATS; i++) {
if (!strcmp(optarg, format_names[i])) {
settings->format = i;
break;
}
}
if (settings->format < 0) {
fprintf(stderr, "Invalid format: %s\n", optarg);
return -1;
}
} break;
case 'F': {
settings->file_number = strtol(optarg, NULL, 0);
if (settings->file_number < 0 || settings->file_number > 255) {
fprintf(stderr, "Invalid file number: %d (must be in 0-255 range)\n", settings->file_number);
return -1;
}
} break;
case 'C': {
settings->channel_number = strtol(optarg, NULL, 0);
if (settings->channel_number < 0 || settings->channel_number > 31) {
fprintf(stderr, "Invalid channel number: %d (must be in 0-31 range)\n", settings->channel_number);
return -1;
}
} break;
case 'f': {
settings->frequency = strtol(optarg, NULL, 0);
if (settings->frequency < 1000) {
fprintf(stderr, "Invalid frequency: %d (must be at least 1000)\n", settings->frequency);
return -1;
}
} break;
case 'b': {
settings->bits_per_sample = strtol(optarg, NULL, 0);
if (settings->bits_per_sample != 4 && settings->bits_per_sample != 8) {
fprintf(stderr, "Invalid bit depth: %d (must be 4 or 8)\n", settings->bits_per_sample);
return -1;
}
} break;
case 'c': {
settings->channels = strtol(optarg, NULL, 0);
if (settings->channels < 1) {
fprintf(stderr, "Invalid channel count: %d (must be at least 1)\n", settings->channels);
return -1;
}
} break;
case 'L': {
settings->loop = true;
} break;
case 'R': {
settings->swresample_options = optarg;
} break;
case 'i': {
settings->interleave = (strtol(optarg, NULL, 0) + 15) & ~15;
if (settings->interleave < 16) {
fprintf(stderr, "Invalid interleave: %d (must be at least 16)\n", settings->interleave);
return -1;
}
} break;
case 'a': {
settings->alignment = strtol(optarg, NULL, 0);
if (settings->alignment < 16) {
fprintf(stderr, "Invalid alignment: %d (must be at least 16)\n", settings->alignment);
return -1;
}
} break;
case 's': {
settings->video_width = (strtol(optarg, &next, 0) + 15) & ~15;
if (*next != 'x') {
fprintf(stderr, "Invalid video size (must be specified as <width>x<height>)\n");
return -1;
}
settings->video_height = (strtol(next + 1, NULL, 0) + 15) & ~15;
if (settings->video_width < 16 || settings->video_width > 320) {
fprintf(stderr, "Invalid video width: %d (must be in 16-320 range)\n", settings->video_width);
return -1;
}
if (settings->video_height < 16 || settings->video_height > 256) {
fprintf(stderr, "Invalid video height: %d (must be in 16-256 range)\n", settings->video_height);
return -1;
}
} break;
case 'I': {
settings->ignore_aspect_ratio = true;
} break;
case 'S': {
settings->swscale_options = optarg;
} break;
case 'r': {
settings->video_fps_num = strtol(optarg, &next, 0);
if (*next == '/') {
settings->video_fps_den = strtol(next + 1, NULL, 0);
} else {
settings->video_fps_den = 1;
}
if (!settings->video_fps_den) {
fprintf(stderr, "Invalid frame rate denominator\n");
return -1;
}
i = settings->video_fps_num / settings->video_fps_den;
if (i < 1 || i > 60) {
fprintf(stderr, "Invalid frame rate: %d/%d (must be in 1-60 range)\n", settings->video_fps_num, settings->video_fps_den);
return -1;
}
} break;
case 'x': {
settings->cd_speed = strtol(optarg, NULL, 0);
if (settings->cd_speed < 1 || settings->cd_speed > 2) {
fprintf(stderr, "Invalid CD-ROM speed: %d (must be 1 or 2)\n", settings->cd_speed);
return -1;
}
} break;
}
}
// Some settings' (frequency, channels, interleave and alignment) default
// values are initialized here as they depend on the chosen format.
switch (settings->format) {
case FORMAT_XA:
case FORMAT_XACD:
case FORMAT_STR2:
case FORMAT_STR2CD:
case FORMAT_STR2V:
if (!settings->frequency) {
settings->frequency = PSX_AUDIO_XA_FREQ_DOUBLE;
} else if (settings->frequency != PSX_AUDIO_XA_FREQ_SINGLE && settings->frequency != PSX_AUDIO_XA_FREQ_DOUBLE) {
fprintf(
stderr, "Invalid XA-ADPCM frequency: %d Hz (must be %d or %d Hz)\n", settings->frequency,
PSX_AUDIO_XA_FREQ_SINGLE, PSX_AUDIO_XA_FREQ_DOUBLE
);
return -1;
}
if (!settings->channels) {
settings->channels = 2;
} else if (settings->channels > 2) {
fprintf(stderr, "Invalid XA-ADPCM channel count: %d (must be 1 or 2)\n", settings->channels);
return -1;
}
if (settings->interleave || settings->alignment) {
fprintf(stderr, "Interleave and frame size cannot be specified for this format\n");
return -1;
}
if (settings->loop) {
fprintf(stderr, "XA-ADPCM does not support loop markers\n");
return -1;
}
break;
case FORMAT_SPU:
case FORMAT_VAG:
if (!settings->frequency) {
settings->frequency = 44100;
}
if (settings->bits_per_sample != 4) {
fprintf(stderr, "Invalid SPU-ADPCM bit depth: %d (must be 4)\n", settings->bits_per_sample);
return -1;
}
if (!settings->channels) {
settings->channels = 1;
} else if (settings->channels > 1) {
fprintf(stderr, "Invalid SPU-ADPCM channel count: %d (must be 1)\n", settings->channels);
return -1;
}
if (settings->interleave) {
fprintf(stderr, "Interleave cannot be specified for this format\n");
return -1;
}
if (!settings->alignment) {
settings->alignment = 64;
}
break;
case FORMAT_SPUI:
case FORMAT_VAGI:
if (!settings->frequency) {
settings->frequency = 44100;
}
if (settings->bits_per_sample != 4) {
fprintf(stderr, "Invalid SPU-ADPCM bit depth: %d (must be 4)\n", settings->bits_per_sample);
return -1;
}
if (!settings->channels) {
settings->channels = 2;
}
if (!settings->interleave) {
settings->interleave = 2048;
}
if (!settings->alignment) {
settings->alignment = 2048;
}
break;
case FORMAT_SBS2:
if (settings->interleave) {
fprintf(stderr, "Interleave cannot be specified for this format\n");
return -1;
}
if (!settings->alignment) {
settings->alignment = 8192;
} else if (settings->alignment < 256) {
fprintf(stderr, "Invalid frame size: %d (must be at least 256)\n", settings->alignment);
return -1;
}
break;
default:
fprintf(stderr, "Output format must be specified\n");
return -1;
}
return optind;
}
int main(int argc, char **argv) {
settings_t settings;
int arg_offset;
FILE* output;
memset(&settings,0,sizeof(settings_t));
settings.quiet = false;
settings.show_progress = isatty(fileno(stderr));
settings.format = -1;
settings.file_number = 0;
settings.channel_number = 0;
settings.cd_speed = 2;
settings.channels = 0;
settings.frequency = 0;
settings.bits_per_sample = 4;
settings.interleave = 0;
settings.alignment = 0;
settings.loop = false;
// NOTE: ffmpeg/ffplay's .str demuxer has the frame rate hardcoded to 15fps
// so if you're messing around with this make sure you test generated files
// with another player and/or in an emulator.
settings.video_width = 320;
settings.video_height = 240;
settings.video_fps_num = 15;
settings.video_fps_den = 1;
settings.ignore_aspect_ratio = false;
settings.swresample_options = NULL;
settings.swscale_options = NULL;
settings.audio_samples = NULL;
settings.audio_sample_count = 0;
settings.video_frames = NULL;
settings.video_frame_count = 0;
settings.state_vid.huffman_encoding_map = NULL;
settings.state_vid.coeff_clamp_map = NULL;
settings.state_vid.dct_context = NULL;
for(int i = 0; i < 6; i++) {
settings.state_vid.dct_block_lists[i] = NULL;
}
if (argc < 2) {
print_help();
return 1;
}
arg_offset = parse_args_old(&settings, argc, argv);
if (arg_offset < 0) {
return 1;
} else if (argc < arg_offset + 2) {
print_help();
return 1;
}
bool has_audio =
(settings.format != FORMAT_STR2V) &&
(settings.format != FORMAT_SBS2);
bool has_video =
(settings.format == FORMAT_STR2) ||
(settings.format == FORMAT_STR2CD) ||
(settings.format == FORMAT_STR2V) ||
(settings.format == FORMAT_SBS2);
bool did_open_data = open_av_data(argv[arg_offset + 0], &settings,
has_audio, has_video, !has_video, has_video);
if (!did_open_data) {
fprintf(stderr, "Could not open input file!\n");
return 1;
}
output = fopen(argv[arg_offset + 1], "wb");
if (output == NULL) {
fprintf(stderr, "Could not open output file!\n");
return 1;
}
settings.start_time = time(NULL);
settings.last_progress_update = 0;
switch (settings.format) {
case FORMAT_XA:
case FORMAT_XACD:
if (!settings.quiet) {
fprintf(stderr, "Audio format: XA-ADPCM, %d Hz %d-bit %s, F=%d C=%d\n",
settings.frequency, settings.bits_per_sample,
(settings.channels == 2) ? "stereo" : "mono",
settings.file_number, settings.channel_number
);
}
encode_file_xa(&settings, output);
break;
case FORMAT_SPU:
case FORMAT_VAG:
if (!settings.quiet) {
fprintf(stderr, "Audio format: SPU-ADPCM, %d Hz mono\n",
settings.frequency
);
}
encode_file_spu(&settings, output);
break;
case FORMAT_SPUI:
case FORMAT_VAGI:
if (!settings.quiet) {
fprintf(stderr, "Audio format: SPU-ADPCM, %d Hz %d channels, interleave=%d\n",
settings.frequency, settings.channels, settings.interleave
);
}
encode_file_spu_interleaved(&settings, output);
break;
case FORMAT_STR2:
case FORMAT_STR2CD:
case FORMAT_STR2V:
if (!settings.quiet) {
if (settings.decoder_state_av.audio_stream) {
fprintf(stderr, "Audio format: XA-ADPCM, %d Hz %d-bit %s, F=%d C=%d\n",
settings.frequency, settings.bits_per_sample,
(settings.channels == 2) ? "stereo" : "mono",
settings.file_number, settings.channel_number
);
}
fprintf(stderr, "Video format: BS v2, %dx%d, %.2f fps\n",
settings.video_width, settings.video_height,
(double)settings.video_fps_num / (double)settings.video_fps_den
);
}
encode_file_str(&settings, output);
break;
case FORMAT_SBS2:
if (!settings.quiet) {
fprintf(stderr, "Video format: BS v2, %dx%d, %.2f fps\n",
settings.video_width, settings.video_height,
(double)settings.video_fps_num / (double)settings.video_fps_den
);
}
encode_file_sbs(&settings, output);
break;
}
if (settings.show_progress) {
fprintf(stderr, "\nDone.\n");
}
fclose(output);
close_av_data(&settings);
return 0;
}