- Use the new Java Client instead of MJPEG via HTTP. See https://github.com/Maschell/StreamingPluginClient for more information.

- Add an option to choose which screen to stream.
This commit is contained in:
Maschell 2018-09-15 14:49:05 +02:00
parent 993504ed15
commit 5c1733f55c
16 changed files with 558 additions and 235 deletions

View File

@ -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://<ip of your ip>: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

View File

@ -17,20 +17,22 @@
#include <vector>
#include "EncodingHelper.h"
#include "MJPEGStreamServer.hpp"
#include "MJPEGStreamServerUDP.hpp"
#include "stream_utils.h"
#include "JpegInformation.h"
#include <gx2/event.h>
#include <gx2/surface.h>
#include <gx2/mem.h>
#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);

View File

@ -22,10 +22,10 @@
#include <algorithm>
#include <system/CThread.h>
#include <coreinit/cache.h>
#include <coreinit/messagequeue.h>
#include <utils/logger.h>
#include <MJPEGStreamServer.hpp>
#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;

96
src/HeartBeatServer.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "HeartBeatServer.hpp"
#include "MJPEGStreamServerUDP.hpp"
#include "EncodingHelper.h"
#include "turbojpeg.h"
#include <malloc.h>
#include <network/net.h>
#include <stdio.h>
#include <string.h>
#include <utils/logger.h>
#include <utils/StringTools.h>
#include <coreinit/thread.h>
#include <coreinit/time.h>
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;
}
}

76
src/HeartBeatServer.hpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef _HEARTBEAT_SERVER_H_
#define _HEARTBEAT_SERVER_H_
#include <utils/TCPServer.hpp>
#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_

View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "MJPEGStreamServer.hpp"
#include "turbojpeg.h"
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <utils/logger.h>
#include <utils/StringTools.h>
#include <coreinit/thread.h>
#include <coreinit/time.h>
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 <network/net.h> 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");
}

View File

@ -14,93 +14,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef _MJPEG_STREAM_SERVER_H_
#define _MJPEG_STREAM_SERVER_H_
#ifndef _MJPEG_STREAM_SERVER_WINDOW_H_
#define _MJPEG_STREAM_SERVER_WINDOW_H_
#include <utils/TCPServer.hpp>
#include <network/net.h>
#include <coreinit/messagequeue.h>
#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_

View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "MJPEGStreamServerUDP.hpp"
#include <stdio.h>
#include <string.h>
#include "retain_vars.hpp"
#include <coreinit/thread.h>
#include <utils/logger.h>
#include <coreinit/cache.h>
#include <nsysnet/socket.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef _UDPCLIENT_WINDOW_H_
#define _UDPCLIENT_WINDOW_H_
#define DEFAULT_UDP_CLIENT_PORT 9445
#include <coreinit/messagequeue.h>
#include <coreinit/cache.h>
#include <utils/logger.h>
#include <system/CThread.h>
#include <nsysnet/socket.h>
#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_

37
src/crc32.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#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);
}

18
src/crc32.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __CRC32_H_
#define __CRC32_H_
#include <inttypes.h>
#include <dirent.h>
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

View File

@ -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);
}

View File

@ -7,7 +7,8 @@
#include <utils/logger.h>
#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<int32_t,std::string> 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();
}
}

View File

@ -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;

View File

@ -3,11 +3,18 @@
#include <wups.h>
#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_

View File

@ -1,7 +1,7 @@
#include "stream_utils.h"
#include "retain_vars.hpp"
#include "EncodingHelper.h"
#include "MJPEGStreamServer.hpp"
#include "HeartBeatServer.hpp"
#include <fs/FSUtils.h>
#include <malloc.h>
@ -106,7 +106,7 @@ bool streamVideo(GX2ColorBuffer *srcBuffer) {
return false;
}
if(!MJPEGStreamServer::isInstanceConnected()) {
if(!HeartBeatServer::isInstanceConnected()) {
return false;
}