Mercurial > libedl
view src/edl.c @ 8:0c98b46eaf73 v2.0
*: update API to 2.0, big changes
all APIs now use pointers to an EDL object. it is up to the
user to make sure that the pointer is valid.
additionally, many things have been separated into new files
to make it easier to digest
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Sun, 03 Mar 2024 17:56:58 -0500 |
parents | 7137fbac0b85 |
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); }