// Native NTLite Bink video player. // // This application intentionally uses the native syscall ABI directly. Do not // include here; Win32 compatibility belongs in the Win32 personality // layer, not in native apps. #include #include #include #include #include #include "bink.h" #define VIDEO_USAGE "usage: video \n" \ " video --help\n" \ "Plays a Bink video file from disk.\n" static void print_span(const char* s, size_t len) { if (!s || !len) return; SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(s, len) }; SysWriteConsole(&req); } static void print(const char* s) { if (!s) return; SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); } static void print_u64(uint64_t v) { char buf[32]; int i = 0; if (v == 0) { print("0"); return; } while (v && i < (int)sizeof(buf)) { buf[i++] = (char)('0' + (v % 10)); v /= 10; } while (i) { char c = buf[--i]; print_span(&c, 1); } } static void fail_status(const char* what, uint32_t status) { print("video: "); print(what); print(" failed status="); print_u64(status); print("\n"); while (true) SysExitProcess(1); } static void exit_now(int code) { while (true) SysExitProcess(code); } static SYS_STRING_VIEW command_line_view(void) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC) return SYS_SV_NULL; return peb->CommandLine; } static int sv_is_space(char c) { return c == ' ' || c == '\t'; } static SYS_STRING_VIEW skip_one_token(SYS_STRING_VIEW s) { size_t i = 0; while (i < s.Length && sv_is_space(s.Data[i])) i++; if (i < s.Length && s.Data[i] == '"') { i++; while (i < s.Length && s.Data[i] != '"') i++; if (i < s.Length && s.Data[i] == '"') i++; } else { while (i < s.Length && !sv_is_space(s.Data[i])) i++; } while (i < s.Length && sv_is_space(s.Data[i])) i++; return SysSvFromParts(s.Data + i, s.Length - i); } static SYS_STRING_VIEW command_line_first_argument(SYS_STRING_VIEW cmdline) { SYS_STRING_VIEW rest = skip_one_token(cmdline); if (!rest.Data || rest.Length == 0) return SYS_SV_NULL; size_t begin = 0; size_t end = 0; if (rest.Data[0] == '"') { begin = 1; end = begin; while (end < rest.Length && rest.Data[end] != '"') end++; } else { begin = 0; end = 0; while (end < rest.Length && !sv_is_space(rest.Data[end])) end++; } if (end <= begin) return SYS_SV_NULL; return SysSvFromParts(rest.Data + begin, end - begin); } static int sv_equals_cstr(SYS_STRING_VIEW s, const char* cstr) { size_t len = 0; if (!s.Data || !cstr) return 0; while (cstr[len]) len++; if (s.Length != len) return 0; return memcmp(s.Data, cstr, len) == 0; } static int video_argument_is_help(SYS_STRING_VIEW arg) { return sv_equals_cstr(arg, "-h") || sv_equals_cstr(arg, "--help") || sv_equals_cstr(arg, "/?"); } static void print_usage(void) { print(VIDEO_USAGE); } static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize) { if (!out || outSize == 0) return ""; if (!path.Data || path.Length == 0) { out[0] = '\0'; return out; } size_t n = path.Length; if (n + 1 > outSize) n = outSize - 1; memcpy(out, path.Data, n); out[n] = '\0'; return out; } static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) { SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(path, SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, handleOut); return SysOpenFile(&req); } int main(void) { SYS_STRING_VIEW cmdline = command_line_view(); SYS_STRING_VIEW pathView = command_line_first_argument(cmdline); if (!pathView.Data || pathView.Length == 0 || video_argument_is_help(pathView)) { print_usage(); exit_now(0); } print("video: starting\n"); if (cmdline.Data && cmdline.Length) { print_span(cmdline.Data, cmdline.Length); print("\n"); } uint32_t file = 0; uint32_t status = open_file_view(pathView, &file); if (status != NTLSTATUS_SUCCESS) { char pathBuf[260]; print("video: failed to open input file: "); print(path_to_cstr(pathView, pathBuf, sizeof(pathBuf))); print("\n"); fail_status("SysOpenFile", status); } uint64_t fileSize = 0; status = SysGetFileSize(file, &fileSize); if (status != NTLSTATUS_SUCCESS) { SysCloseHandle(file); fail_status("SysGetFileSize", status); } if (fileSize == 0) { SysCloseHandle(file); print("video: input file is empty\n"); exit_now(1); } uint32_t mapping = 0; status = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, fileSize, &mapping); if (status != NTLSTATUS_SUCCESS || !mapping) { SysCloseHandle(file); fail_status("SysCreateFileMapping", status); } void* buf = 0; status = SysMapFile(mapping, 0, (size_t)fileSize, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &buf); if (status != NTLSTATUS_SUCCESS || !buf) { SysCloseHandle(mapping); SysCloseHandle(file); fail_status("SysMapFile", status); } // The mapped VAD owns the file reference now; keep only the mapped pointer // alive while Bink is open. This avoids a second whole-file heap allocation // and lets the VM fault file pages in on demand. SysCloseHandle(mapping); SysCloseHandle(file); print("video: input file mapped\n"); if (!BinkSoundUseWaveOut()) { print("video: BinkSoundUseWaveOut failed: "); const char* err = BinkGetError(); print(err ? err : "unknown"); print("\n"); SysFreeVm(buf); exit_now(1); } print("video: WaveOut sound system ready\n"); HBINK b = BinkOpen((const char*)buf, BINKFROMMEMORY); if (!b) { print("video: BinkOpen failed: "); const char* err = BinkGetError(); print(err ? err : "unknown"); print("\n"); SysFreeVm(buf); exit_now(1); } print("video: Bink open tracks="); print_u64((uint64_t)(b->NumTracks < 0 ? 0 : b->NumTracks)); print(" playing="); print_u64(b->playingtracks); print(" soundon="); print_u64(b->soundon); print("\n"); for (int32_t track = 0; track < b->NumTracks; track++) { print("video: track "); print_u64((uint64_t)track); print(" id="); print_u64(BinkGetTrackID(b, (uint32_t)track)); print(" type="); print_u64(BinkGetTrackType(b, (uint32_t)track)); print(" maxBytes="); print_u64(BinkGetTrackMaxSize(b, (uint32_t)track)); print("\n"); } uint32_t width = b->Width; uint32_t height = b->Height; if (!width || !height) { print("video: invalid video dimensions\n"); BinkClose(b); SysFreeVm(buf); exit_now(1); } uint32_t surface = 0; SYS_CREATE_SURFACE_REQUEST surfaceReq; memset(&surfaceReq, 0, sizeof(surfaceReq)); surfaceReq.Size = sizeof(surfaceReq); surfaceReq.Width = width; surfaceReq.Height = height; surfaceReq.Format = SYS_SURFACE_FORMAT_XRGB8888; surfaceReq.Flags = SYS_SURFACE_FLAG_NONE; surfaceReq.HandleOut = &surface; status = SysCreateSurface(&surfaceReq); if (status != NTLSTATUS_SUCCESS || !surface) { BinkClose(b); SysFreeVm(buf); fail_status("SysCreateSurface", status); } void* surfaceBase = 0; uint32_t pitchBytes = 0; status = SysMapSurface(surface, &surfaceBase, &pitchBytes); if (status != NTLSTATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) { SysCloseHandle(surface); BinkClose(b); SysFreeVm(buf); fail_status("SysMapSurface", status); } print("video: native surface ready\n"); print("video: decode loop entered\n"); DISPLAY_RECT fullFrame = { 0, 0, (int32_t)width, (int32_t)height }; int firstFramePresented = 0; while (1) { if (BinkWait(b)) { // Bink can be waiting on its decode, I/O, or WaveOut worker. Yield // a millisecond instead of monopolizing the CPU with a hot poll. SysSleep(1); continue; } BinkDoFrame(b); if (BinkShouldSkip(b)) { BinkNextFrame(b); BinkDoFrame(b); continue; } BinkCopyToBuffer(b, surfaceBase, pitchBytes, height, 0, 0, BINKSURFACE32); status = SysPresentSurface(surface, &fullFrame, 1); if (status != NTLSTATUS_SUCCESS) { SysCloseHandle(surface); BinkClose(b); SysFreeVm(buf); fail_status("SysPresentSurface", status); } if (!firstFramePresented) { firstFramePresented = 1; print("video: first frame presented sndBytes="); print_u64(b->SndSize); print(" soundon="); print_u64(b->soundon); print(" playing="); print_u64(b->playingtracks); print("\n"); } if (b->FrameNum == b->Frames) break; BinkNextFrame(b); } print("video: playback complete frame="); print_u64(b->FrameNum); print(" sndBytes="); print_u64(b->SndSize); print("\n"); print("video: closing surface\n"); SysCloseHandle(surface); print("video: closing Bink\n"); BinkClose(b); print("video: Bink closed\n"); print("video: freeing input mapping\n"); SysFreeVm(buf); print("video: goodbye\n"); exit_now(0); }