V1.0
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/* Movie playback (RAC1 Wrapper).
|
||||
*
|
||||
* The game plays its movies (the intro, the cut-scenes) with Sony's sample
|
||||
* player (src/game/movie: disc streaming, the IPU, EE threads, the SPU),
|
||||
* entered through func_0023B670 (host/overrides). On the PC the whole call is
|
||||
* replaced: the movie's sectors are read from the disc image, the MPEG-2
|
||||
* video is decoded with pl_mpeg2.h and shown at the movie's frame rate with
|
||||
* its sound (PS2 ADPCM, the language track the game asks for). A skip key
|
||||
* ends it early, like a pad button on the PS2. Shared with the UYA Wrapper. */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define PL_MPEG_IMPLEMENTATION
|
||||
#include "pl_mpeg2.h"
|
||||
#include "pss.h"
|
||||
#include "uya_runtime.h"
|
||||
|
||||
int uya_movie_play_track(uint32_t lsn, uint32_t bytes, int track)
|
||||
{
|
||||
uint32_t sectors = (bytes + 2047) / 2048;
|
||||
uint8_t *ps = malloc((size_t)sectors * 2048), *es = 0;
|
||||
int skipped = 0, frames = 0;
|
||||
if (!ps || uya_disc_read(lsn, sectors, ps) != 0) { uya_log("movie at LSN %u: disc read failed", lsn); free(ps); return 0; }
|
||||
es = malloc((size_t)sectors * 2048);
|
||||
size_t n = pss_extract_video(ps, (size_t)sectors * 2048, es, (size_t)sectors * 2048);
|
||||
int16_t *pcm = 0;
|
||||
size_t pcm_frames = 0;
|
||||
int rate = 0;
|
||||
{
|
||||
uint8_t *au = malloc((size_t)sectors * 2048);
|
||||
size_t an = au ? pss_extract_audio(ps, (size_t)sectors * 2048, track, au, (size_t)sectors * 2048) : 0;
|
||||
if (!an && track && au) an = pss_extract_audio(ps, (size_t)sectors * 2048, 0, au, (size_t)sectors * 2048);
|
||||
if (an) pcm_frames = pss_decode_audio(au, an, &pcm, &rate);
|
||||
free(au);
|
||||
}
|
||||
free(ps);
|
||||
plm_video_t *v = plm_video_create_with_buffer(plm_buffer_create_with_memory(es, n, 0), 1);
|
||||
int w = plm_video_get_width(v), h = plm_video_get_height(v);
|
||||
double fps = plm_video_get_framerate(v);
|
||||
if (w <= 0 || fps <= 0) { uya_log("movie at LSN %u: no video", lsn); plm_video_destroy(v); free(es); return 0; }
|
||||
uya_log("movie at LSN %u: %ux%u at %.2f fps, %u KB; sound track %d: %s%.1f s at %d Hz", lsn, w, h, fps, (unsigned)(n / 1024), track,
|
||||
pcm_frames ? "" : "none ", pcm_frames && rate ? (double)pcm_frames / rate : 0.0, rate);
|
||||
uint32_t *px = malloc((size_t)w * h * 4);
|
||||
char tag[32];
|
||||
snprintf(tag, sizeof tag, "movie_%u", lsn);
|
||||
if (pcm_frames) plat_audio_play(pcm, pcm_frames, rate, tag);
|
||||
plat_movie_started();
|
||||
uint64_t t0 = plat_time_us();
|
||||
int nowait = getenv("UYA_NOWAIT") != 0, dropped = 0;
|
||||
plm_frame_t *f;
|
||||
while ((f = plm_video_decode(v))) {
|
||||
uint64_t due = t0 + (uint64_t)(frames * 1000000.0 / fps), now = plat_time_us();
|
||||
int late = !nowait && now > due + (uint64_t)(1000000.0 / fps); /* a frame behind the sound: not shown, to catch up */
|
||||
if (due > now && !nowait) plat_sleep_us(due - now);
|
||||
if (!late) {
|
||||
plm_frame_to_bgra(f, (uint8_t *)px, w * 4);
|
||||
{ /* GL mode: the GS thread draws it into the window */
|
||||
extern int uya_gs_movie_frame(const uint32_t *px, int w, int h);
|
||||
int shown = uya_gs_movie_frame(px, w, h);
|
||||
if (!plat_present(shown ? 0 : px, w, h)) { uya_log("window closed"); exit(0); }
|
||||
}
|
||||
} else dropped++;
|
||||
frames++;
|
||||
if (plat_key_skip()) { skipped = 1; break; }
|
||||
}
|
||||
plat_audio_stop();
|
||||
free(pcm);
|
||||
uya_log("movie at LSN %u: %d frames shown%s (%d dropped to keep up with the sound)", lsn, frames - dropped, skipped ? " (skipped)" : "", dropped);
|
||||
free(px);
|
||||
plm_video_destroy(v);
|
||||
free(es);
|
||||
return skipped;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/* pss.h: MPEG-2 program stream (PS2 .pss) demuxer: pulls the video elementary
|
||||
* stream (stream id 0xE0) out of pack/PES framing. */
|
||||
#ifndef UYA_PSS_H
|
||||
#define UYA_PSS_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Appends the payload of every video PES packet in `ps` to `out` (capacity
|
||||
* `cap`); returns the number of bytes written. */
|
||||
static size_t pss_extract_video(const uint8_t *ps, size_t n, uint8_t *out, size_t cap)
|
||||
{
|
||||
size_t i = 0, w = 0;
|
||||
while (i + 4 <= n) {
|
||||
if (ps[i] != 0 || ps[i + 1] != 0 || ps[i + 2] != 1) { i++; continue; }
|
||||
uint8_t id = ps[i + 3];
|
||||
if (id == 0xBA) { /* pack header */
|
||||
if ((ps[i + 4] & 0xC0) == 0x40) i += 14 + (ps[i + 13] & 7); /* MPEG-2 */
|
||||
else i += 12; /* MPEG-1 */
|
||||
continue;
|
||||
}
|
||||
if (id == 0xB9) { i += 4; continue; } /* program end */
|
||||
if (id < 0xBB) { i++; continue; } /* not a PES/system packet */
|
||||
if (i + 6 > n) break;
|
||||
size_t len = ((size_t)ps[i + 4] << 8) | ps[i + 5];
|
||||
size_t end = i + 6 + len;
|
||||
if (end > n) end = n;
|
||||
if (id == 0xE0 && (ps[i + 6] & 0xC0) == 0x80) { /* MPEG-2 PES header */
|
||||
size_t p = i + 9 + ps[i + 8];
|
||||
if (p < end && w + (end - p) <= cap) { memcpy(out + w, ps + p, end - p); w += end - p; }
|
||||
}
|
||||
i = end;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/* Audio: private stream 1 (0xBD). Each payload starts with a 4-byte substream
|
||||
* header (ff a1 00 0N, N = the track; the movies carry several language
|
||||
* tracks). The track data is an "SShd" header then "SSbd" and PS2 ADPCM.
|
||||
* Appends track `track`'s bytes to `out`; returns the count. */
|
||||
static size_t pss_extract_audio(const uint8_t *ps, size_t n, int track, uint8_t *out, size_t cap)
|
||||
{
|
||||
size_t i = 0, w = 0;
|
||||
while (i + 4 <= n) {
|
||||
if (ps[i] != 0 || ps[i + 1] != 0 || ps[i + 2] != 1) { i++; continue; }
|
||||
uint8_t id = ps[i + 3];
|
||||
if (id == 0xBA) { i += ((ps[i + 4] & 0xC0) == 0x40) ? 14 + (ps[i + 13] & 7) : 12; continue; }
|
||||
if (id == 0xB9) { i += 4; continue; }
|
||||
if (id < 0xBB) { i++; continue; }
|
||||
if (i + 6 > n) break;
|
||||
size_t len = ((size_t)ps[i + 4] << 8) | ps[i + 5];
|
||||
size_t end = i + 6 + len;
|
||||
if (end > n) end = n;
|
||||
if (id == 0xBD && (ps[i + 6] & 0xC0) == 0x80) {
|
||||
size_t p = i + 9 + ps[i + 8];
|
||||
if (p + 4 <= end && ps[p] == 0xFF && ps[p + 3] == track) {
|
||||
p += 4;
|
||||
if (w + (end - p) <= cap) { memcpy(out + w, ps + p, end - p); w += end - p; }
|
||||
}
|
||||
}
|
||||
i = end;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static uint32_t pss_le32(const uint8_t *p) { return p[0] | (p[1] << 8) | (p[2] << 16) | ((uint32_t)p[3] << 24); }
|
||||
|
||||
/* Decodes an SShd/SSbd track (PS2 ADPCM, channels interleaved in blocks of
|
||||
* `interleave` bytes) into interleaved 16-bit stereo. Returns frames, or 0. */
|
||||
static size_t pss_decode_audio(const uint8_t *a, size_t n, int16_t **out, int *rate)
|
||||
{
|
||||
static const int fk[5][2] = {{0, 0}, {60, 0}, {115, -52}, {98, -55}, {122, -60}};
|
||||
if (n < 0x28 || memcmp(a, "SShd", 4) || pss_le32(a + 8) != 0x10) return 0;
|
||||
uint32_t hsz = pss_le32(a + 4), ch = pss_le32(a + 16), il = pss_le32(a + 20);
|
||||
*rate = (int)pss_le32(a + 12);
|
||||
const uint8_t *b = a + 8 + hsz;
|
||||
if (b + 8 > a + n || memcmp(b, "SSbd", 4) || ch < 1 || ch > 2 || il < 16 || il % 16) return 0;
|
||||
size_t len = pss_le32(b + 4);
|
||||
b += 8;
|
||||
if (len > (size_t)(a + n - b)) len = (size_t)(a + n - b);
|
||||
size_t blocks = len / (il * ch), per = il / 16 * 28;
|
||||
size_t frames = blocks * per;
|
||||
int16_t *o = (int16_t *)malloc(frames * 4 + 4);
|
||||
if (!o) return 0;
|
||||
int hist[2][2] = {{0, 0}, {0, 0}};
|
||||
for (size_t k = 0; k < blocks; k++)
|
||||
for (uint32_t c = 0; c < ch; c++) {
|
||||
const uint8_t *blk = b + (k * ch + c) * il;
|
||||
for (uint32_t f = 0; f < il / 16; f++) {
|
||||
const uint8_t *fr = blk + f * 16;
|
||||
int sh = fr[0] & 15, flt = (fr[0] >> 4) & 7;
|
||||
if (flt > 4) flt = 0;
|
||||
if (sh > 12) sh = 9;
|
||||
for (int s = 0; s < 28; s++) {
|
||||
int nib = (fr[2 + s / 2] >> ((s & 1) * 4)) & 15;
|
||||
if (nib >= 8) nib -= 16;
|
||||
int v = (nib << 12) >> sh;
|
||||
v += (hist[c][0] * fk[flt][0] + hist[c][1] * fk[flt][1] + 32) >> 6;
|
||||
if (v > 32767) v = 32767;
|
||||
if (v < -32768) v = -32768;
|
||||
hist[c][1] = hist[c][0];
|
||||
hist[c][0] = v;
|
||||
size_t idx = (k * per + f * 28 + s) * 2;
|
||||
o[idx + c] = (int16_t)v;
|
||||
if (ch == 1) o[idx + 1] = (int16_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
*out = o;
|
||||
return frames;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user