GameAnywhere is an open source cloud game platform, developed by Chun-Ying Huang in 2013, the first is used for graduation thesis research, recently with the cloud game outlet is in full bloom, this project attention and improvement, this paper introduces GameAnywhere code composition. The License of GameAnywhere is BSD3. You can modify the code and close the source. However, the project relies on many open source components, so you need to pay attention to the License.
The code structure
The directory structure
├─bin │ ├─config │ ├─ ├─ common │ ├─data │ ├─log │ ├─ ├─ ├─ bass │ ├─bin │ ├─NvCodec │ ├─ ├─ NVENC ├ ─ deps. # rely on posix component of Unix package │ └ ─ lib ├ ─ deps. SRC # based on component source │ └ ─ patches ├ ─ deps. Win32 # dependent component of Windows header files and libraries │ ├ ─ bin │ ├ ─ the include │ │ ├ ─ the include │ │ ├ ─ lib │ │ ├ ─ libavcodec │ │ ├ ─ libavdevice │ │ ├ ─ libavfilter │ │ ├ ─ libavformat │ │ ├ ─ libavutil │ │ ├ ─ libpostproc │ │ ├ ─ libswresample │ │ ├ ─ libswscale │ │ ├ ─ live555 │ │ ├ ─ NVENC │ │ └ ─ SDL2 │ └ ─ lib ├ ─ docs └ ─ ga ├ ─ android # │ ├─ Asource system │ ├─ Ctrl-sDL # │ ├─ Encoder - Audio # ├─ Encoder - MFX │ ├─ Encoder - Nvenc # NVIDIA Hard Coding │ ├─ Encoder - Video # FFmpeg soft coding │ ├─ Encoder - Video # FFmpeg soft coding │ ├─ Encoder - Video # │ ├─ Heavy Metal Flag School │ ├─ Heavy Metal Flag School │ ├─ Heavy Metal Flag School │ ├─ Heavy metal Flag School │ ├─ ├─ Server-Live555 # ├─ Server-Live555 # │ ├─ vsource-Desktop # ├─ Server │ ├─ Event-Posix # │ ├ ─periodic # ├ ─periodic # ├─ ├─ Encoder ├─ Nvenc ├─ Ga/Core ├─ Microsoft ├─ Microsoft ├─ Microsoft ├─ Microsoft ├─ Microsoft ├─ Bass Exercises ├ ─ ga - hook ├ ─ ga - server - the event - driven ├ ─ ga - server - periodic ├ ─ groupsock ├ ─ ipch ├ ─ libga ├ ─ liveMedia ├ ─ the module - asource - system ├─ Module-Ctrl-SDL ├─ Module-Encoder - Audio ├─ Module-Encoder - Video ├─ Module-Encoder - X264 ├─ Module-Filter - RGB2YUV ├ ─ the module - server - ffmpeg ├ ─ the module - server - live555 ├ ─ the module - vsource - desktop ├ ─ the module - vsource - desktop - d ├ ─ the module - vsource - desktop - DFM └ ─ UsageEnvironmentCopy the code
The service side
Main: server\ga\server\periodic\ga-server-periodic.cpp
Functions of the Server side: image collection, image preprocessing, sound collection, keyboard input, audio and video coding, packet, transmission function, these functions are respectively implemented by the corresponding GameAnywhere defined module.
//
if (load_modules() < 0) {
write_log("error, load_modules error ! \n");
return - 1;
}
if (init_modules() < 0) {
write_log("error, init_modules error ! \n");
return - 1;
}
if (run_modules() < 0) {
write_log("error, run_modules error ! \n");
return - 1;
}
Copy the code
GameAnywhere module type definition: Server \ga\ Core \ga-module.h
/** * Enumeration for types of a module. */
enum ga_module_types {
GA_MODULE_TYPE_NULL = 0./**< Not used */
GA_MODULE_TYPE_CONTROL, /**< Is a controller module */
GA_MODULE_TYPE_ASOURCE, /**< Is an audio source module */
GA_MODULE_TYPE_VSOURCE, /**< Is an video source module */
GA_MODULE_TYPE_FILTER, /**< Is a filter module */
GA_MODULE_TYPE_AENCODER, /**< Is an audio encoder module */
GA_MODULE_TYPE_VENCODER, /**< Is a video encoder module */
GA_MODULE_TYPE_ADECODER, /**< Is an audio decoder module */
GA_MODULE_TYPE_VDECODER, /**< Is a video decoder module */
GA_MODULE_TYPE_SERVER /**< Is a server module */
};
Copy the code
GameAnywhere module definition: Server \ga\ Core \ga-module.h
/** * Data strucure to represent a module. */
typedef struct ga_module_s {
HMODULE handle; /**< Handle to a module */
int type; /**< Type of the module */
char *name; /**< Name of the module */
char *mimetype; /**< MIME-type of the module */
int (*init)(void *arg); /**< Pointer to the init function */
int (*start)(void *arg); /**< Pointer to the start function */
//void * (*threadproc)(void *arg);
int (*stop)(void *arg); /**< Pointer to the stop function */
int (*deinit)(void *arg); /**< Pointer to the deinit function */
int (*ioctl)(int command, int argsize, void *arg); /**< Pointer to ioctl function */
int (*notify)(void *arg); /**< Pointer to the notify function */
void * (*raw)(void *arg, int *size); /**< Pointer to the raw function */
int (*send_packet)(const char *prefix, int channelId, AVPacket *pkt, int64_t encoderPts, struct timeval *ptv); /**< Pointer to the send packet function: sink only */
void * privdata; /**< Private data of this module */
} ga_module_t;
Copy the code
Module lifecycle action: Server \ga\ Core \ga-module.h
EXPORT ga_module_t * ga_load_module(const char *modname, const char *prefix);
EXPORT void ga_unload_module(ga_module_t *m);
EXPORT int ga_init_single_module(const char *name, ga_module_t *m, void *arg);
EXPORT void ga_init_single_module_or_quit(const char *name, ga_module_t *m, void *arg);
EXPORT int ga_run_single_module(const char *name, void * (*threadproc)(void*), void *arg);
EXPORT void ga_run_single_module_or_quit(const char *name, void * (*threadproc)(void*), void *arg);
// module function wrappers
EXPORT int ga_module_init(ga_module_t *m, void *arg);
EXPORT int ga_module_start(ga_module_t *m, void *arg);
EXPORT int ga_module_stop(ga_module_t *m, void *arg);
EXPORT int ga_module_deinit(ga_module_t *m, void *arg);
EXPORT int ga_module_ioctl(ga_module_t *m, int command, int argsize, void *arg);
EXPORT int ga_module_notify(ga_module_t *m, void *arg);
EXPORT void * ga_module_raw(ga_module_t *m, void *arg, int *size);
EXPORT int ga_module_send_packet(ga_module_t *m, const char *prefix, int channelId, AVPacket *pkt, int64_t encoderPts, struct timeval *ptv);
Copy the code
Image acquisition module: Server \ GA \ Module \vsource-desktop
This module provides desktop level image collection, but does not provide process level image collection. DirectX and GDI two image acquisition implementation.
ga_module_t *
module_load(a) {
static ga_module_t m;
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_VSOURCE;
m.name = strdup("vsource-desktop");
m.init = vsource_init;
m.start = vsource_start;
m.stop = vsource_stop;
m.deinit = vsource_deinit;
m.ioctl = vsource_ioctl;
return &m;
}
Copy the code
Image preprocessing module: Server \ GA \ Module \ filter-rGB2yuv \ filter-rGB2yuv.cpp
This module provides the conversion of captured RGB images to YUV format.
ga_module_t *
module_load(a) {
static ga_module_t m;
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_FILTER;
m.name = strdup("filter-RGB2YUV");
m.init = filter_RGB2YUV_init;
m.start = filter_RGB2YUV_start;
m.stop = filter_RGB2YUV_stop;
m.deinit = filter_RGB2YUV_deinit;
//m.threadproc = filter_RGB2YUV_threadproc;
return &m;
}
Copy the code
NVIDIA hard coding module: Server \ga\module\encoder-nvenc\encoder-nvenc.cpp
ga_module_t *
module_load(a) {
static ga_module_t m;
struct RTSPConf *rtspconf = rtspconf_global();
//
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_VENCODER;
m.name = strdup("nvenc-video-encoder");
m.mimetype = strdup("video/H264");
m.init = nvenc_init;
m.deinit= nvenc_deinit;
m.start = nvenc_start;
m.ioctl = nvenc_ioctl;
m.stop = nvenc_stop;
return &m;
}
Copy the code
Soft coding module (FFMPGE implementation): server\ga\module\encoder-video\encoder-video.cpp
ga_module_t *
module_load(a) {
static ga_module_t m;
//struct RTSPConf *rtspconf = rtspconf_global();
char mime[64];
//
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_VENCODER;
m.name = strdup("ffmpeg-video-encoder");
if(ga_conf_readv("video-mimetype", mime, sizeof(mime)) ! =NULL) {
m.mimetype = strdup(mime);
}
m.init = vencoder_init;
m.start = vencoder_start;
//m.threadproc = vencoder_threadproc;
m.stop = vencoder_stop;
m.deinit = vencoder_deinit;
//
m.raw = vencoder_raw;
m.ioctl = vencoder_ioctl;
return &m;
}
Copy the code
Soft coding module (X264 implementation): Server \ga\module\encoder-x264\encoder-x264. CPP
ga_module_t *
module_load(a) {
static ga_module_t m;
//
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_VENCODER;
m.name = strdup("x264-video-encoder");
m.mimetype = strdup("video/H264");
m.init = vencoder_init;
m.start = vencoder_start;
//m.threadproc = vencoder_threadproc;
m.stop = vencoder_stop;
m.deinit = vencoder_deinit;
//
m.raw = vencoder_raw;
m.ioctl = vencoder_ioctl;
return &m;
}
Copy the code
Audio acquisition module: Server \ga\module\asource-system\asource-system.cpp
On Windows, the Microsoft IAudioClient API is used to collect sound.
ga_module_t *
module_load(a) {
static ga_module_t m;
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_ASOURCE;
m.name = strdup("asource-system");
m.init = asource_init;
m.start = asource_start;
//m.threadproc = asource_threadproc;
m.stop = asource_stop;
m.deinit = asource_deinit;
return &m;
}
Copy the code
Audio encoding module: Server \ga\module\encoder-audio\encoder-audio. CPP
Encodes audio using FFMPEG encapsulated APIS in LAME/OPUS format.
ga_module_t *
module_load(a) {
static ga_module_t m;
struct RTSPConf *rtspconf = rtspconf_global();
char mime[64];
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_AENCODER;
m.name = strdup("ffmpeg-audio-encoder");
if(ga_conf_readv("audio-mimetype", mime, sizeof(mime)) ! =NULL) {
m.mimetype = strdup(mime);
}
m.init = aencoder_init;
m.start = aencoder_start;
//m.threadproc = aencoder_threadproc;
m.stop = aencoder_stop;
m.deinit = aencoder_deinit;
return &m;
}
Copy the code
Media Transfer module (FFMPGE-server): server\ga\ Module \server- FFmpeg \server-ffmpeg.cpp
This module is no longer used.
ga_module_t *
module_load(a) {
static ga_module_t m;
//
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_SERVER;
m.name = strdup("ffmpeg-rtsp-server");
m.init = ff_server_init;
m.start = ff_server_start;
m.stop = ff_server_stop;
m.deinit = ff_server_deinit;
m.send_packet = ff_server_send_packet;
//
encoder_register_sinkserver(&m);
//
return &m;
}
Copy the code
Audio and Video Transmission module (LIVE555): server\ga\ Module \server-live555\server-live555.cpp
This module is currently used. Live555 has performance optimization space, more online information, you can refer to.
ga_module_t *
module_load(a) {
static ga_module_t m;
//
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_SERVER;
m.name = strdup("live555-rtsp-server");
m.init = live_server_init;
m.start = live_server_start;
m.stop = live_server_stop;
m.deinit = live_server_deinit;
m.send_packet = live_server_send_packet;
//
encoder_register_sinkserver(&m);
//
return &m;
}
Copy the code
Control instruction input module
This module supports keyboard, mouse event input, based on SDL implementation.
ga_module_t *
module_load(a) {
static ga_module_t m;
bzero(&m, sizeof(m));
m.type = GA_MODULE_TYPE_CONTROL;
m.name = strdup("control-SDL");
m.init = sdlmsg_replay_init;
m.deinit = sdlmsg_replay_deinit;
return &m;
}
Copy the code
The Android client
The client consists of C/C++ Native library and Java App.
- C/C++ Native: provides touch command transmission, media stream reception, decoding, and playback functions.
- Java App: provides touch command collection and cloud game App functions.
C/C++ Native API (main entry): Server \ga\ Android \jni\ SRC \libgaclient.h
Native library JNI function definition.
- Initialize the
JNIEXPORT jboolean JNICALL Java_org_gaminganywhere_gaclient_GAClient_initGAClient( JNIEnv *env, jobject thisObj, jobject weak_this);
Copy the code
- Configuration parameters
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_resetConfig( JNIEnv *env, jobject thisObj);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setProtocol( JNIEnv *env, jobject thisObj, jstring proto);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setHost( JNIEnv *env, jobject thisObj, jstring host);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setPort( JNIEnv *env, jobject thisObj, jint port);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setObjectPath( JNIEnv *env, jobject thisObj, jstring objpath);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setRTPOverTCP( JNIEnv *env, jobject thisObj, jboolean enabled);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setCtrlEnable( JNIEnv *env, jobject thisObj, jboolean enabled);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setCtrlProtocol( JNIEnv *env, jobject thisObj, jboolean tcp);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_setCtrlPort( JNIEnv *env, jobject thisObj, jint port);
JNIEXPORT void JNICALL
Java_org_gaminganywhere_gaclient_GAClient_setBuiltinAudioInternal( JNIEnv *env, jobject thisObj, jboolean enable);
JNIEXPORT void JNICALL
Java_org_gaminganywhere_gaclient_GAClient_setBuiltinVideoInternal( JNIEnv *env, jobject thisObj, jboolean enable);
JNIEXPORT void JNICALL
Java_org_gaminganywhere_gaclient_GAClient_setAudioCodec(
JNIEnv *env, jobject thisObj,
/*jstring codecname,*/ jint samplerate, jint channels);
JNIEXPORT void JNICALL
Java_org_gaminganywhere_gaclient_GAClient_setDropLateVideoFrame(JNIEnv *env, jobject thisObj, jint ms);
Copy the code
- Sends keyboard and mouse events
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendKeyEvent( JNIEnv *env, jobject thisObj, jboolean pressed, jint scancode, jint sym, jint mod, jint unicode);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseKey( JNIEnv *env, jobject thisObj, jboolean pressed, jint button, jint x, jint y);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseMotion( JNIEnv *env, jobject thisObj, jint x, jint y, jint xrel, jint yrel, jint state, jboolean relative);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseWheel( JNIEnv *env, jobject thisObj, jint dx, jint dy);
Copy the code
- Media decoding and playback
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendKeyEvent( JNIEnv *env, jobject thisObj, jboolean pressed, jint scancode, jint sym, jint mod, jint unicode);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseKey( JNIEnv *env, jobject thisObj, jboolean pressed, jint button, jint x, jint y);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseMotion( JNIEnv *env, jobject thisObj, jint x, jint y, jint xrel, jint yrel, jint state, jboolean relative);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_sendMouseWheel( JNIEnv *env, jobject thisObj, jint dx, jint dy);
Copy the code
GameAnywhere uses soft decoding for video (presumably for compatibility reasons), which is less performance than hard decoding and can be replaced with hard decoding.
- Media stream connection request
JNIEXPORT jboolean JNICALL Java_org_gaminganywhere_gaclient_GAClient_rtspConnect( JNIEnv *env, jobject thisObj);
JNIEXPORT void JNICALL Java_org_gaminganywhere_gaclient_GAClient_rtspDisconnect( JNIEnv *env, jobject thisObj);
Copy the code
Media streaming client (Live555 client): Server \ga\ Android \jni\ SRC \ rtspClient.cpp
Media streaming client is an RTSP client that relies on Live555 library (BasicUsageEnvironment, UsageEnvironment, GroupSock, liveMedia).
RTSP client to start a thread, through BasicTaskScheduler. SingleStep () method from the service side read audio and video data, then the decoding and playing.
Read data:
void *
rtsp_thread(void *param) {
RTSPClient *client = NULL; BasicTaskScheduler0 *bs = BasicTaskScheduler::createNew(); .while(rtspParam->quitLive555 == 0) {
bs->SingleStep(1000000); }... }Copy the code
If it is video data, call play_video:
static void
play_video(int channel, unsigned char *buffer, int bufsize, struct timeval pts, bool marker) {
struct decoder_buffer *pdb = &db[channel];
int left;
//
if(bufsize <= 0 || buffer == NULL) {
rtsperror("empty buffer? \n");
return;
}
#ifdef ANDROID
if(rtspconf->builtin_video_decoder ! =0) {
//////// Work with built-in decoders
if(video_codec_id == AV_CODEC_ID_H264) {
if(android_decode_h264(rtspParam, buffer, bufsize, pts, marker) < 0)
return;
} else if(video_codec_id == AV_CODEC_ID_VP8) {
if(android_decode_vp8(rtspParam, buffer, bufsize, pts, marker) < 0)
return;
}
image_rendered = 1;
} else {
//////// Work with ffmpeg
#endif. }Copy the code
If it is audio data, call play_audio:
static void
play_audio(unsigned char *buffer, int bufsize, struct timeval pts) {
#ifdef ANDROID
if(rtspconf->builtin_audio_decoder ! =0) {
android_decode_audio(rtspParam, buffer, bufsize, pts);
} else {
////////////////////////////////////////
#endif. }Copy the code
Media stream decoding and playback: Server \ga\ Android \jni\ SRC \ Android-decoders.cpp
The specific media stream decoding logic is implemented in Android -decoders. CPP. This includes configuring the decoder parameters, starting the decoder, and decoding a frame of video.
int android_prepare_audio(RTSPThreadParam *rtspParam, const char *mime, bool builtinDecoder);
int android_decode_audio(RTSPThreadParam *rtspParam, unsigned char *buffer, int bufsize, struct timeval pts);
int android_config_h264_sprop(RTSPThreadParam *rtspParam, const char *sprop);
int android_decode_h264(RTSPThreadParam *rtspParam, unsigned char *buffer, int bufsize, struct timeval pts, bool marker);
int android_decode_vp8(RTSPThreadParam *rtspParam, unsigned char *buffer, int bufsize, struct timeval pts, bool marker);
Copy the code
Command transmission: Server \ga\ Android \jni\ SRC \controller.cpp
The instructions sent by the Java App layer are stored in a queue, and the thread implemented by Controller.cpp sends the queue instructions to the server. An instruction channel is a separate channel.
Java App
Java apps are relatively simple, providing an interface and capturing user input.
Reference
GameAnywhere On Github
GameAnyhwere Offical Site
More cloud best practices best practices