222 lines
9.9 KiB
Markdown
222 lines
9.9 KiB
Markdown
Рендеринг потокового видео (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**
|
||
1. **CPU отправляет команду чтения**:
|
||
```c
|
||
CdReadSector(/*Сектор начала*/, /*Кол-во секторов*/, /*Буфер в RAM*/);
|
||
```
|
||
2. **DMA (канал 3) копирует данные** из буфера CD-ROM в RAM:
|
||
- Используется **двойная буферизация**:
|
||
- Пока DMA заполняет **Буфер 1**, CPU/MDEC обрабатывает **Буфер 2**.
|
||
- Размер буфера обычно **16–32 КБ** (8–16 секторов).
|
||
|
||
3. **Ожидание завершения**:
|
||
```c
|
||
CdReadSync(0); // Блокирующее ожидание
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Декодирование видео через MDEC**
|
||
1. **CPU настраивает MDEC** (канал DMA 0/1):
|
||
- Указывает адрес сжатых данных в RAM.
|
||
- Запускает декодирование:
|
||
```asm
|
||
li t0, 0x1F801820 // MDEC_CMD
|
||
li t1, 0x60000000 // Команда "Decode Macroblock"
|
||
sw t1, 0(t0)
|
||
```
|
||
|
||
2. **MDEC декодирует MPEG-1**:
|
||
- Кадр разбит на **макроблоки 16x16**.
|
||
- Используется **DCT (Discrete Cosine Transform)** + **RLE (Run-Length Encoding)**.
|
||
- На выходе — **RGB16 (15-битный)** или **YUV** пиксели.
|
||
|
||
3. **DMA (канал 1) копирует результат** в RAM:
|
||
- Готовые кадры сохраняются в **отдельный буфер** (например, 320x240, 16-бит).
|
||
|
||
---
|
||
|
||
### **4. Вывод на экран через GPU**
|
||
1. **CPU отправляет кадр в VRAM**:
|
||
- Через DMA (канал 2) или вручную (порт `GP0`).
|
||
- Пример:
|
||
```asm
|
||
li a0, 0x1F8010A0 // DMA GPU (канал 2)
|
||
la a1, frame_buffer
|
||
sw a1, 0(a0) // Источник
|
||
li a1, 0x01000200 // Размер + режим
|
||
sw a1, 4(a0)
|
||
```
|
||
|
||
2. **GPU выводит кадр**:
|
||
- Используется **полноэкранный спрайт** (Rectangular Texture).
|
||
- Команда:
|
||
```asm
|
||
li t0, 0x1F801810 // GP0
|
||
li t1, 0xA0000000 // Команда "Draw Rectangle"
|
||
sw t1, 0(t0)
|
||
```
|
||
|
||
3. **Синхронизация с VBlank**:
|
||
- Чтобы избежать артефактов, обновление кадра происходит **во время VBlank**:
|
||
```c
|
||
VSync(0);
|
||
```
|
||
|
||
---
|
||
|
||
### **5. Аудиосопровождение**
|
||
- **ADPCM-аудио** декодируется через SPU (канал DMA 4):
|
||
```c
|
||
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
|
||
SpuWrite(audio_buffer, audio_size);
|
||
```
|
||
- **CD-DA (Red Book)** воспроизводится напрямую с диска (аппаратно).
|
||
|
||
---
|
||
|
||
### **6. Оптимизации**
|
||
1. **Чередование данных (Interleaving)**:
|
||
- Видео и аудио секторы чередуются на диске для плавности.
|
||
- Пример структуры:
|
||
```
|
||
[VIDEO] [AUDIO] [VIDEO] [AUDIO]...
|
||
```
|
||
|
||
2. **Предзагрузка**:
|
||
- Игры заранее грузят несколько кадров в память.
|
||
|
||
3. **Пропуск кадров**:
|
||
- Если декодирование не успевает, PS1 пропускает кадры (например, в Resident Evil).
|
||
|
||
---
|
||
|
||
### **7. Пример кода (упрощённый цикл FMV)**
|
||
```c
|
||
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 не успевает, видео тормозит.
|
||
|
||
---
|
||
|
||
### **Итог**
|
||
1. **Чтение** с CD-ROM через DMA.
|
||
2. **Декодирование** через MDEC.
|
||
3. **Вывод** через GPU.
|
||
4. **Синхронизация** с VBlank.
|
||
|
||
|
||
-----------------
|
||
|
||
|
||
# Функция `DecDCTvlc2` в библиотеке PsyQ для PlayStation 1
|
||
|
||
Функция `DecDCTvlc2` является частью аппаратно-оптимизированной библиотеки декодирования видеоданных в реальном времени для PlayStation 1. Она выполняет декодирование **переменнодлинных кодов (VLC - Variable Length Coding)** в процессе декомпрессии **DCT (Discrete Cosine Transform)** данных, обычно используемых в видеоформатах типа MPEG.
|
||
|
||
## Основное назначение
|
||
|
||
`DecDCTvlc2` декодирует:
|
||
1. Битовые потоки с переменнодлинным кодированием (VLC)
|
||
2. Коэффициенты DCT после обратного зигзаг-сканирования
|
||
3. Данные в формате, используемом аппаратным декодером PS1
|
||
|
||
## Технические детали
|
||
|
||
```c
|
||
void DecDCTvlc2(unsigned short *data, int *macroblock);
|
||
```
|
||
|
||
Где:
|
||
- `data` - указатель на входной битовый поток VLC
|
||
- `macroblock` - указатель на выходной буфер для декодированных данных макроблока (6 блоков по 64 элемента: 4 яркостных, 2 цветностных)
|
||
|
||
## Особенности реализации
|
||
|
||
1. **Аппаратное ускорение** - использует специальные возможности графического процессора PS1
|
||
2. **Оптимизировано для MPEG-подобных потоков** - но не является полным MPEG-декодером
|
||
3. **Работает с макроблоками** - 16x16 пикселей (4 блока яркости Y, 1 блок каждой цветности Cr/Cb)
|
||
4. **Требует предварительной настройки** - через `DecDCTin()`, `DecDCTout()` и другие функции видеоцепи
|
||
|
||
## Пример использования
|
||
|
||
```c
|
||
#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);
|
||
|
||
// Дальнейшая обработка декодированных данных...
|
||
}
|
||
```
|
||
|
||
## Типичный контекст использования
|
||
|
||
1. **Декодирование внутрикадровых (I-кадров) MPEG-видео**
|
||
2. **Обработка видеопотоков в реальном времени**
|
||
3. **Специальные эффекты с использованием DCT-данных**
|
||
4. **Собственные алгоритмы видеокомпрессии**
|
||
|
||
## Важные нюансы
|
||
|
||
1. **Не является полным MPEG-декодером** - требует дополнительной логики для полного декодирования видео
|
||
|
||
2. **Требует точного соответствия формату данных** - входной поток должен быть правильно сформирован
|
||
|
||
3. **Работает в паре с другими функциями DCT-цепи**:
|
||
- `DecDCTin()` - настройка входного буфера
|
||
- `DecDCTout()` - настройка выходного буфера
|
||
- `DecDCToutCallback()` - установка callback'а по завершении
|
||
|
||
4. **Использует аппаратные возможности PS1** - дает значительный выигрыш в производительности по сравнению с программной реализацией
|
||
|
||
Функция `DecDCTvlc2` представляет собой низкоуровневый инструмент для работы с видео на PS1 и требует глубокого понимания принципов DCT-компрессии и форматов видеоданных, используемых на этой платформе. |