9.9 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.
Функция DecDCTvlc2 в библиотеке PsyQ для PlayStation 1
Функция DecDCTvlc2 является частью аппаратно-оптимизированной библиотеки декодирования видеоданных в реальном времени для PlayStation 1. Она выполняет декодирование переменнодлинных кодов (VLC - Variable Length Coding) в процессе декомпрессии DCT (Discrete Cosine Transform) данных, обычно используемых в видеоформатах типа MPEG.
Основное назначение
DecDCTvlc2 декодирует:
- Битовые потоки с переменнодлинным кодированием (VLC)
- Коэффициенты DCT после обратного зигзаг-сканирования
- Данные в формате, используемом аппаратным декодером PS1
Технические детали
void DecDCTvlc2(unsigned short *data, int *macroblock);
Где:
data- указатель на входной битовый поток VLCmacroblock- указатель на выходной буфер для декодированных данных макроблока (6 блоков по 64 элемента: 4 яркостных, 2 цветностных)
Особенности реализации
- Аппаратное ускорение - использует специальные возможности графического процессора PS1
- Оптимизировано для MPEG-подобных потоков - но не является полным MPEG-декодером
- Работает с макроблоками - 16x16 пикселей (4 блока яркости Y, 1 блок каждой цветности Cr/Cb)
- Требует предварительной настройки - через
DecDCTin(),DecDCTout()и другие функции видеоцепи
Пример использования
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <libds.h>
#include <libsn.h>
void decode_video_frame(unsigned short *vlc_data) {
// Буфер для декодированного макроблока (6 блоков по 64 коэффициента)
int macroblock[6*64];
// Настройка ввода/вывода декодера
DecDCTin(vlc_data, 0);
DecDCTout((unsigned char*)macroblock, 0);
// Декодирование VLC-потока
DecDCTvlc2(vlc_data, macroblock);
// Дальнейшая обработка декодированных данных...
}
Типичный контекст использования
- Декодирование внутрикадровых (I-кадров) MPEG-видео
- Обработка видеопотоков в реальном времени
- Специальные эффекты с использованием DCT-данных
- Собственные алгоритмы видеокомпрессии
Важные нюансы
-
Не является полным MPEG-декодером - требует дополнительной логики для полного декодирования видео
-
Требует точного соответствия формату данных - входной поток должен быть правильно сформирован
-
Работает в паре с другими функциями DCT-цепи:
DecDCTin()- настройка входного буфераDecDCTout()- настройка выходного буфераDecDCToutCallback()- установка callback'а по завершении
-
Использует аппаратные возможности PS1 - дает значительный выигрыш в производительности по сравнению с программной реализацией
Функция DecDCTvlc2 представляет собой низкоуровневый инструмент для работы с видео на PS1 и требует глубокого понимания принципов DCT-компрессии и форматов видеоданных, используемых на этой платформе.