Mercurial > libedl
view src/edl.c @ 11:a0bd92c4c0e8
Added tag v2.1 for changeset d6f8f73a0c50
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Fri, 22 Mar 2024 20:51:46 -0400 |
parents | 0c98b46eaf73 |
children | 0cc2555db371 |
line wrap: on
line source
#include <string.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include "edl.h" #include "str.h" #include "datatypes.h" #include "util.h" /* CRC32 hashes of EDL headers */ #define EDL_HEADER_ID 0x1541c503 #define EDL_HEADER_TRACK 0x4b211812 #define EDL_HEADER_STARTTIME 0xbb46516f #define EDL_HEADER_LENGTH 0xaeac5df7 #define EDL_HEADER_PLAYRATE 0x03834606 #define EDL_HEADER_LOCKED 0x0c2083d3 #define EDL_HEADER_NORMALIZED 0xe60c8b1d #define EDL_HEADER_STRETCHMETHOD 0xbe5802c9 #define EDL_HEADER_LOOPED 0x4ec8be4c #define EDL_HEADER_ONRULER 0xe6cb84d1 #define EDL_HEADER_MEDIATYPE 0x035f84c2 #define EDL_HEADER_FILENAME 0x379d32c5 #define EDL_HEADER_STREAM 0x9f334738 #define EDL_HEADER_STREAMSTART 0x07b4e0e7 #define EDL_HEADER_STREAMLENGTH 0x8f16c7b8 #define EDL_HEADER_FADETIMEIN 0xd2edd7e4 #define EDL_HEADER_FADETIMEOUT 0x792e8c40 #define EDL_HEADER_SUSTAINGAIN 0x98374657 #define EDL_HEADER_CURVEIN 0x3e998b1f #define EDL_HEADER_GAININ 0x82fb09c4 #define EDL_HEADER_CURVEOUT 0x53add388 #define EDL_HEADER_GAINOUT 0x4210ba56 #define EDL_HEADER_LAYER 0x89c4df6c #define EDL_HEADER_COLOR 0xadf2f1a3 #define EDL_HEADER_CURVEINR 0xa56b43e1 #define EDL_HEADER_CURVEOUTR 0xcb6d715e #define EDL_HEADER_PLAYPITCH 0x9da1b9ed #define EDL_HEADER_LOCKPITCH 0x2bda6ed4 #define EDL_HEADER_FIRSTCHANNEL 0x3071a4c6 #define EDL_HEADER_CHANNELS 0xe94981a4 /* use a CRC32 hash function to process the headers */ static uint32_t EDL_internal_crcn32b(const unsigned char* message, size_t length) { uint32_t crc = 0xffffffff; size_t i; for (i = 0; i < length && message[i]; i++) { size_t j; crc = crc ^ message[i]; for (j = 0; j < 8; j++) crc = (crc >> 1) ^ (0xedb88320 & -(crc & 1)); } return ~crc; } static size_t EDL_internal_size_t_min(size_t a, size_t b) { return (a < b) ? a : b; } /* This parses the header of the EDL and creates an array with CRC32 hashes * in the order of the strings. */ static uint32_t* EDL_internal_parse_header(const char* data, size_t* length) { /* */ const size_t newline = EDL_internal_size_t_min(strchr(data, '\n') - data, strchr(data, '\r') - data); size_t current_hash = 0, hashes_size = 32; uint32_t* hashes = malloc(hashes_size * sizeof(uint32_t)); /* overall iterates over the entire line, * column stores the length of the current column */ size_t overall, column; for (overall = 0, column = 0; overall <= newline; overall++) { if (data[overall] == ';' || data[overall] == ':') { /* process the current column */ if (hashes_size < current_hash) hashes = realloc(hashes, (hashes_size *= 2) * sizeof(uint32_t)); hashes[current_hash++] = EDL_internal_crcn32b((const unsigned char*)&data[overall - column], column); column = 0; /* reset */ } else column++; } *length = current_hash; return hashes; } int EDL_reallocate(EDL* edl, size_t new_capacity) { edl->arr = realloc(edl->arr, new_capacity * sizeof(EDL_line)); if (!edl->arr) return 0; if (new_capacity > edl->capacity) memset(&edl->arr[edl->capacity], 0, (new_capacity - edl->capacity) * sizeof(EDL_line)); edl->capacity = new_capacity; return 1; } int EDL_append(EDL* edl, EDL_line line) { if (edl->size + 1 < edl->capacity && !EDL_reallocate(edl, edl->capacity)) return 0; edl->arr[++edl->size] = line; return 1; } /* the big important function */ EDL_parse_error_t EDL_parse(EDL* edl, const char* data, size_t length) { size_t order_size = 0; uint32_t* order = NULL; size_t offset = 0; if (!EDL_reallocate(edl, 16)) return EDL_PARSE_ERROR_OUT_OF_MEMORY; order = EDL_internal_parse_header(data, &order_size); while ((offset = EDL_internal_strnchr(&data[offset], '\n', length - offset) - data + 1) < length) { size_t local_offset = offset; /* original offset stays intact */ bool ok = true; int i; for (i = 0; i < order_size; i++) { #define ADD_TO_OFFSET(x, a) \ { \ size_t o = a(data, local_offset, length, &edl->arr[edl->size].x); \ if (!o) \ ok = false; \ local_offset += o; \ } switch (order[i]) { case EDL_HEADER_ID: ADD_TO_OFFSET(id, EDL_internal_parse_int); break; case EDL_HEADER_TRACK: ADD_TO_OFFSET(track, EDL_internal_parse_int); break; case EDL_HEADER_STARTTIME: ADD_TO_OFFSET(start_time, EDL_internal_parse_double); break; case EDL_HEADER_LENGTH: ADD_TO_OFFSET(length, EDL_internal_parse_double); break; case EDL_HEADER_PLAYRATE: ADD_TO_OFFSET(play_rate, EDL_internal_parse_double); break; case EDL_HEADER_LOCKED: ADD_TO_OFFSET(locked, EDL_internal_parse_bool); break; case EDL_HEADER_NORMALIZED: ADD_TO_OFFSET(normalized, EDL_internal_parse_bool); break; case EDL_HEADER_STRETCHMETHOD: ADD_TO_OFFSET(stretch_method, EDL_internal_parse_int); break; case EDL_HEADER_LOOPED: ADD_TO_OFFSET(looped, EDL_internal_parse_bool); break; case EDL_HEADER_ONRULER: ADD_TO_OFFSET(on_ruler, EDL_internal_parse_bool); break; case EDL_HEADER_MEDIATYPE: ADD_TO_OFFSET(media_type, EDL_internal_parse_media_type); break; case EDL_HEADER_FILENAME: ADD_TO_OFFSET(filename, EDL_internal_parse_string); break; case EDL_HEADER_STREAM: ADD_TO_OFFSET(stream, EDL_internal_parse_int); break; case EDL_HEADER_STREAMSTART: ADD_TO_OFFSET(stream_start, EDL_internal_parse_double); break; case EDL_HEADER_STREAMLENGTH: ADD_TO_OFFSET(stream_length, EDL_internal_parse_double); break; case EDL_HEADER_FADETIMEIN: ADD_TO_OFFSET(fade_time_in, EDL_internal_parse_double); break; case EDL_HEADER_FADETIMEOUT: ADD_TO_OFFSET(fade_time_out, EDL_internal_parse_double); break; case EDL_HEADER_SUSTAINGAIN: ADD_TO_OFFSET(sustain_gain, EDL_internal_parse_double); break; case EDL_HEADER_CURVEIN: ADD_TO_OFFSET(curve_in, EDL_internal_parse_int); break; case EDL_HEADER_GAININ: ADD_TO_OFFSET(gain_in, EDL_internal_parse_double); break; case EDL_HEADER_CURVEOUT: ADD_TO_OFFSET(curve_out, EDL_internal_parse_int); break; case EDL_HEADER_GAINOUT: ADD_TO_OFFSET(gain_out, EDL_internal_parse_double); break; case EDL_HEADER_LAYER: ADD_TO_OFFSET(layer, EDL_internal_parse_int); break; case EDL_HEADER_COLOR: ADD_TO_OFFSET(color, EDL_internal_parse_int); break; case EDL_HEADER_CURVEINR: ADD_TO_OFFSET(curve_in_r, EDL_internal_parse_int); break; case EDL_HEADER_CURVEOUTR: ADD_TO_OFFSET(curve_out_r, EDL_internal_parse_int); break; case EDL_HEADER_PLAYPITCH: ADD_TO_OFFSET(play_pitch, EDL_internal_parse_double); break; case EDL_HEADER_LOCKPITCH: ADD_TO_OFFSET(lock_pitch, EDL_internal_parse_bool); break; case EDL_HEADER_FIRSTCHANNEL: ADD_TO_OFFSET(first_channel, EDL_internal_parse_int); break; case EDL_HEADER_CHANNELS: ADD_TO_OFFSET(channels, EDL_internal_parse_int); break; default: /* ... what */ break; #undef ADD_TO_OFFSET } } if (!ok) break; if (++edl->size >= edl->capacity) EDL_reallocate(edl, edl->capacity * 2); } /* put on the shrinkwrap */ EDL_reallocate(edl, edl->size); free(order); return EDL_PARSE_ERROR_SUCCESS; } static void EDL_dump_line(EDL_internal_string* str, const EDL_line* line, const uint32_t* order, const size_t order_len) { size_t i; for (i = 0; i < order_len; i++) { switch (order[i]) { #define APPEND_ITEM(a, x) \ { \ char* tstr = x(line->a); \ EDL_internal_string_append(str, tstr, strlen(tstr)); \ free(tstr); \ } case EDL_HEADER_ID: APPEND_ITEM(id, EDL_internal_integer_to_string); break; case EDL_HEADER_TRACK: APPEND_ITEM(track, EDL_internal_integer_to_string); break; case EDL_HEADER_STARTTIME: APPEND_ITEM(start_time, EDL_internal_double_to_string); break; case EDL_HEADER_LENGTH: APPEND_ITEM(length, EDL_internal_double_to_string); break; case EDL_HEADER_PLAYRATE: APPEND_ITEM(play_rate, EDL_internal_double_to_string); break; case EDL_HEADER_LOCKED: APPEND_ITEM(locked, EDL_internal_bool_to_string); break; case EDL_HEADER_NORMALIZED: APPEND_ITEM(normalized, EDL_internal_bool_to_string); break; case EDL_HEADER_STRETCHMETHOD: APPEND_ITEM(stretch_method, EDL_internal_integer_to_string); break; case EDL_HEADER_LOOPED: APPEND_ITEM(looped, EDL_internal_bool_to_string); break; case EDL_HEADER_ONRULER: APPEND_ITEM(on_ruler, EDL_internal_bool_to_string); break; case EDL_HEADER_MEDIATYPE: APPEND_ITEM(media_type, EDL_internal_media_type_to_string); break; case EDL_HEADER_FILENAME: EDL_internal_string_append(str, "\"", 1); EDL_internal_string_append(str, line->filename, strlen(line->filename)); EDL_internal_string_append(str, "\"", 1); break; case EDL_HEADER_STREAM: APPEND_ITEM(stream, EDL_internal_integer_to_string); break; case EDL_HEADER_STREAMSTART: APPEND_ITEM(stream_start, EDL_internal_double_to_string); break; case EDL_HEADER_STREAMLENGTH: APPEND_ITEM(stream_length, EDL_internal_double_to_string); break; case EDL_HEADER_FADETIMEIN: APPEND_ITEM(fade_time_in, EDL_internal_double_to_string); break; case EDL_HEADER_FADETIMEOUT: APPEND_ITEM(fade_time_out, EDL_internal_double_to_string); break; case EDL_HEADER_SUSTAINGAIN: APPEND_ITEM(sustain_gain, EDL_internal_double_to_string); break; case EDL_HEADER_CURVEIN: APPEND_ITEM(curve_in, EDL_internal_integer_to_string); break; case EDL_HEADER_GAININ: APPEND_ITEM(gain_in, EDL_internal_double_to_string); break; case EDL_HEADER_CURVEOUT: APPEND_ITEM(curve_out, EDL_internal_integer_to_string); break; case EDL_HEADER_GAINOUT: APPEND_ITEM(gain_out, EDL_internal_double_to_string); break; case EDL_HEADER_LAYER: APPEND_ITEM(layer, EDL_internal_integer_to_string); break; case EDL_HEADER_COLOR: APPEND_ITEM(color, EDL_internal_integer_to_string); break; case EDL_HEADER_CURVEINR: APPEND_ITEM(curve_in_r, EDL_internal_integer_to_string); break; case EDL_HEADER_CURVEOUTR: APPEND_ITEM(curve_out_r, EDL_internal_integer_to_string); break; case EDL_HEADER_PLAYPITCH: APPEND_ITEM(play_pitch, EDL_internal_double_to_string); break; case EDL_HEADER_LOCKPITCH: APPEND_ITEM(lock_pitch, EDL_internal_bool_to_string); break; case EDL_HEADER_FIRSTCHANNEL: APPEND_ITEM(first_channel, EDL_internal_integer_to_string); break; case EDL_HEADER_CHANNELS: APPEND_ITEM(channels, EDL_internal_integer_to_string); break; default: /* ... what */ break; #undef APPEND_ITEM } if (i < order_len - 1) EDL_internal_string_append(str, ";\t", 2); else EDL_internal_string_append(str, ";", 1); } EDL_internal_string_append(str, "\n", 1); } /* this gets progressively slower and slower... oops */ char* EDL_dump(const EDL* edl) { static const char order_str[] = "\"ID\";\"Track\";\"StartTime\";\"Length\";\"PlayRate\";\"Locked\";\"Normalized\";\"StretchMethod\";" "\"Looped\";\"OnRuler\";\"MediaType\";\"FileName\";\"Stream\";\"StreamStart\";\"StreamLength\";" "\"FadeTimeIn\";\"FadeTimeOut\";\"SustainGain\";\"CurveIn\";\"GainIn\";\"CurveOut\";\"GainOut\";" "\"Layer\";\"Color\";\"CurveInR\";\"CurveOutR\":\"PlayPitch\";\"LockPitch\";\"FirstChannel\";\"Channels\"\n"; EDL_internal_string ret; EDL_internal_string_init(&ret); EDL_internal_string_append(&ret, order_str, strlen(order_str)); { size_t order_len; uint32_t* order = EDL_internal_parse_header(order_str, &order_len); size_t i; for (i = 0; i < edl->size; i++) EDL_dump_line(&ret, &edl->arr[i], order, order_len); free(order); } return ret.data; } void EDL_free(EDL* edl) { size_t i; for (i = 0; i < edl->size; i++) { if (edl->arr[i].filename) free(edl->arr[i].filename); } free(edl->arr); }