Oops unlinked my source

This commit is contained in:
devl0rd 2020-04-23 17:55:43 -06:00
parent 3bc3ba5e1f
commit 3e92241d5c
15 changed files with 3995 additions and 1 deletions

2
.gitignore vendored
View file

@ -11,6 +11,6 @@ SkyNX-Streamer/SkyNXStreamer-win32-x64
SkyNX-Streamer/SkyNXStreamer-win32-x64.zip
SkyNX-Streamer/SkyNXStreamer-win32-ia32
SkyNX-Streamer/SkyNXStreamer-win32-ia32.zip
SkyNX/source
SkyNX/build
SkyNX/SkyNX.zip
SkyNX-Streamer/settings.json

2337
SkyNX/source/SDL_FontCache.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,268 @@
/*
SDL_FontCache v0.9.0: A font cache for SDL and SDL_ttf
by Jonathan Dearborn
Dedicated to the memory of Florian Hufsky
License:
The short:
Use it however you'd like, but keep the copyright and license notice
whenever these files or parts of them are distributed in uncompiled form.
The long:
Copyright (c) 2016 Jonathan Dearborn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _SDL_FONTCACHE_H__
#define _SDL_FONTCACHE_H__
#include "SDL.h"
#include "SDL_ttf.h"
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
// Let's pretend this exists...
#define TTF_STYLE_OUTLINE 16
#define FC_Rect SDL_Rect
#define FC_Target SDL_Renderer
#define FC_Image SDL_Texture
#define FC_Log SDL_Log
// SDL_FontCache types
typedef enum
{
FC_ALIGN_LEFT,
FC_ALIGN_CENTER,
FC_ALIGN_RIGHT
} FC_AlignEnum;
typedef enum
{
FC_FILTER_NEAREST,
FC_FILTER_LINEAR
} FC_FilterEnum;
typedef struct FC_Scale
{
float x;
float y;
} FC_Scale;
typedef struct FC_Effect
{
FC_AlignEnum alignment;
FC_Scale scale;
SDL_Color color;
} FC_Effect;
// Opaque type
typedef struct FC_Font FC_Font;
typedef struct FC_GlyphData
{
SDL_Rect rect;
int cache_level;
} FC_GlyphData;
// Object creation
FC_Rect FC_MakeRect(float x, float y, float w, float h);
FC_Scale FC_MakeScale(float x, float y);
SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color);
FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
// Font object
FC_Font* FC_CreateFont(void);
Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, TTF_Font* ext, SDL_Color color);
Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, SDL_RWops* file_rwops_ext, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style);
void FC_ClearFont(FC_Font* font);
void FC_FreeFont(FC_Font* font);
// Built-in loading strings
char* FC_GetStringASCII(void);
// UTF-8 to SDL_FontCache codepoint conversion
/*!
Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string.
\param c A pointer to a string of proper UTF-8 character values.
\param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints.
*/
Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer);
/*!
Parses the given codepoint and stores the UTF-8 bytes in 'result'. The result is NULL terminated.
\param result A memory buffer for the UTF-8 values. Must be at least 5 bytes long.
\param codepoint The Uint32 codepoint to parse (not UTF-32).
*/
void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint);
// UTF-8 string operations
/*! Allocates a new string of 'size' bytes that is already NULL-terminated. The NULL byte counts toward the size limit, as usual. Returns NULL if size is 0. */
char* U8_alloc(unsigned int size);
/*! Deallocates the given string. */
void U8_free(char* string);
/*! Allocates a copy of the given string. */
char* U8_strdup(const char* string);
/*! Returns the number of UTF-8 characters in the given string. */
int U8_strlen(const char* string);
/*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */
int U8_charsize(const char* character);
/*! Copies the source multibyte character into the given buffer without overrunning it. Returns 0 on failure. */
int U8_charcpy(char* buffer, const char* source, int buffer_size);
/*! Returns a pointer to the next UTF-8 character. */
const char* U8_next(const char* string);
/*! Inserts a UTF-8 string into 'string' at the given position. Use a position of -1 to append. Returns 0 when unable to insert the string. */
int U8_strinsert(char* string, int position, const char* source, int max_bytes);
/*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */
void U8_strdel(char* string, int position);
// Internal settings
/*! Sets the string from which to load the initial glyphs. Use this if you need upfront loading for any reason (such as lack of render-target support). */
void FC_SetLoadingString(FC_Font* font, const char* string);
/*! Returns the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */
unsigned int FC_GetBufferSize(void);
/*! Changes the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */
void FC_SetBufferSize(unsigned int size);
void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale));
FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale);
// Custom caching
/*! Returns the number of cache levels that are active. */
int FC_GetNumCacheLevels(FC_Font* font);
/*! Returns the cache source texture at the given cache level. */
FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level);
// TODO: Specify ownership of the texture (should be shareable)
/*! Sets a cache source texture for rendering. New cache levels must be sequential. */
Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture);
/*! Copies the given surface to the given cache level as a texture. New cache levels must be sequential. */
Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface);
/*! Returns the number of codepoints that are stored in the font's glyph data map. */
unsigned int FC_GetNumCodepoints(FC_Font* font);
/*! Copies the stored codepoints into the given array. */
void FC_GetCodepoints(FC_Font* font, Uint32* result);
/*! Stores the glyph data for the given codepoint in 'result'. Returns 0 if the codepoint was not found in the cache. */
Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint);
/*! Sets the glyph data for the given codepoint. Duplicates are not checked. Returns a pointer to the stored data. */
FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data);
// Rendering
FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...);
FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...);
FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...);
FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...);
FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...);
FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...);
FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...);
FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...);
FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...);
FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...);
FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...);
FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...);
FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...);
FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...);
FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...);
// Getters
FC_FilterEnum FC_GetFilterMode(FC_Font* font);
Uint16 FC_GetLineHeight(FC_Font* font);
Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...);
Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...);
// Returns a 1-pixel wide box in front of the character in the given position (index)
FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...);
Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...);
int FC_GetAscent(FC_Font* font, const char* formatted_text, ...);
int FC_GetDescent(FC_Font* font, const char* formatted_text, ...);
int FC_GetBaseline(FC_Font* font);
int FC_GetSpacing(FC_Font* font);
int FC_GetLineSpacing(FC_Font* font);
Uint16 FC_GetMaxWidth(FC_Font* font);
SDL_Color FC_GetDefaultColor(FC_Font* font);
Uint8 FC_InRect(float x, float y, FC_Rect input_rect);
// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index)
Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...);
// Setters
void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter);
void FC_SetSpacing(FC_Font* font, int LetterSpacing);
void FC_SetLineSpacing(FC_Font* font, int LineSpacing);
void FC_SetDefaultColor(FC_Font* font, SDL_Color color);
#ifdef __cplusplus
}
#endif
#endif

144
SkyNX/source/audio.c Normal file
View file

@ -0,0 +1,144 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <inttypes.h>
#include <switch.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SAMPLERATE 48000
#define CHANNELCOUNT 2
#define FRAMERATE (1000 / 30)
#define SAMPLECOUNT (SAMPLERATE / FRAMERATE)
#define BYTESPERSAMPLE 2
void diep(char *s)
{
perror(s);
while (1)
;
}
int setup_socket()
{
struct sockaddr_in si_me;
int s;
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
diep("socket");
memset((char *)&si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(2224);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, &si_me, sizeof(si_me)) == -1)
diep("bind");
return s;
}
#define BUF_COUNT 5
AudioOutBuffer audiobuf[BUF_COUNT];
u8 *buf_data[BUF_COUNT];
int curBuf = 0;
#define swapbuf (curBuf = (curBuf + 1) % (BUF_COUNT))
AudioOutBuffer *audout_released_buf;
int audout_filled = 0;
void play_buf(int buffer_size, int data_size)
{
if (audout_filled >= BUF_COUNT)
{
u32 released_count;
//audoutPlayBuffer(&audout_released_buf, &released_count);
audoutWaitPlayFinish(&audout_released_buf, &released_count, UINT64_MAX);
}
audiobuf[curBuf].next = 0;
audiobuf[curBuf].buffer = buf_data[curBuf];
audiobuf[curBuf].buffer_size = buffer_size;
audiobuf[curBuf].data_size = data_size;
audiobuf[curBuf].data_offset = 0;
audoutAppendAudioOutBuffer(&audiobuf[curBuf]);
audout_filled++;
swapbuf;
}
// out_buf has to be in_buf_size*fact*2 big
short lastAbove0Value = 0;
void resample(unsigned short *in_buf, int in_buf_size, unsigned short *out_buf, short fact)
{
int channels = 2;
//channels are right next to each other
int dataLength = in_buf_size / sizeof(unsigned short);
for (int i = 0; i < dataLength; i += channels) //skip through audio data based on channel count
{
int out_base = i * fact; //get next starting point in upsampled audio buffer
for (int j = 0; j < fact; j++) //do this thing 3 times to fill missing audio
{
for (int chan = 0; chan < channels; chan++) //For both channels
{
out_buf[out_base++] = in_buf[i + chan]; //Smoothing here?? maybe..
}
}
}
}
#define DATA_SIZE 1920
#define IN_RATE 160000 // Bitrate.
#define OUT_RATE 480000 // Bitrate.
#define FACT (OUT_RATE / IN_RATE)
#define IN_BUFSIZE (DATA_SIZE / FACT)
void audioHandlerLoop()
{
char in_buf[IN_BUFSIZE] = {0};
u32 buffer_size = (DATA_SIZE + 0xfff) & ~0xfff;
for (int curBuf = 0; curBuf < BUF_COUNT; curBuf++)
{
buf_data[curBuf] = memalign(0x1000, buffer_size);
}
int sock = setup_socket();
printf("%d\n", sock);
int played = 0;
struct sockaddr si_other;
socklen_t slen = sizeof(si_other);
while (appletMainLoop())
{
int ret = recvfrom(sock, in_buf, sizeof(in_buf), 0, &si_other, &slen);
if (ret < 0)
{
perror("recv failed:");
continue;
}
if (ret != sizeof(in_buf))
{
printf("Bad input %d\n", ret);
continue;
}
resample((unsigned short *)in_buf, sizeof(in_buf), (unsigned short *)buf_data[curBuf], FACT);
play_buf(buffer_size, DATA_SIZE);
played++;
// svcSleepThread(16666665); //Nano sleep to keep at 60fps
}
for (int curBuf = 0; curBuf < BUF_COUNT; curBuf++)
{
free(buf_data[curBuf]);
}
}

3
SkyNX/source/audio.h Normal file
View file

@ -0,0 +1,3 @@
// TODO: Proper context stuff. Pretty hacky right now.
void audioHandlerLoop();

58
SkyNX/source/context.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef _CONTEXT_H
#define _CONTEXT_H
#include <libavformat/avformat.h>
#include <switch.h>
#include <SDL.h>
#include <SDL2/SDL_image.h>
#include "SDL_FontCache.h"
#define RESX 1280
#define RESY 720
typedef struct
{
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *yuv_text;
SDL_Rect rect;
SDL_Texture *logoTexture;
Mutex texture_mut;
u8 YPlane[RESX * RESY];
u8 UPlane[RESX * RESY / 4];
u8 VPlane[RESX * RESY / 4];
bool frame_avail;
Mutex frame_avail_mut;
FC_Font *font;
bool video_active;
Mutex video_active_mut;
} RenderContext;
typedef struct
{
AVFormatContext *fmt_ctx;
AVCodecContext *video_dec_ctx; //, *audio_dec_ctx;
AVStream *video_stream; //, *audio_stream = NULL;
int video_stream_idx; //, audio_stream_idx = -1;
AVFrame *rgbframe;
AVFrame *frame;
uint8_t *video_dst_data[4];
int video_dst_linesize[4];
int video_frame_count;
RenderContext *renderContext;
//static int audio_frame_count = 0;
} VideoContext;
typedef struct
{
int lissock;
int sock;
} JoyConSocket;
#endif

45
SkyNX/source/input.c Normal file
View file

@ -0,0 +1,45 @@
#include <switch.h>
#include "context.h"
#include "input.h"
#include "network.h"
void gamePadSend(JoyConSocket *connection)
{
JoystickPosition lJoy;
JoystickPosition rJoy;
touchPosition touch;
JoyPkg pkg;
/* Recieve switch input and generate the package */
hidScanInput();
pkg.heldKeys = hidKeysHeld(CONTROLLER_P1_AUTO);
hidJoystickRead(&lJoy, CONTROLLER_P1_AUTO, JOYSTICK_LEFT);
hidJoystickRead(&rJoy, CONTROLLER_P1_AUTO, JOYSTICK_RIGHT);
pkg.lJoyX = lJoy.dx;
pkg.lJoyY = lJoy.dy;
pkg.rJoyX = rJoy.dx;
pkg.rJoyY = rJoy.dy;
hidTouchRead(&touch, 0);
pkg.touchX = touch.px;
pkg.touchY = touch.py;
sendJoyConInput(connection, &pkg);
}
void handleInput(JoyConSocket *connection)
{
if (connectJoyConSocket(connection, 2223))
gamePadSend(connection);
}
void inputHandlerLoop(void *dummy)
{
JoyConSocket *connection = createJoyConSocket();
while (appletMainLoop())
{
handleInput(connection);
svcSleepThread(23333333);
}
freeJoyConSocket(connection);
}

7
SkyNX/source/input.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef INPUT_H
#define INPUT_H
/* Loop to handle joycon inputs and send theme to the server */
void inputHandlerLoop(void* dummy);
#endif

145
SkyNX/source/main.c Normal file
View file

@ -0,0 +1,145 @@
// The following ffmpeg code is inspired by an official ffmpeg example, so here is its Copyright notice:
/*
* Copyright (c) 2012 Stefano Sabatini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @file
* Demuxing and decoding example.
*
* Show how to use the libavformat and libavcodec API to demux and
* decode audio and video data.
* @example demuxing_decoding.c
*/
#include <libavutil/samplefmt.h>
#include <switch.h>
#include <SDL.h>
#include "context.h"
#include "input.h"
#include "video.h"
#include "network.h"
#include "renderer.h"
#include "audio.h"
static const SocketInitConfig socketInitConf = {
.bsdsockets_version = 1,
.tcp_tx_buf_size = 0x80000,
.tcp_rx_buf_size = 0x100000,
.tcp_tx_buf_max_size = 0x400000,
.tcp_rx_buf_max_size = 0x400000,
.udp_tx_buf_size = 0x1400,
.udp_rx_buf_size = 0x2500,
.sb_efficiency = 2,
};
void switchInit()
{
plInitialize();
pcvInitialize();
romfsInit();
networkInit(&socketInitConf);
audoutInitialize();
audoutStartAudioOut();
}
void switchDestroy()
{
audoutStopAudioOut();
audoutExit();
networkDestroy();
pcvExit();
plExit();
}
void startInput()
{
static Thread inputHandlerThread;
threadCreate(&inputHandlerThread, inputHandlerLoop, NULL, NULL, 0x1000, 0x2b, 0);
threadStart(&inputHandlerThread);
}
void startAudio()
{
static Thread audioHandlerThread;
// On same thread as input and preemptive
threadCreate(&audioHandlerThread, audioHandlerLoop, NULL, NULL, 0x10000, 0x20, 1);
threadStart(&audioHandlerThread);
}
void startRender(VideoContext *videoContext)
{
static Thread renderThread;
threadCreate(&renderThread, videoLoop, videoContext, NULL, 0x800000, 0x2b, 2);
threadStart(&renderThread);
}
RenderContext *renderContext = NULL;
VideoContext *videoContext = NULL;
void init()
{
/* Init all switch required systems */
switchInit();
pcvSetClockRate(PcvModule_CpuBus, 1785000000); //Overclock CPU
renderContext = createRenderer();
videoContext = createVideoContext();
videoContext->renderContext = renderContext;
/* Run audio handling in background */
startAudio();
/* Run input handling in background */
startInput();
/* Run input handling in background */
startRender(videoContext);
}
void unInit()
{
freeRenderer(renderContext);
freeVideoContext(videoContext);
pcvSetClockRate(PcvModule_CpuBus, 1020000000); //Reset CPU clock to default
}
int main(int argc, char **argv)
{
init();
while (appletMainLoop())
{
if (isVideoActive(renderContext))
{
displayFrame(renderContext);
}
else
{
drawSplash(renderContext);
}
svcSleepThread(12500000); //Nano sleep to keep at 80fps to handle drop frames without stutter
}
/* Deinitialize all used systems */
unInit();
}

88
SkyNX/source/network.c Normal file
View file

@ -0,0 +1,88 @@
#include "network.h"
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
void networkInit(const SocketInitConfig *conf)
{
socketInitialize(conf);
nxlinkStdio();
avformat_network_init();
}
void networkDestroy()
{
avformat_network_deinit();
socketExit();
}
JoyConSocket *createJoyConSocket()
{
JoyConSocket *socket = (JoyConSocket *)malloc(sizeof(JoyConSocket));
socket->sock = -1;
socket->lissock = -1;
return socket;
}
void freeJoyConSocket(JoyConSocket *connection)
{
free(connection);
}
int connectJoyConSocket(JoyConSocket *connection, int port)
{
if (connection->lissock == -1)
{
if (connection->sock != -1)
{
close(connection->sock);
connection->sock = -1;
}
struct sockaddr_in server;
connection->lissock = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
if (bind(connection->lissock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
close(connection->lissock);
connection->lissock = -1;
return 0;
}
listen(connection->lissock, 1);
}
if (connection->sock == -1)
{
// TODO: We might want to be able to not block if no client is connected
struct sockaddr_in client;
int c = sizeof(struct sockaddr_in);
connection->sock = accept(connection->lissock, (struct sockaddr *)&client, (socklen_t *)&c);
if (connection->sock < 0)
{
close(connection->lissock);
connection->sock = -1;
connection->lissock = -1;
return 0;
}
}
return 1;
}
void sendJoyConInput(JoyConSocket *connection, const JoyPkg *pkg)
{
if (send(connection->sock, pkg, sizeof(JoyPkg), 0) != sizeof(JoyPkg))
{
connection->sock = -1;
}
}

43
SkyNX/source/network.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef _NETWORK_H
#define _NETWORK_H
#include <switch.h>
#include "context.h"
#define URL "tcp://0.0.0.0:2222"
//#define TCP_RECV_BUFFER "500000"
/* Data to send to server */
typedef struct
{
unsigned long heldKeys;
short lJoyX;
short lJoyY;
short rJoyX;
short rJoyY;
short touchX;
short touchY;
} JoyPkg;
/* Init nx network and av network */
void networkInit(const SocketInitConfig *conf);
/* Deinitialize nx network and av network*/
void networkDestroy();
/* Creates the context for sending joycon inputs */
JoyConSocket *createJoyConSocket();
/* Deallocate from memory the constext used to sent joycon inputs */
void freeJoyConSocket(JoyConSocket *connection);
/* Send joycon input over the network */
void sendJoyConInput(JoyConSocket *connection, const JoyPkg *pkg);
/*
* Binds, listens and accepts connection with the server
* If the connection was previously opened reuses it
*/
int connectJoyConSocket(JoyConSocket *connection, int port);
#endif

520
SkyNX/source/renderer.c Normal file
View file

@ -0,0 +1,520 @@
#include "renderer.h"
#include <stdbool.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <unistd.h>
#include "video.h"
#include <time.h>
float timeThen = 0;
float timeNow = 1;
float delta = 1;
void initDelta()
{
timeThen = svcGetSystemTick();
timeNow = svcGetSystemTick();
delta = (timeNow - timeThen) / 10000000;
}
void loopStart()
{
timeNow = svcGetSystemTick();
delta = (timeNow - timeThen) / 10000000;
if (delta > 1)
{
//to much lag. set to 1
delta = 1.0f;
}
}
void loopEnd()
{
timeThen = timeNow;
}
int getRandomInt(int minimum_number, int max_number)
{
return rand() % (max_number + 1 - minimum_number) + minimum_number;
}
typedef struct
{
float x;
float y;
float r;
float vx;
float vy;
SDL_Color color;
} bubble;
typedef struct
{
bubble a;
bubble aIndex;
bubble b;
bubble bIndex;
} bubblePair;
typedef struct
{
float vx;
float vy;
} vector;
vector globalForce = {0, -30};
int bubblesLength = 0;
int maxBubbles = 20;
bubble bubbles[20];
long getDistance(long ax, long ay, long bx, long by)
{
long a = ax - bx;
long b = ay - by;
return sqrt(a * a + b * b);
}
bubble resolveBubble(bubble b1, bubble b2)
{ //Fix colliding circles
long distance_x = b1.x - b2.x;
long distance_y = b1.y - b2.y;
long radii_sum = b1.r + b2.r;
long distance = getDistance(b1.x, b1.y, b2.x, b2.y);
long unit_x = distance_x / distance;
long unit_y = distance_y / distance;
b1.x = b2.x + (radii_sum)*unit_x; //Uncollide
b1.y = b2.y + (radii_sum)*unit_y; //Uncollide
//Conservation of momentum
long newVelX1 = (b1.vx * (b1.r - b2.r) + (2 * b2.r * b2.vx)) / radii_sum;
long newVelY1 = (b1.vy * (b1.r - b2.r) + (2 * b2.r * b2.vy)) / radii_sum;
// long newVelX2 = (b2.vx * (b2.r - b1.r) + (2 * b1.r * b1.vx)) / radii_sum;
// long newVelY2 = (b2.vy * (b2.r - b1.r) + (2 * b1.r * b1.vy)) / radii_sum;
b1.vx = newVelX1;
b1.vy = newVelY1;
// b2.vx = newVelX2;
// b2.vy = newVelY2;
bubble newBubble = b1;
return newBubble;
}
bool detectCircleToCircleCollision(bubble b1, bubble b2)
{ //check for collision between circles
long radii_sum = b1.r + b2.r;
long distance = getDistance(b1.x, b1.y, b2.x, b2.y); //If distance is less than radius added together a collision is occuring
if (distance < radii_sum)
{
return true;
}
return false;
}
bool testForCollision(bubble b, int bI)
{
for (int i = 0; i < bubblesLength; i++)
{
if (i != bI)
{
if (detectCircleToCircleCollision(b, bubbles[i]))
{
return true;
}
}
}
return false;
}
void resolveCollisions()
{
bubble newBubbles[20];
bool aCollided = false;
for (int a = 0; a < bubblesLength; a++)
{
for (int b = a + 1; b < bubblesLength; b++)
{
if (detectCircleToCircleCollision(bubbles[a], bubbles[b]))
{
aCollided = true;
newBubbles[a] = resolveBubble(bubbles[a], bubbles[b]);
}
}
if (!aCollided)
{ //If nothing collided keep the same data
newBubbles[a] = bubbles[a];
}
aCollided = false;
}
for (int i = 0; i < bubblesLength; i++)
{
bubbles[i] = newBubbles[i];
}
}
void initBubbles()
{
while (bubblesLength < maxBubbles)
{
SDL_Color bubbleColor = {126, 242, 213, 255};
float randR = 15;
if (bubblesLength < 8)
{
SDL_Color nbc = {145, 255, 249, 120};
randR = getRandomInt(5, 20);
bubbleColor = nbc;
}
else if (bubblesLength < 13)
{
SDL_Color nbc = {75, 219, 211, 180};
randR = getRandomInt(25, 40);
bubbleColor = nbc;
}
else if (bubblesLength < 17)
{
SDL_Color nbc = {24, 161, 153, 200};
randR = getRandomInt(45, 60);
bubbleColor = nbc;
}
else if (bubblesLength < 20)
{
SDL_Color nbc = {0, 102, 96, 230};
randR = getRandomInt(65, 80);
bubbleColor = nbc;
}
float randXv = 0;
if (getRandomInt(0, 1) == 0)
{
randXv = getRandomInt(10, 25);
}
else
{
randXv = getRandomInt(10, 25) * -1;
}
float randYv = getRandomInt(20, 50) * -1;
bool collides = true;
while (collides)
{
int randX = getRandomInt(0, 1280);
int randY = getRandomInt(0, 720);
bubble nb = {randX, randY, randR, randXv, randYv, bubbleColor};
if (!testForCollision(nb, -1)) //-1 is because bubble has not yet spawned
{
bubblesLength++;
bubbles[bubblesLength] = nb;
collides = false;
}
};
}
}
SDL_Texture *logoTexture = NULL;
RenderContext *createRenderer()
{
RenderContext *context = malloc(sizeof(RenderContext));
context->window = SDL_CreateWindow("sdl2_gles2", 0, 0, RESX, RESY, SDL_WINDOW_FULLSCREEN);
if (context->window == NULL)
{
SDL_Log("SDL_CreateWindow: %s\n", SDL_GetError());
SDL_Quit();
while (1)
;
}
context->renderer = SDL_CreateRenderer(context->window, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (context->renderer == NULL)
{
SDL_Log("SDL_CreateRenderer: %s\n", SDL_GetError());
SDL_Quit();
while (1)
;
}
SDL_SetRenderDrawBlendMode(context->renderer, SDL_BLENDMODE_BLEND); //enable transparency
logoTexture = IMG_LoadTexture(context->renderer, "iconTransparent.png");
//Create font cache
context->yuv_text = SDL_CreateTexture(context->renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, RESX, RESY);
context->rect.x = 0;
context->rect.y = 0;
context->rect.w = RESX;
context->rect.h = RESY;
mutexInit(&context->texture_mut);
mutexInit(&context->frame_avail_mut);
mutexInit(&context->video_active_mut);
context->frame_avail = false;
context->video_active = false;
PlFontData fontData, fontExtData;
plGetSharedFontByType(&fontData, PlSharedFontType_Standard);
plGetSharedFontByType(&fontExtData, PlSharedFontType_NintendoExt);
context->font = FC_CreateFont();
FC_LoadFont_RW(context->font, context->renderer, SDL_RWFromMem((void *)fontData.address, fontData.size), SDL_RWFromMem((void *)fontExtData.address, fontExtData.size), 1, 40, FC_MakeColor(0, 0, 0, 255), TTF_STYLE_NORMAL);
initDelta();
initBubbles();
return context;
}
void setFrameAvail(RenderContext *context)
{
mutexLock(&context->frame_avail_mut);
context->frame_avail = true;
mutexUnlock(&context->frame_avail_mut);
}
bool checkFrameAvail(RenderContext *context)
{
bool ret;
mutexLock(&context->frame_avail_mut);
ret = context->frame_avail;
context->frame_avail = false;
mutexUnlock(&context->frame_avail_mut);
return ret;
}
bool isVideoActive(RenderContext *context)
{
bool ret;
mutexLock(&context->video_active_mut);
ret = context->video_active;
mutexUnlock(&context->video_active_mut);
return ret;
}
void setVideoActive(RenderContext *context, bool active)
{
mutexLock(&context->video_active_mut);
context->video_active = active;
mutexUnlock(&context->video_active_mut);
}
void SDL_ClearScreen(RenderContext *context, SDL_Color colour)
{
SDL_SetRenderDrawColor(context->renderer, colour.r, colour.g, colour.b, colour.a);
SDL_RenderClear(context->renderer);
}
void SDL_DrawText(RenderContext *context, int x, int y, SDL_Color colour, const char *text)
{
FC_DrawColor(context->font, context->renderer, x, y, colour, text);
}
void draw_rect(RenderContext *context, int x, int y, int w, int h, SDL_Color colour)
{
SDL_SetRenderDrawColor(context->renderer, colour.r, colour.g, colour.b, colour.a);
SDL_Rect r = {x, y, w, h};
SDL_RenderFillRect(context->renderer, &r);
}
void strokeCircle(RenderContext *context, float centreX, float centreY, float radius, SDL_Color colour)
{
int diameter = (radius * 2);
int x = (radius - 1);
int y = 0;
int tx = 1;
int ty = 1;
int error = (tx - diameter);
SDL_SetRenderDrawColor(context->renderer, colour.r, colour.g, colour.b, colour.a);
while (x >= y)
{
// Each of the following renders an octant of the circle
SDL_RenderDrawPoint(context->renderer, centreX + x, centreY - y);
SDL_RenderDrawPoint(context->renderer, centreX + x, centreY + y);
SDL_RenderDrawPoint(context->renderer, centreX - x, centreY - y);
SDL_RenderDrawPoint(context->renderer, centreX - x, centreY + y);
SDL_RenderDrawPoint(context->renderer, centreX + y, centreY - x);
SDL_RenderDrawPoint(context->renderer, centreX + y, centreY + x);
SDL_RenderDrawPoint(context->renderer, centreX - y, centreY - x);
SDL_RenderDrawPoint(context->renderer, centreX - y, centreY + x);
if (error <= 0)
{
++y;
error += ty;
ty += 2;
}
if (error > 0)
{
--x;
tx += 2;
error += (tx - diameter);
}
}
}
void drawCircle(RenderContext *context, float centreX, float centreY, float radius, int lineThicknes, SDL_Color colour)
{
for (int i = 0; i < lineThicknes; i++)
{
strokeCircle(context, centreX, centreY, radius - i, colour);
}
}
void fillCircle(RenderContext *context, float centreX, float centreY, float radius, SDL_Color colour)
{
for (double dy = 1; dy <= radius; dy += 1.0)
{
double dx = floor(sqrt((2.0 * radius * dy) - (dy * dy)));
SDL_SetRenderDrawColor(context->renderer, colour.r, colour.g, colour.b, colour.a);
SDL_RenderDrawLine(context->renderer, centreX - dx, centreY + dy - radius, centreX + dx, centreY + dy - radius);
SDL_RenderDrawLine(context->renderer, centreX - dx, (centreY + 1) - dy + radius, centreX + dx, (centreY + 1) - dy + radius);
}
}
void drawGradient(RenderContext *context, int x, int y, int w, int h, SDL_Color colourStart, SDL_Color colourEnd, int direction)
{
int drawLines = 1;
switch (direction)
{
case 1: //Top to bottom gradient
drawLines = h;
break;
case 2: //Bottom to top gradient
drawLines = h;
break;
case 3: //Left to right gradient
drawLines = w;
break;
case 4: //Right to left gradient
drawLines = w;
break;
}
for (int i = 0; i < drawLines; i++)
{ //Top to bottom gradient
float t = ((float)(i)) / ((float)(drawLines));
int r = ((float)colourStart.r) * (1.0f - t) + ((float)colourEnd.r) * t;
int g = ((float)colourStart.g) * (1.0f - t) + ((float)colourEnd.g) * t;
int b = ((float)colourStart.b) * (1.0f - t) + ((float)colourEnd.b) * t;
int a = ((float)colourStart.a) * (1.0f - t) + ((float)colourEnd.a) * t;
SDL_Color glc = {r, g, b, a};
switch (direction)
{
case 1:
draw_rect(context, x, y + i, w, 1, glc);
break;
case 2:
draw_rect(context, x, (y + h) - i, w, 1, glc);
break;
case 3:
draw_rect(context, x + 1, y, 0, h, glc);
break;
case 4:
draw_rect(context, (x + w) - i, y, 0, h, glc);
break;
}
}
}
void renderBubbles(RenderContext *context)
{
//Buggy right now. I need to understand C a bit better first.
//resolveCollisions();
for (int i = 0; i < bubblesLength; i++)
{
bubbles[i].vx += globalForce.vx * delta;
bubbles[i].vy += globalForce.vy * delta;
bubbles[i].x += bubbles[i].vx * delta;
bubbles[i].y += bubbles[i].vy * delta;
float negRadius = bubbles[i].r * -1;
if (bubbles[i].y < negRadius || bubbles[i].y > 720 + bubbles[i].r + 1) //bubble off top or overflowed to bottom because large delta
{
float randYv = getRandomInt(20, 50) * -1;
bubbles[i].vy = randYv;
bubbles[i].y = 720 + bubbles[i].r;
bool isColliding = true;
int attempts = 3;
while (isColliding && attempts > 3)
{
bubbles[i].x = getRandomInt(0, 1280);
if (!testForCollision(bubbles[i], i))
{
isColliding = false;
}
attempts--;
}
float randXv = 0;
if (getRandomInt(0, 1) == 0)
{
randXv = getRandomInt(10, 25);
}
else
{
randXv = getRandomInt(10, 25) * -1;
}
bubbles[i].vx = randXv;
}
fillCircle(context, bubbles[i].x, bubbles[i].y, bubbles[i].r, bubbles[i].color);
}
}
void drawSplash(RenderContext *context)
{
loopStart();
SDL_Color bg = {50, 50, 50, 255};
SDL_ClearScreen(context, bg);
SDL_Color bgf = {0, 181, 178, 255};
SDL_Color bgt = {0, 41, 40, 255};
drawGradient(context, 0, 0, 1280, 720, bgf, bgt, 1);
renderBubbles(context);
SDL_Color gf = {0, 0, 0, 250};
SDL_Color gt = {0, 0, 0, 0};
drawGradient(context, 0, 0, 1280, 180, gf, gt, 1);
drawGradient(context, 0, 720 - 100, 1280, 180, gf, gt, 2);
int imgW = 0;
int imgH = 0;
SDL_QueryTexture(logoTexture, NULL, NULL, &imgW, &imgH);
SDL_Rect imgDest;
imgDest.x = (1280 / 2) - (imgW / 2);
imgDest.y = (720 / 2) - (imgH / 2);
imgDest.w = imgW;
imgDest.h = imgH;
SDL_RenderCopy(context->renderer, logoTexture, NULL, &imgDest);
SDL_Color white = {230, 230, 230, 255};
u32 ip = gethostid();
char str_buf[300];
snprintf(str_buf, 300, "IP-Address: %u.%u.%u.%u\n",
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
SDL_DrawText(context, 400, imgDest.y + imgH + 8, white, str_buf);
SDL_RenderPresent(context->renderer);
loopEnd();
}
u64 old_time = 0, new_time = 0;
void handleFrame(RenderContext *renderContext, VideoContext *videoContext)
{
AVFrame *frame = videoContext->frame;
mutexLock(&renderContext->texture_mut);
memcpy(renderContext->YPlane, frame->data[0], sizeof(renderContext->YPlane));
memcpy(renderContext->UPlane, frame->data[1], sizeof(renderContext->UPlane));
memcpy(renderContext->VPlane, frame->data[2], sizeof(renderContext->VPlane));
mutexUnlock(&renderContext->texture_mut);
setFrameAvail(renderContext);
if (++videoContext->video_frame_count % 60 == 0)
{
new_time = svcGetSystemTick();
printf("Framerate: %f\n", 60.0 / ((new_time - old_time) / 19200000.0));
old_time = new_time;
}
}
void displayFrame(RenderContext *renderContext)
{
// while (!checkFrameAvail(renderContext))
// {
// }
if (checkFrameAvail(renderContext))
{
SDL_RenderClear(renderContext->renderer);
mutexLock(&renderContext->texture_mut);
SDL_UpdateYUVTexture(renderContext->yuv_text, &renderContext->rect, renderContext->YPlane, RESX,
renderContext->UPlane, RESX / 2,
renderContext->VPlane, RESX / 2);
mutexUnlock(&renderContext->texture_mut);
SDL_RenderCopy(renderContext->renderer, renderContext->yuv_text, NULL, NULL);
SDL_RenderPresent(renderContext->renderer);
}
}
void freeRenderer(RenderContext *context)
{
free(context);
}

37
SkyNX/source/renderer.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _RENDERER_H
#define _RENDERER_H
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include "context.h"
/* Allocates a render context */
RenderContext* createRenderer(void);
/* Draws an image filling all screen */
void drawSplash(RenderContext *context);
/* Handles a frame received from server */
void handleFrame(RenderContext* context, VideoContext* videoContext);
/* Draws a frame */
void displayFrame(RenderContext *renderContext);
/* Deallocates the render context */
void freeRenderer(RenderContext* context);
/* Sets the variable that indicates that there's a frame ready to be drawn */
void setFrameAvail(RenderContext* context);
/* Checks if a frame is ready to be drawn and sets that variable to false */
bool checkFrameAvail(RenderContext* context);
/* Returns true if there is a video playing right now */
bool isVideoActive(RenderContext *context);
/* Sets the video-playing status */
void setVideoActive(RenderContext *context, bool active);
#endif

282
SkyNX/source/video.c Normal file
View file

@ -0,0 +1,282 @@
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <switch.h>
#include "video.h"
#include "network.h"
#include "renderer.h"
VideoContext *createVideoContext()
{
VideoContext *context = (VideoContext *)malloc(sizeof(VideoContext));
context->fmt_ctx = NULL;
context->video_dec_ctx = NULL;
context->video_stream = NULL;
context->video_stream_idx = -1;
context->rgbframe = NULL;
context->video_frame_count = 0;
for (size_t i = 0; i < 4; i++)
context->video_dst_data[i] = NULL;
// Frame
context->frame = av_frame_alloc();
if (context->frame == NULL)
{
fprintf(stderr, "Could not allocate frame\n");
return NULL;
}
// RGBA Frame
context->rgbframe = av_frame_alloc();
context->rgbframe->width = RESX;
context->rgbframe->height = RESY;
context->rgbframe->format = AV_PIX_FMT_RGBA;
av_image_alloc(context->rgbframe->data,
context->rgbframe->linesize,
context->rgbframe->width,
context->rgbframe->height,
context->rgbframe->format, 32);
return context;
}
void freeVideoContext(VideoContext *context)
{
avcodec_free_context(&(context->video_dec_ctx));
avformat_close_input(&(context->fmt_ctx));
av_frame_free(&(context->frame));
av_free(context->video_dst_data[0]);
free(context);
}
/* Decodes a single frame and returns 0 on success */
int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
{
int ret;
*got_frame = 0;
if (pkt)
{
ret = avcodec_send_packet(avctx, pkt);
if (ret < 0)
return ret == AVERROR_EOF ? 0 : ret;
}
ret = avcodec_receive_frame(avctx, frame);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
return ret;
if (ret >= 0)
*got_frame = 1;
return 0;
}
/* Returns 1 if frame format is the same as the AVCodecContext format */
int expected_frame_format(AVCodecContext *avctx, AVFrame *frame)
{
int width = avctx->width;
int height = avctx->height;
enum AVPixelFormat pix_fmt = avctx->pix_fmt;
return frame->width == width || frame->height == height || frame->format == pix_fmt;
}
static int decode_packet(VideoContext *context, int *got_frame, AVPacket *pkt)
{
if (pkt->stream_index == context->video_stream_idx &&
decode_frame(context->video_dec_ctx, context->frame, got_frame, pkt) == 0)
{
if (!expected_frame_format(context->video_dec_ctx, context->frame))
{
fprintf(stderr, "Error: Width, height and pixel format have to be "
"constant in a rawvideo file, but the width, height or "
"pixel format of the input video changed:\n"
"old: width = %d, height = %d, format = %s\n"
"new: width = %d, height = %d, format = %s\n",
context->video_dec_ctx->width,
context->video_dec_ctx->height,
av_get_pix_fmt_name(context->video_dec_ctx->pix_fmt),
context->frame->width, context->frame->height,
av_get_pix_fmt_name(context->frame->format));
return -1;
}
handleFrame(context->renderContext, context);
}
else
{
fprintf(stderr, "Error decoding video frame \n");
return -1;
}
return context->frame->pkt_size;
}
static int open_codec_context(VideoContext *context, enum AVMediaType type)
{
int ret, stream_index;
AVStream *st;
AVCodec *dec = NULL;
ret = av_find_best_stream(context->fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0)
{
fprintf(stderr, "Could not find %s stream in input file \n",
av_get_media_type_string(type));
return ret;
}
else
{
stream_index = ret;
st = context->fmt_ctx->streams[stream_index];
// find decoder for the stream
dec = avcodec_find_decoder(st->codecpar->codec_id);
if (dec == NULL)
{
fprintf(stderr, "Failed to find %s codec\n",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
// Allocate a codec context for the decoder
context->video_dec_ctx = avcodec_alloc_context3(dec);
if (context->video_dec_ctx == NULL)
{
fprintf(stderr, "Failed to allocate the %s codec context\n",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
/*
Copy codec parameters from input stream to output codec context
*/
if ((ret = avcodec_parameters_to_context(context->video_dec_ctx, st->codecpar)) < 0)
{
fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(type));
return ret;
}
//Init the decoders, without reference counting
if ((ret = avcodec_open2(context->video_dec_ctx, dec, NULL)) < 0)
{
fprintf(stderr, "Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
context->video_stream_idx = stream_index;
}
return 0;
}
void videoLoop(void *context_ptr)
{
VideoContext* context = (VideoContext*) context_ptr;
while(appletMainLoop())
handleVid(context);
}
int handleVid(VideoContext *context)
{
int ret = 0;
int got_frame = 0;
AVFormatContext *fmt_ctx = NULL;
AVPacket pkt;
// setting TCP input options
AVDictionary *opts = 0;
av_dict_set(&opts, "listen", "1", 0); // set option for listening
av_dict_set(&opts, "probesize", "50000", 0);
//open input file, and allocate format context
ret = avformat_open_input(&fmt_ctx, URL, 0, &opts);
if (ret < 0)
{
char errbuf[100];
av_strerror(ret, errbuf, 100);
fprintf(stderr, "Input Error %s\n", errbuf);
return ret;
}
setVideoActive(context->renderContext, true);
context->fmt_ctx = fmt_ctx;
// Retrieve stream information
if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
{
fprintf(stderr, "Could not find stream information\n");
return ret;
}
// Context for the video
if (open_codec_context(context, AVMEDIA_TYPE_VIDEO) >= 0)
{
context->video_stream = fmt_ctx->streams[context->video_stream_idx];
// Allocate image where the decoded image will be put
ret = av_image_alloc(context->video_dst_data,
context->video_dst_linesize,
context->video_dec_ctx->width,
context->video_dec_ctx->height,
context->video_dec_ctx->pix_fmt, 1);
if (ret < 0)
{
char errbuf[100];
av_strerror(ret, errbuf, 100);
fprintf(stderr, "Could not allocate raw video buffer %s\n", errbuf);
return ret;
}
}
//dump input information to stderr
//av_dump_format(context->fmt_ctx, 0, URL, 0);
if (context->video_stream == NULL)
{
fprintf(stderr, "Could not find stream in the input, aborting\n");
ret = 1;
return ret;
}
//initialize packet, set data to NULL, let the demuxer fill it
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//read frames from the file
while (av_read_frame(fmt_ctx, &pkt) >= 0)
{
AVPacket orig_pkt = pkt;
do
{
ret = decode_packet(context, &got_frame, &pkt);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
}
while (pkt.size > 0);
av_packet_unref(&orig_pkt);
}
//flush cached frames
pkt.data = NULL;
pkt.size = 0;
do
{
decode_packet(context, &got_frame, &pkt);
}
while (got_frame);
printf("Stream finished.\n");
checkFrameAvail(context->renderContext);
setVideoActive(context->renderContext, false);
return ret;
}

17
SkyNX/source/video.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _VIDEO_H
#define _VIDEO_H
#include "context.h"
/* Allocates a video context and all its av fields (frame, rgbaFrame ...) */
VideoContext* createVideoContext(void);
/* Loop to handle video streaming with the server */
int handleVid(VideoContext* context);
/* Deallocates a video context */
void freeVideoContext(VideoContext* context);
/* Loop for receiving and decoding video */
void videoLoop(void *context_ptr);
#endif