From 15e7fc6e4af06b0eadf1b273b7c36c322b19100e Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 13 Nov 2015 17:42:27 -0500 Subject: [PATCH 01/21] Add comments & braces to format_ebml.c. This should help make future changes to the code clearer. --- src/format_ebml.c | 117 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 09c28a89..70e9b62c 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -102,6 +102,7 @@ int format_ebml_get_plugin(source_t *source) source->format = plugin; ebml_source_state->ebml = ebml_create(); + return 0; } @@ -117,6 +118,8 @@ static void ebml_free_plugin(format_plugin_t *plugin) free(plugin); } +/* Write to a client from the header buffer. + */ static int send_ebml_header(client_t *client) { @@ -141,6 +144,8 @@ static int send_ebml_header(client_t *client) } +/* Initial write-to-client function. + */ static int ebml_write_buf_to_client (client_t *client) { @@ -152,12 +157,16 @@ static int ebml_write_buf_to_client (client_t *client) } else { + /* Now that the header's sent, short-circuit to the generic + * write-refbufs function. */ client->write_to_client = format_generic_write_to_client; return client->write_to_client(client); } } +/* Return a refbuf to add to the queue. + */ static refbuf_t *ebml_get_buffer(source_t *source) { @@ -173,11 +182,13 @@ static refbuf_t *ebml_get_buffer(source_t *source) if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0) { + /* A chunk is available for reading */ refbuf = refbuf_new(bytes); ebml_read(ebml_source_state->ebml, refbuf->data, bytes); if (ebml_source_state->header == NULL) { + /* Capture header before adding clusters to the queue */ ebml_source_state->header = refbuf; continue; } @@ -191,7 +202,7 @@ static refbuf_t *ebml_get_buffer(source_t *source) } else { - + /* Feed more bytes into the parser */ data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE); bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE); if (bytes <= 0) @@ -210,6 +221,8 @@ static refbuf_t *ebml_get_buffer(source_t *source) } } +/* Initialize client state. + */ static int ebml_create_client_data(source_t *source, client_t *client) { ebml_client_data_t *ebml_client_data; @@ -301,6 +314,9 @@ static ebml_t *ebml_create() } +/* Return the size of a buffer needed to store the next + * chunk that ebml_read can yield. + */ static int ebml_read_space(ebml_t *ebml) { @@ -308,73 +324,98 @@ static int ebml_read_space(ebml_t *ebml) if (ebml->header_read == 1) { - if (ebml->cluster_start > 0) + /* The header has previously been read */ + if (ebml->cluster_start > 0) { + /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; - else + } else { + /* return most of what we have, but leave enough unread + * to detect the next cluster. + */ read_space = ebml->position - 4; + } return read_space; } else { - if (ebml->header_size != 0) + if (ebml->header_size != 0) { + /* The header can be read */ return ebml->header_size; - else + } else { + /* The header's not ready yet */ return 0; + } } } +/* Return a chunk of the EBML/MKV/WebM stream. + * A cluster element's opening tag will always start a new chunk. + */ static int ebml_read(ebml_t *ebml, char *buffer, int len) { int read_space; int to_read; - if (len < 1) + if (len < 1) { return 0; + } if (ebml->header_read == 1) { - if (ebml->cluster_start > 0) + /* The header has previously been read */ + if (ebml->cluster_start > 0) { + /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; - else + } else { read_space = ebml->position - 4; + } - if (read_space < 1) + if (read_space < 1) { return 0; + } - if (read_space >= len ) + if (read_space >= len ) { to_read = len; - else + } else { to_read = read_space; + } memcpy(buffer, ebml->buffer, to_read); + + /* Shift unread data down to the start of the buffer */ memmove(ebml->buffer, ebml->buffer + to_read, ebml->position - to_read); ebml->position -= to_read; - if (ebml->cluster_start > 0) + if (ebml->cluster_start > 0) { ebml->cluster_start -= to_read; + } } else { if (ebml->header_size != 0) { + /* Can read a chunk of the header */ read_space = ebml->header_size - ebml->header_read_position; - if (read_space >= len) + if (read_space >= len) { to_read = len; - else + } else { to_read = read_space; + } memcpy(buffer, ebml->header, to_read); ebml->header_read_position += to_read; - if (ebml->header_read_position == ebml->header_size) + if (ebml->header_read_position == ebml->header_size) { ebml->header_read = 1; + } } else { + /* The header's not ready yet */ return 0; } } @@ -383,17 +424,24 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len) } +/* Return nonzero if the chunk last returned by ebml_read was + * a sync point. + */ static int ebml_last_was_sync(ebml_t *ebml) { - if (ebml->cluster_start == 0) - { + if (ebml->cluster_start == 0) { + /* The data buffer now starts with a cluster, so the chunk + * just removed from it was (probably) not a cluster's start. + */ ebml->cluster_start -= 1; return 0; } - if (ebml->cluster_start == -1) - { + if (ebml->cluster_start == -1) { + /* Above logic triggered for the previous chunk, therefore the + * chunk just removed was a cluster's start. + */ ebml->cluster_start -= 1; return 1; } @@ -409,16 +457,16 @@ static char *ebml_write_buffer(ebml_t *ebml, int len) } - +/* Process data that has been written to the EBML parser's input buffer. + */ static int ebml_wrote(ebml_t *ebml, int len) { int b; - if (ebml->header_size == 0) - { - if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) - { + if (ebml->header_size == 0) { + /* Still reading header */ + if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) { ICECAST_LOG_ERROR("EBML Header too large, failing"); return -1; } @@ -430,14 +478,19 @@ static int ebml_wrote(ebml_t *ebml, int len) memcpy(ebml->header + ebml->header_position, ebml->input_buffer, len); ebml->header_position += len; - } - else - { + } else { + /* Header's already been determined, read into data buffer */ memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len); } for (b = 0; b < len - 4; b++) { + /* Scan for cluster start marker. + * False positives are possible, but unlikely, and only + * permanently corrupt a stream if they occur while scanning + * the initial header. Else, a client can reconnect in a few + * seconds and get a real sync point. + */ if (!memcmp(ebml->input_buffer + b, ebml->cluster_id, 4)) { /* @@ -446,14 +499,18 @@ static int ebml_wrote(ebml_t *ebml, int len) if (ebml->header_size == 0) { + /* We were looking for the header end; now we've found it */ ebml->header_size = ebml->header_position - len + b; + + /* Shift data after the header into the data buffer */ memcpy(ebml->buffer, ebml->input_buffer + b, len - b); ebml->position = len - b; + + /* Mark the start of the data as the first sync point */ ebml->cluster_start = -1; return len; - } - else - { + } else { + /* We've located a sync point in the data stream */ ebml->cluster_start = ebml->position + b; } } From 744b66c40e2c45f0a67a01283785ef70ddcc4390 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:12:33 -0500 Subject: [PATCH 02/21] Make the state-machine nature of the EBML parser more evident. --- src/format_ebml.c | 155 +++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 70e9b62c..095bdf25 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -39,6 +39,10 @@ #define EBML_HEADER_MAX_SIZE 131072 #define EBML_SLICE_SIZE 4096 +typedef enum ebml_read_mode { + EBML_STATE_READING_HEADER = 0, + EBML_STATE_READING_CLUSTERS +} ebml_read_mode; typedef struct ebml_client_data_st ebml_client_data_t; @@ -51,14 +55,15 @@ struct ebml_client_data_st { struct ebml_st { + ebml_read_mode output_state; + char *cluster_id; int cluster_start; int position; unsigned char *input_buffer; unsigned char *buffer; - - int header_read; + int header_size; int header_position; int header_read_position; @@ -302,6 +307,8 @@ static ebml_t *ebml_create() ebml_t *ebml = calloc(1, sizeof(ebml_t)); + ebml->output_state = EBML_STATE_READING_HEADER; + ebml->header = calloc(1, EBML_HEADER_MAX_SIZE); ebml->buffer = calloc(1, EBML_SLICE_SIZE * 4); ebml->input_buffer = calloc(1, EBML_SLICE_SIZE); @@ -322,35 +329,39 @@ static int ebml_read_space(ebml_t *ebml) int read_space; - if (ebml->header_read == 1) - { - /* The header has previously been read */ - if (ebml->cluster_start > 0) { - /* return up until just before a new cluster starts */ - read_space = ebml->cluster_start; - } else { - /* return most of what we have, but leave enough unread - * to detect the next cluster. - */ - read_space = ebml->position - 4; - } + switch (ebml->output_state) { + case EBML_STATE_READING_HEADER: + + if (ebml->header_size != 0) { + /* The header can be read */ + return ebml->header_size; + } else { + /* The header's not ready yet */ + return 0; + } + break; + + case EBML_STATE_READING_CLUSTERS: + + if (ebml->cluster_start > 0) { + /* return up until just before a new cluster starts */ + read_space = ebml->cluster_start; + } else { + /* return most of what we have, but leave enough unread + * to detect the next cluster. + */ + read_space = ebml->position - 4; + } - return read_space; + return read_space; } - else - { - if (ebml->header_size != 0) { - /* The header can be read */ - return ebml->header_size; - } else { - /* The header's not ready yet */ - return 0; - } - } - + + ICECAST_LOG_ERROR("EBML: Invalid parser read state"); + return 0; } /* Return a chunk of the EBML/MKV/WebM stream. + * The header will be buffered until it can be returned as one chunk. * A cluster element's opening tag will always start a new chunk. */ static int ebml_read(ebml_t *ebml, char *buffer, int len) @@ -363,61 +374,63 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len) return 0; } - if (ebml->header_read == 1) - { - /* The header has previously been read */ - if (ebml->cluster_start > 0) { - /* return up until just before a new cluster starts */ - read_space = ebml->cluster_start; - } else { - read_space = ebml->position - 4; - } - - if (read_space < 1) { - return 0; - } - - if (read_space >= len ) { - to_read = len; - } else { - to_read = read_space; - } - - memcpy(buffer, ebml->buffer, to_read); + switch (ebml->output_state) { + case EBML_STATE_READING_HEADER: - /* Shift unread data down to the start of the buffer */ - memmove(ebml->buffer, ebml->buffer + to_read, ebml->position - to_read); - ebml->position -= to_read; + if (ebml->header_size != 0) + { + /* Can read a chunk of the header */ + read_space = ebml->header_size - ebml->header_read_position; - if (ebml->cluster_start > 0) { - ebml->cluster_start -= to_read; - } - } - else - { - if (ebml->header_size != 0) - { - /* Can read a chunk of the header */ - read_space = ebml->header_size - ebml->header_read_position; + if (read_space >= len) { + to_read = len; + } else { + to_read = read_space; + } - if (read_space >= len) { + memcpy(buffer, ebml->header, to_read); + ebml->header_read_position += to_read; + + if (ebml->header_read_position == ebml->header_size) { + ebml->output_state = EBML_STATE_READING_CLUSTERS; + } + } else { + /* The header's not ready yet */ + return 0; + } + + break; + + case EBML_STATE_READING_CLUSTERS: + + if (ebml->cluster_start > 0) { + /* return up until just before a new cluster starts */ + read_space = ebml->cluster_start; + } else { + read_space = ebml->position - 4; + } + + if (read_space < 1) { + return 0; + } + + if (read_space >= len ) { to_read = len; } else { to_read = read_space; } - memcpy(buffer, ebml->header, to_read); - ebml->header_read_position += to_read; + memcpy(buffer, ebml->buffer, to_read); + + /* Shift unread data down to the start of the buffer */ + memmove(ebml->buffer, ebml->buffer + to_read, ebml->position - to_read); + ebml->position -= to_read; - if (ebml->header_read_position == ebml->header_size) { - ebml->header_read = 1; + if (ebml->cluster_start > 0) { + ebml->cluster_start -= to_read; } - } - else - { - /* The header's not ready yet */ - return 0; - } + + break; } return to_read; From 13dc880d29e8e80d5161994d24fd589795ca2e09 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:16:07 -0500 Subject: [PATCH 03/21] Yield sync point status from ebml_read directly. This simplifies some fragile "was the last chunk a sync point?" logic. --- src/format_ebml.c | 82 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 095bdf25..49e1a6c2 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -44,6 +44,13 @@ typedef enum ebml_read_mode { EBML_STATE_READING_CLUSTERS } ebml_read_mode; + +typedef enum ebml_chunk_type { + EBML_CHUNK_HEADER = 0, + EBML_CHUNK_CLUSTER_START, + EBML_CHUNK_CLUSTER_CONTINUE +} ebml_chunk_type; + typedef struct ebml_client_data_st ebml_client_data_t; struct ebml_client_data_st { @@ -81,8 +88,7 @@ static void ebml_free_client_data(client_t *client); static ebml_t *ebml_create(); static void ebml_destroy(ebml_t *ebml); static int ebml_read_space(ebml_t *ebml); -static int ebml_read(ebml_t *ebml, char *buffer, int len); -static int ebml_last_was_sync(ebml_t *ebml); +static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type); static char *ebml_write_buffer(ebml_t *ebml, int len); static int ebml_wrote(ebml_t *ebml, int len); @@ -179,6 +185,7 @@ static refbuf_t *ebml_get_buffer(source_t *source) format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; + ebml_chunk_type chunk_type; refbuf_t *refbuf; int ret; @@ -189,7 +196,7 @@ static refbuf_t *ebml_get_buffer(source_t *source) { /* A chunk is available for reading */ refbuf = refbuf_new(bytes); - ebml_read(ebml_source_state->ebml, refbuf->data, bytes); + ebml_read(ebml_source_state->ebml, refbuf->data, bytes, &chunk_type); if (ebml_source_state->header == NULL) { @@ -198,9 +205,14 @@ static refbuf_t *ebml_get_buffer(source_t *source) continue; } - if (ebml_last_was_sync(ebml_source_state->ebml)) +/* ICECAST_LOG_DEBUG("EBML: generated refbuf, size %i : %hhi %hhi %hhi", + * bytes, refbuf->data[0], refbuf->data[1], refbuf->data[2]); + */ + + if (chunk_type == EBML_CHUNK_CLUSTER_START) { refbuf->sync_point = 1; +/* ICECAST_LOG_DEBUG("EBML: ^ was sync point"); */ } return refbuf; @@ -315,7 +327,7 @@ static ebml_t *ebml_create() ebml->cluster_id = "\x1F\x43\xB6\x75"; - ebml->cluster_start = -2; + ebml->cluster_start = -1; return ebml; @@ -347,10 +359,8 @@ static int ebml_read_space(ebml_t *ebml) /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; } else { - /* return most of what we have, but leave enough unread - * to detect the next cluster. - */ - read_space = ebml->position - 4; + /* return what we have */ + read_space = ebml->position; } return read_space; @@ -363,12 +373,17 @@ static int ebml_read_space(ebml_t *ebml) /* Return a chunk of the EBML/MKV/WebM stream. * The header will be buffered until it can be returned as one chunk. * A cluster element's opening tag will always start a new chunk. + * + * chunk_type will be set to indicate if the chunk is the header, + * the start of a cluster, or continuing the current cluster. */ -static int ebml_read(ebml_t *ebml, char *buffer, int len) +static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type) { int read_space; int to_read; + + *chunk_type = EBML_CHUNK_HEADER; if (len < 1) { return 0; @@ -390,7 +405,9 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len) memcpy(buffer, ebml->header, to_read); ebml->header_read_position += to_read; - + + *chunk_type = EBML_CHUNK_HEADER; + if (ebml->header_read_position == ebml->header_size) { ebml->output_state = EBML_STATE_READING_CLUSTERS; } @@ -403,11 +420,18 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len) case EBML_STATE_READING_CLUSTERS: - if (ebml->cluster_start > 0) { + *chunk_type = EBML_CHUNK_CLUSTER_CONTINUE; + read_space = ebml->position; + + if (ebml->cluster_start == 0) { + /* new cluster is starting now */ + *chunk_type = EBML_CHUNK_CLUSTER_START; + + /* mark end of cluster */ + ebml->cluster_start = -1; + } else if (ebml->cluster_start > 0) { /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; - } else { - read_space = ebml->position - 4; } if (read_space < 1) { @@ -437,32 +461,6 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len) } -/* Return nonzero if the chunk last returned by ebml_read was - * a sync point. - */ -static int ebml_last_was_sync(ebml_t *ebml) -{ - - if (ebml->cluster_start == 0) { - /* The data buffer now starts with a cluster, so the chunk - * just removed from it was (probably) not a cluster's start. - */ - ebml->cluster_start -= 1; - return 0; - } - - if (ebml->cluster_start == -1) { - /* Above logic triggered for the previous chunk, therefore the - * chunk just removed was a cluster's start. - */ - ebml->cluster_start -= 1; - return 1; - } - - return 0; - -} - static char *ebml_write_buffer(ebml_t *ebml, int len) { @@ -496,7 +494,7 @@ static int ebml_wrote(ebml_t *ebml, int len) memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len); } - for (b = 0; b < len - 4; b++) + for (b = 0; b <= len - 4; b++) { /* Scan for cluster start marker. * False positives are possible, but unlikely, and only @@ -520,7 +518,7 @@ static int ebml_wrote(ebml_t *ebml, int len) ebml->position = len - b; /* Mark the start of the data as the first sync point */ - ebml->cluster_start = -1; + ebml->cluster_start = 0; return len; } else { /* We've located a sync point in the data stream */ From 531d060d16e4f744586a2e50820c63c162dc3ae8 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:20:30 -0500 Subject: [PATCH 04/21] Adjust EBML parser writing protocol. This will allow leaving unparsed data in the input buffer to wait for completion. --- src/format_ebml.c | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 49e1a6c2..f77a9a41 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -68,8 +68,10 @@ struct ebml_st { int cluster_start; int position; - unsigned char *input_buffer; unsigned char *buffer; + + int input_position; + unsigned char *input_buffer; int header_size; int header_position; @@ -89,7 +91,7 @@ static ebml_t *ebml_create(); static void ebml_destroy(ebml_t *ebml); static int ebml_read_space(ebml_t *ebml); static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type); -static char *ebml_write_buffer(ebml_t *ebml, int len); +static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes); static int ebml_wrote(ebml_t *ebml, int len); int format_ebml_get_plugin(source_t *source) @@ -183,20 +185,20 @@ static refbuf_t *ebml_get_buffer(source_t *source) ebml_source_state_t *ebml_source_state = source->format->_state; format_plugin_t *format = source->format; - char *data = NULL; - int bytes = 0; + unsigned char *write_buffer = NULL; + int read_bytes = 0; + int write_bytes = 0; ebml_chunk_type chunk_type; refbuf_t *refbuf; int ret; while (1) { - - if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0) - { + read_bytes = ebml_read_space(ebml_source_state->ebml); + if (read_bytes > 0) { /* A chunk is available for reading */ - refbuf = refbuf_new(bytes); - ebml_read(ebml_source_state->ebml, refbuf->data, bytes, &chunk_type); + refbuf = refbuf_new(read_bytes); + ebml_read(ebml_source_state->ebml, refbuf->data, read_bytes, &chunk_type); if (ebml_source_state->header == NULL) { @@ -206,7 +208,7 @@ static refbuf_t *ebml_get_buffer(source_t *source) } /* ICECAST_LOG_DEBUG("EBML: generated refbuf, size %i : %hhi %hhi %hhi", - * bytes, refbuf->data[0], refbuf->data[1], refbuf->data[2]); + * read_bytes, refbuf->data[0], refbuf->data[1], refbuf->data[2]); */ if (chunk_type == EBML_CHUNK_CLUSTER_START) @@ -216,24 +218,25 @@ static refbuf_t *ebml_get_buffer(source_t *source) } return refbuf; - } - else - { + } else if(read_bytes == 0) { /* Feed more bytes into the parser */ - data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE); - bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE); - if (bytes <= 0) - { + write_buffer = ebml_get_write_buffer(ebml_source_state->ebml, &write_bytes); + read_bytes = client_read_bytes (source->client, write_buffer, write_bytes); + if (read_bytes <= 0) { ebml_wrote (ebml_source_state->ebml, 0); return NULL; } - format->read_bytes += bytes; - ret = ebml_wrote (ebml_source_state->ebml, bytes); - if (ret != bytes) { + format->read_bytes += read_bytes; + ret = ebml_wrote (ebml_source_state->ebml, read_bytes); + if (ret != read_bytes) { ICECAST_LOG_ERROR("Problem processing stream"); source->running = 0; return NULL; } + } else { + ICECAST_LOG_ERROR("Problem processing stream"); + source->running = 0; + return NULL; } } } @@ -461,11 +464,15 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk } -static char *ebml_write_buffer(ebml_t *ebml, int len) +/* Get pointer & length of the buffer able to accept input. + * + * Returns the start of the writable space; + * Sets bytes to the amount of space available. + */ +static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes) { - - return (char *) ebml->input_buffer; - + *bytes = EBML_SLICE_SIZE - ebml->input_position; + return ebml->input_buffer + ebml->input_position; } /* Process data that has been written to the EBML parser's input buffer. From 50c4984c788589d8ace36dbc0fb9ae325f9d0ac0 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:22:43 -0500 Subject: [PATCH 05/21] Add functions to comprehend EBML tags. --- src/format_ebml.c | 114 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index f77a9a41..67cf8854 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -39,12 +39,14 @@ #define EBML_HEADER_MAX_SIZE 131072 #define EBML_SLICE_SIZE 4096 +/* A value that no EBML var-int is allowed to take. */ +#define EBML_UNKNOWN ((unsigned long long) -1) + typedef enum ebml_read_mode { EBML_STATE_READING_HEADER = 0, EBML_STATE_READING_CLUSTERS } ebml_read_mode; - typedef enum ebml_chunk_type { EBML_CHUNK_HEADER = 0, EBML_CHUNK_CLUSTER_START, @@ -93,6 +95,12 @@ static int ebml_read_space(ebml_t *ebml); static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type); static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes); static int ebml_wrote(ebml_t *ebml, int len); +static int ebml_parse_tag(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *payload_length); +static int ebml_parse_var_int(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *out_value); int format_ebml_get_plugin(source_t *source) { @@ -539,3 +547,107 @@ static int ebml_wrote(ebml_t *ebml, int len) return len; } + +/* Try to parse an EBML tag at the given location, returning the + * length of the tag & the length of the associated payload. + * + * Returns the length of the tag on success, and writes the payload + * size to *payload_length. + * + * Return 0 if it would be necessary to read past the + * given end-of-buffer address to read a complete tag. + * + * Returns -1 if the tag is corrupt. + */ + +static int ebml_parse_tag(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *payload_length) +{ + int type_length; + int size_length; + unsigned long long value; + + *payload_length = 0; + + /* read past the type tag */ + type_length = ebml_parse_var_int(buffer, buffer_end, &value); + + if (type_length <= 0) { + return type_length; + } + + /* read the length tag */ + size_length = ebml_parse_var_int(buffer + type_length, buffer_end, payload_length); + + if (size_length <= 0) { + return size_length; + } + + return type_length + size_length; +} + +/* Try to parse an EBML variable-length integer. + * Returns 0 if there's not enough space to read the number; + * Returns -1 if the number is malformed. + * Else, returns the length of the number in bytes and writes the + * value to *out_value. + */ +static int ebml_parse_var_int(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *out_value) +{ + int size = 1; + int i; + unsigned char mask = 0x80; + unsigned long long value; + unsigned long long unknown_marker; + + if (buffer >= buffer_end) { + return 0; + } + + /* find the length marker bit in the first byte */ + value = buffer[0]; + + while (mask) { + if (value & mask) { + value = value & ~mask; + unknown_marker = mask - 1; + break; + } + size++; + mask = mask >> 1; + } + + /* catch malformed number (no prefix) */ + if (mask == 0) { + ICECAST_LOG_DEBUG("Corrupt var-int"); + return -1; + } + + /* catch number bigger than parsing buffer */ + if (buffer + size - 1 >= buffer_end) { + return 0; + } + + /* read remaining bytes of (big-endian) number */ + for (i = 1; i < size; i++) { + value = (value << 8) + buffer[i]; + unknown_marker = (unknown_marker << 8) + 0xFF; + } + + /* catch special "unknown" length */ + + if (value == unknown_marker) { + *out_value = EBML_UNKNOWN; + } else { + *out_value = value; + } + +/* + ICECAST_LOG_DEBUG("Varint: value %lli, unknown %llu, mask %hhu, size %i", value, unknown_marker, mask, size); +*/ + + return size; +} From def5a4cf6cfe3562f9b0cad226e32564292fa95e Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:25:30 -0500 Subject: [PATCH 06/21] Implement EBML-aware parser. * Loop over elements in input buffer. * Most are literally copied with their contents without inspection into the header or data buffers as appropriate. * Some only copy the element header, to allow inspecting children elements. * Cluster elements are identified and used as sync points. No probing is done for keyframes *yet* --- src/format_ebml.c | 208 +++++++++++++++++++++++++++++++++------------- 1 file changed, 152 insertions(+), 56 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 67cf8854..68e95047 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -47,6 +47,14 @@ typedef enum ebml_read_mode { EBML_STATE_READING_CLUSTERS } ebml_read_mode; +typedef enum ebml_parsing_state { + EBML_STATE_PARSING_HEADER = 0, + EBML_STATE_COPYING_TO_HEADER, + EBML_STATE_START_CLUSTER, + EBML_STATE_PARSING_CLUSTERS, + EBML_STATE_COPYING_TO_DATA +} ebml_parsing_state; + typedef enum ebml_chunk_type { EBML_CHUNK_HEADER = 0, EBML_CHUNK_CLUSTER_START, @@ -65,8 +73,9 @@ struct ebml_client_data_st { struct ebml_st { ebml_read_mode output_state; + ebml_parsing_state parse_state; + unsigned long long copy_len; - char *cluster_id; int cluster_start; int position; @@ -333,11 +342,9 @@ static ebml_t *ebml_create() ebml->output_state = EBML_STATE_READING_HEADER; ebml->header = calloc(1, EBML_HEADER_MAX_SIZE); - ebml->buffer = calloc(1, EBML_SLICE_SIZE * 4); + ebml->buffer = calloc(1, EBML_SLICE_SIZE); ebml->input_buffer = calloc(1, EBML_SLICE_SIZE); - ebml->cluster_id = "\x1F\x43\xB6\x75"; - ebml->cluster_start = -1; return ebml; @@ -487,65 +494,154 @@ static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes) */ static int ebml_wrote(ebml_t *ebml, int len) { + int processing = 1; + int cursor = 0; + int to_copy; + unsigned char *end_of_buffer; - int b; + int tag_length; + unsigned long long payload_length; + int copy_state; - if (ebml->header_size == 0) { - /* Still reading header */ - if ((ebml->header_position + len) > EBML_HEADER_MAX_SIZE) { - ICECAST_LOG_ERROR("EBML Header too large, failing"); - return -1; - } - -/* - ICECAST_LOG_DEBUG("EBML: Adding to header, ofset is %d size is %d adding %d", - ebml->header_size, ebml->header_position, len); -*/ - - memcpy(ebml->header + ebml->header_position, ebml->input_buffer, len); - ebml->header_position += len; - } else { - /* Header's already been determined, read into data buffer */ - memcpy(ebml->buffer + ebml->position, ebml->input_buffer, len); - } - - for (b = 0; b <= len - 4; b++) - { - /* Scan for cluster start marker. - * False positives are possible, but unlikely, and only - * permanently corrupt a stream if they occur while scanning - * the initial header. Else, a client can reconnect in a few - * seconds and get a real sync point. - */ - if (!memcmp(ebml->input_buffer + b, ebml->cluster_id, 4)) - { -/* - ICECAST_LOG_DEBUG("EBML: found cluster"); -*/ - - if (ebml->header_size == 0) - { - /* We were looking for the header end; now we've found it */ - ebml->header_size = ebml->header_position - len + b; + char *segment_id = "\x18\x53\x80\x67"; + char *cluster_id = "\x1F\x43\xB6\x75"; + + ebml->input_position += len; + end_of_buffer = ebml->input_buffer + ebml->input_position; + + while (processing) { + + /*ICECAST_LOG_DEBUG("Parse State: %i", ebml->parse_state);*/ + + switch (ebml->parse_state) { + + case EBML_STATE_PARSING_HEADER: + case EBML_STATE_PARSING_CLUSTERS: - /* Shift data after the header into the data buffer */ - memcpy(ebml->buffer, ebml->input_buffer + b, len - b); - ebml->position = len - b; + if (ebml->parse_state == EBML_STATE_PARSING_HEADER) { + copy_state = EBML_STATE_COPYING_TO_HEADER; + } else { + copy_state = EBML_STATE_COPYING_TO_DATA; + } - /* Mark the start of the data as the first sync point */ - ebml->cluster_start = 0; - return len; - } else { - /* We've located a sync point in the data stream */ - ebml->cluster_start = ebml->position + b; - } + tag_length = ebml_parse_tag(ebml->input_buffer + cursor, + end_of_buffer, &payload_length); + + if (tag_length > 0) { + + if (payload_length == EBML_UNKNOWN) { + /* Parse all children for tags we can't skip */ + payload_length = 0; + } + + if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { + /* Found a cluster */ + ebml->parse_state = EBML_STATE_START_CLUSTER; + + } else if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { + /* Segment tag, copy tag to buffer but parse children */ + ebml->copy_len = tag_length; + ebml->parse_state = copy_state; + + } else { + /* Non-cluster tag, copy it & children into buffer */ + ebml->copy_len = tag_length + payload_length; + ebml->parse_state = copy_state; + + } + + } else if (tag_length == 0) { + /* Wait for more data */ + /* ICECAST_LOG_DEBUG("Wait"); */ + processing = 0; + } else if (tag_length < 0) { + /* Parse error */ + /* ICECAST_LOG_DEBUG("Stop"); */ + return -1; + } + break; + + case EBML_STATE_START_CLUSTER: + /* found a cluster; wait to process it until + * any previous cluster tag has been flushed + * from the read buffer, so as to not lose the + * sync point. + */ + if (ebml->cluster_start >= 0) { + processing = 0; + } else { + + tag_length = ebml_parse_tag(ebml->input_buffer + cursor, + end_of_buffer, &payload_length); + + /* The header has been fully read by now, publish its size. */ + ebml->header_size = ebml->header_position; + + /* Mark this sync point */ + ebml->cluster_start = ebml->position; + + /* Copy cluster tag to read buffer */ + ebml->copy_len = tag_length; + ebml->parse_state = EBML_STATE_COPYING_TO_DATA; + } + break; + + case EBML_STATE_COPYING_TO_HEADER: + case EBML_STATE_COPYING_TO_DATA: + to_copy = ebml->input_position - cursor; + if (to_copy > ebml->copy_len) { + to_copy = ebml->copy_len; + } + + if (ebml->parse_state == EBML_STATE_COPYING_TO_HEADER) { + if ((ebml->header_position + to_copy) > EBML_HEADER_MAX_SIZE) { + ICECAST_LOG_ERROR("EBML Header too large, failing"); + return -1; + } + + memcpy(ebml->header + ebml->header_position, ebml->input_buffer + cursor, to_copy); + ebml->header_position += to_copy; + + } else if (ebml->parse_state == EBML_STATE_COPYING_TO_DATA) { + if ((ebml->position + to_copy) > EBML_SLICE_SIZE) { + to_copy = EBML_SLICE_SIZE - ebml->position; + } + + memcpy(ebml->buffer + ebml->position, ebml->input_buffer + cursor, to_copy); + ebml->position += to_copy; + } + /* ICECAST_LOG_DEBUG("Copied %i of %hhu", to_copy, ebml->copy_len); */ + + cursor += to_copy; + ebml->copy_len -= to_copy; + + if (ebml->copy_len == 0) { + /* resume parsing */ + if (ebml->parse_state == EBML_STATE_COPYING_TO_HEADER) { + ebml->parse_state = EBML_STATE_PARSING_HEADER; + } else { + ebml->parse_state = EBML_STATE_PARSING_CLUSTERS; + } + } else { + /* wait for more data */ + processing = 0; + } + + break; + + default: + processing = 0; + } + } - - ebml->position += len; - + + /* Shift unprocessed data down to the start of the buffer */ + memmove(ebml->input_buffer, ebml->input_buffer + cursor, ebml->input_position - cursor); + ebml->input_position -= cursor; + return len; - + } /* Try to parse an EBML tag at the given location, returning the From f461ff6763f0107441ee789e5cb630d606cc1276 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 20 Nov 2015 13:03:53 -0500 Subject: [PATCH 07/21] Buffer clusters to have time to probe them for metadata, like keyframes. --- src/format_ebml.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 68e95047..4c4ec51f 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -77,6 +77,7 @@ struct ebml_st { unsigned long long copy_len; int cluster_start; + int flush_cluster; int position; unsigned char *buffer; @@ -377,8 +378,24 @@ static int ebml_read_space(ebml_t *ebml) /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; } else { - /* return what we have */ - read_space = ebml->position; + + if (ebml->position == EBML_SLICE_SIZE) { + /* The current cluster fills the buffer, + * we have no choice but to start flushing it. + */ + + ebml->flush_cluster = 1; + } + + if (ebml->flush_cluster) { + /* return what we have */ + read_space = ebml->position; + } else { + /* wait until we've read more, so the parser has + * time to gather metadata + */ + read_space = 0; + } } return read_space; @@ -568,6 +585,8 @@ static int ebml_wrote(ebml_t *ebml, int len) * sync point. */ if (ebml->cluster_start >= 0) { + /* Allow the cluster in the read buffer to flush. */ + ebml->flush_cluster = 1; processing = 0; } else { @@ -580,6 +599,9 @@ static int ebml_wrote(ebml_t *ebml, int len) /* Mark this sync point */ ebml->cluster_start = ebml->position; + /* Buffer data to give us time to probe for keyframes, etc. */ + ebml->flush_cluster = 0; + /* Copy cluster tag to read buffer */ ebml->copy_len = tag_length; ebml->parse_state = EBML_STATE_COPYING_TO_DATA; From 98994138395c2ce9b72ac194dbdfb5799e696c23 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 20 Nov 2015 13:33:41 -0500 Subject: [PATCH 08/21] Add parser for bodies of Integer elements. --- src/format_ebml.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/format_ebml.c b/src/format_ebml.c index 4c4ec51f..d10fc2f4 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -111,6 +111,11 @@ static int ebml_parse_tag(unsigned char *buffer, static int ebml_parse_var_int(unsigned char *buffer, unsigned char *buffer_end, unsigned long long *out_value); +static int ebml_parse_sized_int(unsigned char *buffer, + unsigned char *buffer_end, + int len, + int is_signed, + unsigned long long *out_value); int format_ebml_get_plugin(source_t *source) { @@ -769,3 +774,42 @@ static int ebml_parse_var_int(unsigned char *buffer, return size; } + +/* Parse a normal int that may be from 1-8 bytes long. + * Returns 0 if there's not enough space to read the number; + * Returns -1 if the number is mis-sized. + * Else, returns the length of the number in bytes and writes the + * value to *out_value. + */ +static int ebml_parse_sized_int(unsigned char *buffer, + unsigned char *buffer_end, + int len, + int is_signed, + unsigned long long *out_value) +{ + long long value; + int i; + + if (len < 1 || len > 8) { + ICECAST_LOG_DEBUG("Sized int of %i bytes", len); + return -1; + } + + if (buffer + len >= buffer_end) { + return 0; + } + + if (is_signed && ((signed char) buffer[0]) < 0) { + value = -1; + } else { + value = 0; + } + + for (i = 0; i < len; i++) { + value = (value << 8) + ((unsigned char) buffer[i]); + } + + *out_value = value; + + return len; +} From 6e11db5de283506c8c0e6fe315252f27cee2b7e0 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 12:59:14 -0500 Subject: [PATCH 09/21] Probe Tracks section of header to identify the video track. We'll sync keyframes on that track next commit. --- src/format_ebml.c | 92 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index d10fc2f4..1f59d82a 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -89,7 +89,10 @@ struct ebml_st { int header_position; int header_read_position; unsigned char *header; - + + unsigned long long keyframe_track_number; + unsigned long long parsing_track_number; + int parsing_track_is_video; }; static void ebml_free_plugin(format_plugin_t *plugin); @@ -116,6 +119,7 @@ static int ebml_parse_sized_int(unsigned char *buffer, int len, int is_signed, unsigned long long *out_value); +static inline void ebml_check_track(ebml_t *ebml); int format_ebml_get_plugin(source_t *source) { @@ -352,6 +356,10 @@ static ebml_t *ebml_create() ebml->input_buffer = calloc(1, EBML_SLICE_SIZE); ebml->cluster_start = -1; + + ebml->keyframe_track_number = EBML_UNKNOWN; + ebml->parsing_track_number = EBML_UNKNOWN; + ebml->parsing_track_is_video = 0; return ebml; @@ -522,11 +530,17 @@ static int ebml_wrote(ebml_t *ebml, int len) unsigned char *end_of_buffer; int tag_length; + int value_length; unsigned long long payload_length; + unsigned long long data_value; int copy_state; char *segment_id = "\x18\x53\x80\x67"; char *cluster_id = "\x1F\x43\xB6\x75"; + char *tracks_id = "\x16\x54\xAE\x6B"; + char *track_entry_id = "\xAE"; + char *track_number_id = "\xD7"; + char *track_type_id = "\x83"; ebml->input_position += len; end_of_buffer = ebml->input_buffer + ebml->input_position; @@ -556,16 +570,69 @@ static int ebml_wrote(ebml_t *ebml, int len) payload_length = 0; } + /* Recognize tags of interest */ + if (tag_length > 4) { + if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { + /* Parse all Segment children */ + payload_length = 0; + + } else if (!memcmp(ebml->input_buffer + cursor, tracks_id, 4)) { + /* Parse all Tracks children */ + payload_length = 0; + + } + + } + + if (tag_length > 1) { + if (!memcmp(ebml->input_buffer + cursor, track_entry_id, 1)) { + /* Parse all TrackEntry children; reset the state */ + payload_length = 0; + ebml->parsing_track_number = EBML_UNKNOWN; + ebml->parsing_track_is_video = 0; + + } else if (!memcmp(ebml->input_buffer + cursor, track_number_id, 1)) { + /* Probe TrackNumber for value */ + value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, + end_of_buffer, payload_length, 0, &data_value); + + if (value_length == 0) { + /* Wait for more data */ + processing = 0; + } else if (value_length < 0) { + return -1; + } else { + ebml->parsing_track_number = data_value; + ebml_check_track(ebml); + } + + } else if (!memcmp(ebml->input_buffer + cursor, track_type_id, 1)) { + /* Probe TrackType for a video flag */ + value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, + end_of_buffer, payload_length, 0, &data_value); + + if (value_length == 0) { + /* Wait for more data */ + processing = 0; + } else if (value_length < 0) { + return -1; + } else { + if (data_value & 0x01) { + /* This is a video track (0x01 flag = video) */ + ebml->parsing_track_is_video = 1; + ebml_check_track(ebml); + } + } + + } + } + + /* Take appropriate next action */ if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { /* Found a cluster */ ebml->parse_state = EBML_STATE_START_CLUSTER; - } else if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { - /* Segment tag, copy tag to buffer but parse children */ - ebml->copy_len = tag_length; - ebml->parse_state = copy_state; - - } else { + } else if (processing) { /* Non-cluster tag, copy it & children into buffer */ ebml->copy_len = tag_length + payload_length; ebml->parse_state = copy_state; @@ -671,6 +738,17 @@ static int ebml_wrote(ebml_t *ebml, int len) } +static inline void ebml_check_track(ebml_t *ebml) +{ + if (ebml->keyframe_track_number == EBML_UNKNOWN + && ebml->parsing_track_is_video + && ebml->parsing_track_number != EBML_UNKNOWN) { + + ebml->keyframe_track_number = ebml->parsing_track_number; + ICECAST_LOG_DEBUG("Identified track #%ffu as the video track", ebml->keyframe_track_number); + } +} + /* Try to parse an EBML tag at the given location, returning the * length of the tag & the length of the associated payload. * From 9cf4ed560cded13a65a241e557be12780af3f6e5 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 03:29:56 -0500 Subject: [PATCH 10/21] Probe SimpleBlocks to determine which clusters start with a keyframe. --- src/format_ebml.c | 57 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 1f59d82a..9ae48b9e 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -61,6 +61,12 @@ typedef enum ebml_chunk_type { EBML_CHUNK_CLUSTER_CONTINUE } ebml_chunk_type; +typedef enum ebml_keyframe_status { + EBML_KEYFRAME_UNKNOWN = -1, + EBML_KEYFRAME_DOES_NOT_START_CLUSTER = 0, + EBML_KEYFRAME_STARTS_CLUSTER = 1 +} ebml_keyframe_status; + typedef struct ebml_client_data_st ebml_client_data_t; struct ebml_client_data_st { @@ -77,6 +83,7 @@ struct ebml_st { unsigned long long copy_len; int cluster_start; + ebml_keyframe_status cluster_starts_with_keyframe; int flush_cluster; int position; @@ -473,7 +480,11 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk if (ebml->cluster_start == 0) { /* new cluster is starting now */ - *chunk_type = EBML_CHUNK_CLUSTER_START; + + if (ebml->cluster_starts_with_keyframe == EBML_KEYFRAME_STARTS_CLUSTER) { + /* Successfully found a keyframe in this cluster's video track */ + *chunk_type = EBML_CHUNK_CLUSTER_START; + } /* mark end of cluster */ ebml->cluster_start = -1; @@ -531,8 +542,11 @@ static int ebml_wrote(ebml_t *ebml, int len) int tag_length; int value_length; + int track_number_length; unsigned long long payload_length; unsigned long long data_value; + unsigned long long track_number; + unsigned char flags; int copy_state; char *segment_id = "\x18\x53\x80\x67"; @@ -541,6 +555,7 @@ static int ebml_wrote(ebml_t *ebml, int len) char *track_entry_id = "\xAE"; char *track_number_id = "\xD7"; char *track_type_id = "\x83"; + char *simple_block_id = "\xA3"; ebml->input_position += len; end_of_buffer = ebml->input_buffer + ebml->input_position; @@ -585,7 +600,42 @@ static int ebml_wrote(ebml_t *ebml, int len) } if (tag_length > 1) { - if (!memcmp(ebml->input_buffer + cursor, track_entry_id, 1)) { + if (!memcmp(ebml->input_buffer + cursor, simple_block_id, 1)) { + /* Probe SimpleBlock header for the keyframe status */ + if (ebml->cluster_starts_with_keyframe == EBML_KEYFRAME_UNKNOWN) { + track_number_length = ebml_parse_var_int(ebml->input_buffer + cursor + tag_length, + end_of_buffer, &track_number); + + if (track_number_length == 0) { + /* Wait for more data */ + processing = 0; + } else if (track_number_length < 0) { + return -1; + } else if (track_number == ebml->keyframe_track_number) { + /* this block belongs to the video track */ + + /* skip the 16-bit timecode for now, read the flags byte */ + if (cursor + tag_length + track_number_length + 2 >= ebml->input_position) { + /* Wait for more data */ + processing = 0; + } else { + flags = ebml->input_buffer[cursor + tag_length + track_number_length + 2]; + + if (flags & 0x80) { + /* "keyframe" flag is set */ + ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_STARTS_CLUSTER; + /* ICECAST_LOG_DEBUG("Found keyframe in track %hhu", track_number); */ + } else { + ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_DOES_NOT_START_CLUSTER; + /* ICECAST_LOG_DEBUG("Found non-keyframe in track %hhu", track_number); */ + } + } + + } + + } + + } else if (!memcmp(ebml->input_buffer + cursor, track_entry_id, 1)) { /* Parse all TrackEntry children; reset the state */ payload_length = 0; ebml->parsing_track_number = EBML_UNKNOWN; @@ -668,8 +718,9 @@ static int ebml_wrote(ebml_t *ebml, int len) /* The header has been fully read by now, publish its size. */ ebml->header_size = ebml->header_position; - /* Mark this sync point */ + /* Mark this potential sync point, prepare probe */ ebml->cluster_start = ebml->position; + ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_UNKNOWN; /* Buffer data to give us time to probe for keyframes, etc. */ ebml->flush_cluster = 0; From e4258bfad3e18a60e2f3faa44b13fdfdfac0b2cf Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 20 Nov 2015 14:27:00 -0500 Subject: [PATCH 11/21] Don't wait for video keyframes on audio-only streams. --- src/format_ebml.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 9ae48b9e..3e613596 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -481,8 +481,13 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk if (ebml->cluster_start == 0) { /* new cluster is starting now */ - if (ebml->cluster_starts_with_keyframe == EBML_KEYFRAME_STARTS_CLUSTER) { - /* Successfully found a keyframe in this cluster's video track */ + if (ebml->cluster_starts_with_keyframe != EBML_KEYFRAME_DOES_NOT_START_CLUSTER) { + /* If we positively identified the first video frame as a non-keyframe, + * don't use this cluster as a sync point. Since some files lack + * video tracks completely, or we may have failed to probe + * the first video frame, it's better to be pass through + * ambiguous cases to avoid blocking the stream forever. + */ *chunk_type = EBML_CHUNK_CLUSTER_START; } From 9de8fe483d0276ac67c148e5750d3f0308160f74 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 15:05:44 -0500 Subject: [PATCH 12/21] Remove spare whitespace on blank lines. --- src/format_ebml.c | 194 +++++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 99 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 3e613596..64822df9 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -81,7 +81,7 @@ struct ebml_st { ebml_read_mode output_state; ebml_parsing_state parse_state; unsigned long long copy_len; - + int cluster_start; ebml_keyframe_status cluster_starts_with_keyframe; int flush_cluster; @@ -91,12 +91,12 @@ struct ebml_st { int input_position; unsigned char *input_buffer; - + int header_size; int header_position; int header_read_position; unsigned char *header; - + unsigned long long keyframe_track_number; unsigned long long parsing_track_number; int parsing_track_is_video; @@ -149,7 +149,7 @@ int format_ebml_get_plugin(source_t *source) source->format = plugin; ebml_source_state->ebml = ebml_create(); - + return 0; } @@ -244,7 +244,7 @@ static refbuf_t *ebml_get_buffer(source_t *source) /* ICECAST_LOG_DEBUG("EBML: generated refbuf, size %i : %hhi %hhi %hhi", * read_bytes, refbuf->data[0], refbuf->data[1], refbuf->data[2]); */ - + if (chunk_type == EBML_CHUNK_CLUSTER_START) { refbuf->sync_point = 1; @@ -296,7 +296,6 @@ static int ebml_create_client_data(source_t *source, client_t *client) return 0; } - static void ebml_free_client_data (client_t *client) { @@ -307,7 +306,6 @@ static void ebml_free_client_data (client_t *client) client->format_data = NULL; } - static void ebml_write_buf_to_file_fail (source_t *source) { ICECAST_LOG_WARN("Write to dump file failed, disabling"); @@ -315,7 +313,6 @@ static void ebml_write_buf_to_file_fail (source_t *source) source->dumpfile = NULL; } - static void ebml_write_buf_to_file (source_t *source, refbuf_t *refbuf) { @@ -338,7 +335,6 @@ static void ebml_write_buf_to_file (source_t *source, refbuf_t *refbuf) } - /* internal ebml parsing */ static void ebml_destroy(ebml_t *ebml) @@ -363,7 +359,7 @@ static ebml_t *ebml_create() ebml->input_buffer = calloc(1, EBML_SLICE_SIZE); ebml->cluster_start = -1; - + ebml->keyframe_track_number = EBML_UNKNOWN; ebml->parsing_track_number = EBML_UNKNOWN; ebml->parsing_track_is_video = 0; @@ -382,7 +378,7 @@ static int ebml_read_space(ebml_t *ebml) switch (ebml->output_state) { case EBML_STATE_READING_HEADER: - + if (ebml->header_size != 0) { /* The header can be read */ return ebml->header_size; @@ -391,22 +387,22 @@ static int ebml_read_space(ebml_t *ebml) return 0; } break; - + case EBML_STATE_READING_CLUSTERS: - + if (ebml->cluster_start > 0) { /* return up until just before a new cluster starts */ read_space = ebml->cluster_start; } else { - + if (ebml->position == EBML_SLICE_SIZE) { /* The current cluster fills the buffer, * we have no choice but to start flushing it. */ - + ebml->flush_cluster = 1; } - + if (ebml->flush_cluster) { /* return what we have */ read_space = ebml->position; @@ -420,7 +416,7 @@ static int ebml_read_space(ebml_t *ebml) return read_space; } - + ICECAST_LOG_ERROR("EBML: Invalid parser read state"); return 0; } @@ -437,7 +433,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk int read_space; int to_read; - + *chunk_type = EBML_CHUNK_HEADER; if (len < 1) { @@ -446,7 +442,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk switch (ebml->output_state) { case EBML_STATE_READING_HEADER: - + if (ebml->header_size != 0) { /* Can read a chunk of the header */ @@ -460,9 +456,9 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk memcpy(buffer, ebml->header, to_read); ebml->header_read_position += to_read; - + *chunk_type = EBML_CHUNK_HEADER; - + if (ebml->header_read_position == ebml->header_size) { ebml->output_state = EBML_STATE_READING_CLUSTERS; } @@ -470,17 +466,17 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk /* The header's not ready yet */ return 0; } - + break; - + case EBML_STATE_READING_CLUSTERS: - + *chunk_type = EBML_CHUNK_CLUSTER_CONTINUE; read_space = ebml->position; - + if (ebml->cluster_start == 0) { /* new cluster is starting now */ - + if (ebml->cluster_starts_with_keyframe != EBML_KEYFRAME_DOES_NOT_START_CLUSTER) { /* If we positively identified the first video frame as a non-keyframe, * don't use this cluster as a sync point. Since some files lack @@ -490,7 +486,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk */ *chunk_type = EBML_CHUNK_CLUSTER_START; } - + /* mark end of cluster */ ebml->cluster_start = -1; } else if (ebml->cluster_start > 0) { @@ -509,7 +505,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk } memcpy(buffer, ebml->buffer, to_read); - + /* Shift unread data down to the start of the buffer */ memmove(ebml->buffer, ebml->buffer + to_read, ebml->position - to_read); ebml->position -= to_read; @@ -517,7 +513,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk if (ebml->cluster_start > 0) { ebml->cluster_start -= to_read; } - + break; } @@ -561,56 +557,56 @@ static int ebml_wrote(ebml_t *ebml, int len) char *track_number_id = "\xD7"; char *track_type_id = "\x83"; char *simple_block_id = "\xA3"; - + ebml->input_position += len; end_of_buffer = ebml->input_buffer + ebml->input_position; - + while (processing) { - + /*ICECAST_LOG_DEBUG("Parse State: %i", ebml->parse_state);*/ - + switch (ebml->parse_state) { - + case EBML_STATE_PARSING_HEADER: case EBML_STATE_PARSING_CLUSTERS: - + if (ebml->parse_state == EBML_STATE_PARSING_HEADER) { copy_state = EBML_STATE_COPYING_TO_HEADER; } else { copy_state = EBML_STATE_COPYING_TO_DATA; } - + tag_length = ebml_parse_tag(ebml->input_buffer + cursor, end_of_buffer, &payload_length); - + if (tag_length > 0) { - + if (payload_length == EBML_UNKNOWN) { /* Parse all children for tags we can't skip */ payload_length = 0; } - + /* Recognize tags of interest */ if (tag_length > 4) { if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { /* Parse all Segment children */ payload_length = 0; - + } else if (!memcmp(ebml->input_buffer + cursor, tracks_id, 4)) { /* Parse all Tracks children */ payload_length = 0; - + } - + } - + if (tag_length > 1) { if (!memcmp(ebml->input_buffer + cursor, simple_block_id, 1)) { /* Probe SimpleBlock header for the keyframe status */ if (ebml->cluster_starts_with_keyframe == EBML_KEYFRAME_UNKNOWN) { track_number_length = ebml_parse_var_int(ebml->input_buffer + cursor + tag_length, end_of_buffer, &track_number); - + if (track_number_length == 0) { /* Wait for more data */ processing = 0; @@ -618,14 +614,14 @@ static int ebml_wrote(ebml_t *ebml, int len) return -1; } else if (track_number == ebml->keyframe_track_number) { /* this block belongs to the video track */ - + /* skip the 16-bit timecode for now, read the flags byte */ if (cursor + tag_length + track_number_length + 2 >= ebml->input_position) { /* Wait for more data */ processing = 0; } else { flags = ebml->input_buffer[cursor + tag_length + track_number_length + 2]; - + if (flags & 0x80) { /* "keyframe" flag is set */ ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_STARTS_CLUSTER; @@ -635,22 +631,22 @@ static int ebml_wrote(ebml_t *ebml, int len) /* ICECAST_LOG_DEBUG("Found non-keyframe in track %hhu", track_number); */ } } - + } - + } - + } else if (!memcmp(ebml->input_buffer + cursor, track_entry_id, 1)) { /* Parse all TrackEntry children; reset the state */ payload_length = 0; ebml->parsing_track_number = EBML_UNKNOWN; ebml->parsing_track_is_video = 0; - + } else if (!memcmp(ebml->input_buffer + cursor, track_number_id, 1)) { /* Probe TrackNumber for value */ value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, end_of_buffer, payload_length, 0, &data_value); - + if (value_length == 0) { /* Wait for more data */ processing = 0; @@ -660,12 +656,12 @@ static int ebml_wrote(ebml_t *ebml, int len) ebml->parsing_track_number = data_value; ebml_check_track(ebml); } - + } else if (!memcmp(ebml->input_buffer + cursor, track_type_id, 1)) { /* Probe TrackType for a video flag */ value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, end_of_buffer, payload_length, 0, &data_value); - + if (value_length == 0) { /* Wait for more data */ processing = 0; @@ -678,20 +674,20 @@ static int ebml_wrote(ebml_t *ebml, int len) ebml_check_track(ebml); } } - + } } - + /* Take appropriate next action */ if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { /* Found a cluster */ ebml->parse_state = EBML_STATE_START_CLUSTER; - + } else if (processing) { /* Non-cluster tag, copy it & children into buffer */ ebml->copy_len = tag_length + payload_length; ebml->parse_state = copy_state; - + } } else if (tag_length == 0) { @@ -704,7 +700,7 @@ static int ebml_wrote(ebml_t *ebml, int len) return -1; } break; - + case EBML_STATE_START_CLUSTER: /* found a cluster; wait to process it until * any previous cluster tag has been flushed @@ -716,55 +712,55 @@ static int ebml_wrote(ebml_t *ebml, int len) ebml->flush_cluster = 1; processing = 0; } else { - + tag_length = ebml_parse_tag(ebml->input_buffer + cursor, end_of_buffer, &payload_length); - + /* The header has been fully read by now, publish its size. */ ebml->header_size = ebml->header_position; - + /* Mark this potential sync point, prepare probe */ ebml->cluster_start = ebml->position; ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_UNKNOWN; - + /* Buffer data to give us time to probe for keyframes, etc. */ ebml->flush_cluster = 0; - + /* Copy cluster tag to read buffer */ ebml->copy_len = tag_length; ebml->parse_state = EBML_STATE_COPYING_TO_DATA; } break; - + case EBML_STATE_COPYING_TO_HEADER: case EBML_STATE_COPYING_TO_DATA: to_copy = ebml->input_position - cursor; if (to_copy > ebml->copy_len) { to_copy = ebml->copy_len; } - + if (ebml->parse_state == EBML_STATE_COPYING_TO_HEADER) { if ((ebml->header_position + to_copy) > EBML_HEADER_MAX_SIZE) { ICECAST_LOG_ERROR("EBML Header too large, failing"); return -1; } - + memcpy(ebml->header + ebml->header_position, ebml->input_buffer + cursor, to_copy); ebml->header_position += to_copy; - + } else if (ebml->parse_state == EBML_STATE_COPYING_TO_DATA) { if ((ebml->position + to_copy) > EBML_SLICE_SIZE) { to_copy = EBML_SLICE_SIZE - ebml->position; } - + memcpy(ebml->buffer + ebml->position, ebml->input_buffer + cursor, to_copy); ebml->position += to_copy; } /* ICECAST_LOG_DEBUG("Copied %i of %hhu", to_copy, ebml->copy_len); */ - + cursor += to_copy; ebml->copy_len -= to_copy; - + if (ebml->copy_len == 0) { /* resume parsing */ if (ebml->parse_state == EBML_STATE_COPYING_TO_HEADER) { @@ -776,22 +772,22 @@ static int ebml_wrote(ebml_t *ebml, int len) /* wait for more data */ processing = 0; } - + break; - + default: processing = 0; - + } - + } - + /* Shift unprocessed data down to the start of the buffer */ memmove(ebml->input_buffer, ebml->input_buffer + cursor, ebml->input_position - cursor); ebml->input_position -= cursor; - + return len; - + } static inline void ebml_check_track(ebml_t *ebml) @@ -799,7 +795,7 @@ static inline void ebml_check_track(ebml_t *ebml) if (ebml->keyframe_track_number == EBML_UNKNOWN && ebml->parsing_track_is_video && ebml->parsing_track_number != EBML_UNKNOWN) { - + ebml->keyframe_track_number = ebml->parsing_track_number; ICECAST_LOG_DEBUG("Identified track #%ffu as the video track", ebml->keyframe_track_number); } @@ -824,23 +820,23 @@ static int ebml_parse_tag(unsigned char *buffer, int type_length; int size_length; unsigned long long value; - + *payload_length = 0; - + /* read past the type tag */ type_length = ebml_parse_var_int(buffer, buffer_end, &value); - + if (type_length <= 0) { return type_length; } - + /* read the length tag */ size_length = ebml_parse_var_int(buffer + type_length, buffer_end, payload_length); - + if (size_length <= 0) { return size_length; } - + return type_length + size_length; } @@ -859,14 +855,14 @@ static int ebml_parse_var_int(unsigned char *buffer, unsigned char mask = 0x80; unsigned long long value; unsigned long long unknown_marker; - + if (buffer >= buffer_end) { return 0; } - + /* find the length marker bit in the first byte */ value = buffer[0]; - + while (mask) { if (value & mask) { value = value & ~mask; @@ -876,26 +872,26 @@ static int ebml_parse_var_int(unsigned char *buffer, size++; mask = mask >> 1; } - + /* catch malformed number (no prefix) */ if (mask == 0) { ICECAST_LOG_DEBUG("Corrupt var-int"); return -1; } - + /* catch number bigger than parsing buffer */ if (buffer + size - 1 >= buffer_end) { return 0; } - + /* read remaining bytes of (big-endian) number */ for (i = 1; i < size; i++) { value = (value << 8) + buffer[i]; unknown_marker = (unknown_marker << 8) + 0xFF; } - + /* catch special "unknown" length */ - + if (value == unknown_marker) { *out_value = EBML_UNKNOWN; } else { @@ -905,7 +901,7 @@ static int ebml_parse_var_int(unsigned char *buffer, /* ICECAST_LOG_DEBUG("Varint: value %lli, unknown %llu, mask %hhu, size %i", value, unknown_marker, mask, size); */ - + return size; } @@ -923,27 +919,27 @@ static int ebml_parse_sized_int(unsigned char *buffer, { long long value; int i; - + if (len < 1 || len > 8) { ICECAST_LOG_DEBUG("Sized int of %i bytes", len); return -1; } - + if (buffer + len >= buffer_end) { return 0; } - + if (is_signed && ((signed char) buffer[0]) < 0) { value = -1; } else { value = 0; } - + for (i = 0; i < len; i++) { value = (value << 8) + ((unsigned char) buffer[i]); } - + *out_value = value; - + return len; } From d394a244f0249c9676719fd49f3a2244af31cc1e Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 15:41:30 -0500 Subject: [PATCH 13/21] Bugfix: Cluster test could have read past the end of the input buffer. Move the cluster test inside the space-to-read check with the other tests. --- src/format_ebml.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 64822df9..93f7b806 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -588,7 +588,11 @@ static int ebml_wrote(ebml_t *ebml, int len) /* Recognize tags of interest */ if (tag_length > 4) { - if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { + if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { + /* Found a Cluster */ + ebml->parse_state = EBML_STATE_START_CLUSTER; + break; + } else if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { /* Parse all Segment children */ payload_length = 0; @@ -678,16 +682,11 @@ static int ebml_wrote(ebml_t *ebml, int len) } } - /* Take appropriate next action */ - if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { - /* Found a cluster */ - ebml->parse_state = EBML_STATE_START_CLUSTER; - - } else if (processing) { + /* Copy any data we don't need to probe any more */ + if (processing) { /* Non-cluster tag, copy it & children into buffer */ ebml->copy_len = tag_length + payload_length; ebml->parse_state = copy_state; - } } else if (tag_length == 0) { From af238d91293f7f1ce1368651c53022ff8391fd46 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 21 Nov 2015 17:29:26 -0500 Subject: [PATCH 14/21] Move MKV element magic values into proper #define constants. --- src/format_ebml.c | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 93f7b806..fad42fb0 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -42,6 +42,27 @@ /* A value that no EBML var-int is allowed to take. */ #define EBML_UNKNOWN ((unsigned long long) -1) +/* The magic numbers for each element we are interested in. + * Defined here: + * http://www.matroska.org/technical/specs/index.html + * http://www.webmproject.org/docs/container/ + * + * Some of the higher-level elements have 4-byte identifiers; + * The lower-level elements have 1-byte identifiers. + */ +#define UNCOMMON_MAGIC_LEN 4 + +#define SEGMENT_MAGIC "\x18\x53\x80\x67" +#define CLUSTER_MAGIC "\x1F\x43\xB6\x75" +#define TRACKS_MAGIC "\x16\x54\xAE\x6B" + +#define COMMON_MAGIC_LEN 1 + +#define TRACK_ENTRY_MAGIC "\xAE" +#define TRACK_NUMBER_MAGIC "\xD7" +#define TRACK_TYPE_MAGIC "\x83" +#define SIMPLE_BLOCK_MAGIC "\xA3" + typedef enum ebml_read_mode { EBML_STATE_READING_HEADER = 0, EBML_STATE_READING_CLUSTERS @@ -550,14 +571,6 @@ static int ebml_wrote(ebml_t *ebml, int len) unsigned char flags; int copy_state; - char *segment_id = "\x18\x53\x80\x67"; - char *cluster_id = "\x1F\x43\xB6\x75"; - char *tracks_id = "\x16\x54\xAE\x6B"; - char *track_entry_id = "\xAE"; - char *track_number_id = "\xD7"; - char *track_type_id = "\x83"; - char *simple_block_id = "\xA3"; - ebml->input_position += len; end_of_buffer = ebml->input_buffer + ebml->input_position; @@ -587,16 +600,16 @@ static int ebml_wrote(ebml_t *ebml, int len) } /* Recognize tags of interest */ - if (tag_length > 4) { - if (!memcmp(ebml->input_buffer + cursor, cluster_id, 4)) { + if (tag_length > UNCOMMON_MAGIC_LEN) { + if (!memcmp(ebml->input_buffer + cursor, CLUSTER_MAGIC, UNCOMMON_MAGIC_LEN)) { /* Found a Cluster */ ebml->parse_state = EBML_STATE_START_CLUSTER; break; - } else if (!memcmp(ebml->input_buffer + cursor, segment_id, 4)) { + } else if (!memcmp(ebml->input_buffer + cursor, SEGMENT_MAGIC, UNCOMMON_MAGIC_LEN)) { /* Parse all Segment children */ payload_length = 0; - } else if (!memcmp(ebml->input_buffer + cursor, tracks_id, 4)) { + } else if (!memcmp(ebml->input_buffer + cursor, TRACKS_MAGIC, UNCOMMON_MAGIC_LEN)) { /* Parse all Tracks children */ payload_length = 0; @@ -604,8 +617,8 @@ static int ebml_wrote(ebml_t *ebml, int len) } - if (tag_length > 1) { - if (!memcmp(ebml->input_buffer + cursor, simple_block_id, 1)) { + if (tag_length > COMMON_MAGIC_LEN) { + if (!memcmp(ebml->input_buffer + cursor, SIMPLE_BLOCK_MAGIC, COMMON_MAGIC_LEN)) { /* Probe SimpleBlock header for the keyframe status */ if (ebml->cluster_starts_with_keyframe == EBML_KEYFRAME_UNKNOWN) { track_number_length = ebml_parse_var_int(ebml->input_buffer + cursor + tag_length, @@ -640,13 +653,13 @@ static int ebml_wrote(ebml_t *ebml, int len) } - } else if (!memcmp(ebml->input_buffer + cursor, track_entry_id, 1)) { + } else if (!memcmp(ebml->input_buffer + cursor, TRACK_ENTRY_MAGIC, COMMON_MAGIC_LEN)) { /* Parse all TrackEntry children; reset the state */ payload_length = 0; ebml->parsing_track_number = EBML_UNKNOWN; ebml->parsing_track_is_video = 0; - } else if (!memcmp(ebml->input_buffer + cursor, track_number_id, 1)) { + } else if (!memcmp(ebml->input_buffer + cursor, TRACK_NUMBER_MAGIC, COMMON_MAGIC_LEN)) { /* Probe TrackNumber for value */ value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, end_of_buffer, payload_length, 0, &data_value); @@ -661,7 +674,7 @@ static int ebml_wrote(ebml_t *ebml, int len) ebml_check_track(ebml); } - } else if (!memcmp(ebml->input_buffer + cursor, track_type_id, 1)) { + } else if (!memcmp(ebml->input_buffer + cursor, TRACK_TYPE_MAGIC, COMMON_MAGIC_LEN)) { /* Probe TrackType for a video flag */ value_length = ebml_parse_sized_int(ebml->input_buffer + cursor + tag_length, end_of_buffer, payload_length, 0, &data_value); From dc263858a45bb9a3661710489f67dc173bf3a32a Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sun, 22 Nov 2015 13:58:43 -0500 Subject: [PATCH 15/21] Additional format_ebml.c comments. --- src/format_ebml.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index fad42fb0..af8c268f 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -36,7 +36,17 @@ #include "logging.h" +/* The size of the header buffer; should be large enough to contain + * everything before the first Cluster in a reasonable stream + */ #define EBML_HEADER_MAX_SIZE 131072 + +/* The size of the input/staging buffers; this much of a cluster + * will be buffered before being returned. Should be large enough + * that the first video block will be encountered before it is full, + * to allow probing for the keyframe flag while we still have the + * option to mark the cluster as a sync point. + */ #define EBML_SLICE_SIZE 4096 /* A value that no EBML var-int is allowed to take. */ @@ -63,28 +73,55 @@ #define TRACK_TYPE_MAGIC "\x83" #define SIMPLE_BLOCK_MAGIC "\xA3" +/* If support for Tags gets added, it may make sense + * to convert this into a pair of flags signaling + * "new headers" and "new tags" + */ typedef enum ebml_read_mode { + /* The header buffer has not been extracted yet */ EBML_STATE_READING_HEADER = 0, + /* The header buffer has been read, begin normal operation */ EBML_STATE_READING_CLUSTERS } ebml_read_mode; typedef enum ebml_parsing_state { + /* Examine EBML elements, output to header buffer */ EBML_STATE_PARSING_HEADER = 0, + + /* Blindly copy a specified number of bytes to the header buffer */ EBML_STATE_COPYING_TO_HEADER, + + /* Finalize header buffer and wait for previous cluster to flush (as necessary) */ EBML_STATE_START_CLUSTER, + + /* Examine EBML elements, output to data buffer */ EBML_STATE_PARSING_CLUSTERS, + + /* Blindly copy a specified number of bytes to the data buffer */ EBML_STATE_COPYING_TO_DATA } ebml_parsing_state; typedef enum ebml_chunk_type { + /* This chunk is the header buffer */ EBML_CHUNK_HEADER = 0, + + /* This chunk starts a cluster that works as a sync point */ EBML_CHUNK_CLUSTER_START, + + /* This chunk continues the previous cluster, or + * else starts a non-sync-point cluster + */ EBML_CHUNK_CLUSTER_CONTINUE } ebml_chunk_type; typedef enum ebml_keyframe_status { + /* Have not found a video track block yet */ EBML_KEYFRAME_UNKNOWN = -1, + + /* Found the first video track block, it was not a keyframe */ EBML_KEYFRAME_DOES_NOT_START_CLUSTER = 0, + + /* Found the first video track block, it was a keyframe */ EBML_KEYFRAME_STARTS_CLUSTER = 1 } ebml_keyframe_status; @@ -695,9 +732,8 @@ static int ebml_wrote(ebml_t *ebml, int len) } } - /* Copy any data we don't need to probe any more */ if (processing) { - /* Non-cluster tag, copy it & children into buffer */ + /* Moving to next element, copy current to buffer */ ebml->copy_len = tag_length + payload_length; ebml->parse_state = copy_state; } From 8a09627d05515077e4945fcbcc7cc9eabfd7ddf9 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 27 Nov 2015 23:22:44 -0500 Subject: [PATCH 16/21] Fix type of copy_state in EBML parser to an enum variable. --- src/format_ebml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index af8c268f..36ec944f 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -606,7 +606,7 @@ static int ebml_wrote(ebml_t *ebml, int len) unsigned long long data_value; unsigned long long track_number; unsigned char flags; - int copy_state; + ebml_parsing_state copy_state; ebml->input_position += len; end_of_buffer = ebml->input_buffer + ebml->input_position; From 0d7448efc76cecbb9ff310ae84335cd94944e0a1 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 28 Nov 2015 12:49:45 -0500 Subject: [PATCH 17/21] Change buffer-related sizes & indices to size_t or ssize_t, as applicable. --- src/format_ebml.c | 106 +++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 36ec944f..842cbaad 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -130,7 +130,7 @@ typedef struct ebml_client_data_st ebml_client_data_t; struct ebml_client_data_st { refbuf_t *header; - int header_pos; + size_t header_pos; }; @@ -140,19 +140,19 @@ struct ebml_st { ebml_parsing_state parse_state; unsigned long long copy_len; - int cluster_start; + ssize_t cluster_start; ebml_keyframe_status cluster_starts_with_keyframe; int flush_cluster; - int position; + size_t position; unsigned char *buffer; - int input_position; + size_t input_position; unsigned char *input_buffer; - int header_size; - int header_position; - int header_read_position; + size_t header_size; + size_t header_position; + size_t header_read_position; unsigned char *header; unsigned long long keyframe_track_number; @@ -169,21 +169,21 @@ static void ebml_free_client_data(client_t *client); static ebml_t *ebml_create(); static void ebml_destroy(ebml_t *ebml); -static int ebml_read_space(ebml_t *ebml); -static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type); -static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes); -static int ebml_wrote(ebml_t *ebml, int len); -static int ebml_parse_tag(unsigned char *buffer, - unsigned char *buffer_end, - unsigned long long *payload_length); -static int ebml_parse_var_int(unsigned char *buffer, +static size_t ebml_read_space(ebml_t *ebml); +static size_t ebml_read(ebml_t *ebml, char *buffer, size_t len, ebml_chunk_type *chunk_type); +static unsigned char *ebml_get_write_buffer(ebml_t *ebml, size_t *bytes); +static ssize_t ebml_wrote(ebml_t *ebml, size_t len); +static ssize_t ebml_parse_tag(unsigned char *buffer, unsigned char *buffer_end, - unsigned long long *out_value); -static int ebml_parse_sized_int(unsigned char *buffer, - unsigned char *buffer_end, - int len, - int is_signed, - unsigned long long *out_value); + unsigned long long *payload_length); +static ssize_t ebml_parse_var_int(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *out_value); +static ssize_t ebml_parse_sized_int(unsigned char *buffer, + unsigned char *buffer_end, + size_t len, + int is_signed, + unsigned long long *out_value); static inline void ebml_check_track(ebml_t *ebml); int format_ebml_get_plugin(source_t *source) @@ -229,7 +229,7 @@ static int send_ebml_header(client_t *client) { ebml_client_data_t *ebml_client_data = client->format_data; - int len = EBML_SLICE_SIZE; + size_t len = EBML_SLICE_SIZE; int ret; if (ebml_client_data->header->len - ebml_client_data->header_pos < len) @@ -278,11 +278,11 @@ static refbuf_t *ebml_get_buffer(source_t *source) ebml_source_state_t *ebml_source_state = source->format->_state; format_plugin_t *format = source->format; unsigned char *write_buffer = NULL; - int read_bytes = 0; - int write_bytes = 0; + size_t read_bytes = 0; + size_t write_bytes = 0; ebml_chunk_type chunk_type; refbuf_t *refbuf; - int ret; + size_t ret; while (1) { @@ -429,10 +429,10 @@ static ebml_t *ebml_create() /* Return the size of a buffer needed to store the next * chunk that ebml_read can yield. */ -static int ebml_read_space(ebml_t *ebml) +static size_t ebml_read_space(ebml_t *ebml) { - int read_space; + size_t read_space; switch (ebml->output_state) { case EBML_STATE_READING_HEADER: @@ -486,11 +486,11 @@ static int ebml_read_space(ebml_t *ebml) * chunk_type will be set to indicate if the chunk is the header, * the start of a cluster, or continuing the current cluster. */ -static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk_type) +static size_t ebml_read(ebml_t *ebml, char *buffer, size_t len, ebml_chunk_type *chunk_type) { - int read_space; - int to_read; + size_t read_space; + size_t to_read; *chunk_type = EBML_CHUNK_HEADER; @@ -584,7 +584,7 @@ static int ebml_read(ebml_t *ebml, char *buffer, int len, ebml_chunk_type *chunk * Returns the start of the writable space; * Sets bytes to the amount of space available. */ -static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes) +static unsigned char *ebml_get_write_buffer(ebml_t *ebml, size_t *bytes) { *bytes = EBML_SLICE_SIZE - ebml->input_position; return ebml->input_buffer + ebml->input_position; @@ -592,16 +592,16 @@ static unsigned char *ebml_get_write_buffer(ebml_t *ebml, int *bytes) /* Process data that has been written to the EBML parser's input buffer. */ -static int ebml_wrote(ebml_t *ebml, int len) +static ssize_t ebml_wrote(ebml_t *ebml, size_t len) { int processing = 1; - int cursor = 0; - int to_copy; + size_t cursor = 0; + size_t to_copy; unsigned char *end_of_buffer; - int tag_length; - int value_length; - int track_number_length; + ssize_t tag_length; + ssize_t value_length; + ssize_t track_number_length; unsigned long long payload_length; unsigned long long data_value; unsigned long long track_number; @@ -861,12 +861,12 @@ static inline void ebml_check_track(ebml_t *ebml) * Returns -1 if the tag is corrupt. */ -static int ebml_parse_tag(unsigned char *buffer, - unsigned char *buffer_end, - unsigned long long *payload_length) +static ssize_t ebml_parse_tag(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *payload_length) { - int type_length; - int size_length; + size_t type_length; + size_t size_length; unsigned long long value; *payload_length = 0; @@ -894,12 +894,12 @@ static int ebml_parse_tag(unsigned char *buffer, * Else, returns the length of the number in bytes and writes the * value to *out_value. */ -static int ebml_parse_var_int(unsigned char *buffer, - unsigned char *buffer_end, - unsigned long long *out_value) +static ssize_t ebml_parse_var_int(unsigned char *buffer, + unsigned char *buffer_end, + unsigned long long *out_value) { - int size = 1; - int i; + size_t size = 1; + size_t i; unsigned char mask = 0x80; unsigned long long value; unsigned long long unknown_marker; @@ -959,14 +959,14 @@ static int ebml_parse_var_int(unsigned char *buffer, * Else, returns the length of the number in bytes and writes the * value to *out_value. */ -static int ebml_parse_sized_int(unsigned char *buffer, - unsigned char *buffer_end, - int len, - int is_signed, - unsigned long long *out_value) +static ssize_t ebml_parse_sized_int(unsigned char *buffer, + unsigned char *buffer_end, + size_t len, + int is_signed, + unsigned long long *out_value) { long long value; - int i; + size_t i; if (len < 1 || len > 8) { ICECAST_LOG_DEBUG("Sized int of %i bytes", len); From e6cb7e26ee6340cbacdbd2f5b8b894596ebc559e Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Fri, 27 Nov 2015 23:28:32 -0500 Subject: [PATCH 18/21] Move ebml-private structure definitions out of header file. --- src/format_ebml.c | 28 +++++++++++++++++----------- src/format_ebml.h | 11 ----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 842cbaad..23f7e315 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -125,16 +125,7 @@ typedef enum ebml_keyframe_status { EBML_KEYFRAME_STARTS_CLUSTER = 1 } ebml_keyframe_status; -typedef struct ebml_client_data_st ebml_client_data_t; - -struct ebml_client_data_st { - - refbuf_t *header; - size_t header_pos; - -}; - -struct ebml_st { +typedef struct ebml_st { ebml_read_mode output_state; ebml_parsing_state parse_state; @@ -158,7 +149,22 @@ struct ebml_st { unsigned long long keyframe_track_number; unsigned long long parsing_track_number; int parsing_track_is_video; -}; +} ebml_t; + +typedef struct ebml_source_state_st { + + ebml_t *ebml; + refbuf_t *header; + int file_headers_written; + +} ebml_source_state_t; + +typedef struct ebml_client_data_st { + + refbuf_t *header; + size_t header_pos; + +} ebml_client_data_t; static void ebml_free_plugin(format_plugin_t *plugin); static refbuf_t *ebml_get_buffer(source_t *source); diff --git a/src/format_ebml.h b/src/format_ebml.h index f466134b..09142fe6 100644 --- a/src/format_ebml.h +++ b/src/format_ebml.h @@ -19,17 +19,6 @@ #include "format.h" -typedef struct ebml_st ebml_t; -typedef struct ebml_source_state_st ebml_source_state_t; - -struct ebml_source_state_st { - - ebml_t *ebml; - refbuf_t *header; - int file_headers_written; - -}; - int format_ebml_get_plugin (source_t *source); #endif /* __FORMAT_EBML_H__ */ From de004670c37612401cf5b17de1d47032d53674b5 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Tue, 1 Dec 2015 02:21:24 -0500 Subject: [PATCH 19/21] Be clearer about ebml_parse_sized_int's sign & endian behavior. --- src/format_ebml.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 23f7e315..579a52b4 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -959,19 +959,23 @@ static ssize_t ebml_parse_var_int(unsigned char *buffer, return size; } -/* Parse a normal int that may be from 1-8 bytes long. +/* Parse a big-endian int that may be from 1-8 bytes long. * Returns 0 if there's not enough space to read the number; * Returns -1 if the number is mis-sized. * Else, returns the length of the number in bytes and writes the * value to *out_value. + * If is_signed is true, then the int is assumed to be two's complement + * signed, negative values will be correctly promoted, and the returned + * long long can be safely cast to a signed number on systems using + * two's complement arithmatic. */ static ssize_t ebml_parse_sized_int(unsigned char *buffer, - unsigned char *buffer_end, - size_t len, - int is_signed, - unsigned long long *out_value) + unsigned char *buffer_end, + size_t len, + int is_signed, + unsigned long long *out_value) { - long long value; + unsigned long long value; size_t i; if (len < 1 || len > 8) { From 1f6a0a497fcb4ab5478fc68a0095d28f342c1ce0 Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 19 Dec 2015 15:02:33 -0500 Subject: [PATCH 20/21] Replace "long long"s with an appropriate type from --- src/format_ebml.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 579a52b4..75a4bdb1 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -20,6 +20,7 @@ #include #endif +#include #include #include #include @@ -50,7 +51,7 @@ #define EBML_SLICE_SIZE 4096 /* A value that no EBML var-int is allowed to take. */ -#define EBML_UNKNOWN ((unsigned long long) -1) +#define EBML_UNKNOWN ((uint_least64_t) -1) /* The magic numbers for each element we are interested in. * Defined here: @@ -129,7 +130,7 @@ typedef struct ebml_st { ebml_read_mode output_state; ebml_parsing_state parse_state; - unsigned long long copy_len; + uint_least64_t copy_len; ssize_t cluster_start; ebml_keyframe_status cluster_starts_with_keyframe; @@ -146,8 +147,8 @@ typedef struct ebml_st { size_t header_read_position; unsigned char *header; - unsigned long long keyframe_track_number; - unsigned long long parsing_track_number; + uint_least64_t keyframe_track_number; + uint_least64_t parsing_track_number; int parsing_track_is_video; } ebml_t; @@ -181,15 +182,15 @@ static unsigned char *ebml_get_write_buffer(ebml_t *ebml, size_t *bytes); static ssize_t ebml_wrote(ebml_t *ebml, size_t len); static ssize_t ebml_parse_tag(unsigned char *buffer, unsigned char *buffer_end, - unsigned long long *payload_length); + uint_least64_t *payload_length); static ssize_t ebml_parse_var_int(unsigned char *buffer, unsigned char *buffer_end, - unsigned long long *out_value); + uint_least64_t *out_value); static ssize_t ebml_parse_sized_int(unsigned char *buffer, unsigned char *buffer_end, size_t len, int is_signed, - unsigned long long *out_value); + uint_least64_t *out_value); static inline void ebml_check_track(ebml_t *ebml); int format_ebml_get_plugin(source_t *source) @@ -608,9 +609,9 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) ssize_t tag_length; ssize_t value_length; ssize_t track_number_length; - unsigned long long payload_length; - unsigned long long data_value; - unsigned long long track_number; + uint_least64_t payload_length; + uint_least64_t data_value; + uint_least64_t track_number; unsigned char flags; ebml_parsing_state copy_state; @@ -869,11 +870,11 @@ static inline void ebml_check_track(ebml_t *ebml) static ssize_t ebml_parse_tag(unsigned char *buffer, unsigned char *buffer_end, - unsigned long long *payload_length) + uint_least64_t *payload_length) { size_t type_length; size_t size_length; - unsigned long long value; + uint_least64_t value; *payload_length = 0; @@ -902,13 +903,13 @@ static ssize_t ebml_parse_tag(unsigned char *buffer, */ static ssize_t ebml_parse_var_int(unsigned char *buffer, unsigned char *buffer_end, - unsigned long long *out_value) + uint_least64_t *out_value) { size_t size = 1; size_t i; unsigned char mask = 0x80; - unsigned long long value; - unsigned long long unknown_marker; + uint_least64_t value; + uint_least64_t unknown_marker; if (buffer >= buffer_end) { return 0; @@ -966,16 +967,16 @@ static ssize_t ebml_parse_var_int(unsigned char *buffer, * value to *out_value. * If is_signed is true, then the int is assumed to be two's complement * signed, negative values will be correctly promoted, and the returned - * long long can be safely cast to a signed number on systems using + * unsigned number can be safely cast to a signed number on systems using * two's complement arithmatic. */ static ssize_t ebml_parse_sized_int(unsigned char *buffer, unsigned char *buffer_end, size_t len, int is_signed, - unsigned long long *out_value) + uint_least64_t *out_value) { - unsigned long long value; + uint_least64_t value; size_t i; if (len < 1 || len > 8) { From d196e75416973936da590babc6720a3a785c791e Mon Sep 17 00:00:00 2001 From: Joseph Wallace Date: Sat, 19 Dec 2015 15:32:37 -0500 Subject: [PATCH 21/21] Convert ints used as boolean values into actual bools. --- src/format_ebml.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/format_ebml.c b/src/format_ebml.c index 75a4bdb1..d35f1166 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -20,6 +20,7 @@ #include #endif +#include #include #include #include @@ -134,7 +135,7 @@ typedef struct ebml_st { ssize_t cluster_start; ebml_keyframe_status cluster_starts_with_keyframe; - int flush_cluster; + bool flush_cluster; size_t position; unsigned char *buffer; @@ -149,14 +150,14 @@ typedef struct ebml_st { uint_least64_t keyframe_track_number; uint_least64_t parsing_track_number; - int parsing_track_is_video; + bool parsing_track_is_video; } ebml_t; typedef struct ebml_source_state_st { ebml_t *ebml; refbuf_t *header; - int file_headers_written; + bool file_headers_written; } ebml_source_state_t; @@ -189,7 +190,7 @@ static ssize_t ebml_parse_var_int(unsigned char *buffer, static ssize_t ebml_parse_sized_int(unsigned char *buffer, unsigned char *buffer_end, size_t len, - int is_signed, + bool is_signed, uint_least64_t *out_value); static inline void ebml_check_track(ebml_t *ebml); @@ -383,14 +384,14 @@ static void ebml_write_buf_to_file (source_t *source, refbuf_t *refbuf) ebml_source_state_t *ebml_source_state = source->format->_state; - if (ebml_source_state->file_headers_written == 0) + if ( ! ebml_source_state->file_headers_written) { if (fwrite (ebml_source_state->header->data, 1, ebml_source_state->header->len, source->dumpfile) != ebml_source_state->header->len) ebml_write_buf_to_file_fail(source); else - ebml_source_state->file_headers_written = 1; + ebml_source_state->file_headers_written = true; } if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len) @@ -427,7 +428,7 @@ static ebml_t *ebml_create() ebml->keyframe_track_number = EBML_UNKNOWN; ebml->parsing_track_number = EBML_UNKNOWN; - ebml->parsing_track_is_video = 0; + ebml->parsing_track_is_video = false; return ebml; @@ -465,7 +466,7 @@ static size_t ebml_read_space(ebml_t *ebml) * we have no choice but to start flushing it. */ - ebml->flush_cluster = 1; + ebml->flush_cluster = true; } if (ebml->flush_cluster) { @@ -601,7 +602,7 @@ static unsigned char *ebml_get_write_buffer(ebml_t *ebml, size_t *bytes) */ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) { - int processing = 1; + bool processing = true; size_t cursor = 0; size_t to_copy; unsigned char *end_of_buffer; @@ -670,7 +671,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) if (track_number_length == 0) { /* Wait for more data */ - processing = 0; + processing = false; } else if (track_number_length < 0) { return -1; } else if (track_number == ebml->keyframe_track_number) { @@ -679,7 +680,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) /* skip the 16-bit timecode for now, read the flags byte */ if (cursor + tag_length + track_number_length + 2 >= ebml->input_position) { /* Wait for more data */ - processing = 0; + processing = false; } else { flags = ebml->input_buffer[cursor + tag_length + track_number_length + 2]; @@ -701,7 +702,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) /* Parse all TrackEntry children; reset the state */ payload_length = 0; ebml->parsing_track_number = EBML_UNKNOWN; - ebml->parsing_track_is_video = 0; + ebml->parsing_track_is_video = false; } else if (!memcmp(ebml->input_buffer + cursor, TRACK_NUMBER_MAGIC, COMMON_MAGIC_LEN)) { /* Probe TrackNumber for value */ @@ -710,7 +711,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) if (value_length == 0) { /* Wait for more data */ - processing = 0; + processing = false; } else if (value_length < 0) { return -1; } else { @@ -725,13 +726,13 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) if (value_length == 0) { /* Wait for more data */ - processing = 0; + processing = false; } else if (value_length < 0) { return -1; } else { if (data_value & 0x01) { /* This is a video track (0x01 flag = video) */ - ebml->parsing_track_is_video = 1; + ebml->parsing_track_is_video = true; ebml_check_track(ebml); } } @@ -748,7 +749,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) } else if (tag_length == 0) { /* Wait for more data */ /* ICECAST_LOG_DEBUG("Wait"); */ - processing = 0; + processing = false; } else if (tag_length < 0) { /* Parse error */ /* ICECAST_LOG_DEBUG("Stop"); */ @@ -764,8 +765,8 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) */ if (ebml->cluster_start >= 0) { /* Allow the cluster in the read buffer to flush. */ - ebml->flush_cluster = 1; - processing = 0; + ebml->flush_cluster = true; + processing = false; } else { tag_length = ebml_parse_tag(ebml->input_buffer + cursor, @@ -779,7 +780,7 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) ebml->cluster_starts_with_keyframe = EBML_KEYFRAME_UNKNOWN; /* Buffer data to give us time to probe for keyframes, etc. */ - ebml->flush_cluster = 0; + ebml->flush_cluster = false; /* Copy cluster tag to read buffer */ ebml->copy_len = tag_length; @@ -825,13 +826,13 @@ static ssize_t ebml_wrote(ebml_t *ebml, size_t len) } } else { /* wait for more data */ - processing = 0; + processing = false; } break; default: - processing = 0; + processing = false; } @@ -973,7 +974,7 @@ static ssize_t ebml_parse_var_int(unsigned char *buffer, static ssize_t ebml_parse_sized_int(unsigned char *buffer, unsigned char *buffer_end, size_t len, - int is_signed, + bool is_signed, uint_least64_t *out_value) { uint_least64_t value;