diff --git a/README.md b/README.md index 9836f56..f880e24 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ## Still an early PROOF OF CONCEPT. DON'T EXPECT MAGIC. -This is just a simple plugin that allows you to stream the content of the DRC to any browser. -Currently no configuration without recompiling is supported. It streams in a resolution of 428x240 and tries to achieve 20 fps. These numbers might improve in the future. +This is just a simple plugin that allows you to stream the content of the Gamepad or TV screen to your Computer. With default settings streams in a resolution of 428x240 with selft adjusting quality and tries to achieve as much fps as possible. +It's possible to adjust the resolution via the config menu (Press **L, DPAD DOWN and MINUS** on your Wii U Gamepad whenever using the home menu is allowed). But general notes: - This is still an early PoC. @@ -13,14 +13,18 @@ But general notes: - No streaming of the home menu. - Probably unstable. - Some games might be too dark, some might be too bright, some doesn't work at all. -- Currently streaming is achieved via "MJPEG via HTTP", this might change in the future to improve performance. +- Currently streaming is achieved via a custom Java client. + +## Configuration +While the plugin is running, is possible to configure certain parameters. To open the config menu press **L, DPAD DOWN and MINUS** on your Wii U Gamepad. For more information check the [Wii U Plugin System](https://github.com/Maschell/WiiUPluginSystem). +Currently the following options are available: +- Change the resolution, possible options: 240p, 360p and 480p +- Choose the screen to stream, possible options: Gamepad, TV. + # Usage -Simply load the plugin with the plugin loader. When the system menu is loaded, you can open `http://:8080` on your browser and should see the stream. Whenever you switch the application (e.g. load a game), you need to refresh the site in your browser. -Example when the IP of your Wii U is 192.168.0.44. -``` -http:/192.168.0.44:8080 -``` +Simply load the plugin with the plugin loader, after that start the [StreamingPluginClient](https://github.com/Maschell/StreamingPluginClient). The StreamingPluginClient requires a computer with Java 8. Double click on the `.jar` and enter the IP address of your Wii U console. + If you don't know the IP of your Wii U, you can start for example [ftpiiu](https://github.com/dimok789/ftpiiu) which shows the IP when running. ## Wii U Plugin System @@ -29,6 +33,7 @@ This is a plugin for the [Wii U Plugin System (WUPS)](https://github.com/Maschel ``` sd:/wiiu/plugins ``` + When the file is placed on the SDCard you can load it with [plugin loader](https://github.com/Maschell/WiiUPluginSystem/). ## Building diff --git a/src/EncodingHelper.cpp b/src/EncodingHelper.cpp index e983800..d4539ce 100644 --- a/src/EncodingHelper.cpp +++ b/src/EncodingHelper.cpp @@ -17,20 +17,22 @@ #include #include "EncodingHelper.h" -#include "MJPEGStreamServer.hpp" +#include "MJPEGStreamServerUDP.hpp" #include "stream_utils.h" #include "JpegInformation.h" #include #include #include +#include "retain_vars.hpp" + EncodingHelper *EncodingHelper::instance = NULL; OSMessageQueue encodeQueue __attribute__((section(".data"))); OSMessage encodeQueueMessages[ENCODE_QUEUE_MESSAGE_COUNT] __attribute__((section(".data"))); void EncodingHelper::StartAsyncThread() { - int32_t priority = 31; + int32_t priority = gEncodePriority; this->pThread = CThread::create(DoAsyncThread, this, CThread::eAttributeAffCore0 |CThread::eAttributeAffCore2 , priority,0x40000); this->pThread->resumeThread(); } @@ -115,7 +117,9 @@ void EncodingHelper::DoAsyncThreadInternal(CThread *thread) { JpegInformation * info = convertToJpeg((uint8_t*) colorBuffer->surface.image,colorBuffer->surface.width,colorBuffer->surface.height,colorBuffer->surface.pitch,colorBuffer->surface.format, curQuality); if(info != NULL ) { - MJPEGStreamServer::getInstance()->streamJPEG(info); + if(mjpegServer == NULL || !mjpegServer->streamJPEG(info)){ + delete info; + } } //DEBUG_FUNCTION_LINE("We can now kill the colorBuffer\n",colorBuffer); diff --git a/src/EncodingHelper.h b/src/EncodingHelper.h index e35e65e..a163383 100644 --- a/src/EncodingHelper.h +++ b/src/EncodingHelper.h @@ -22,10 +22,10 @@ #include #include +#include #include #include -#include - +#include "MJPEGStreamServerUDP.hpp" #define ENCODE_QUEUE_MESSAGE_COUNT 1 @@ -53,7 +53,6 @@ public: delete instance; instance = NULL; } - MJPEGStreamServer::destroyInstance(); } static bool addFSQueueMSG(OSMessage message) { @@ -72,6 +71,16 @@ public: DCFlushRange((void*) &shouldExit,sizeof(shouldExit)); } + void setMJPEGStreamServer(MJPEGStreamServer * server){ + this->mjpegServer = server; + } + + void setThreadPriority(int32_t priority){ + if(pThread != NULL){ + pThread->setThreadPriority(priority); + } + } + private: EncodingHelper() { OSInitMessageQueue(&encodeQueue, encodeQueueMessages, ENCODE_QUEUE_MESSAGE_COUNT); @@ -85,6 +94,8 @@ private: CThread *pThread; + MJPEGStreamServer * mjpegServer = NULL; + volatile bool serverRunning = false; volatile bool shouldExit = false; diff --git a/src/HeartBeatServer.cpp b/src/HeartBeatServer.cpp new file mode 100644 index 0000000..8393efe --- /dev/null +++ b/src/HeartBeatServer.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** + * Copyright (C) 2018 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "HeartBeatServer.hpp" +#include "MJPEGStreamServerUDP.hpp" +#include "EncodingHelper.h" +#include "turbojpeg.h" +#include +#include +#include +#include + +#include +#include +#include +#include + + +HeartBeatServer * HeartBeatServer::instance = NULL; + +HeartBeatServer::HeartBeatServer(int32_t port): TCPServer(port,HeartBeatServer::getPriority()) { + +} + +HeartBeatServer::~HeartBeatServer() { + if(mjpegStreamServer != NULL) { + delete mjpegStreamServer; + mjpegStreamServer = NULL; + } +} + +BOOL HeartBeatServer::whileLoop() { + int32_t ret; + int32_t clientfd = getClientFD(); + + while (1) { + if(shouldExit()) { + break; + } + ret = checkbyte(clientfd); + if (ret < 0) { + if(socketlasterr() != 6) { + return false; + } + OSSleepTicks(OSMillisecondsToTicks(1000)); + continue; + } + if(ret == 0x15) { + sendbyte(clientfd,0x16); + } + } + return true; +} + +BOOL HeartBeatServer::acceptConnection() { + int32_t clientfd = getClientFD(); + struct sockaddr_in sockaddr = getSockAddr(); + + int32_t connectedIP = sockaddr.sin_addr.s_addr; + + EncodingHelper::getInstance()->setMJPEGStreamServer(NULL); + + if(mjpegStreamServer != NULL) { + delete mjpegStreamServer; + mjpegStreamServer = NULL; + } + + mjpegStreamServer = MJPEGStreamServerUDP::createInstance(connectedIP, DEFAULT_UDP_CLIENT_PORT); + EncodingHelper::getInstance()->setMJPEGStreamServer(mjpegStreamServer); + + DEBUG_FUNCTION_LINE("Handshake done! Success!\n"); + return true; +} + + +void HeartBeatServer::onConnectionClosed() { + DEBUG_FUNCTION_LINE("disconnected\n"); + EncodingHelper::getInstance()->setMJPEGStreamServer(NULL); + if(mjpegStreamServer != NULL) { + delete mjpegStreamServer; + mjpegStreamServer = NULL; + } +} diff --git a/src/HeartBeatServer.hpp b/src/HeartBeatServer.hpp new file mode 100644 index 0000000..e27b369 --- /dev/null +++ b/src/HeartBeatServer.hpp @@ -0,0 +1,76 @@ +/**************************************************************************** + * Copyright (C) 2018 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef _HEARTBEAT_SERVER_H_ +#define _HEARTBEAT_SERVER_H_ + +#include +#include "MJPEGStreamServerUDP.hpp" + +#define DEFAULT_TCP_PORT 8092 + +class HeartBeatServer: TCPServer { + +public: + static HeartBeatServer *getInstance() { + if(!instance) { + instance = new HeartBeatServer(DEFAULT_TCP_PORT); + } + return instance; + } + + static void destroyInstance() { + if(instance) { + delete instance; + instance = NULL; + } + } + + static int32_t getPriority() { + return 31; + } + + static volatile bool isInstanceConnected() { + if(instance) { + return instance->isConnected(); + } + return false; + } + + volatile void setMJPEGServerThreadPriority(int32_t priority) { + if(mjpegStreamServer != NULL) { + mjpegStreamServer->setThreadPriority(priority); + } + } + + HeartBeatServer(int32_t port); + virtual ~HeartBeatServer(); + + MJPEGStreamServer * getMJPEGServer(){ + return this->mjpegStreamServer; + } + + virtual BOOL whileLoop(); + + virtual BOOL acceptConnection(); + + virtual void onConnectionClosed(); + + static HeartBeatServer * instance; + MJPEGStreamServerUDP * mjpegStreamServer = NULL; +}; + +#endif //_MJPEG_STREAM_SERVER_H_ diff --git a/src/MJPEGStreamServer.cpp b/src/MJPEGStreamServer.cpp deleted file mode 100644 index b307382..0000000 --- a/src/MJPEGStreamServer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2018 Maschell - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - ****************************************************************************/ -#include "MJPEGStreamServer.hpp" -#include "turbojpeg.h" -#include -#include -#include - -#include -#include -#include -#include - -OSMessageQueue streamSendQueue __attribute__((section(".data"))); -OSMessage streamSendQueueMessages[STREAM_SEND_QUEUE_MESSAGE_COUNT] __attribute__((section(".data"))); - -MJPEGStreamServer * MJPEGStreamServer::instance = NULL; - -MJPEGStreamServer::MJPEGStreamServer(int32_t port): TCPServer(port,MJPEGStreamServer::getPriority()) { - OSInitMessageQueue(&streamSendQueue, streamSendQueueMessages, STREAM_SEND_QUEUE_MESSAGE_COUNT); -} - -MJPEGStreamServer::~MJPEGStreamServer() { - -} - -/** - The sendwait from is reaaally slow. -**/ -int32_t mysendwait(int32_t sock, const void *buffer, int32_t len) { - int32_t ret; - while (len > 0) { - ret = send(sock, buffer, len, 0); - if(ret < 0) { - return ret; - } - len -= ret; - buffer = (void *)(((char *) buffer) + ret); - } - return 0; -} - -void MJPEGStreamServer::sendJPEG(uint8_t * buffer, uint64_t size) { - int32_t clientfd = getClientFD(); - - char str[90]; - snprintf(str, 90, "\r\n--boundary\r\nContent-Type: image/jpeg\r\nContent-Length: %llu \r\n\r\n", size); - - - mysendwait(clientfd, str, strlen(str)); - mysendwait(clientfd, buffer, size); - - //DEBUG_FUNCTION_LINE("Send frame\n"); -} - - -BOOL MJPEGStreamServer::whileLoop() { - int32_t ret; - int32_t clientfd = getClientFD(); - - OSMessage message; - - while (1) { - ret = checkbyte(clientfd); - if (ret < 0) { - if(socketlasterr() != 6) { - // Ending Server on error. - return false; - } - } - - //DEBUG_FUNCTION_LINE("Waiting\n",message.message,message.data1); - while(!OSReceiveMessage(&streamSendQueue,&message,OS_MESSAGE_FLAGS_NONE)) { - if(shouldExit()) { - break; - } - OSSleepTicks(OSMicrosecondsToTicks(500)); - } - - if((uint32_t) message.message == 0xDEADBEEF) { - DEBUG_FUNCTION_LINE("We should exit\n"); - break; - } - - DCFlushRange(&message,sizeof(OSMessage)); - - JpegInformation * info = (JpegInformation *) message.args[0]; - if(info != NULL) { - DCFlushRange(info,sizeof(JpegInformation)); - sendJPEG(info->getBuffer(),info->getSize()); - delete info; - } - } - return true; -} - -const char * headerHTTP = "HTTP/1.1 200 OK\r\nContent-Type: multipart/x-mixed-replace; boundary=--boundary\r\n"; - -BOOL MJPEGStreamServer::acceptConnection() { - int32_t clientfd = getClientFD(); - DEBUG_FUNCTION_LINE("TCP Connection accepted! \n"); - - mysendwait(clientfd, headerHTTP, strlen(headerHTTP)); - - // Consume the first response of the browser. - while(checkbyte(clientfd) > 0); - - DEBUG_FUNCTION_LINE("Handshake done! Success!\n"); - return true; -} - -void MJPEGStreamServer::onConnectionClosed() { - DEBUG_FUNCTION_LINE("disconnected\n"); -} diff --git a/src/MJPEGStreamServer.hpp b/src/MJPEGStreamServer.hpp index 03bfd81..e908c22 100644 --- a/src/MJPEGStreamServer.hpp +++ b/src/MJPEGStreamServer.hpp @@ -14,93 +14,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ -#ifndef _MJPEG_STREAM_SERVER_H_ -#define _MJPEG_STREAM_SERVER_H_ +#ifndef _MJPEG_STREAM_SERVER_WINDOW_H_ +#define _MJPEG_STREAM_SERVER_WINDOW_H_ -#include -#include -#include -#include "turbojpeg.h" #include "JpegInformation.h" -#define STREAM_SEND_QUEUE_MESSAGE_COUNT 1 - -extern OSMessageQueue streamSendQueue; -extern OSMessage streamSendQueueMessages[STREAM_SEND_QUEUE_MESSAGE_COUNT]; - -extern uint32_t frame_counter_skipped; - -class MJPEGStreamServer: TCPServer { - +class MJPEGStreamServer { public: - static MJPEGStreamServer *getInstance() { - if(!instance) { - instance = new MJPEGStreamServer(8080); - } - return instance; + MJPEGStreamServer(){ + } + virtual ~MJPEGStreamServer(){ } - static void destroyInstance() { - if(instance) { - instance->StopAsyncThread(); - while(instance->isConnected()) { - OSSleepTicks(OSMicrosecondsToTicks(1000)); - } - OSSleepTicks(OSMillisecondsToTicks(500)); - delete instance; - instance = NULL; - } - } - - void StopAsyncThread() { - DEBUG_FUNCTION_LINE("StopAsyncThread\n"); - OSMessage message; - message.message = (void *)0xDEADBEEF; - OSSendMessage(&streamSendQueue,&message,OS_MESSAGE_FLAGS_BLOCKING); - } - - - static int32_t getPriority() { - return 31; - } - - static volatile bool isInstanceConnected() { - if(instance) { - return instance->isConnected(); - } - return false; - } - - MJPEGStreamServer(int32_t port); - - void sendJPEG(uint8_t * buffer, uint64_t size); - - virtual bool streamJPEG(JpegInformation * info) { - if(this->isConnected()) { - OSMessage message; - message.message = (void *) 0x11111; - message.args[0] = (uint32_t) info; - if(!OSSendMessage(&streamSendQueue,&message,OS_MESSAGE_FLAGS_NONE)) { - frame_counter_skipped++; - //DEBUG_FUNCTION_LINE("Dropping frame\n"); - delete info; - return false; - }; - } else { - delete info; - } - return true; - } - - virtual ~MJPEGStreamServer(); - - virtual BOOL whileLoop(); - - virtual BOOL acceptConnection(); - - virtual void onConnectionClosed(); - - static MJPEGStreamServer * instance; + virtual bool streamJPEG(JpegInformation * info) = 0; }; -#endif //_MJPEG_STREAM_SERVER_H_ +#endif //_MJPEG_STREAM_SERVER_WINDOW_H_ diff --git a/src/MJPEGStreamServerUDP.cpp b/src/MJPEGStreamServerUDP.cpp new file mode 100644 index 0000000..8d190ab --- /dev/null +++ b/src/MJPEGStreamServerUDP.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "MJPEGStreamServerUDP.hpp" +#include +#include + +#include "retain_vars.hpp" +#include +#include +#include +#include +#include "crc32.h" + +#define MAX_UDP_SIZE 0x578 + +extern int frame_counter_skipped; + +MJPEGStreamServerUDP::MJPEGStreamServerUDP(uint32_t ip, int32_t port) { + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sockfd < 0) { + return; + } + + struct sockaddr_in connect_addr; + memset(&connect_addr, 0, sizeof(struct sockaddr_in)); + connect_addr.sin_family = AF_INET; + connect_addr.sin_port = port; + connect_addr.sin_addr.s_addr = ip; + + if(connect(sockfd, (struct sockaddr*)&connect_addr, sizeof(connect_addr)) < 0) { + socketclose(sockfd); + sockfd = -1; + } + + crc32_init(&crc32Buffer); + + OSInitMessageQueue(&dataQueue, dataQueueMessages, DATA_SEND_QUEUE_MESSAGE_COUNT); + + //StartAsyncThread(); +} + +void MJPEGStreamServerUDP::StartAsyncThread() { + int32_t priority = 31; + this->pThread = CThread::create(DoAsyncThread, this, CThread::eAttributeAffCore0 |CThread::eAttributeAffCore2, priority,0x80000); + this->pThread->resumeThread(); +} + +void MJPEGStreamServerUDP::DoAsyncThread(CThread *thread, void *arg) { + MJPEGStreamServerUDP * arg_instance = (MJPEGStreamServerUDP *) arg; + return arg_instance->DoAsyncThreadInternal(thread); +} + +MJPEGStreamServerUDP::~MJPEGStreamServerUDP() { + StopAsyncThread(); + + OSSleepTicks(OSMillisecondsToTicks(100)); + + if(pThread != NULL) { + delete pThread; + pThread = NULL; + } + + if (this->sockfd != -1) { + socketclose(sockfd); + } + DEBUG_FUNCTION_LINE("Thread has been closed\n"); +} + +void MJPEGStreamServerUDP::DoAsyncThreadInternal(CThread *thread) { + OSMessage message; + bool breakOut = false; + while (1) { + if(breakOut) { + break; + } + while(!OSReceiveMessage(&dataQueue,&message,OS_MESSAGE_FLAGS_NONE)) { + if(shouldExit) { + breakOut = true; + break; + } + OSSleepTicks(OSMicrosecondsToTicks(500)); + } + + if(breakOut) { + break; + } + + DCFlushRange(&message,sizeof(OSMessage)); + + JpegInformation * info = (JpegInformation *) message.args[0]; + if(info != NULL) { + //DEBUG_FUNCTION_LINE("GOT FRAME INFO! %08X\n",info); + DCFlushRange(info,sizeof(JpegInformation)); + sendJPEG(info->getBuffer(),info->getSize()); + delete info; + } + } + return; +} + +bool MJPEGStreamServerUDP::streamJPEGThreaded(JpegInformation * info) { + OSMessage message; + message.message = (void *) 0x11111; + message.args[0] = (uint32_t) info; + if(!OSSendMessage(&dataQueue,&message,OS_MESSAGE_FLAGS_NONE)) { + frame_counter_skipped++; + //DEBUG_FUNCTION_LINE("Dropping frame\n"); + delete info; + return false; + }; + return true; +} + +bool MJPEGStreamServerUDP::streamJPEG(JpegInformation * info) { + if(info != NULL) { + //return streamJPEGThreaded(info); + + DCFlushRange(info,sizeof(JpegInformation)); + sendJPEG(info->getBuffer(),info->getSize()); + delete info; + return true; + } + return false; +} + +void MJPEGStreamServerUDP::sendJPEG(uint8_t * buffer, uint64_t size) { + uint32_t crcValue = crc32_crc(&crc32Buffer,buffer, size); + + sendData((uint8_t*)&crcValue, sizeof(crcValue)); + sendData((uint8_t*)&size, sizeof(size)); + sendData((uint8_t*)buffer, size); +} + +bool MJPEGStreamServerUDP::sendData(uint8_t * data,int32_t length) { + int len = length; + int ret = -1; + while (len > 0) { + int block = len < MAX_UDP_SIZE ? len : MAX_UDP_SIZE; // take max 508 bytes per UDP packet + ret = send(sockfd, data, block, 0); + + if(ret < 0) { + return false; + } + + len -= ret; + data += ret; + } + + return true; +} diff --git a/src/MJPEGStreamServerUDP.hpp b/src/MJPEGStreamServerUDP.hpp new file mode 100644 index 0000000..3ceb57c --- /dev/null +++ b/src/MJPEGStreamServerUDP.hpp @@ -0,0 +1,86 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef _UDPCLIENT_WINDOW_H_ +#define _UDPCLIENT_WINDOW_H_ + +#define DEFAULT_UDP_CLIENT_PORT 9445 + +#include +#include +#include +#include +#include +#include "crc32.h" +#include "JpegInformation.h" +#include "MJPEGStreamServer.hpp" + +#define DATA_SEND_QUEUE_MESSAGE_COUNT 1 + +class MJPEGStreamServerUDP : public MJPEGStreamServer { +public: + ~MJPEGStreamServerUDP(); + + static MJPEGStreamServerUDP *createInstance(int32_t ip, int32_t port) { + return new MJPEGStreamServerUDP(ip, port); + } + + void StartAsyncThread(); + + static void DoAsyncThread(CThread *thread, void *arg); + + void DoAsyncThreadInternal(CThread *thread); + + void StopAsyncThread() { + DEBUG_FUNCTION_LINE("StopAsyncThread\n"); + shouldExit = true; + DCFlushRange((void*) &shouldExit,sizeof(shouldExit)); + } + + void setThreadPriority(int priority) { + if(pThread != NULL) { + pThread->setThreadPriority(priority); + } + } + + void proccessData(CThread *thread, void *arg); + + bool streamJPEG(JpegInformation * info); + + bool streamJPEGThreaded(JpegInformation * info); + + void sendJPEG(uint8_t * buffer, uint64_t size); + + bool sendData(uint8_t * data,int32_t length); + + volatile int32_t sockfd = -1; + + static MJPEGStreamServerUDP *instance; + + crc32_t crc32Buffer; + +private: + MJPEGStreamServerUDP(uint32_t ip,int32_t port); + + + bool shouldExit = false; + CThread * pThread = NULL; + + OSMessageQueue dataQueue; + OSMessage dataQueueMessages[DATA_SEND_QUEUE_MESSAGE_COUNT]; +}; + +#endif //_UDPClient_WINDOW_H_ diff --git a/src/crc32.cpp b/src/crc32.cpp new file mode 100644 index 0000000..0906c20 --- /dev/null +++ b/src/crc32.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include "crc32.h" + +void crc32_init(crc32_t* crc) { + uint32_t v; + + for(int i = 0; i < 256; ++i) { + v = i; + + for(int j = 0; j < 8; ++j) { + v = (v & 1) ? (CRC32_INITIAL ^ (v >> 1)) : (v >> 1); + } + crc->table[i] = v; + } +} + +void crc32_update(crc32_t* c, uint8_t* buf, size_t len) { + for(size_t i = 0; i < len; ++i) { + c->value = c->table[(c->value ^ buf[i]) & 0xFF] ^ (c->value >> 8); + } +} + +void crc32_start(crc32_t* c) { + c->value = 0xfffffffful; +} + +uint32_t crc32_finalize(crc32_t* c) { + return c->value ^ 0xfffffffful; +} + +uint32_t crc32_crc(crc32_t* c, uint8_t* buf, size_t len) { + crc32_start(c); + crc32_update(c, buf, len); + return crc32_finalize(c); +} diff --git a/src/crc32.h b/src/crc32.h new file mode 100644 index 0000000..aa48d65 --- /dev/null +++ b/src/crc32.h @@ -0,0 +1,18 @@ +#ifndef __CRC32_H_ +#define __CRC32_H_ + +#include +#include + +typedef struct crc32 { + uint32_t table[256]; + uint32_t value; +} crc32_t; + +#define CRC32_INITIAL 0xedb88320 + +void crc32_init(crc32_t* crc); + +uint32_t crc32_crc(crc32_t* c, uint8_t* buf, size_t len); + +#endif diff --git a/src/function_patcher.cpp b/src/function_patcher.cpp index 5d4e437..fda08a6 100644 --- a/src/function_patcher.cpp +++ b/src/function_patcher.cpp @@ -10,8 +10,11 @@ uint32_t count = 0; DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, int32_t scan_target) { if(gAppStatus == WUPS_APP_STATUS_FOREGROUND) { - - if(scan_target == 4 /*&& (count++ % 4 == 0)*/ && colorBuffer != NULL ) { + int32_t use_scan_target = 4; + if(gScreen == WUPS_STREAMING_SCREEN_TV){ + use_scan_target = 1; + } + if(scan_target == use_scan_target /*&& (count++ % 4 == 0)*/ && colorBuffer != NULL ) { count = 0; streamVideo((GX2ColorBuffer *)colorBuffer); } diff --git a/src/main.cpp b/src/main.cpp index 8232772..f89e08b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,8 @@ #include #include "retain_vars.hpp" #include "EncodingHelper.h" -#include "MJPEGStreamServer.hpp" +#include "MJPEGStreamServerUDP.hpp" +#include "HeartBeatServer.hpp" // Mandatory plugin information. WUPS_PLUGIN_NAME("Gamepad streaming tool."); @@ -19,18 +20,25 @@ WUPS_PLUGIN_LICENSE("GPL"); // Something is using "write"... WUPS_FS_ACCESS() -void resolutionChanged(int32_t newResolution) { +void resolutionChanged(WUPSConfigItemMultipleValues* configItem, int32_t newResolution) { DEBUG_FUNCTION_LINE("Resolution changed %d \n",newResolution); gResolution = newResolution; // Restart server. EncodingHelper::destroyInstance(); - MJPEGStreamServer::destroyInstance(); - EncodingHelper::getInstance()->StartAsyncThread(); - MJPEGStreamServer::getInstance(); + EncodingHelper::getInstance()->setMJPEGStreamServer(HeartBeatServer::getInstance()->getMJPEGServer()); } +void screenChanged(WUPSConfigItemMultipleValues* configItem, int32_t newScreen) { + DEBUG_FUNCTION_LINE("Screen changed %d \n",newScreen); + gScreen = newScreen; + + // Restart server. + EncodingHelper::destroyInstance(); + EncodingHelper::getInstance()->StartAsyncThread(); + EncodingHelper::getInstance()->setMJPEGStreamServer(HeartBeatServer::getInstance()->getMJPEGServer()); +} WUPS_GET_CONFIG() { WUPSConfig* config = new WUPSConfig("Streaming Plugin"); @@ -41,7 +49,12 @@ WUPS_GET_CONFIG() { resolutionValues[WUPS_STREAMING_RESOLUTION_360P] = "360p"; resolutionValues[WUPS_STREAMING_RESOLUTION_480P] = "480p"; + std::map screenValues; + screenValues[WUPS_STREAMING_SCREEN_DRC] = "Gamepad"; + screenValues[WUPS_STREAMING_SCREEN_TV] = "TV"; + // item Type config id displayed name default value onChangeCallback. + catOther->addItem(new WUPSConfigItemMultipleValues("screen", "Screen", gScreen, screenValues, screenChanged)); catOther->addItem(new WUPSConfigItemMultipleValues("resolution", "Streaming resolution", gResolution, resolutionValues, resolutionChanged)); return config; @@ -61,16 +74,18 @@ ON_APPLICATION_START(my_args) { gAppStatus = WUPS_APP_STATUS_FOREGROUND; + EncodingHelper::destroyInstance(); EncodingHelper::getInstance()->StartAsyncThread(); - MJPEGStreamServer::getInstance(); + EncodingHelper::getInstance()->setMJPEGStreamServer(HeartBeatServer::getInstance()->getMJPEGServer()); log_init(); } ON_APP_STATUS_CHANGED(status) { gAppStatus = status; + if(status == WUPS_APP_STATUS_CLOSED) { EncodingHelper::destroyInstance(); - MJPEGStreamServer::destroyInstance(); + HeartBeatServer::destroyInstance(); } } diff --git a/src/retain_vars.cpp b/src/retain_vars.cpp index ecca1fe..cfb107d 100644 --- a/src/retain_vars.cpp +++ b/src/retain_vars.cpp @@ -1,3 +1,6 @@ #include "retain_vars.hpp" wups_loader_app_status_t gAppStatus __attribute__((section(".data"))) = WUPS_APP_STATUS_UNKNOWN; int32_t gResolution __attribute__((section(".data"))) = WUPS_STREAMING_RESOLUTION_240P; +int32_t gScreen __attribute__((section(".data"))) = WUPS_STREAMING_SCREEN_DRC; +int32_t gSendPriority __attribute__((section(".data"))) = 31; +int32_t gEncodePriority __attribute__((section(".data"))) = 31; diff --git a/src/retain_vars.hpp b/src/retain_vars.hpp index 4635183..a61e212 100644 --- a/src/retain_vars.hpp +++ b/src/retain_vars.hpp @@ -3,11 +3,18 @@ #include +#define WUPS_STREAMING_SCREEN_DRC 0 +#define WUPS_STREAMING_SCREEN_TV 1 + + #define WUPS_STREAMING_RESOLUTION_240P 1 #define WUPS_STREAMING_RESOLUTION_360P 2 #define WUPS_STREAMING_RESOLUTION_480P 3 extern wups_loader_app_status_t gAppStatus; +extern int32_t gScreen; extern int32_t gResolution; +extern int32_t gSendPriority; +extern int32_t gEncodePriority; #endif // _RETAINS_VARS_H_ diff --git a/src/stream_utils.cpp b/src/stream_utils.cpp index 87a1f45..6f2977c 100644 --- a/src/stream_utils.cpp +++ b/src/stream_utils.cpp @@ -1,7 +1,7 @@ #include "stream_utils.h" #include "retain_vars.hpp" #include "EncodingHelper.h" -#include "MJPEGStreamServer.hpp" +#include "HeartBeatServer.hpp" #include #include @@ -106,7 +106,7 @@ bool streamVideo(GX2ColorBuffer *srcBuffer) { return false; } - if(!MJPEGStreamServer::isInstanceConnected()) { + if(!HeartBeatServer::isInstanceConnected()) { return false; }