diff --git a/psxavenc/args.c b/psxavenc/args.c index 8c92346..fb74a1f 100644 --- a/psxavenc/args.c +++ b/psxavenc/args.c @@ -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; diff --git a/psxavenc/args.h b/psxavenc/args.h index f0fab88..d313646 100644 --- a/psxavenc/args.h +++ b/psxavenc/args.h @@ -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; diff --git a/psxavenc/decoding.c b/psxavenc/decoding.c index a29e90a..a9cec89 100644 --- a/psxavenc/decoding.c +++ b/psxavenc/decoding.c @@ -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) { diff --git a/psxavenc/filefmt.c b/psxavenc/filefmt.c index e5d930b..3f6ce45 100644 --- a/psxavenc/filefmt.c +++ b/psxavenc/filefmt.c @@ -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(); diff --git a/psxavenc/main.c b/psxavenc/main.c index 277aa26..0f5e225 100644 --- a/psxavenc/main.c +++ b/psxavenc/main.c @@ -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( diff --git a/psxavenc/mdec.c b/psxavenc/mdec.c index 2221764..3587ce1 100644 --- a/psxavenc/mdec.c +++ b/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; diff --git a/psxavenc/mdec.h b/psxavenc/mdec.h index 3d1e4dc..4b8e026 100644 --- a/psxavenc/mdec.h +++ b/psxavenc/mdec.h @@ -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 +);