diff options
Diffstat (limited to 'Src/Plugins/Input/in_mp4/VirtualIO.cpp')
-rw-r--r-- | Src/Plugins/Input/in_mp4/VirtualIO.cpp | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp4/VirtualIO.cpp b/Src/Plugins/Input/in_mp4/VirtualIO.cpp new file mode 100644 index 00000000..b5ea4fc9 --- /dev/null +++ b/Src/Plugins/Input/in_mp4/VirtualIO.cpp @@ -0,0 +1,716 @@ +#include "main.h" +#include "VirtualIO.h" +#include "api__in_mp4.h" +#include "api/service/waservicefactory.h" +#include "../../..\Components\wac_network\wac_network_http_receiver_api.h" +#include "../nu/AutoChar.h" +#include "../nu/ProgressTracker.h" + +#include <assert.h> +#include <strsafe.h> + +#define HTTP_BUFFER_SIZE 65536 +// {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C} +static const GUID internetConfigGroupGUID = +{ + 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c } +}; + +static void SetUserAgent(api_httpreceiver *http) +{ + char agent[256] = {0}; + StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString()); + http->addheader(agent); +} + +static api_httpreceiver *SetupConnection(const char *url, uint64_t start_position, uint64_t end_position) +{ + api_httpreceiver *http = 0; + waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); + if (sf) http = (api_httpreceiver *)sf->getInterface(); + + if (!http) + return http; + + int use_proxy = 1; + bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false); + if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3))) + use_proxy = 0; + + const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0; + http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL); + if (start_position && start_position != (uint64_t)-1) + { + if (end_position == (uint64_t)-1) + { + char temp[128] = {0}; + StringCchPrintfA(temp, 128, "Range: bytes=%I64u-", start_position); + http->addheader(temp); + } + else + { + char temp[128] = {0}; + StringCchPrintfA(temp, 128, "Range: bytes=%I64u-%I64u", start_position, end_position); + http->addheader(temp); + } + } + SetUserAgent(http); + http->connect(url); + return http; +} + +static DWORD CALLBACK ProgressiveThread(LPVOID param); + +static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod) +{ + LARGE_INTEGER li; + + li.QuadPart = distance; + + li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod); + + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + li.QuadPart = -1; + } + + return li.QuadPart; +} + +int bufferCount; +static void Buffering(int bufStatus, const wchar_t *displayString) +{ + if (bufStatus < 0 || bufStatus > 100) + return; + + char tempdata[75*2] = {0, }; + + int csa = mod.SAGetMode(); + if (csa & 1) + { + for (int x = 0; x < bufStatus*75 / 100; x ++) + tempdata[x] = x * 16 / 75; + } + else if (csa&2) + { + int offs = (csa & 1) ? 75 : 0; + int x = 0; + while (x < bufStatus*75 / 100) + { + tempdata[offs + x++] = -6 + x * 14 / 75; + } + while (x < 75) + { + tempdata[offs + x++] = 0; + } + } + else if (csa == 4) + { + tempdata[0] = tempdata[1] = (bufStatus * 127 / 100); + } + if (csa) mod.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa); + + /* + TODO + wchar_t temp[64] = {0}; + StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus); + SetStatus(temp); + */ + //SetVideoStatusText(temp); // TODO: find a way to set the old status back + // videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f)); +} + +class ProgressiveReader +{ +public: + ProgressiveReader(const char *url, HANDLE killswitch) : killswitch(killswitch) + { + thread_abort = CreateEvent(NULL, FALSE, FALSE, NULL); + download_thread = 0; + progressive_file_read = 0; + progressive_file_write = 0; + + content_length=0; + current_position=0; + stream_disconnected=false; + connected=false; + end_of_file=false; + + wchar_t temppath[MAX_PATH-14] = {0}; // MAX_PATH-14 'cause MSDN said so + GetTempPathW(MAX_PATH-14, temppath); + GetTempFileNameW(temppath, L"wdl", 0, filename); + this->url = _strdup(url); + http = SetupConnection(url, 0, (uint64_t)-1); + + progressive_file_read = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); + progressive_file_write = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); + download_thread = CreateThread(0, 0, ProgressiveThread, this, 0, 0); + + while (!connected && !stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT) + { + // nop + } + Buffer(); + } + + ~ProgressiveReader() + { + if (download_thread) + { + SetEvent(thread_abort); + WaitForSingleObject(download_thread, INFINITE); + CloseHandle(download_thread); + } + + if (thread_abort) + { + CloseHandle(thread_abort); + } + + CloseHandle(progressive_file_read); + CloseHandle(progressive_file_write); + DeleteFile(filename); + + if (http) + { + waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); + if (sf) http = (api_httpreceiver *)sf->releaseInterface(http); + http=0; + } + } + + void Buffer() + { + bufferCount=0; + for (int i=0;i<101;i++) + { + Buffering(i, L"Buffering: "); + WaitForSingleObject(killswitch, 55); + } + } + + void OnFinish() + { + stream_disconnected=true; + } + + + bool WaitForPosition(uint64_t position, uint64_t size) + { + do + { + bool valid = progress_tracker.Valid(position, position+size); + if (valid) + return true; + else + { + if (position < current_position) + { + Reconnect(position, position+size); + } + else + { + Buffer(); + } + } + } while (WaitForSingleObject(killswitch, 0) == WAIT_TIMEOUT); + return false; + } + + size_t Read(void *buffer, size_t size) + { + if (WaitForPosition(current_position, (uint64_t)size) == false) + return 0; + + DWORD bytes_read=0; + ReadFile(progressive_file_read, buffer, size, &bytes_read, NULL); + current_position += bytes_read; + return bytes_read; + } + + uint64_t GetFileLength() + { + return content_length; + } + + void Reconnect(uint64_t position, uint64_t end) + { + SetEvent(thread_abort); + WaitForSingleObject(download_thread, INFINITE); + ResetEvent(thread_abort); + + uint64_t new_start, new_end; + progress_tracker.Seek(position, end, &new_start, &new_end); + + CloseHandle(download_thread); + stream_disconnected=false; + connected=false; + if (http) + { + waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); + if (sf) http = (api_httpreceiver *)sf->releaseInterface(http); + http=0; + } + + http = SetupConnection(url, new_start, new_end); + Seek64(progressive_file_write, new_start, SEEK_SET); + download_thread = CreateThread(0, 0, ProgressiveThread, this, 0, 0); + while (!connected && !stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT) + { + // nop + } + Buffer(); + } + + int SetPosition(uint64_t position) + { + if (position == content_length) + { + end_of_file=true; + } + else + { + if (!progress_tracker.Valid(position, position)) + { + Reconnect(position, (uint64_t)-1); + } + current_position = Seek64(progressive_file_read, position, SEEK_SET); + end_of_file=false; + } + return 0; + } + + int GetPosition(uint64_t *position) + { + if (end_of_file) + *position = content_length; + else + *position = current_position; + return 0; + } + + int EndOfFile() + { + return !!stream_disconnected; + } + + int Close() + { + SetEvent(thread_abort); + while (!stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT) + { + // nop + } + return 0; + } + + /* API used by download thread */ + void Write(const void *data, size_t data_len) + { + DWORD bytes_written = 0; + WriteFile(progressive_file_write, data, data_len, &bytes_written, 0); + progress_tracker.Write(data_len); + } + + int Wait(int milliseconds) + { + HANDLE handles[] = {killswitch, thread_abort}; + int ret = WaitForMultipleObjects(2, handles, FALSE, milliseconds); + if (ret == WAIT_OBJECT_0+1) + return 1; + else if (ret == WAIT_TIMEOUT) + return 0; + else + return -1; + } + + int DoRead(void *buffer, size_t bufferlen) + { + int ret = http->run(); + int bytes_received; + do + { + ret = http->run(); + bytes_received= http->get_bytes(buffer, bufferlen); + if (bytes_received) + Write(buffer, bytes_received); + } while (bytes_received); + return ret; + } + + int Connect() + { + do + { + int ret = http->run(); + if (ret == -1) // connection failed + return ret; + + // ---- check our reply code ---- + int replycode = http->getreplycode(); + switch (replycode) + { + case 0: + case 100: + break; + case 200: + case 206: + { + + const char *content_length_header = http->getheader("Content-Length"); + if (content_length_header) + { + uint64_t new_content_length = _strtoui64(content_length_header, 0, 10); + //InterlockedExchange64((volatile LONGLONG *)&content_length, new_content_length); + content_length = new_content_length; // TODO interlock on win32 + } + connected=true; + + return 0; + } + default: + return -1; + } + } + while (Wait(55) == 0); + return 0; + } + +private: + uint64_t current_position; + volatile uint64_t content_length; + bool end_of_file; + bool stream_disconnected; + bool connected; + char *url; + wchar_t filename[MAX_PATH]; + HANDLE progressive_file_read, progressive_file_write; + ProgressTracker progress_tracker; + HANDLE killswitch; + HANDLE download_thread; + api_httpreceiver *http; + HANDLE thread_abort; +}; + +static DWORD CALLBACK ProgressiveThread(LPVOID param) +{ + ProgressiveReader *reader = (ProgressiveReader *)param; + + if (reader->Connect() == 0) + { + int ret = 0; + while (ret == 0) + { + ret=reader->Wait(10); + if (ret >= 0) + { + char buffer[HTTP_BUFFER_SIZE] = {0}; + reader->DoRead(buffer, sizeof(buffer)); + } + } + } + reader->OnFinish(); + + return 0; +} + +u_int64_t HTTPGetFileLength(void *user) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->GetFileLength(); +} + +int HTTPSetPosition(void *user, u_int64_t position) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->SetPosition(position); +} + +int HTTPGetPosition(void *user, u_int64_t *position) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->GetPosition(position); +} + +size_t HTTPRead(void *user, void *buffer, size_t size) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->Read(buffer, size); +} + +size_t HTTPWrite(void *user, void *buffer, size_t size) +{ + return 1; +} + +int HTTPEndOfFile(void *user) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->EndOfFile(); +} + +int HTTPClose(void *user) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + return reader->Close(); +} + +Virtual_IO HTTPIO = +{ + HTTPGetFileLength, + HTTPSetPosition, + HTTPGetPosition, + HTTPRead, + HTTPWrite, + HTTPEndOfFile, + HTTPClose, +}; + +void *CreateReader(const wchar_t *url, HANDLE killswitch) +{ + + if ( WAC_API_DOWNLOADMANAGER ) + { + return new ProgressiveReader(AutoChar(url), killswitch); + } + else + return 0; +} + +void DestroyReader(void *user) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + delete reader; +} + +void StopReader(void *user) +{ + ProgressiveReader *reader = (ProgressiveReader *)user; + reader->Close(); +} + +/* ----------------------------------- */ + +struct Win32_State +{ + Win32_State() + { + memset(buffer, 0, sizeof(buffer)); + handle=0; + endOfFile=false; + position.QuadPart = 0; + event = CreateEvent(NULL, TRUE, TRUE, NULL); + read_offset=0; + io_active=false; + } + ~Win32_State() + { + if (handle && handle != INVALID_HANDLE_VALUE) + CancelIo(handle); + CloseHandle(event); + } + // void *userData; + HANDLE handle; + bool endOfFile; + LARGE_INTEGER position; + HANDLE event; + OVERLAPPED overlapped; + DWORD read_offset; + bool io_active; + char buffer[16384]; +}; + +static __int64 FileSize64(HANDLE file) +{ + LARGE_INTEGER position; + position.QuadPart=0; + position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart); + + if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) + return INVALID_FILE_SIZE; + else + return position.QuadPart; +} + +u_int64_t UnicodeGetFileLength(void *user) +{ + Win32_State *state = static_cast<Win32_State *>(user); + assert(state->handle); + return FileSize64(state->handle); +} + + + +int UnicodeSetPosition(void *user, u_int64_t position) +{ + Win32_State *state = static_cast<Win32_State *>(user); + assert(state->handle); + __int64 diff = position - state->position.QuadPart; + + if ((diff+state->read_offset) >= sizeof(state->buffer) + || (diff+state->read_offset) < 0) + { + CancelIo(state->handle); + state->io_active = 0; + state->read_offset = 0; + } + else if (diff) + state->read_offset += (DWORD)diff; + + state->position.QuadPart = position; + state->endOfFile = false; + return 0; +} + +int UnicodeGetPosition(void *user, u_int64_t *position) +{ + Win32_State *state = static_cast<Win32_State *>(user); + assert(state->handle); + *position = state->position.QuadPart; + return 0; +} + +static void DoRead(Win32_State *state) +{ + WaitForSingleObject(state->event, INFINITE); + state->overlapped.hEvent = state->event; + state->overlapped.Offset = state->position.LowPart; + state->overlapped.OffsetHigh = state->position.HighPart; + state->read_offset = 0; + ResetEvent(state->event); + ReadFile(state->handle, state->buffer, sizeof(state->buffer), NULL, &state->overlapped); + //int error = GetLastError();//ERROR_IO_PENDING = 997 + state->io_active=true; +} + +size_t UnicodeRead(void *user, void *buffer, size_t size) +{ + Win32_State *state = static_cast<Win32_State *>(user); + assert(state->handle); + size_t totalRead=0; + HANDLE file = state->handle; + if (!state->io_active) + { + DoRead(state); + } + + if (state->read_offset == sizeof(state->buffer)) + { + DoRead(state); + } + + while (size > (sizeof(state->buffer) - state->read_offset)) + { + DWORD bytesRead=0; + BOOL res = GetOverlappedResult(file, &state->overlapped, &bytesRead, TRUE); + if ((res && bytesRead != sizeof(state->buffer)) + || (!res && GetLastError() == ERROR_HANDLE_EOF)) + { + state->endOfFile = true; + } + + if (bytesRead > state->read_offset) + { + size_t bytesToCopy = bytesRead-state->read_offset; + memcpy(buffer, state->buffer + state->read_offset, bytesToCopy); + buffer=(uint8_t *)buffer + bytesToCopy; + totalRead+=bytesToCopy; + size-=bytesToCopy; + + + if (state->endOfFile) + return totalRead; + + state->position.QuadPart += bytesToCopy; + DoRead(state); + } + else + break; + } + + while (1) + { + DWORD bytesRead=0; + BOOL res = GetOverlappedResult(file, &state->overlapped, &bytesRead, FALSE); + if ((res && bytesRead != sizeof(state->buffer)) + || (!res && GetLastError() == ERROR_HANDLE_EOF)) + { + state->endOfFile = true; + } + if (bytesRead >= (size + state->read_offset)) + { + memcpy(buffer, state->buffer + state->read_offset, size); + state->read_offset += size; + totalRead+=size; + state->position.QuadPart += size; + break; + } + + if (state->endOfFile) + break; + + WaitForSingleObject(state->event, 10); // wait 10 milliseconds or when buffer is done, whichever is faster + } + + return totalRead; +} + +size_t UnicodeWrite(void *user, void *buffer, size_t size) +{ + Win32_State *state = static_cast<Win32_State *>(user); + DWORD written = 0; + assert(state->handle); + WriteFile(state->handle, buffer, size, &written, NULL); + return 0; +} + +int UnicodeEndOfFile(void *user) +{ + Win32_State *state = static_cast<Win32_State *>(user); + return state->endOfFile; +} + +int UnicodeClose(void *user) +{ + Win32_State *state = static_cast<Win32_State *>(user); + if (state->handle) + CloseHandle(state->handle); + + state->handle=0; + return 0; +} + +Virtual_IO UnicodeIO = +{ + UnicodeGetFileLength, + UnicodeSetPosition, + UnicodeGetPosition, + UnicodeRead, + UnicodeWrite, + UnicodeEndOfFile, + UnicodeClose, +}; + + +void *CreateUnicodeReader(const wchar_t *filename) +{ + HANDLE fileHandle = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (fileHandle == INVALID_HANDLE_VALUE) + return 0; + + Win32_State *state = new Win32_State; + if (!state) + { + CloseHandle(fileHandle); + return 0; + } + state->endOfFile = false; + state->handle = fileHandle; + return state; +} + +void DestroyUnicodeReader(void *reader) +{ + if (reader) // need to check because of the cast + delete (Win32_State *)reader; +} |