Bugfixes, add -T and -A options

This commit is contained in:
spicyjpeg 2025-03-02 20:15:06 +01:00
parent 7d537edffb
commit 24d37145c6
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
7 changed files with 271 additions and 251 deletions

View File

@ -123,9 +123,9 @@ static const char *const general_options_help =
" spui: [A.] raw SPU-ADPCM interleaved data\n"
" vag: [A.] .vag SPU-ADPCM mono\n"
" vagi: [A.] .vag SPU-ADPCM interleaved\n"
" str: [AV] .str video, 2336-byte sectors\n"
" strcd: [AV] .str video, 2352-byte sectors\n"
" strspu: [AV] .str video, 2048-byte sectors\n"
" str: [AV] .str video + XA-ADPCM, 2336-byte sectors\n"
" strcd: [AV] .str video + XA-ADPCM, 2352-byte sectors\n"
" strspu: [AV] .str video + SPU-ADPCM, 2048-byte sectors\n"
" strv: [.V] .str video, 2048-byte sectors\n"
" sbs: [.V] .sbs video\n"
" -R key=value,... Pass custom options to libswresample (see FFmpeg docs)\n"
@ -148,12 +148,15 @@ static const char *const format_names[NUM_FORMATS] = {
static void init_default_args(args_t *args) {
if (
args->format == FORMAT_XA || args->format == FORMAT_XACD ||
args->format == FORMAT_STR || args->format == FORMAT_STRCD
args->format == FORMAT_XA ||
args->format == FORMAT_XACD ||
args->format == FORMAT_STR ||
args->format == FORMAT_STRCD
)
args->audio_frequency = 37800;
else
args->audio_frequency = 44100;
if (args->format == FORMAT_SPU || args->format == FORMAT_VAG)
args->audio_channels = 1;
else
@ -172,11 +175,13 @@ static void init_default_args(args_t *args) {
args->str_fps_num = 15;
args->str_fps_den = 1;
args->str_cd_speed = 2;
args->str_video_id = 0x8001;
args->str_audio_id = 0x0001;
if (args->format == FORMAT_SPU || args->format == FORMAT_VAG)
args->alignment = 64;
args->alignment = 64; // Default SPU DMA chunk size
else if (args->format == FORMAT_SBS)
args->alignment = 8192;
args->alignment = 8192; // Default for System 573 games
else
args->alignment = 2048;
}
@ -264,7 +269,7 @@ static int parse_xa_option(args_t *args, char option, const char *param) {
}
static const char *const spu_options_help =
"SPU-ADPCM options:\n"
"Mono SPU-ADPCM options:\n"
" [-f freq] [-a size] [-l ms | -L] [-D]\n"
"\n"
" -f freq Use specified sample rate (default 44100)\n"
@ -411,11 +416,13 @@ static int parse_bs_option(args_t *args, char option, const char *param) {
static const char *const str_options_help =
".str container options:\n"
" [-r num[/den]] [-x 1|2] [-A]\n"
" [-r num[/den]] [-x 1|2] [-T id] [-A id] [-X]\n"
"\n"
" -r num[/den] Set video frame rate to specified integer or fraction (default 15)\n"
" -x 1|2 Set CD-ROM speed the file is meant to played at (default 2)\n"
" -A Place audio sectors after corresponding video sectors\n"
" -T id Tag video sectors with specified .str type ID (default 0x8001)\n"
" -A id Tag SPU-ADPCM sectors with specified .str type ID (default 0x0001)\n"
" -X Place audio sectors after corresponding video sectors\n"
" (rather than ahead of them)\n"
"\n";
@ -453,7 +460,13 @@ static int parse_str_option(args_t *args, char option, const char *param) {
case 'x':
return parse_int_one_of(&(args->str_cd_speed), "CD-ROM speed", param, 1, 2);
case 'T':
return parse_int(&(args->str_video_id), "video track type ID", param, 0x0000, 0xFFFF);
case 'A':
return parse_int(&(args->str_audio_id), "audio track type ID", param, 0x0000, 0xFFFF);
case 'X':
args->flags |= FLAG_STR_TRAILING_AUDIO;
return 1;

View File

@ -87,6 +87,8 @@ typedef struct {
int str_fps_num;
int str_fps_den;
int str_cd_speed; // 1 or 2
int str_video_id;
int str_audio_id;
int alignment;
} args_t;

View File

@ -36,27 +36,22 @@ freely, subject to the following restrictions:
#include "args.h"
#include "decoding.h"
static int decode_frame(
AVCodecContext *codec,
AVFrame *frame,
int *frame_size,
AVPacket *packet
) {
static bool decode_frame(AVCodecContext *codec, AVFrame *frame, int *frame_size, AVPacket *packet) {
if (packet != NULL) {
if (avcodec_send_packet(codec, packet) != 0)
return 0;
return false;
}
int ret = avcodec_receive_frame(codec, frame);
if (ret >= 0) {
*frame_size = ret;
return 1;
} else if (ret == AVERROR(EAGAIN)) {
return 1;
} else {
return 0;
return true;
}
if (ret == AVERROR(EAGAIN))
return true;
return false;
}
bool open_av_data(decoder_t *decoder, const args_t *args, int flags) {
@ -261,35 +256,39 @@ bool open_av_data(decoder_t *decoder, const args_t *args, int flags) {
static void poll_av_packet_audio(decoder_t *decoder, AVPacket *packet) {
decoder_state_t *av = &(decoder->state);
int frame_size, frame_sample_count;
uint8_t *buffer[1];
int frame_size;
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);
if (!decode_frame(av->audio_codec_context, av->frame, &frame_size, packet))
return;
buffer[0] = malloc(buffer_size);
memset(buffer[0], 0, buffer_size);
int frame_sample_count = swr_get_out_samples(av->resampler, av->frame->nb_samples);
frame_sample_count = swr_convert(
av->resampler,
buffer,
av->frame->nb_samples,
(const uint8_t**)av->frame->data,
av->frame->nb_samples
);
if (frame_sample_count == 0)
return;
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]);
}
size_t buffer_size = sizeof(int16_t) * av->sample_count_mul * frame_sample_count;
uint8_t *buffer = malloc(buffer_size);
memset(buffer, 0, buffer_size);
frame_sample_count = swr_convert(
av->resampler,
&buffer,
frame_sample_count,
(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,
sizeof(int16_t) * frame_sample_count * av->sample_count_mul
);
decoder->audio_sample_count += frame_sample_count * av->sample_count_mul;
free(buffer);
}
static void poll_av_packet_video(decoder_t *decoder, AVPacket *packet) {
@ -303,63 +302,63 @@ static void poll_av_packet_video(decoder_t *decoder, AVPacket *packet) {
decoder->video_width, decoder->video_width
};
if (decode_frame(av->video_codec_context, av->frame, &frame_size, packet)) {
if (!av->frame->width || !av->frame->height || !av->frame->data[0])
return;
if (!decode_frame(av->video_codec_context, av->frame, &frame_size, packet))
return;
if (!av->frame->width || !av->frame->height || !av->frame->data[0])
return;
// Some files seem to have timestamps starting from a negative value
// (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;
// Some files seem to have timestamps starting from a negative value
// (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;
#if 0
if (pts < 0.0)
return;
if (pts < 0.0)
return;
#endif
if (decoder->video_frame_count >= 1 && pts < av->video_next_pts)
return;
if (decoder->video_frame_count < 1)
av->video_next_pts = pts;
else
av->video_next_pts += pts_step;
if (decoder->video_frame_count >= 1 && pts < av->video_next_pts)
return;
if (decoder->video_frame_count < 1)
av->video_next_pts = pts;
else
av->video_next_pts += pts_step;
//fprintf(stderr, "%d %f %f %f\n", decoder->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
// lower than the target frame rate.
int dupe_frames = (int) ceil((pts - av->video_next_pts) / pts_step);
if (dupe_frames < 0) dupe_frames = 0;
decoder->video_frames = realloc(
decoder->video_frames,
(decoder->video_frame_count + dupe_frames + 1) * av->video_frame_dst_size
// Insert duplicate frames if the frame rate of the input stream is
// lower than the target frame rate.
int dupe_frames = (int) ceil((pts - av->video_next_pts) / pts_step);
if (dupe_frames < 0) dupe_frames = 0;
decoder->video_frames = realloc(
decoder->video_frames,
(decoder->video_frame_count + dupe_frames + 1) * av->video_frame_dst_size
);
for (; dupe_frames; dupe_frames--) {
memcpy(
(decoder->video_frames) + av->video_frame_dst_size * decoder->video_frame_count,
(decoder->video_frames) + av->video_frame_dst_size * (decoder->video_frame_count - 1),
av->video_frame_dst_size
);
for (; dupe_frames; dupe_frames--) {
memcpy(
(decoder->video_frames) + av->video_frame_dst_size * decoder->video_frame_count,
(decoder->video_frames) + av->video_frame_dst_size * (decoder->video_frame_count - 1),
av->video_frame_dst_size
);
decoder->video_frame_count += 1;
av->video_next_pts += pts_step;
}
uint8_t *dst_frame = decoder->video_frames + av->video_frame_dst_size * decoder->video_frame_count;
uint8_t *dst_pointers[2] = {
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
);
decoder->video_frame_count += 1;
av->video_next_pts += pts_step;
}
uint8_t *dst_frame = decoder->video_frames + av->video_frame_dst_size * decoder->video_frame_count;
uint8_t *dst_pointers[2] = {
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
);
decoder->video_frame_count += 1;
}
bool poll_av_data(decoder_t *decoder) {

View File

@ -104,15 +104,15 @@ static void write_vag_header(const args_t *args, int size_per_channel, uint8_t *
if (args->format == FORMAT_VAGI) {
header[0x08] = (uint8_t)args->audio_interleave;
header[0x09] = (uint8_t)(args->audio_interleave >> 8);
header[0x0a] = (uint8_t)(args->audio_interleave >> 16);
header[0x0b] = (uint8_t)(args->audio_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)
header[0x0c] = (uint8_t)(size_per_channel >> 24);
header[0x0d] = (uint8_t)(size_per_channel >> 16);
header[0x0e] = (uint8_t)(size_per_channel >> 8);
header[0x0f] = (uint8_t)size_per_channel;
header[0x0C] = (uint8_t)(size_per_channel >> 24);
header[0x0D] = (uint8_t)(size_per_channel >> 16);
header[0x0E] = (uint8_t)(size_per_channel >> 8);
header[0x0F] = (uint8_t)size_per_channel;
// Sample rate (big-endian)
header[0x10] = (uint8_t)(args->audio_frequency >> 24);
@ -121,8 +121,8 @@ static void write_vag_header(const args_t *args, int size_per_channel, uint8_t *
header[0x13] = (uint8_t)args->audio_frequency;
// Number of channels (little-endian)
header[0x1e] = (uint8_t)args->audio_channels;
header[0x1f] = 0x00;
header[0x1E] = (uint8_t)args->audio_channels;
header[0x1F] = 0x00;
// Filename
int name_offset = strlen(args->output_file);
@ -213,7 +213,7 @@ void encode_file_spu(const args_t *args, decoder_t *decoder, FILE *output) {
int loop_start_block = -1;
if (args->audio_loop_point >= 0)
loop_start_block = (args->audio_loop_point * args->audio_frequency) / (PSX_AUDIO_SPU_SAMPLES_PER_BLOCK * 1000);
loop_start_block = block_count + (args->audio_loop_point * args->audio_frequency) / (PSX_AUDIO_SPU_SAMPLES_PER_BLOCK * 1000);
for (; ensure_av_data(decoder, PSX_AUDIO_SPU_SAMPLES_PER_BLOCK, 0); block_count++) {
int samples_length = decoder->audio_sample_count;
@ -279,7 +279,7 @@ void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output) {
// NOTE: since the interleaved .vag format is not standardized, some tools
// (such as vgmstream) will not properly play files with interleave < 2048,
// alignment != 2048 or channels != 2.
int buffer_size = args->audio_interleave + args->alignment - 1;
int buffer_size = args->audio_interleave * args->audio_channels + args->alignment - 1;
buffer_size -= buffer_size % args->alignment;
int header_size = VAG_HEADER_SIZE + args->alignment - 1;
@ -297,30 +297,30 @@ void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output) {
for (; ensure_av_data(decoder, audio_samples_per_chunk * args->audio_channels, 0); chunk_count++) {
int samples_length = decoder->audio_sample_count / args->audio_channels;
int buffer_offset = 0;
if (samples_length > audio_samples_per_chunk)
samples_length = audio_samples_per_chunk;
memset(buffer, 0, buffer_size);
uint8_t *buffer_ptr = buffer;
// Insert leading silent block
if (chunk_count == 0 && !(args->flags & FLAG_SPU_NO_LEADING_DUMMY)) {
buffer_offset = PSX_AUDIO_SPU_BLOCK_SIZE;
samples_length -= PSX_AUDIO_SPU_BLOCK_SIZE;
buffer_ptr += PSX_AUDIO_SPU_BLOCK_SIZE;
samples_length -= PSX_AUDIO_SPU_SAMPLES_PER_BLOCK;
}
for (int ch = 0; ch < args->audio_channels; ch++) {
memset(buffer, 0, buffer_size);
for (int ch = 0; ch < args->audio_channels; ch++, buffer_ptr += args->audio_interleave) {
int length = psx_audio_spu_encode(
audio_state + ch,
decoder->audio_samples + ch,
samples_length,
args->audio_channels,
buffer + buffer_offset
buffer_ptr
);
if (length > 0) {
uint8_t *last_block = buffer + length - PSX_AUDIO_SPU_BLOCK_SIZE;
uint8_t *last_block = buffer_ptr + length - PSX_AUDIO_SPU_BLOCK_SIZE;
if (args->flags & FLAG_SPU_LOOP_END) {
last_block[1] = PSX_AUDIO_SPU_LOOP_REPEAT;
@ -332,24 +332,27 @@ void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output) {
last_block[1] = PSX_AUDIO_SPU_LOOP_START | PSX_AUDIO_SPU_LOOP_END;
}
}
fwrite(buffer, buffer_size, 1, output);
time_t t = get_elapsed_time();
if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rChunk: %6d | Encoding speed: %5.2fx",
chunk_count,
(double)(chunk_count * audio_samples_per_chunk) / (double)(args->audio_frequency * t)
);
}
}
retire_av_data(decoder, samples_length * args->audio_channels, 0);
fwrite(buffer, buffer_size, 1, output);
time_t t = get_elapsed_time();
if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rChunk: %6d | Encoding speed: %5.2fx",
chunk_count,
(double)(chunk_count * audio_samples_per_chunk) / (double)(args->audio_frequency * t)
);
}
}
free(audio_state);
free(buffer);
if (args->format == FORMAT_VAGI) {
uint8_t *header = malloc(header_size);
memset(header, 0, header_size);
@ -359,32 +362,20 @@ void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output) {
fwrite(header, header_size, 1, output);
free(header);
}
free(audio_state);
free(buffer);
}
void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
int audio_samples_per_sector;
int offset, sector_size;
if (args->format == FORMAT_STRV) {
sector_size = 2048;
offset = 0x18;
} else {
sector_size = psx_audio_xa_get_buffer_size_per_sector(xa_settings);
offset = PSX_CDROM_SECTOR_SIZE - sector_size;
}
int sector_size = psx_audio_xa_get_buffer_size_per_sector(xa_settings);
int interleave;
int audio_samples_per_sector;
int video_sectors_per_block;
if (decoder->state.audio_stream != NULL) {
// 1/N audio, (N-1)/N video
audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
interleave = psx_audio_xa_get_sector_interleave(xa_settings) * args->str_cd_speed;
audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
video_sectors_per_block = interleave - 1;
if (!(args->flags & FLAG_QUIET))
@ -398,8 +389,8 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
);
} else {
// 0/1 audio, 1/1 video
audio_samples_per_sector = 0;
interleave = 1;
audio_samples_per_sector = 0;
video_sectors_per_block = 1;
}
@ -426,7 +417,9 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
// FIXME: this needs an extra frame to prevent A/V desync
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; !decoder->end_of_input || encoder.state.frame_data_offset < encoder.state.frame_max_size; j++) {
ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, frames_needed);
@ -440,9 +433,16 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
is_video_sector = (j % interleave) > 0;
if (is_video_sector) {
init_sector_buffer_video(args, (psx_cdrom_sector_mode2_t*) buffer, j);
init_sector_buffer_video(args, (psx_cdrom_sector_mode2_t*)buffer, j);
int frames_used = encode_sector_str(
&encoder,
args->format,
args->str_video_id,
decoder->video_frames,
buffer
);
int frames_used = encode_sector_str(&encoder, args->format, decoder->video_frames, buffer);
retire_av_data(decoder, 0, frames_used);
} else {
int samples_length = decoder->audio_sample_count / args->audio_channels;
@ -481,7 +481,7 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
if (is_video_sector)
psx_cdrom_calculate_checksums((psx_cdrom_sector_t *)buffer, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1);
fwrite(buffer + offset, sector_size, 1, output);
fwrite(buffer + PSX_CDROM_SECTOR_SIZE - sector_size, sector_size, 1, output);
time_t t = get_elapsed_time();

View File

@ -120,7 +120,6 @@ int main(int argc, const char **argv) {
case FORMAT_STR:
case FORMAT_STRCD:
case FORMAT_STRV:
if (!(args.flags & FLAG_QUIET)) {
if (decoder.state.audio_stream)
fprintf(
@ -147,6 +146,7 @@ int main(int argc, const char **argv) {
break;
case FORMAT_STRSPU:
case FORMAT_STRV:
if (!(args.flags & FLAG_QUIET)) {
if (decoder.state.audio_stream)
fprintf(

View File

@ -32,13 +32,6 @@ freely, subject to the following restrictions:
#include "args.h"
#include "mdec.h"
// https://stackoverflow.com/a/60011209
#if 0
#define DIVIDE_ROUNDED(n, d) (((n) >= 0) ? (((n) + (d)/2) / (d)) : (((n) - (d)/2) / (d)))
#else
#define DIVIDE_ROUNDED(n, d) ((int)round((double)(n) / (double)(d)))
#endif
#define AC_PAIR(zeroes, value) \
(((zeroes) << 10) | ((+(value)) & 0x3FF)), \
(((zeroes) << 10) | ((-(value)) & 0x3FF))
@ -166,39 +159,31 @@ static const struct {
static const struct {
int c_bits;
uint32_t c_value;
int sign_bits;
int value_bits;
int dc_bits;
} dc_c_huffman_tree[] = {
{2, 0x0, 0, 0},
{2, 0x1, 1, 0},
{2, 0x2, 1, 1},
{3, 0x6, 1, 2},
{4, 0xE, 1, 3},
{5, 0x1E, 1, 4},
{6, 0x3E, 1, 5},
{7, 0x7E, 1, 6},
{8, 0xFE, 1, 7},
{2, 0x1, 0},
{2, 0x2, 1},
{3, 0x6, 2},
{4, 0xE, 3},
{5, 0x1E, 4},
{6, 0x3E, 5},
{7, 0x7E, 6},
{8, 0xFE, 7}
};
static const struct {
int c_bits;
uint32_t c_value;
int sign_bits;
int value_bits;
int dc_bits;
} dc_y_huffman_tree[] = {
{3, 0x4, 0, 0},
{2, 0x0, 1, 0},
{2, 0x1, 1, 1},
{3, 0x5, 1, 2},
{3, 0x6, 1, 3},
{4, 0xE, 1, 4},
{5, 0x1E, 1, 5},
{6, 0x3E, 1, 6},
{7, 0x7E, 1, 7},
};
static const uint8_t dc_coeff_indices[6] = {
0, 1, 2, 2, 2, 2
{2, 0x0, 0},
{2, 0x1, 1},
{3, 0x5, 2},
{3, 0x6, 3},
{4, 0xE, 4},
{5, 0x1E, 5},
{6, 0x3E, 6},
{7, 0x7E, 7}
};
static const uint8_t quant_dec[8*8] = {
@ -260,82 +245,75 @@ static const int16_t dct_scale_table[8*8] = {
};
#endif
enum {
INDEX_CR,
INDEX_CB,
INDEX_Y
};
#define HUFFMAN_CODE(bits, value) (((bits) << 24) | (value))
static void init_dct_data(mdec_encoder_state_t *state, bs_codec_t codec) {
for(int i = 0; i <= 0xFFFF; i++) {
// high 8 bits = bit count
// low 24 bits = value
state->ac_huffman_map[i] = ((6+16)<<24)|((0x01<<16)|(i));
state->ac_huffman_map[i] = HUFFMAN_CODE(6 + 16, (0x1 << 16) | i);
int16_t coeff = (int16_t)i;
if (coeff < -0x200)
coeff = -0x200;
else if (coeff > +0x1FE)
coeff = +0x1FE; // 0x1FF = v2 end of frame
state->coeff_clamp_map[i] = coeff;
int16_t delta = (int16_t)DIVIDE_ROUNDED(i, 4);
if (delta < -0xFF)
delta = -0xFF;
else if (delta > +0xFF)
delta = +0xFF;
// Some versions of Sony's BS v3 decoder compute each DC coefficient as
// ((last + delta * 4) & 0x3FF) instead of just (last + delta * 4). The
// encoder can leverage this behavior to represent large coefficient
// differences as smaller deltas that cause the decoder to overflow and
// wrap around (e.g. -1 to encode -512 -> 511 as opposed to +1023). This
// saves some space as larger DC values take up more bits.
if (codec == BS_CODEC_V3DC) {
if (delta > +0x80)
delta -= 0x100;
}
state->delta_clamp_map[i] = delta;
}
state->dc_huffman_map[(INDEX_CR << 9) | 0] = HUFFMAN_CODE(2, 0x0);
state->dc_huffman_map[(INDEX_CB << 9) | 0] = HUFFMAN_CODE(2, 0x0);
state->dc_huffman_map[(INDEX_Y << 9) | 0] = HUFFMAN_CODE(3, 0x4);
int ac_tree_item_count = sizeof(ac_huffman_tree) / sizeof(ac_huffman_tree[0]);
int dc_c_tree_item_count = sizeof(dc_c_huffman_tree) / sizeof(dc_c_huffman_tree[0]);
int dc_y_tree_item_count = sizeof(dc_y_huffman_tree) / sizeof(dc_y_huffman_tree[0]);
for (int i = 0; i < ac_tree_item_count; i++) {
int bits = ac_huffman_tree[i].c_bits+1;
int bits = ac_huffman_tree[i].c_bits + 1;
uint32_t base_value = ac_huffman_tree[i].c_value;
state->ac_huffman_map[ac_huffman_tree[i].u_hword_pos] = (bits << 24) | (base_value << 1) | 0;
state->ac_huffman_map[ac_huffman_tree[i].u_hword_neg] = (bits << 24) | (base_value << 1) | 1;
state->ac_huffman_map[ac_huffman_tree[i].u_hword_pos] = HUFFMAN_CODE(bits, (base_value << 1) | 0);
state->ac_huffman_map[ac_huffman_tree[i].u_hword_neg] = HUFFMAN_CODE(bits, (base_value << 1) | 1);
}
for (int i = 0; i < dc_c_tree_item_count; i++) {
int dc_bits = dc_c_huffman_tree[i].sign_bits + dc_c_huffman_tree[i].value_bits;
int bits = dc_c_huffman_tree[i].c_bits + dc_bits;
uint32_t base_value = dc_c_huffman_tree[i].c_value << dc_bits;
int dc_bits = dc_c_huffman_tree[i].dc_bits;
int bits = dc_c_huffman_tree[i].c_bits + 1 + dc_bits;
uint32_t base_value = dc_c_huffman_tree[i].c_value;
int pos_offset = 1 << dc_bits;
int neg_offset = 1 - (1 << (dc_bits + 1));
for (int j = 0; j < (1 << dc_bits); j++) {
int delta = j;
int pos = (j + pos_offset) & 0x1FF;
int neg = (j + neg_offset) & 0x1FF;
if ((j >> dc_c_huffman_tree[i].value_bits) == 0) {
delta -= (1 << dc_bits) - 1;
delta &= 0x1FF;
}
state->dc_huffman_map[(0 << 9) | delta] = (bits << 24) | base_value | j;
state->dc_huffman_map[(1 << 9) | delta] = (bits << 24) | base_value | j;
state->dc_huffman_map[(INDEX_CR << 9) | pos] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (1 << dc_bits) | j);
state->dc_huffman_map[(INDEX_CR << 9) | neg] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (0 << dc_bits) | j);
state->dc_huffman_map[(INDEX_CB << 9) | pos] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (1 << dc_bits) | j);
state->dc_huffman_map[(INDEX_CB << 9) | neg] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (0 << dc_bits) | j);
}
}
for (int i = 0; i < dc_y_tree_item_count; i++) {
int dc_bits = dc_y_huffman_tree[i].sign_bits + dc_y_huffman_tree[i].value_bits;
int bits = dc_y_huffman_tree[i].c_bits + dc_bits;
uint32_t base_value = dc_y_huffman_tree[i].c_value << dc_bits;
int dc_bits = dc_y_huffman_tree[i].dc_bits;
int bits = dc_y_huffman_tree[i].c_bits + 1 + dc_bits;
uint32_t base_value = dc_y_huffman_tree[i].c_value;
int pos_offset = 1 << dc_bits;
int neg_offset = 1 - (1 << (dc_bits + 1));
for (int j = 0; j < (1 << dc_bits); j++) {
int delta = j;
int pos = (j + pos_offset) & 0x1FF;
int neg = (j + neg_offset) & 0x1FF;
if ((j >> dc_y_huffman_tree[i].value_bits) == 0) {
delta -= (1 << dc_bits) - 1;
delta &= 0x1FF;
}
state->dc_huffman_map[(2 << 9) | delta] = (bits << 24) | base_value | j;
state->dc_huffman_map[(INDEX_Y << 9) | pos] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (1 << dc_bits) | j);
state->dc_huffman_map[(INDEX_Y << 9) | neg] = HUFFMAN_CODE(bits, (base_value << (dc_bits + 1)) | (0 << dc_bits) | j);
}
}
}
@ -453,6 +431,13 @@ static int reduce_dct_block(mdec_encoder_state_t *state, int32_t *block, int32_t
}
#endif
// https://stackoverflow.com/a/60011209
#if 0
#define DIVIDE_ROUNDED(n, d) (((n) >= 0) ? (((n) + (d)/2) / (d)) : (((n) - (d)/2) / (d)))
#else
#define DIVIDE_ROUNDED(n, d) ((int)round((double)(n) / (double)(d)))
#endif
static bool encode_dct_block(
mdec_encoder_state_t *state,
bs_codec_t codec,
@ -467,11 +452,26 @@ static bool encode_dct_block(
if (!encode_bits(state, 10, dc & 0x3FF))
return false;
} else {
int index = dc_coeff_indices[state->block_type];
int last = state->last_dc_values[index];
int index = state->block_type;
int delta = state->delta_clamp_map[(dc - last) & 0xFFFF];
state->last_dc_values[index] = (last + delta * 4) & 0x3FF;
if (index > INDEX_Y)
index = INDEX_Y;
int delta = DIVIDE_ROUNDED(dc - state->last_dc_values[index], 4);
state->last_dc_values[index] += delta * 4;
// Some versions of Sony's BS v3 decoder compute each DC coefficient as
// ((last + delta * 4) & 0x3FF) instead of just (last + delta * 4). The
// encoder can leverage this behavior to represent large coefficient
// differences as smaller deltas that cause the decoder to overflow and
// wrap around (e.g. -1 to encode -512 -> 511 as opposed to +1023). This
// saves some space as larger DC values take up more bits.
if (codec == BS_CODEC_V3DC) {
if (delta < -0x80)
delta += 0x100;
else if (delta > +0x80)
delta -= 0x100;
}
uint32_t outword = state->dc_huffman_map[(index << 9) | (delta & 0x1FF)];
@ -488,7 +488,7 @@ static bool encode_dct_block(
if (ac == 0) {
zeroes++;
} else {
uint32_t outword = state->ac_huffman_map[(zeroes << 10) | ac];
uint32_t outword = state->ac_huffman_map[(zeroes << 10) | (ac & 0x3FF)];
if (!encode_bits(state, outword >> 24, outword & 0xFFFFFF))
return false;
@ -516,21 +516,21 @@ bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, int vide
mdec_encoder_state_t *state = &(encoder->state);
#if 0
if (state->dct_context != NULL)
return true;
#endif
state->dct_context = avcodec_dct_alloc();
state->ac_huffman_map = malloc(0x10000 * sizeof(uint32_t));
state->dc_huffman_map = malloc(0x600 * sizeof(uint32_t));
state->dc_huffman_map = malloc(0x200 * 3 * sizeof(uint32_t));
state->coeff_clamp_map = malloc(0x10000 * sizeof(int16_t));
state->delta_clamp_map = malloc(0x10000 * sizeof(int16_t));
if (
state->dct_context == NULL ||
state->ac_huffman_map == NULL ||
state->dc_huffman_map == NULL ||
state->coeff_clamp_map == NULL ||
state->delta_clamp_map == NULL
state->coeff_clamp_map == NULL
)
return false;
@ -569,12 +569,8 @@ void destroy_mdec_encoder(mdec_encoder_t *encoder) {
free(state->coeff_clamp_map);
state->coeff_clamp_map = NULL;
}
if (state->delta_clamp_map) {
free(state->delta_clamp_map);
state->delta_clamp_map = NULL;
}
for (int i = 0; i < 6; i++) {
if (state->dct_block_lists[i]) {
if (state->dct_block_lists[i] != NULL) {
free(state->dct_block_lists[i]);
state->dct_block_lists[i] = NULL;
}
@ -653,7 +649,6 @@ void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame) {
} else {
end_of_block = 0x3FF;
assert(state->dc_huffman_map);
assert(state->delta_clamp_map);
}
assert(state->ac_huffman_map);
@ -681,9 +676,9 @@ void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame) {
memset(state->frame_output, 0, state->frame_max_size);
state->block_type = 0;
state->last_dc_values[0] = 0;
state->last_dc_values[1] = 0;
state->last_dc_values[2] = 0;
state->last_dc_values[INDEX_CR] = 0;
state->last_dc_values[INDEX_CB] = 0;
state->last_dc_values[INDEX_Y] = 0;
state->bits_value = 0;
state->bits_left = 16;
@ -759,7 +754,13 @@ void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame) {
state->frame_output[0x007] = 0x00;
}
int encode_sector_str(mdec_encoder_t *encoder, format_t format, uint8_t *video_frames, uint8_t *output) {
int encode_sector_str(
mdec_encoder_t *encoder,
format_t format,
uint16_t str_video_id,
uint8_t *video_frames,
uint8_t *output
) {
mdec_encoder_state_t *state = &(encoder->state);
int last_frame_index = state->frame_index;
int frame_size = encoder->video_width * encoder->video_height * 2;
@ -784,9 +785,9 @@ int encode_sector_str(mdec_encoder_t *encoder, format_t format, uint8_t *video_f
header[0x000] = 0x60;
header[0x001] = 0x01;
// Chunk type: MDEC data
header[0x002] = 0x01;
header[0x003] = 0x80;
// Chunk type
header[0x002] = (uint8_t)str_video_id;
header[0x003] = (uint8_t)(str_video_id >> 8);
// Muxed chunk index/count
int chunk_index = state->frame_data_offset / 2016;

View File

@ -51,7 +51,6 @@ typedef struct {
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;
@ -66,4 +65,10 @@ typedef struct {
bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, 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, format_t format, uint8_t *video_frames, uint8_t *output);
int encode_sector_str(
mdec_encoder_t *encoder,
format_t format,
uint16_t str_video_id,
uint8_t *video_frames,
uint8_t *output
);