Live Streaming System Architecture for Academic Project

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.

Tags: live-streaming nginx rtmp WebSocket c-programming

Posted on Thu, 14 May 2026 15:16:08 +0000 by fredcool