This document outlines the architecture and implementation details of a live streaming platform developed as an undergraduate graduation project.
1: Development Environment
Linux server configuration: debian10, gcc 10.2.1, php7.4.3, mysql 5.7.26, nginx1.15.11
Windows development environment: nginx1.15.11, php7.4.3, mysql 5.7.26 via XAMPP or similar stack.
Linux deployment requires compilation toolchain:
su root
apt update
apt install build-essential
//Verify installation
gcc --version
g++ --version
Additionally, nginx with http-flv module is required for streaming functionality.
Create nginx directory
mkdir /nginx
cd nginx
Download nginx
wget http://nginx.org/download/nginx-1.18.0.tar.gz
Download nginx-http-flv-module
wget https://github.com/winshining/nginx-http-flv-module/archive/master.zip
Extract packages
tar -zxvf nginx-1.18.0.tar.gz
unzip nginx-http-flv-master.zip
Install dependencies
apt install gcc
apt install libpcre3 libpcre3-dev
apt install openssl libssl-dev
apt install zlib1g-dev
Navigate to nginx source directory
cd nginx-1.18.0/
./configure --add-module=/nginx/nginx-http-flv-master/
Note: path must be absolute
make && make install
Runtime binary: /usr/local/nginx/sbin/nginx
Configuration path: /usr/local/nginx/conf/nginx.conf
#nginx configuration
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
rtmp_auto_push on;
rtmp_auto_push_reconnect 1s;
rtmp_socket_dir /tmp;
rtmp {
server {
listen 8082; # Ingest port for streaming
chunk_size 8192; # Maximum single packet size
application myapp { # Application name
live on; # Enable live streaming
# Critical: enables connection handling for exec_record_done
# and client reconnect functionality
drop_idle_publisher 5s;
meta off; # Disable meta for flv.js compatibility
gop_cache off; # Disable GOP cache for faster initial playback
allow play all; # Allow playback from any source
}
}
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8083;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /live { # Playback URI
flv_live on; # Enable http-flv
chunked_transfer_encoding on;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Verify installation: nginx -V
Validate configuration: nginx -t
2: Core Implementation
The system's core functionality revolves around WebSocket protocol handling for real-time communication. The following C implementation demonstrates the key components:
//Extract Sec-WebSocket-Key header value
char * extractSecKey(const char * buffer){
char *key;
const char *marker="Sec-WebSocket-Key: ";
int idx=0, bufLen=0;
key=(char *)malloc(256);
memset(key,0, 256);
if(!buffer){
return NULL;
}
const char* keyStart=strstr(buffer,marker);
if(!keyStart){
return NULL;
}
keyStart+=strlen(marker);
bufLen=strlen(buffer);
for(idx=0;idx<bufLen;idx++){
if(keyStart[idx]==0x0A||keyStart[idx]==0x0D){
break;
}
key[idx]=keyStart[idx];
}
return key;
}
//Generate accept key via RFC 6455 specification
char* generateAcceptKey(char *clientKey){
const char *uuidSuffix="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char *tempBuffer=(char *)malloc(strlen(clientKey)+strlen(uuidSuffix)+1);
sprintf(tempBuffer,"%s%s",clientKey,uuidSuffix);
const char *base64Alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t inputLen = strlen(tempBuffer);
unsigned char shaHash[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)tempBuffer,inputLen,shaHash);
int hashLen=strlen((const char *)shaHash);
int encodedLen=0;
if(hashLen%3==0){
encodedLen=(hashLen/3)*4;
}else{
encodedLen=(hashLen/3+1)*4;
}
char *encodedResult=(char *)malloc(encodedLen+1);
encodedResult[encodedLen]='\0';
for(int i=0,j=0;i<hashLen;i+=3,j+=4){
if(i+3<hashLen){
encodedResult[j]=base64Alphabet[shaHash[i]>>2];
encodedResult[j+1]=base64Alphabet[(shaHash[i] & 0x3)<<4 | shaHash[i+1]>>4];
encodedResult[j+2]=base64Alphabet[(shaHash[i+1] & 0xf)<<2 | shaHash[i+2]>>6];
encodedResult[j+3]=base64Alphabet[shaHash[i+2] & 0x3f];
}else{
int remaining=hashLen-i;
if(remaining==1){
encodedResult[j]=base64Alphabet[shaHash[i]>>2];
encodedResult[j+1]=base64Alphabet[(shaHash[i] & 0x3)<<4];
}else if(remaining==2){
encodedResult[j]=base64Alphabet[shaHash[i]>>2];
encodedResult[j+1]=base64Alphabet[(shaHash[i] & 0x3)<<4 | shaHash[i+1]>>4];
encodedResult[j+2]=base64Alphabet[(shaHash[i+1] & 0xf)<<2];
}
}
}
switch(hashLen % 3) {
case 1:
encodedResult[encodedLen-2] = '=';
encodedResult[encodedLen-1] = '=';
break;
case 2:
encodedResult[encodedLen-1] = '=';
break;
}
free(tempBuffer);
return encodedResult;
}
//Compute handshake response key
char * computeHandshakeKey(const char * buf){
char *clientKeyInput=extractSecKey(buf);
char *serverKeyOutput=generateAcceptKey(clientKeyInput);
free(clientKeyInput);
printf("server_key=%s\n",serverKeyOutput);
return serverKeyOutput;
}
//Perform WebSocket handshake
void performHandshake(int connfd,const char *serverKey){
char *responseHeader=(char *)malloc(sizeof("a")*200);
if(!connfd){
return;
}
if(!serverKey){
return;
}
memset(responseHeader,'\0',200);
sprintf(responseHeader, "HTTP/1.1 101 Switching Protocols\r\n");
sprintf(responseHeader, "%sUpgrade: websocket\r\n", responseHeader);
sprintf(responseHeader, "%sConnection: Upgrade\r\n", responseHeader);
sprintf(responseHeader, "%sSec-WebSocket-Accept: %s\r\n\r\n", responseHeader, serverKey);
send(connfd,responseHeader,strlen(responseHeader),0);
free(responseHeader);
}
//Decode incoming WebSocket frame
char* decodeFrame(char *buffer,int bufferLen){
int payloadLen = 0;
char* maskKey = NULL;
int maskOffset=0;
payloadLen = (buffer[1]&0x7f);
if(payloadLen==126){
payloadLen=((buffer[2]) | (buffer[3] << 8));
if(buffer[2]==0){
payloadLen=(buffer[3] & 0x000000ff);
}else{
payloadLen=((buffer[2]&0x00ff)<<8 | (buffer[3] & 0x00ff));
}
maskOffset=2;
}if(payloadLen==127){
return NULL;
}
maskKey=buffer+2+maskOffset;
char* decodedMessage;
decodedMessage=(char*)malloc(sizeof(char)*payloadLen+1);
decodedMessage[payloadLen]=0;
char *payloadData =buffer+4+2+maskOffset;
for(int i=0;i<payloadLen;i++){
decodedMessage[i] = payloadData[i] ^ maskKey[i % 4];
}
return (char*)decodedMessage;
}
//Encode outgoing WebSocket frame
char* encodeFrame(char *data){
int dataLen=strlen(data);
char* outputBuffer=NULL;
if(dataLen<126){
outputBuffer=(char *)malloc(dataLen+3);
memset(outputBuffer,0,dataLen+3);
outputBuffer[0] = 0x81;
outputBuffer[1] = dataLen;
memcpy(outputBuffer+2,data,dataLen);
}else if(dataLen<65535){
outputBuffer=(char *)malloc(dataLen+5);
memset(outputBuffer,0,dataLen+5);
outputBuffer[0] = 0x81;
outputBuffer[1] = 126;
outputBuffer[2]=(dataLen>>8) & 255;
outputBuffer[3]=dataLen & 255;
memcpy(outputBuffer+4,data,dataLen);
}
return outputBuffer;
}
The frontend implementation follows standard patterns for WebSocket client communication and is not detailed here.
3: Deployment Procedures
Start the nginx service on the Linux server:
cd /usr/local/nginx/sbin/nginx && ./nginx
Successful startup is confirmed when the process runs without errors.
Compile and launch the mesaging server:
gcc main.cpp -lstdc++ -lm -lpthread -lcrypto -o main && ./main
If errors occur, retry execution with ./main until successful.
Port configuration can be modified in main.cpp if needed.
Configure the web server environment by starting nginx and PHP services on Windows.
Set up a new website project with the appropriate documant root directory.
Access the application via browser using the configured domain.
Update database credentials and configuration values in the relevant files.
Import the database schema using the provided SQL export file.
For streaming, configure OBS studio with the appropriate server address and stream key.