Various optimizations and refactoring (#259)

* optimizations

* Update menu_game.h

* try new code

* use cached EGLImages

* try drm cache on pi

* nani?

* huh?

* require opengl egl

* find EGL

* find EGL separately

* logging

* add support for versions of ffmpeg less than 6.0

* disable drm cache for now

* Update ui_sdl.c

* use old code for now

* disable avcc hdr for now

* restore error message

* try disabling format modifiers?

* tell me what the layer is

* pi decoder actually produces frames in yuv420p

* print more debug info

* try this

* switch to faster decode path

* switch to old code

* Update ui_sdl.c

* add more timing profiling

* more printing

* enable some optimizations

* draw menus when not in game mode

* call render present

* Update ui_sdl.c

* Update ui_sdl.c

* remove unused code

* Update CMakeLists.txt

* incorporate direct DRM code

* Update CMakeLists.txt

* free drm context and test old decoding code

* move SDL event loop to separate thread so controls are not limited by display rate

* add a wait to reduce CPU thrashing

* fix double free

* separate video into recv and consume threads

* Update ui_sdl_drm.c

* Update ui_sdl_drm.c

* Update gamepad.c

* further optimizations

* anti-alias buttons more

* fix some compiler errors

* only include DRM on linux

* fix windows compile issues

* fix includes

* Update CMakeLists.txt

* add htobe32 define for macOS

* Update video.c

* Update gamepad.c
This commit is contained in:
MattKC authored and GitHub committed 2025-11-30 20:56:52 -08:00
1 parent 6a82715263
commit b0bbc31002
29 files changed
+2123 -2255

No files matched your search

+131 -112
View File
@@ -2,16 +2,6 @@
#include "gamepad.h"
#ifdef _WIN32
#include <winsock2.h>
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
#else
#include <arpa/inet.h>
#include <errno.h>
#include <sys/un.h>
#endif
#include <assert.h>
#include <pthread.h>
#include <signal.h>
@@ -31,18 +21,13 @@ typedef uint16_t in_port_t;
static uint32_t SERVER_ADDRESS = 0;
static const int MAX_PIPE_RETRY = 5;
char wireless_interface[128];
#define EVENT_BUFFER_SIZE 65536
#define EVENT_BUFFER_ARENA_SIZE VANILLA_MAX_EVENT_COUNT * 2
uint8_t *EVENT_BUFFER_ARENA[EVENT_BUFFER_ARENA_SIZE] = {0};
pthread_mutex_t event_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
typedef union {
struct sockaddr_in in;
#ifndef _WIN32
struct sockaddr_un un;
#endif
} sockaddr_u;
static inline int skterr()
{
#ifdef _WIN32
@@ -52,24 +37,10 @@ static inline int skterr()
#endif
}
in_addr_t get_real_server_address()
{
if (SERVER_ADDRESS == VANILLA_ADDRESS_DIRECT) {
// The Wii U always places itself at this address
return inet_addr("192.168.1.10");
} else if (SERVER_ADDRESS != VANILLA_ADDRESS_LOCAL) {
// If there's a remote pipe, we send to that
return SERVER_ADDRESS;
}
// Must be local, in which case we don't care
return INADDR_ANY;
}
void create_sockaddr(sockaddr_u *addr, size_t *size, in_addr_t inaddr, uint16_t port, int delete)
void create_sockaddr(sockaddr_u *addr, size_t *size, in_addr_t inaddr, uint16_t port, int local, int delete)
{
#ifndef _WIN32
if (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) {
if (local) {
memset(&addr->un, 0, sizeof(addr->un));
addr->un.sun_family = AF_UNIX;
snprintf(addr->un.sun_path, sizeof(addr->un.sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port);
@@ -90,6 +61,24 @@ void create_sockaddr(sockaddr_u *addr, size_t *size, in_addr_t inaddr, uint16_t
#endif
}
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete)
{
// The Wii U always places itself at this address
in_addr_t ip = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? inet_addr("192.168.1.10") : SERVER_ADDRESS;
return create_sockaddr(addr, size, ip, port, 0, delete);
}
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size)
{
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) sockaddr, sockaddr_size);
if (sent == -1) {
int err = skterr();
if (err != 111) { // 111 is connection refused, occurs if we lose connection, but we'll already know that for other reasons so we don't need to spam the console with this error
vanilla_log("Failed to send to Wii U socket: fd: %d, errno: %i", fd, skterr());
}
}
}
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
{
sockaddr_u addr;
@@ -97,15 +86,9 @@ void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
in_port_t console_port = port - 100;
create_sockaddr(&addr, &addr_size, get_real_server_address(), console_port, 0);
create_server_sockaddr(&addr, &addr_size, console_port, 0);
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) &addr, addr_size);
if (sent == -1) {
int err = skterr();
if (err != 111) { // 111 is connection refused, occurs if we lose connection, but we'll already know that for other reasons so we don't need to spam the console with this error
vanilla_log("Failed to send to Wii U socket: fd: %d, port: %d, errno: %i", fd, console_port, skterr());
}
}
send_to_sockaddr(fd, data, data_size, &addr, addr_size);
}
void set_socket_rcvtimeo(int skt, uint64_t microseconds)
@@ -121,14 +104,15 @@ void set_socket_rcvtimeo(int skt, uint64_t microseconds)
#endif // _WIN32
}
int create_socket(int *socket_out, in_port_t port)
int create_socket(int *socket_out, in_port_t port, int pipe)
{
sockaddr_u addr;
size_t addr_size;
create_sockaddr(&addr, &addr_size, INADDR_ANY, port, 1);
int is_pipe_and_local = (pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
int domain = is_pipe_and_local ? AF_UNIX : AF_INET;
int domain = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? AF_UNIX : AF_INET;
create_sockaddr(&addr, &addr_size, INADDR_ANY, port, is_pipe_and_local, 1);
int skt = socket(domain, SOCK_DGRAM, 0);
if (skt == -1) {
@@ -148,6 +132,22 @@ int create_socket(int *socket_out, in_port_t port)
set_socket_rcvtimeo(skt, 250000);
// int buf_sz = 16777216;
// setsockopt(skt, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz));
// setsockopt(skt, SOL_SOCKET, SO_SNDBUF, &buf_sz, sizeof(buf_sz));
#if !defined(_WIN32) && !defined(__APPLE__)
if (!pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) {
// Bind to wireless device
setsockopt(skt, SOL_SOCKET, SO_BINDTODEVICE, wireless_interface, strlen(wireless_interface));
}
#endif
// int val = 1000; // microseconds to busy-poll per recv() before sleeping
// if (setsockopt(skt, SOL_SOCKET, SO_BUSY_POLL, &val, sizeof(val)) < 0) {
// perror("setsockopt SO_BUSY_POLL");
// }
return VANILLA_SUCCESS;
}
@@ -156,7 +156,10 @@ int send_pipe_cc(int skt, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait
sockaddr_u addr;
size_t addr_size;
create_sockaddr(&addr, &addr_size, get_real_server_address(), VANILLA_PIPE_CMD_SERVER_PORT, 0);
int pipe_is_local = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
in_addr_t pipe_addr = pipe_is_local ? INADDR_ANY : SERVER_ADDRESS;
create_sockaddr(&addr, &addr_size, pipe_addr, VANILLA_PIPE_CMD_SERVER_PORT, pipe_is_local, 0);
ssize_t read_size;
uint8_t recv_cc;
@@ -195,7 +198,7 @@ int connect_to_backend(int *socket, vanilla_pipe_command_t *cmd, size_t cmd_size
{
// Try to bind with backend
int pipe_cc_skt = -1;
int ret = create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT);
int ret = create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT, 1);
if (ret != VANILLA_SUCCESS) {
return ret;
}
@@ -288,16 +291,14 @@ int install_polkit_internal(thread_data_t *data, int install)
int ret = VANILLA_ERR_GENERIC;
int pipe_cc_skt = -1;
if (SERVER_ADDRESS != VANILLA_ADDRESS_DIRECT) {
vanilla_pipe_command_t cmd;
cmd.control_code = install ? VANILLA_PIPE_CC_INSTALL_POLKIT : VANILLA_PIPE_CC_UNINSTALL_POLKIT;
vanilla_pipe_command_t cmd;
cmd.control_code = install ? VANILLA_PIPE_CC_INSTALL_POLKIT : VANILLA_PIPE_CC_UNINSTALL_POLKIT;
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
// No interrupt is required here because VANILLA_PIPE_CC_INSTALL_POLKIT
// does not start a thread in the pipe
}
// No interrupt is required here because VANILLA_PIPE_CC_INSTALL_POLKIT
// does not start a thread in the pipe
if (pipe_cc_skt != -1) {
// Disconnect from pipe if necessary
@@ -320,47 +321,45 @@ void connect_as_gamepad_internal(thread_data_t *data)
int ret = VANILLA_SUCCESS;
// Open all required sockets
if (create_socket(&info.socket_vid, PORT_VID) != VANILLA_SUCCESS) goto exit_pipe;
if (create_socket(&info.socket_msg, PORT_MSG) != VANILLA_SUCCESS) goto exit_vid;
if (create_socket(&info.socket_hid, PORT_HID) != VANILLA_SUCCESS) goto exit_msg;
if (create_socket(&info.socket_aud, PORT_AUD) != VANILLA_SUCCESS) goto exit_hid;
if (create_socket(&info.socket_cmd, PORT_CMD) != VANILLA_SUCCESS) goto exit_aud;
if (create_socket(&info.socket_vid, PORT_VID, 0) != VANILLA_SUCCESS) goto exit_pipe;
if (create_socket(&info.socket_msg, PORT_MSG, 0) != VANILLA_SUCCESS) goto exit_vid;
if (create_socket(&info.socket_hid, PORT_HID, 0) != VANILLA_SUCCESS) goto exit_msg;
if (create_socket(&info.socket_aud, PORT_AUD, 0) != VANILLA_SUCCESS) goto exit_hid;
if (create_socket(&info.socket_cmd, PORT_CMD, 0) != VANILLA_SUCCESS) goto exit_aud;
int pipe_cc_skt = -1;
if (SERVER_ADDRESS != VANILLA_ADDRESS_DIRECT) {
vanilla_pipe_command_t cmd;
cmd.control_code = VANILLA_PIPE_CC_CONNECT;
vanilla_pipe_command_t cmd;
cmd.control_code = VANILLA_PIPE_CC_CONNECT;
cmd.connection.bssid = data->bssid;
cmd.connection.psk = data->psk;
cmd.connection.bssid = data->bssid;
cmd.connection.psk = data->psk;
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
if (ret == VANILLA_SUCCESS) {
// Wait for backend to be available
vanilla_pipe_command_t connected_state;
ret = VANILLA_ERR_NO_CONNECTION;
while (!is_interrupted()) {
ssize_t read_size = recv(pipe_cc_skt, (char *) &connected_state, sizeof(connected_state), 0);
if (read_size < 0) {
int r = skterr();
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
if (ret == VANILLA_SUCCESS) {
// Wait for backend to be available
vanilla_pipe_command_t connected_state;
ret = VANILLA_ERR_NO_CONNECTION;
while (!is_interrupted()) {
ssize_t read_size = recv(pipe_cc_skt, (char *) &connected_state, sizeof(connected_state), 0);
if (read_size < 0) {
int r = skterr();
#ifdef _WIN32
if (r != WSAETIMEDOUT) {
if (r != WSAETIMEDOUT) {
#else
if (r != EAGAIN) {
if (r != EAGAIN) {
#endif
vanilla_log("FAILED TO GET CONNECTED STATE: %i", r);
ret = VANILLA_ERR_PIPE_UNRESPONSIVE;
break;
}
} else if (connected_state.control_code == VANILLA_PIPE_CC_CONNECTED) {
ret = VANILLA_SUCCESS;
sleep(1);
vanilla_log("FAILED TO GET CONNECTED STATE: %i", r);
ret = VANILLA_ERR_PIPE_UNRESPONSIVE;
break;
}
vanilla_log("STILL WAITING FOR CONNECTED STATE");
} else if (connected_state.control_code == VANILLA_PIPE_CC_CONNECTED) {
ret = VANILLA_SUCCESS;
sleep(1);
break;
}
vanilla_log("STILL WAITING FOR CONNECTED STATE");
}
}
@@ -439,47 +438,67 @@ exit:
wait_for_interrupt();
}
int push_event(event_loop_t *loop, int type, const void *data, size_t size)
int acquire_event(event_loop_t *loop, vanilla_event_t **event)
{
int ret = VANILLA_SUCCESS;
int ret = VANILLA_SUCCESS;
pthread_mutex_lock(&loop->mutex);
if (size <= EVENT_BUFFER_SIZE) {
// Prevent rollover by skipping oldest event if necessary
if (loop->new_index == loop->used_index + VANILLA_MAX_EVENT_COUNT) {
vanilla_free_event(&loop->events[loop->used_index % VANILLA_MAX_EVENT_COUNT]);
vanilla_log("SKIPPED EVENT TO PREVENT ROLLOVER (%lu > %lu + %lu)", loop->new_index, loop->used_index, VANILLA_MAX_EVENT_COUNT);
loop->used_index++;
}
// Prevent rollover by skipping oldest event if necessary
if (loop->new_index == loop->used_index + VANILLA_MAX_EVENT_COUNT) {
vanilla_free_event(&loop->events[loop->used_index % VANILLA_MAX_EVENT_COUNT]);
vanilla_log("SKIPPED EVENT TO PREVENT ROLLOVER (%lu > %lu + %lu)", loop->new_index, loop->used_index, VANILLA_MAX_EVENT_COUNT);
loop->used_index++;
}
vanilla_event_t *ev = &loop->events[loop->new_index % VANILLA_MAX_EVENT_COUNT];
vanilla_event_t *ev = &loop->events[loop->new_index % VANILLA_MAX_EVENT_COUNT];
assert(!ev->data);
assert(!ev->data);
ev->data = get_event_buffer();
if (!ev->data) {
vanilla_log("OUT OF MEMORY FOR NEW EVENTS");
ret = VANILLA_ERR_OUT_OF_MEMORY;
goto exit;
}
ev->data = get_event_buffer();
// if (!ev->data) {
// vanilla_log("OUT OF MEMORY FOR NEW EVENTS");
// ret = VANILLA_ERR_OUT_OF_MEMORY;
// }
ev->type = type;
memcpy(ev->data, data, size);
ev->size = size;
*event = ev;
loop->new_index++;
return ret;
}
pthread_cond_broadcast(&loop->waitcond);
} else {
vanilla_log("FAILED TO PUSH EVENT: wanted %lu, only had %lu. This is a bug, please report to developers.", size, EVENT_BUFFER_SIZE);
ret = VANILLA_ERR_INVALID_ARGUMENT;
}
int release_event(event_loop_t *loop)
{
loop->new_index++;
pthread_cond_broadcast(&loop->waitcond);
// } else {
// vanilla_log("FAILED TO PUSH EVENT: wanted %lu, only had %lu. This is a bug, please report to developers.", size, EVENT_BUFFER_SIZE);
// ret = VANILLA_ERR_INVALID_ARGUMENT;
// }
exit:
pthread_mutex_unlock(&loop->mutex);
return ret;
return VANILLA_SUCCESS;
}
int push_event(event_loop_t *loop, int type, const void *data, size_t size)
{
vanilla_event_t *ev;
int ret = acquire_event(loop, &ev);
if (ret != VANILLA_SUCCESS) {
return ret;
}
ev->type = type;
memcpy(ev->data, data, size);
ev->size = size;
ret = release_event(loop);
return ret;
}
int get_event(event_loop_t *loop, vanilla_event_t *event, int wait)
+26 -3
View File
@@ -1,16 +1,28 @@
#ifndef VANILLA_GAMEPAD_H
#define VANILLA_GAMEPAD_H
#include "vanilla.h"
#include <pthread.h>
#include <stdint.h>
#ifdef _WIN32
#include <winsock2.h>
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
#else
#include <arpa/inet.h>
#include <errno.h>
#include <sys/un.h>
#endif
#include "vanilla.h"
#include "../pipe/ports.h"
struct wpa_ctrl;
#define VANILLA_MAX_EVENT_COUNT 20
extern char wireless_interface[];
#define VANILLA_MAX_EVENT_COUNT 100
typedef struct
{
vanilla_event_t events[VANILLA_MAX_EVENT_COUNT];
@@ -43,12 +55,23 @@ typedef struct thread_data_t
vanilla_psk_t psk;
} thread_data_t;
typedef union {
struct sockaddr_in in;
#ifndef _WIN32
struct sockaddr_un un;
#endif
} sockaddr_u;
void sync_internal(thread_data_t *data);
void connect_as_gamepad_internal(thread_data_t *data);
int install_polkit_internal(thread_data_t *data, int install);
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete);
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size);
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port);
int push_event(event_loop_t *loop, int type, const void *data, size_t size);
int get_event(event_loop_t *loop, vanilla_event_t *event, int wait);
int acquire_event(event_loop_t *loop, vanilla_event_t **event);
int release_event(event_loop_t *loop);
void init_event_buffer_arena();
void free_event_buffer_arena();
+12 -8
View File
@@ -112,7 +112,7 @@ uint16_t resolve_axis_value(float axis, float neg, float pos, int flip)
if (flip) {
val = -val;
}
return ((int) (val * 1024)) + 2048;
}
@@ -160,7 +160,7 @@ void set_battery_status(int status)
pthread_mutex_unlock(&button_mtx);
}
void send_input(int socket_hid)
void send_input(int socket_hid, const sockaddr_u *addr, size_t addr_size)
{
InputPacket ip;
memset(&ip, 0, sizeof(ip));
@@ -213,7 +213,7 @@ void send_input(int socket_hid)
ip.buttons = htons(button_mask);
button_mask = 0;
if (current_buttons[VANILLA_BTN_L3]) button_mask |= 0x80;
if (current_buttons[VANILLA_BTN_R3]) button_mask |= 0x40;
if (current_buttons[VANILLA_BTN_TV]) button_mask |= 0x20;
@@ -242,7 +242,7 @@ void send_input(int socket_hid)
ip.fw_version_neg = 215;
send_to_console(socket_hid, &ip, sizeof(ip), PORT_HID);
send_to_sockaddr(socket_hid, &ip, sizeof(ip), addr, addr_size);
}
void *listen_input(void *x)
@@ -251,14 +251,18 @@ void *listen_input(void *x)
pthread_mutex_init(&button_mtx, NULL);
sockaddr_u addr;
size_t addr_size;
create_server_sockaddr(&addr, &addr_size, PORT_HID - 100, 0);
do {
send_input(info->socket_hid);
usleep(5 * 1000); // Produces 200Hz input, probably no need to go higher for the Wii U (supposedly the real gamepad is 180Hz)
send_input(info->socket_hid, &addr, addr_size);
usleep(5555); // Roughly 180Hz, same as the original gamepad
} while (!is_interrupted());
pthread_mutex_destroy(&button_mtx);
pthread_exit(NULL);
return NULL;
}
}
+322 -115
View File
@@ -20,6 +20,12 @@
#include "vanilla.h"
#include "util.h"
#ifdef __APPLE__
// macOS has no htobe32
#include <libkern/OSByteOrder.h>
#define htobe32(x) OSSwapHostToBigInt32(x)
#endif // __APPLE__
typedef struct
{
unsigned magic : 4;
@@ -36,15 +42,15 @@ typedef struct
uint8_t payload[2048];
} VideoPacket;
static pthread_mutex_t video_mutex;
static pthread_mutex_t idr_mutex;
static int idr_is_queued = 0;
#define VIDEO_PACKET_CACHE_MAX 1024
static VideoPacket video_packet_cache[VIDEO_PACKET_CACHE_MAX];
static size_t video_packet_cache_index = 0;
static uint8_t video_packet[100000];
static size_t generated_sps_params_size = 0;
#define VIDEO_PACKET_QUEUE_MAX 1024
static VideoPacket video_packet_queue[VIDEO_PACKET_QUEUE_MAX];
static size_t video_packet_min = 0;
static size_t video_packet_max = 0;
static pthread_mutex_t video_packet_mutex;
static pthread_cond_t video_packet_cond;
static const uint8_t VANILLA_PPS_PARAMS[] = {
0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x06, 0x0c, 0xe8
@@ -52,9 +58,9 @@ static const uint8_t VANILLA_PPS_PARAMS[] = {
void request_idr()
{
pthread_mutex_lock(&video_mutex);
pthread_mutex_lock(&idr_mutex);
idr_is_queued = 1;
pthread_mutex_unlock(&video_mutex);
pthread_mutex_unlock(&idr_mutex);
}
void send_idr_request_to_console(int socket_msg)
@@ -65,7 +71,80 @@ void send_idr_request_to_console(int socket_msg)
send_to_console(socket_msg, idr_request, sizeof(idr_request), PORT_MSG);
}
void handle_video_packet(gamepad_context_t *ctx, VideoPacket *vp, size_t size, int socket_msg)
static uint8_t *write_slice_nal(int is_idr, int frame_decode_num, uint8_t *out)
{
uint32_t slice_header = is_idr ? 0x25b804ff : (0x21e003ff | ((frame_decode_num & 0xff) << 13));
*((uint32_t *)out) = htobe32(slice_header);
return out + sizeof(uint32_t);
static uint8_t idr_num = 0;
uint8_t slice_nal[200];
size_t bit_index = 0;
// forbidden_zero_bit
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 0, 1);
// nal_ref_idc
write_bits(slice_nal, sizeof(slice_nal), &bit_index, is_idr ? 3 : 1, 2);
// nal_unit_type
write_bits(slice_nal, sizeof(slice_nal), &bit_index, is_idr ? 5 : 1, 5);
// first_mb_in_slice
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0);
// slice_type
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, is_idr ? 2 : 0);
// pic_parameter_set_id
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0);
// frame_num
const int fn_bits = 8;
write_bits(slice_nal, sizeof(slice_nal), &bit_index, frame_decode_num, fn_bits);
if (is_idr) {
// idr_pic_id
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, idr_num);
idr_num++;
// dec_ref_pic_marking() for IDR:
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 0, 1); // no_output_of_prior_pics_flag
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 0, 1); // long_term_reference_flag
} else {
// num_ref_idx_active_override_flag
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 1, 1);
// num_ref_idx_l0_active_minus1
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0);
// ref_pic_list_modification_flag_l0 = 0 (no modifications)
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 0, 1);
// dec_ref_pic_marking() for non-IDR:
write_bits(slice_nal, sizeof(slice_nal), &bit_index, 0, 1); // adaptive_ref_pic_marking_mode_flag = 0
// CABAC: entropy_coding_mode_flag==1 and P-slice → must write cabac_init_idc
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0); // cabac_init_idc = 0 (good default)
}
// slice_qp_delta
write_signed_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0);
// deblocking_filter_control_present_flag == 1 → must write
write_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0); // disable_deblocking_filter_idc = 0
write_signed_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0); // slice_alpha_c0_offset_div2
write_signed_exp_golomb(slice_nal, sizeof(slice_nal), &bit_index, 0); // slice_beta_offset_div2
size_t bytes = (bit_index + 7) >> 3;
memcpy(out, slice_nal, bytes);
out += bytes;
return out;
}
void handle_video_packet(gamepad_context_t *ctx, VideoPacket *vp)
{
//
// === IMPORTANT NOTE! ===
@@ -88,123 +167,211 @@ void handle_video_packet(gamepad_context_t *ctx, VideoPacket *vp, size_t size, i
int is_idr = 0;
for (int i = 0; i < sizeof(vp->extended_header); i++) {
if (vp->extended_header[i] == 0x80) {
// vanilla_log("GOT IDR");
is_idr = 1;
// pthread_mutex_lock(&video_mutex);
// if (idr_is_queued) {
// idr_is_queued = 0;
// }
// pthread_mutex_unlock(&video_mutex);
break;
}
}
// Check seq ID
static int seq_id_expected = -1;
int seq_matched = 1;
if (seq_id_expected == -1) {
seq_id_expected = vp->seq_id;
} else if (seq_id_expected != vp->seq_id) {
seq_matched = 0;
}
seq_id_expected = (vp->seq_id + 1) & 0x3ff; // 10 bit number
static int is_streaming = 0;
if (!seq_matched) {
is_streaming = 0;
}
// Check if this is the beginning of the packet
static VideoPacket *video_segments[1024];
static VideoPacket *video_segments[VIDEO_PACKET_QUEUE_MAX];
static int video_packet_seq = -1;
static int video_packet_seq_end = -1;
static int video_complete_frame = 0;
static uint8_t frame_decode_num = 0;
if (vp->frame_begin) {
video_packet_seq = vp->seq_id;
video_packet_seq_end = -1;
if (!is_streaming) {
if (is_idr) {
is_streaming = 1;
} else {
send_idr_request_to_console(socket_msg);
return;
}
memset(video_segments, 0, sizeof(video_segments));
frame_decode_num++;
if (!video_complete_frame && !is_idr) {
send_idr_request_to_console(ctx->socket_msg);
return;
}
video_complete_frame = 0;
}
pthread_mutex_lock(&video_mutex);
pthread_mutex_lock(&idr_mutex);
if (idr_is_queued) {
send_idr_request_to_console(socket_msg);
send_idr_request_to_console(ctx->socket_msg);
idr_is_queued = 0;
}
pthread_mutex_unlock(&video_mutex);
pthread_mutex_unlock(&idr_mutex);
// vanilla_log("set seq_id %i = %p", vp->seq_id, vp);
video_segments[vp->seq_id] = vp;
if (vp->frame_end) {
if (vp->frame_end)
video_packet_seq_end = vp->seq_id;
}
if (video_packet_seq_end != -1) {
// int complete_frame = 1;
if (is_streaming) {
// Encapsulate packet data into NAL unit
static int frame_decode_num = 0;
uint8_t *nals_current = video_packet + generated_sps_params_size;
int slice_header = is_idr ? 0x25b804ff : (0x21e003ff | ((frame_decode_num & 0xff) << 13));
// begin slice nalu
uint8_t slice[] = {0x00, 0x00, 0x00, 0x01,
(uint8_t) ((slice_header >> 24) & 0xff),
(uint8_t) ((slice_header >> 16) & 0xff),
(uint8_t) ((slice_header >> 8) & 0xff),
(uint8_t) (slice_header & 0xff)
};
memcpy(nals_current, slice, sizeof(slice));
nals_current += sizeof(slice);
// Get pointer to first packet's payload
if (video_packet_seq != -1 && video_packet_seq_end != -1) {
int complete_frame = 1;
{
int current_index = video_packet_seq;
uint8_t *from = video_segments[current_index]->payload;
memcpy(nals_current, from, 2);
nals_current += 2;
// Escape codes
int byte = 2;
while (1) {
uint8_t *data = video_segments[current_index]->payload;
size_t pkt_size = video_segments[current_index]->payload_size;
while (byte < pkt_size) {
if (data[byte] <= 3 && *(nals_current - 2) == 0 && *(nals_current - 1) == 0) {
*nals_current = 3;
nals_current++;
}
*nals_current = data[byte];
nals_current++;
byte++;
if (!video_segments[current_index]) {
complete_frame = 0;
vanilla_log("damn, incomplete frame (missing %i)", current_index);
break;
}
if (current_index == video_packet_seq_end) {
break;
}
byte = 0;
current_index = (current_index + 1) % VIDEO_PACKET_CACHE_MAX;
current_index = (current_index + 1) % VIDEO_PACKET_QUEUE_MAX;
}
}
// Skip IDR parameters if not an IDR frame
uint8_t *nals = (is_idr) ? video_packet : (video_packet + generated_sps_params_size);
push_event(ctx->event_loop, VANILLA_EVENT_VIDEO, nals, (nals_current - nals));
if (complete_frame) {
video_complete_frame = 1;
// Encapsulate packet data into NAL unit
vanilla_event_t *event;
int ret = acquire_event(ctx->event_loop, &event);
event->type = VANILLA_EVENT_VIDEO;
uint8_t *video_packet = event->data;
const int OLD_CODE = 1;
if (OLD_CODE) {
uint8_t *nals_current = video_packet;
static const char *frame_start_word = "\x00\x00\x00\x01";
if (is_idr) {
uint8_t sps[200], pps[200];
size_t sps_size = generate_sps_params(sps, sizeof(sps));
size_t pps_size = generate_pps_params(pps, sizeof(pps));
memcpy(nals_current, frame_start_word, 4);
nals_current += 4;
memcpy(nals_current, sps, sps_size);
nals_current += sps_size;
memcpy(nals_current, pps, pps_size);
nals_current += pps_size;
}
memcpy(nals_current, frame_start_word, 4);
nals_current += 4;
nals_current = write_slice_nal(is_idr, frame_decode_num, nals_current);
// Get pointer to first packet's payload
int current_index = video_packet_seq;
uint8_t *from = video_segments[current_index]->payload;
memcpy(nals_current, from, 2);
nals_current += 2;
// Escape codes
int byte = 2;
while (1) {
uint8_t *data = video_segments[current_index]->payload;
size_t pkt_size = video_segments[current_index]->payload_size;
while (byte < pkt_size) {
if (data[byte] <= 3 && *(nals_current - 2) == 0 && *(nals_current - 1) == 0) {
*nals_current = 3;
nals_current++;
}
*nals_current = data[byte];
nals_current++;
byte++;
}
if (current_index == video_packet_seq_end) {
break;
}
byte = 0;
current_index = (current_index + 1) % VIDEO_PACKET_QUEUE_MAX;
}
event->size = (nals_current - video_packet);
} else {
uint8_t *out = video_packet;
out += 4; // Make room for size
out = write_slice_nal(is_idr, frame_decode_num, out);
// vanilla_log_no_newline("generated slice NAL header:");
// for (size_t i = 0; i < bytes; i++) {
// vanilla_log_no_newline(" %02x", slice_nal[i] & 0xFF);
// }
// vanilla_log_no_newline("\n");
// uint32_t slice_header = is_idr ? 0x25b804ff : (0x21e003ff | ((0 & 0xff) << 13));
// *(uint32_t*)out = htobe32(slice_header);
// out += sizeof(uint32_t);
int i = video_packet_seq;
size_t offset = 0;
while (1) {
uint8_t *data = video_segments[i]->payload;
size_t sz = video_segments[i]->payload_size - offset;
memcpy(out, data + offset, sz);
out += sz;
offset = 0;
if (i == video_packet_seq_end) {
break;
}
i = (i + 1) % VIDEO_PACKET_QUEUE_MAX;
}
// Go back and write size
*((uint32_t *)video_packet) = htobe32(out - (video_packet + 4));
event->size = (out - video_packet);
}
// vanilla_log_no_newline("a few bytes from the packet:");
// for (size_t i = 0; i < 64; i++) {
// // vanilla_log_no_newline(" %02x", video_segments[video_packet_seq]->payload[i] & 0xFF);
// vanilla_log_no_newline(" %02x", video_packet[i] & 0xFF);
// }
// vanilla_log_no_newline("\n");
release_event(ctx->event_loop);
} else {
// We didn't receive the complete frame so we'll skip it here
}
}
}
void *consume_video_packets(void *data)
{
gamepad_context_t *ctx = (gamepad_context_t *) data;
pthread_mutex_lock(&video_packet_mutex);
while (!is_interrupted()) {
while (video_packet_min < video_packet_max) {
VideoPacket *vp = &video_packet_queue[video_packet_min % VIDEO_PACKET_QUEUE_MAX];
pthread_mutex_unlock(&video_packet_mutex);
handle_video_packet(ctx, vp);
pthread_mutex_lock(&video_packet_mutex);
video_packet_min++;
}
pthread_cond_wait(&video_packet_cond, &video_packet_mutex);
}
pthread_mutex_unlock(&video_packet_mutex);
}
size_t generate_h264_header(void *data, size_t size)
{
uint8_t *output = (uint8_t *) data;
@@ -228,36 +395,33 @@ void *listen_video(void *x)
gamepad_context_t *info = (gamepad_context_t *) x;
ssize_t size;
// Set up IDR nals on video_packet
uint8_t *nals_current = video_packet;
pthread_mutex_init(&idr_mutex, NULL);
pthread_mutex_init(&video_packet_mutex, NULL);
pthread_cond_init(&video_packet_cond, NULL);
static const char *frame_start_word = "\x00\x00\x00\x01";
memcpy(nals_current, frame_start_word, 4);
nals_current += 4;
generated_sps_params_size += 4;
size_t generated_sps = generate_sps_params(nals_current, sizeof(video_packet));
nals_current += generated_sps;
generated_sps_params_size += generated_sps;
memcpy(nals_current, VANILLA_PPS_PARAMS, sizeof(VANILLA_PPS_PARAMS));
nals_current += sizeof(VANILLA_PPS_PARAMS);
generated_sps_params_size += sizeof(VANILLA_PPS_PARAMS);
pthread_mutex_init(&video_mutex, NULL);
pthread_t video_consumer_thread;
pthread_create(&video_consumer_thread, 0, consume_video_packets, info);
do {
VideoPacket *vp = &video_packet_cache[video_packet_cache_index % VIDEO_PACKET_CACHE_MAX];
video_packet_cache_index++;
void *data = vp;
size = recv(info->socket_vid, data, sizeof(VideoPacket), 0);
VideoPacket *vp = &video_packet_queue[video_packet_max % VIDEO_PACKET_QUEUE_MAX];
size = recv(info->socket_vid, (void *) vp, sizeof(VideoPacket), 0);
if (size > 0) {
handle_video_packet(info, vp, size, info->socket_msg);
pthread_mutex_lock(&video_packet_mutex);
video_packet_max++;
if (video_packet_max == video_packet_min + VIDEO_PACKET_QUEUE_MAX) {
vanilla_log("WARNING: ROLLED OVER VIDEO PACKET QUEUE");
}
pthread_cond_broadcast(&video_packet_cond);
pthread_mutex_unlock(&video_packet_mutex);
// (info, vp, size, info->socket_msg);
}
} while (!is_interrupted());
pthread_mutex_destroy(&video_mutex);
pthread_join(video_consumer_thread, 0);
pthread_cond_destroy(&video_packet_cond);
pthread_mutex_destroy(&video_packet_mutex);
pthread_mutex_destroy(&idr_mutex);
pthread_exit(NULL);
@@ -269,7 +433,7 @@ void write_bits(void *data, size_t buffer_size, size_t *bit_index, uint8_t value
const size_t size_of_byte = 8;
assert(bit_width <= size_of_byte);
// Calculate offsets
size_t offset = *bit_index;
uint8_t *bytes = (uint8_t *) data;
@@ -325,7 +489,7 @@ void write_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, uint64_
int bit_width = ((sizeof(value) * size_of_byte) - leading_zeros);
int exp_golomb_leading_zeros = bit_width - 1;
for (int i = 0; i < exp_golomb_leading_zeros; i += size_of_byte) {
write_bits(data, buffer_size, bit_index, 0, MIN(size_of_byte, exp_golomb_leading_zeros - i));
}
@@ -348,8 +512,31 @@ void write_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, uint64_
}
}
// void write_signed_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, int64_t value)
// {
// uint64_t codeNum = (value <= 0) ? (uint64_t)(-value*2) : (uint64_t)(value*2-1);
// write_exp_golomb(data, buffer_size, bit_index, codeNum-1);
// }
void write_signed_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, int64_t value)
{
uint64_t codeNum;
if (value > 0) {
codeNum = ((uint64_t)value << 1) - 1; // 2*v - 1
} else {
// value <= 0
codeNum = (uint64_t)(-value) << 1; // -2*v
}
write_exp_golomb(data, buffer_size, bit_index, codeNum);
}
size_t generate_sps_params(void *data, size_t size)
{
// memcpy(data, VANILLA_SPS_PARAMS, MIN(sizeof(VANILLA_SPS_PARAMS), size));
// return sizeof(VANILLA_SPS_PARAMS);
//
// Reference: https://www.cardinalpeak.com/blog/the-h-264-sequence-parameter-set
//
@@ -409,7 +596,7 @@ size_t generate_sps_params(void *data, size_t size)
// seq_scaling_matrix_present_flag
write_bits(data, size, &bit_index, 0, 1);
// log2_max_frame_num_minus4
write_exp_golomb(data, size, &bit_index, 4);
@@ -420,7 +607,7 @@ size_t generate_sps_params(void *data, size_t size)
write_exp_golomb(data, size, &bit_index, 1);
// gaps_in_frame_num_value_allowed_flag
write_bits(data, size, &bit_index, 0, 1);
write_bits(data, size, &bit_index, 1, 1);
// pic_width_in_mbs_minus1
write_exp_golomb(data, size, &bit_index, 53);
@@ -508,7 +695,7 @@ size_t generate_sps_params(void *data, size_t size)
// RBSP trailing stop bit
write_bits(data, size, &bit_index, 1, 1);
// Alignment
const int align = 8;
if (bit_index % align != 0) {
@@ -522,4 +709,24 @@ size_t generate_pps_params(void *data, size_t size)
{
memcpy(data, VANILLA_PPS_PARAMS, MIN(sizeof(VANILLA_PPS_PARAMS), size));
return sizeof(VANILLA_PPS_PARAMS);
// size_t bit_index = 0;
// // forbidden_zero_bit
// write_bits(data, size, &bit_index, 0, 1);
// // nal_ref_idc = 3 (important)
// write_bits(data, size, &bit_index, 3, 2);
// // nal_unit_type = 8 (PPS)
// write_bits(data, size, &bit_index, 7, 5);
// // pic_parameter_set_id
// write_exp_golomb(data, size, &bit_index, 0);
// // seq_parameter_set_id
// write_exp_golomb(data, size, &bit_index, 0);
// // entropy_coding_mode_flag
// write_exp_golomb(data, size, &bit_index, 0);
}
+2 -1
View File
@@ -11,5 +11,6 @@ size_t generate_pps_params(void *data, size_t size);
size_t generate_h264_header(void *data, size_t size);
void write_bits(void *data, size_t size, size_t *bit_index, uint8_t value, size_t bit_width);
void write_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, uint64_t value);
void write_signed_exp_golomb(void *data, size_t buffer_size, size_t *bit_index, int64_t value);
#endif // GAMEPAD_VIDEO_H
#endif // GAMEPAD_VIDEO_H
+5
View File
@@ -255,3 +255,8 @@ void vanilla_send_audio(const void *data, size_t size)
{
send_audio_packet(data, size);
}
void vanilla_set_wireless_interface(const char *intf)
{
strcpy(wireless_interface, intf);
}
+4 -3
View File
@@ -23,7 +23,6 @@ extern "C" {
#define VANILLA_ERR_DISCONNECTED -11
static const uint32_t VANILLA_ADDRESS_LOCAL = 0xFFFFFFFF;
static const uint32_t VANILLA_ADDRESS_DIRECT = 0x0;
enum VanillaGamepadButtons
{
@@ -125,6 +124,8 @@ typedef struct {
int vanilla_start(uint32_t server_address, vanilla_bssid_t bssid, vanilla_psk_t psk);
int vanilla_sync(uint16_t code, uint32_t server_address);
void vanilla_set_wireless_interface(const char *intf);
int vanilla_install_polkit(uint32_t server_address);
int vanilla_uninstall_polkit(uint32_t server_address);
@@ -134,8 +135,6 @@ int vanilla_free_event(vanilla_event_t *event);
/**
* Attempt to stop the current action
*
* This can be called from another thread to safely exit a blocking call to vanilla_start().
*/
void vanilla_stop();
@@ -195,6 +194,8 @@ void vanilla_set_battery_status(int battery_status);
/**
* Retrieve SPS/PPS parameters for H.264 packeting
*/
size_t vanilla_generate_sps_params(void *data, size_t data_size);
size_t vanilla_generate_pps_params(void *data, size_t data_size);
size_t vanilla_generate_h264_header(void *data, size_t size);
/**