diff --git a/assets/convert.sh b/assets/convert.sh index b0f428d..4edd172 100644 --- a/assets/convert.sh +++ b/assets/convert.sh @@ -1,15 +1,15 @@ #!/bin/sh palette="palette.png" -video="video.webm" -# output="video_out.gif" +video="kiss2x.mp4" +# output="kiss2x_out.mp4" output="out/out_%d.bmp" filters="scale=256x192:flags=lanczos:force_original_aspect_ratio=decrease,pad=256:192:-1:-1:color=black" # video ffmpeg -i $video -vf "$filters,palettegen=max_colors=256:reserve_transparent=0:stats_mode=diff" -y $palette -ffmpeg -i $video -i $palette -filter_complex "$filters,setpts=0.8*PTS[x];[x][1:v]paletteuse=dither=none" -y $output +ffmpeg -i $video -i $palette -filter_complex "$filters[x];[x][1:v]paletteuse=dither=none" -y $output # audio -ffmpeg -i $video -f s16le -filter:a "atempo=1.25" -vn -ac 1 -ar 22050 -y music.raw \ No newline at end of file +ffmpeg -i $video -f s16le -vn -ac 1 -ar 22050 -y music.raw \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b2973e5..dc39897 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,96 +1,133 @@ #include "fastlz.h" #include "filesystem.h" +#include "nds/arm9/background.h" #include "nds/arm9/console.h" +#include "nds/cothread.h" +#include "nds/dma.h" +#include "nds/timers.h" #include "raylibds.hpp" -#include +#include +#include +#include +#include #include #include #define PALETTE_SIZE 256 #define FRAME_SIZE 256*192 + +// TODO: hardcode fix #define CHUNK_SIZE 10 #define QUEUE_SIZE 6 #define FRAMERATE 30 +#define VIDEO_TOTALFRAME 827 -#define STREAM_CHUNK_SIZE 32000 -#define STREAM_QUEUE_SIZE 4 -#define MMSTREAM_BUF_SIZE 9600 +#define MMSTREAM_BUF_SIZE 800 +#define MUSIC_BUFFER_SIZE 16000 void wait_forever(const char* msg); -void LoadNextChunk(void *output); +void LoadNextChunk(); void TimerCallback(); void VBLCallback(); void FrameStep(); int ThreadEntrypoint(void *arg); +int ThreadVideo(void *arg); mm_word on_stream_request( mm_word length, mm_addr dest, mm_stream_formats format ); -class CircularQueue { -public: - int front, rear, size, current; - CircularQueue(int s) { - size = s; - front = rear = -1; - current = 0; - } +struct RingBuffer { + char *buffer = nullptr; + int head; + int tail; + int size; + int count; - bool isFull() { - return (front == 0 && rear == size - 1) || (rear == (front - 1) % (size - 1)); - } + RingBuffer(size_t size_t) + { + buffer = (char*)malloc(size_t); + size = size_t; + head = 0; + tail = 0; + count = 0; + } - bool isEmpty() { - return front == -1; - } + ~RingBuffer() + { + free(buffer); + } - void push() { - if (isFull()) { - fprintf(stderr, "Queue is full. Cannot push\n"); - return; - } + int read(char *dest, size_t len) + { + if (count >= len) + { + count -= len; + int linear_len = size - head; - if (isEmpty()) { - front = rear = 0; - } else { - rear = (rear + 1) % size; - } + if (len < linear_len) + memcpy(dest, &buffer[head], len); + // swiCopy(&buffer[head], dest, len); + else + { + memcpy(dest, &buffer[head], linear_len); + memcpy(&dest[linear_len], buffer, len-linear_len); + // swiCopy(&buffer[head], dest, linear_len); + // swiCopy(buffer, &dest[linear_len], len-linear_len); + } - current++; - } + head = (head+len) % size; - void pop() { - if (isEmpty()) { - fprintf(stderr, "Queue is empty. Cannot pop\n"); - return; - } + return 1; + } - if (front == rear) { - front = rear = -1; - } else { - front = (front + 1) % size; - } + return 0; + } - current--; - } + int write(char *data, size_t len) + { + if (count+len <= size) + { + count += len; + int linear_len = size - tail; + + if (len < linear_len) + memcpy(&buffer[tail], data, len); + // swiCopy(data, &buffer[tail], len); + else + { + memcpy(&buffer[tail], data, linear_len); + memcpy(buffer, &data[linear_len], len-linear_len); + // swiCopy(data, &buffer[tail], linear_len); + // swiCopy(&data[linear_len], buffer, len-linear_len); + } + + tail = (tail+len) % size; + + return 1; + } + + return 0; + } }; int ptr_background; int ptr_subbackground; -FILE *file_image = nullptr; +FILE *file_video = nullptr; FILE *file_music = nullptr; +int file_music_len; +int file_video_len; -int chunk_current = 0; +int framecounter = 0; -uint8_t *frame_decompress; -uint8_t *frame_buffer; -uint8_t *stream_buffer; +RingBuffer music_rb(MUSIC_BUFFER_SIZE); +void *music_btemp; -int stream_index; -int stream_buffer_current; -bool stream_need_update = false; +RingBuffer video_rb(FRAME_SIZE*CHUNK_SIZE*QUEUE_SIZE); +void *video_btemp; +void *video_decompress; +void *video_compress; -CircularQueue frame_queue(QUEUE_SIZE); -CircularQueue stream_queue(STREAM_QUEUE_SIZE); - -int music_buffer_size = 4000*16; +u32 timer_video_start = 0; +u32 timer_video_end = 0; +u32 timer_video_avg = 0; int main(void) { @@ -101,27 +138,28 @@ int main(void) { vramSetBankC(VRAM_C_SUB_BG); vramSetBankF(VRAM_F_TEX_PALETTE); - // consoleDemoInit(); + consoleDemoInit(); consoleDebugInit(DebugDevice_NOCASH); - frame_decompress = (uint8_t*)malloc(FRAME_SIZE*CHUNK_SIZE); - frame_buffer = (uint8_t*)malloc(FRAME_SIZE*CHUNK_SIZE*QUEUE_SIZE); - stream_buffer = (uint8_t*)malloc(STREAM_CHUNK_SIZE*STREAM_QUEUE_SIZE); - - sassert( - frame_buffer != nullptr || - frame_decompress != nullptr || - stream_buffer != nullptr, - "failed to allocate memmory"); + video_decompress = malloc(FRAME_SIZE*CHUNK_SIZE); + video_compress = malloc(FRAME_SIZE*CHUNK_SIZE); + video_btemp = malloc(FRAME_SIZE); + music_btemp = malloc(MUSIC_BUFFER_SIZE); bool nitrofs = nitroFSInit(NULL); sassert(nitrofs, "error nitrofs"); - file_image = fopen("nitro:/image.bin", "rb"); - sassert(file_image != nullptr, "failed to load image.bin"); + file_video = fopen("nitro:/image.bin", "rb"); + sassert(file_video != nullptr, "failed to load image.bin"); + fseek(file_video, 0, SEEK_END); + file_video_len = ftell(file_video); + fseek(file_video, 0, SEEK_SET); file_music = fopen("nitro:/music.raw", "rb"); - sassert(file_image != nullptr, "failed to load music.raw"); + sassert(file_music != nullptr, "failed to load music.raw"); + fseek(file_music, 0, SEEK_END); + file_music_len = ftell(file_music); + fseek(file_music, 0, SEEK_SET); int pal_len; unsigned char* pal = Raylib::LoadFile("nitro:/palette.bin", pal_len); @@ -135,18 +173,18 @@ int main(void) { free(pal); - while(!frame_queue.isFull()) { - frame_queue.push(); - LoadNextChunk(&frame_buffer[FRAME_SIZE*CHUNK_SIZE*frame_queue.rear]); + Raylib::nocashMessageFormat("preload video"); + while(video_rb.size-video_rb.count >= FRAME_SIZE*CHUNK_SIZE) { + LoadNextChunk(); + video_rb.write((char*)video_decompress, FRAME_SIZE*CHUNK_SIZE); } - while(!stream_queue.isFull()) { - stream_queue.push(); - fread(&stream_buffer[STREAM_CHUNK_SIZE*stream_queue.rear], sizeof(uint8_t), STREAM_CHUNK_SIZE, file_music); + { + Raylib::nocashMessageFormat("preload music"); + int len = music_rb.size - music_rb.count; + fread(music_btemp, sizeof(char), len, file_music); + music_rb.write((char*)music_btemp, len); } - stream_index = 0; - - DC_FlushAll(); mm_ds_system sys; sys.mod_count = 0; @@ -163,16 +201,58 @@ int main(void) { mystream.timer = MM_TIMER2; mystream.manual = false; - timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(FRAMERATE), TimerCallback); + timerStart(3, ClockDivider_1024, TIMER_FREQ_1024(FRAMERATE), TimerCallback); cothread_create(ThreadEntrypoint, NULL, 0, COTHREAD_DETACHED); + cothread_create(ThreadVideo, NULL, 0, COTHREAD_DETACHED); mmStreamOpen( &mystream ); + cpuStartTiming(0); + while(1) { cothread_yield_irq(IRQ_VBLANK); scanKeys(); if (keysUp() & KEY_A) break; + + printf("\033[4;2H\033[Kvideo buffer: %i", video_rb.count); + printf("\033[5;2H\033[Kmusic buffer: %i", music_rb.count); + printf("\033[6;2H\033[Kprogress: %0.f%%", ((float)framecounter/VIDEO_TOTALFRAME)*100); + + static int frame = 0; + frame++; + + if (frame % 30 == 0) + { + printf("\033[8;2H\033[Kavg decoding time: %" PRIu32 "ms", timerTicks2msec(timer_video_avg/30)); + timer_video_avg = 0; + } + } + + return 0; +} + +int ThreadVideo(void *arg) +{ + while(1) { + cothread_yield(); + + if (file_video != nullptr && video_rb.size-video_rb.count >= FRAME_SIZE*CHUNK_SIZE) + { + timer_video_start = cpuGetTiming(); + + LoadNextChunk(); + video_rb.write((char*)video_decompress, FRAME_SIZE*CHUNK_SIZE); + + timer_video_end = cpuGetTiming(); + timer_video_avg += timer_video_end - timer_video_start; + + if (ftell(file_video) == file_video_len) + { + fclose(file_video); + file_video = nullptr; + } + } } return 0; @@ -181,24 +261,23 @@ int main(void) { int ThreadEntrypoint(void *arg) { while(1) { - if (!frame_queue.isFull()) - { - frame_queue.push(); - LoadNextChunk(&frame_buffer[FRAME_SIZE*CHUNK_SIZE*frame_queue.rear]); - } cothread_yield(); - if (!stream_queue.isFull()) + if (file_music != nullptr && music_rb.count < MUSIC_BUFFER_SIZE/2) { - stream_queue.push(); + int len = music_rb.size - music_rb.count; + + if ((file_music_len - ftell(file_music)) < len) + { + memset(music_btemp, 0, MUSIC_BUFFER_SIZE); + fread(music_btemp, sizeof(char), (file_music_len - ftell(file_music)), file_music); + fclose(file_music); + file_music = nullptr; + } else + fread(music_btemp, sizeof(char), len, file_music); - fread(stream_buffer+ stream_queue.rear*STREAM_CHUNK_SIZE, sizeof(uint8_t), STREAM_CHUNK_SIZE, file_music); - if (feof(file_music)) { - memset(stream_buffer+ stream_queue.rear*STREAM_CHUNK_SIZE, 0, STREAM_CHUNK_SIZE); - mmStreamClose(); - } + music_rb.write((char*)music_btemp, len); } - cothread_yield(); } return 0; @@ -206,16 +285,11 @@ int ThreadEntrypoint(void *arg) void FrameStep() { - // if (feof(file_image)) return; - - dmaCopyAsynch(&frame_buffer[FRAME_SIZE*CHUNK_SIZE*frame_queue.front + FRAME_SIZE*chunk_current], bgGetGfxPtr(ptr_background), FRAME_SIZE); - dmaCopyAsynch(&frame_buffer[FRAME_SIZE*CHUNK_SIZE*frame_queue.front + FRAME_SIZE*chunk_current], bgGetGfxPtr(ptr_subbackground), FRAME_SIZE); - - chunk_current++; - - if (chunk_current >= CHUNK_SIZE) { - chunk_current = 0; - frame_queue.pop(); + if (video_rb.count) + { + video_rb.read((char*)video_btemp, FRAME_SIZE); + dmaCopyAsynch(video_btemp, bgGetGfxPtr(ptr_background), FRAME_SIZE); + framecounter++; } } @@ -224,17 +298,16 @@ void TimerCallback() FrameStep(); } -void LoadNextChunk(void *output) +void LoadNextChunk() { uint32_t compress_size; - fread(&compress_size, sizeof(uint32_t), 1, file_image); + fread(&compress_size, sizeof(uint32_t), 1, file_video); - fread(frame_decompress, sizeof(uint8_t), compress_size, file_image); - fastlz_decompress(frame_decompress, compress_size, output, FRAME_SIZE*CHUNK_SIZE); + fread(video_compress, sizeof(uint8_t), compress_size, file_video); + fastlz_decompress(video_compress, compress_size, video_decompress, FRAME_SIZE*CHUNK_SIZE); } mm_word on_stream_request( mm_word length, mm_addr dest, mm_stream_formats format ) { - size_t samplesize = 1; switch(format){ case MM_STREAM_8BIT_MONO: samplesize = 1; break; @@ -244,29 +317,12 @@ mm_word on_stream_request( mm_word length, mm_addr dest, mm_stream_formats forma } int len = length*samplesize; - if (feof(file_music)) + if (music_rb.count >= len) { - mmStreamClose(); - uint8_t temp[len]; - memset(temp, 0, len); - DC_FlushAll(); - dmaCopyAsynch(temp, dest, len); - return length; + music_rb.read((char*)dest, len); } - int bytesToCopy = std::min(len, STREAM_CHUNK_SIZE-stream_index); - dmaCopyAsynch(stream_buffer + stream_queue.front*STREAM_CHUNK_SIZE + stream_index, dest, bytesToCopy); - stream_index += bytesToCopy; + if (music_rb.count < len && file_music == nullptr) mmStreamClose(); - if (stream_index >= STREAM_CHUNK_SIZE) - { - stream_queue.pop(); - stream_index = len - bytesToCopy; - // stream_index = 0; - - dmaCopyAsynch(stream_buffer + stream_queue.front*STREAM_CHUNK_SIZE, dest+bytesToCopy, stream_index); - } - - // return bytesToCopy/2; return length; } \ No newline at end of file