Bugfixes, add -T and -A options
This commit is contained in:
parent
7d537edffb
commit
24d37145c6
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
203
psxavenc/mdec.c
203
psxavenc/mdec.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue