5.5 KiB
5.5 KiB
Рендеринг потокового видео (FMV, Full Motion Video) на PlayStation 1 — это сложный процесс, сочетающий чтение данных с CD-ROM, декодирование через MDEC (Motion DECoder) и вывод на экран через GPU. Вот пошаговое объяснение:
1. Подготовка данных на диске
- Видео хранится в специальном формате STR (Sony's Video Format), который содержит:
- Видеопоток — сжатый через MPEG-1 (но упрощённый для PS1).
- Аудиопоток — ADPCM или CD-DA (Red Book Audio).
- Субтитры/тайминг — дополнительные данные.
Данные разбиты на секторы CD-ROM (Mode 2 XA), где чередуются видео и аудио.
2. Загрузка данных с CD-ROM
-
CPU отправляет команду чтения:
CdReadSector(/*Сектор начала*/, /*Кол-во секторов*/, /*Буфер в RAM*/); -
DMA (канал 3) копирует данные из буфера CD-ROM в RAM:
- Используется двойная буферизация:
- Пока DMA заполняет Буфер 1, CPU/MDEC обрабатывает Буфер 2.
- Размер буфера обычно 16–32 КБ (8–16 секторов).
- Используется двойная буферизация:
-
Ожидание завершения:
CdReadSync(0); // Блокирующее ожидание
3. Декодирование видео через MDEC
-
CPU настраивает MDEC (канал DMA 0/1):
- Указывает адрес сжатых данных в RAM.
- Запускает декодирование:
li t0, 0x1F801820 // MDEC_CMD li t1, 0x60000000 // Команда "Decode Macroblock" sw t1, 0(t0)
-
MDEC декодирует MPEG-1:
- Кадр разбит на макроблоки 16x16.
- Используется DCT (Discrete Cosine Transform) + RLE (Run-Length Encoding).
- На выходе — RGB16 (15-битный) или YUV пиксели.
-
DMA (канал 1) копирует результат в RAM:
- Готовые кадры сохраняются в отдельный буфер (например, 320x240, 16-бит).
4. Вывод на экран через GPU
-
CPU отправляет кадр в VRAM:
- Через DMA (канал 2) или вручную (порт
GP0). - Пример:
li a0, 0x1F8010A0 // DMA GPU (канал 2) la a1, frame_buffer sw a1, 0(a0) // Источник li a1, 0x01000200 // Размер + режим sw a1, 4(a0)
- Через DMA (канал 2) или вручную (порт
-
GPU выводит кадр:
- Используется полноэкранный спрайт (Rectangular Texture).
- Команда:
li t0, 0x1F801810 // GP0 li t1, 0xA0000000 // Команда "Draw Rectangle" sw t1, 0(t0)
-
Синхронизация с VBlank:
- Чтобы избежать артефактов, обновление кадра происходит во время VBlank:
VSync(0);
- Чтобы избежать артефактов, обновление кадра происходит во время VBlank:
5. Аудиосопровождение
- ADPCM-аудио декодируется через SPU (канал DMA 4):
SpuSetTransferMode(SPU_TRANSFER_BY_DMA); SpuWrite(audio_buffer, audio_size); - CD-DA (Red Book) воспроизводится напрямую с диска (аппаратно).
6. Оптимизации
-
Чередование данных (Interleaving):
- Видео и аудио секторы чередуются на диске для плавности.
- Пример структуры:
[VIDEO] [AUDIO] [VIDEO] [AUDIO]...
-
Предзагрузка:
- Игры заранее грузят несколько кадров в память.
-
Пропуск кадров:
- Если декодирование не успевает, PS1 пропускает кадры (например, в Resident Evil).
7. Пример кода (упрощённый цикл FMV)
void play_fmv() {
uint8_t video_buffer[32768];
uint16_t frame_buffer[320*240];
while (!fmv_finished) {
// 1. Чтение с CD-ROM
CdReadSector(current_sector, 16, video_buffer);
CdReadSync(0);
// 2. Декодирование через MDEC
MDEC_DecodeFrame(video_buffer, frame_buffer);
// 3. Вывод через GPU
GPU_SendFrame(frame_buffer);
// 4. Синхронизация
VSync(0);
current_sector += 16;
}
}
8. Проблемы и ограничения
- Низкое разрешение: 320x240 (чаще 256x224 для экономии памяти).
- Цветовые артефакты: Из-за 15-битного RGB.
- Задержки: Если CD-ROM не успевает, видео тормозит.
Итог
- Чтение с CD-ROM через DMA.
- Декодирование через MDEC.
- Вывод через GPU.
- Синхронизация с VBlank.
Именно так работали FMV в играх вроде Metal Gear Solid и Final Fantasy VII! 🎥