Analyzing the NGINX MP4 Streaming Module's Core Data Structures and File Delivery
Core Data Structure to MP4 Processing
The ngx_http_mp4_file_t structure is central to the NGINX MP4 module's operation, managing file state, parsing buffers, and response assemb.
typedef struct {
ngx_file_t file; // MP4 file descriptor
u_char *parse_buffer; // Buffer for MP4 atom parsing
u_char *buffer_head; // Start of free space in buffer
u_char *parse_start; // Start position for parsing
u_char *parse_end; // End position for parsing
size_t buffer_capacity; // Total size of the parse buffer
off_t file_offset; // File offset for `buffer_head`
off_t file_size; // Total size of the MP4 file
off_t response_length; // Final content length for client
ngx_uint_t request_start; // Requested start time offset
ngx_uint_t request_duration; // Requested video duration
uint32_t time_scale; // Timescale from MP4 file
ngx_http_request_t *http_request; // Current HTTP request object
ngx_array_t track_list; // Array referencing tracks
ngx_http_mp4_trak_t tracks[2]; // Track storage (max 2 tracks)
size_t ftyp_atom_size; // Size of 'ftyp' atom
size_t moov_atom_size; // Size of 'moov' atom
ngx_chain_t *output_chain;
ngx_chain_t ftyp_chain; // Chain for 'ftyp' atom buffer
ngx_chain_t moov_chain; // Chain for 'moov' atom buffer
ngx_chain_t mvhd_chain; // Chain for 'mvhd' atom buffer
ngx_chain_t mdat_chain; // Chain for 'mdat' atom header
ngx_chain_t mdat_data_chain; // Chain for 'mdat' data buffer
ngx_buf_t ftyp_buffer; // Buffer for 'ftyp' atom
ngx_buf_t moov_buffer; // Buffer for 'moov' atom
ngx_buf_t mvhd_buffer; // Buffer for 'mvhd' atom
ngx_buf_t mdat_header_buffer; // Buffer for 'mdat' header
ngx_buf_t mdat_content_buffer;// Buffer for 'mdat' content
u_char moov_header[8]; // Pre-calculated 'moov' header
u_char mdat_header[16]; // Pre-calculated 'mdat' header
} ngx_http_mp4_file_t;
MP4 File Delivery Logic
The module's file delviery process involves configurign I/O, setting HTTP headers, and preparing the response body.
log->action = "streaming MP4 content to client";
// Conditionally enable direct I/O for large files
if (config->directio_threshold <= file_info.size) {
/*
* Direct I/O is enabled only for the data transfer phase.
* This allows the kernel to cache metadata atoms like 'moov'.
*/
if (ngx_directio_on(file_info.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"Failed to enable direct I/O on %V", &path);
}
file_info.is_directio = 1;
if (mp4_ctx) {
mp4_ctx->file.directio = 1;
}
}
// Configure HTTP response headers
request->headers_out.status = NGX_HTTP_OK;
request->headers_out.last_modified_time = file_info.mtime;
if (ngx_http_set_etag(request) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_set_content_type(request) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
// If no MP4 context exists, prepare a simple file buffer
if (mp4_ctx == NULL) {
ngx_buf_t *file_buf = ngx_calloc_buf(request->pool);
if (file_buf == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
// ... buffer initialization continues
}