diff --git a/probe.c b/probe.c index 2b6b01efd7d6f544c269e7f6b3ceec00dd1de67a..52296f49774da442f8b1bf8856912252cf83332f 100644 --- a/probe.c +++ b/probe.c @@ -19,8 +19,10 @@ static char *getxmlattr(xmlAttr *a, char *name, char *err) char *get_xmpchapters(char *xmp) { - int i, len, maxlen; - char *out; + int i; + char *ptr; + size_t size; + FILE *stream; xmlDocPtr doc; xmlXPathContextPtr xpath; xmlXPathObjectPtr res; @@ -41,51 +43,52 @@ char *get_xmpchapters(char *xmp) res = xmlXPathEvalExpression("/x:xmpmeta/rdf:RDF/rdf:Description/xmpDM:Tracks/rdf:Bag/rdf:li/rdf:Description/xmpDM:markers/rdf:Seq/rdf:li/rdf:Description", xpath); if (!res || !res->nodesetval) return ""; - len = 0; maxlen = res->nodesetval->nodeNr*200; - out = malloc(maxlen); - len += xsnprintf(out+len, maxlen-len-1, "["); + ptr = 0; + size = 0; + if (!(stream = open_memstream(&ptr, &size))) + exit(99); + fprintf(stream, "["); for (i = 0; i < res->nodesetval->nodeNr; i ++) { start = atoi(getxmlattr(res->nodesetval->nodeTab[i]->properties, "startTime", "0"))/framerate; if (i) - len += xsnprintf(out+len, maxlen-len-1, ","); - len += xsnprintf(out+len, maxlen-len-1, "{\"time\": %f, \"text\": \"%s\"}", - start, jescape(getxmlattr(res->nodesetval->nodeTab[i]->properties, "name", ""))); + fprintf(stream, ","); + fprintf(stream, "{\"time\": %f, \"text\": \"%s\"}", start, + jescape(getxmlattr(res->nodesetval->nodeTab[i]->properties, "name", ""))); } - len += xsnprintf(out+len, maxlen-len-1, "]"); - return out; + fprintf(stream, "]"); + fclose(stream); + return ptr; } int main(int argc, char *argv[]) { - int jobid; - char path[100], *xmp; + int err; + char *path, *xmp; AVFormatContext *demux, *mux; AVDictionaryEntry *tag; AVDictionary *opts; if (argc != 5) return 1; av_register_all(); - av_log_set_callback(avlogbuf_callback); + init_avlogbuf(); jobid = atoi(argv[1]); - xsnprintf(BL(path), "%s/%s", CONFIG_VIDEOS_RAW, - jstr(jlookup(argv[4], "path"), "")); + path = mprintf("%s/%s", CONFIG_VIDEOS_RAW, jstr(jlookup(argv[4], "path"), "")); ping_job(jobid, "running", 0); opts = 0; av_dict_set_int(&opts, "export_xmp", 1, 0); demux = 0; - if (avformat_open_input(&demux, path, 0, &opts)) - goto fail; + if (err = avformat_open_input(&demux, path, 0, &opts)) + job_failed("Opening input file failed: %s", av_err2str(err)); avformat_find_stream_info(demux, 0); if (tag = av_dict_get(demux->metadata, "xmp", 0, 0)) - ping_job(jobid, "finished", "{\"xmp_chapters\": %s, \"duration\": %f, \"log\": \"%s\"}", get_xmpchapters(tag->value), demux->duration*av_q2d(AV_TIME_BASE_Q), get_avlogbuf()); + ping_job(jobid, "finished", "{\"xmp_chapters\": %s, \"duration\": %f, \"log\": \"%s\"}", + get_xmpchapters(tag->value), demux->duration*av_q2d(AV_TIME_BASE_Q), + jescape(get_avlogbuf())); else - ping_job(jobid, "finished", "{\"duration\": %f, \"log\": \"%s\"}", demux->duration*av_q2d(AV_TIME_BASE_Q), get_avlogbuf()); + ping_job(jobid, "finished", "{\"duration\": %f, \"log\": \"%s\"}", + demux->duration*av_q2d(AV_TIME_BASE_Q), jescape(get_avlogbuf())); return 0; - -fail: - ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf()); - return 1; } diff --git a/remux.c b/remux.c index 9ada5392d695b12639b2b8f3174f86cf95144896..02723c90e52e608e94f1a82641ef6d086890eb53 100644 --- a/remux.c +++ b/remux.c @@ -42,9 +42,9 @@ void parse_chapters(AVFormatContext *ctx, char *s, int duration) int main(int argc, char *argv[]) { - int jobid, i; + int i, err; int *idxmap; - char *p, path[100], tmp[100]; + char *p, *path, *tmp; AVFormatContext *demux, *mux; AVPacket pkt; AVStream *stream; @@ -52,19 +52,18 @@ int main(int argc, char *argv[]) if (argc != 5) return 1; av_register_all(); - av_log_set_callback(avlogbuf_callback); + init_avlogbuf(); memset(&pkt, 0, sizeof(pkt)); av_init_packet(&pkt); jobid = atoi(argv[1]); - xsnprintf(BL(path), "%s/%s", CONFIG_VIDEOS_RELEASED, - jstr(jlookup(argv[4], "path"), "")); - xsnprintf(BL(tmp), "%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid); + path = mprintf("%s/%s", CONFIG_VIDEOS_RELEASED, jstr(jlookup(argv[4], "path"), "")); + tmp = mprintf("%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid); ping_job(jobid, "running", 0); demux = 0; - if (avformat_open_input(&demux, path, 0, 0)) - goto fail; + if (err = avformat_open_input(&demux, path, 0, 0)) + job_failed("Opening input file failed: %s", av_err2str(err)); avformat_find_stream_info(demux, 0); avformat_alloc_output_context2(&mux, 0, jstr(jlookup(argv[4], "format"), 0), path); av_dict_copy(&mux->metadata, demux->metadata, 0); @@ -89,8 +88,8 @@ int main(int argc, char *argv[]) avio_open(&mux->pb, tmp, AVIO_FLAG_WRITE); muxopts = 0; parse_dict(&muxopts, jlookup(argv[4], "options")); - if (avformat_write_header(mux, &muxopts) < 0) - goto fail; + if ((err = avformat_write_header(mux, &muxopts)) < 0) + job_failed("Writing temporary file failed: %s", av_err2str(err)); while (!av_read_frame(demux, &pkt)) { if (pkt.stream_index >= demux->nb_streams @@ -104,21 +103,16 @@ int main(int argc, char *argv[]) pkt.duration = av_rescale_q(pkt.duration, demux->streams[pkt.stream_index]->time_base, mux->streams[pkt.stream_index]->time_base); pkt.pos = -1; - if (av_interleaved_write_frame(mux, &pkt)) - goto fail; + if (err = av_interleaved_write_frame(mux, &pkt)) + job_failed("Could not write frame: %s", av_err2str(err)); } av_interleaved_write_frame(mux, 0); - if (av_write_trailer(mux)) - goto fail; + if (err = av_write_trailer(mux)) + job_failed("Error writing trailer to temporary file", av_err2str(err)); avio_closep(&mux->pb); if (rename(tmp, path)) - goto fail; + job_failed("Overwriting output file failed: %s", strerror(errno)); unlink(tmp); - ping_job(jobid, "finished", "{\"log\": \"%s\"}", get_avlogbuf()); + ping_job(jobid, "finished", "{\"log\": \"%s\"}", jescape(get_avlogbuf())); return 0; - -fail: - unlink(tmp); - ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf()); - return 1; } diff --git a/thumbnail.c b/thumbnail.c index f33ffa8c0e06faa8807a90771fac29bcaec36078..0266bcc9f67f4a4dbeec3039188b613c26f20256 100644 --- a/thumbnail.c +++ b/thumbnail.c @@ -24,8 +24,8 @@ AVFrame *scale_frame(AVFrame *frame, enum AVPixelFormat pix_fmt, int main(int argc, char *argv[]) { - int jobid, vidx; - char lectureid[JSTR_SIZE], src[100], tmp[100], dest[100]; + int err, vidx; + char lectureid[JSTR_SIZE], *src, *tmp, *dest; AVFormatContext *demux, *mux; AVCodecContext *dec, *enc; AVPacket pkt; @@ -33,25 +33,23 @@ int main(int argc, char *argv[]) if (argc != 5) return 1; av_register_all(); - av_log_set_callback(avlogbuf_callback); + init_avlogbuf(); /* Prepare arguments */ jobid = atoi(argv[1]); jstrb(jlookup(argv[4], "lectureid"), "-1", lectureid); - xsnprintf(BL(src), "%s/%s", CONFIG_VIDEOS_RELEASED, - jstr(jlookup(argv[4], "path"), "")); - xsnprintf(BL(tmp), "%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid); - xsnprintf(BL(dest), "%s/thumbnail/l_%s.jpg", CONFIG_VIDEOS_RELEASED, - lectureid); + src = mprintf("%s/%s", CONFIG_VIDEOS_RELEASED, jstr(jlookup(argv[4], "path"), "")); + tmp = mprintf("%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid); + dest = mprintf("%s/thumbnail/l_%s.jpg", CONFIG_VIDEOS_RELEASED, lectureid); ping_job(jobid, "running", 0); /* Open src */ demux = 0; - if (avformat_open_input(&demux, src, 0, 0)) - goto fail; + if (err = avformat_open_input(&demux, src, 0, 0)) + job_failed("Opening input file failed: %s", av_err2str(err)); avformat_find_stream_info(demux, 0); if ((vidx = av_find_best_stream(demux, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0)) < 0) - goto fail; + job_failed("Could not find video stream in input file"); dec = avcodec_alloc_context3(avcodec_find_decoder(demux->streams[vidx]->codecpar->codec_id)); avcodec_parameters_to_context(dec, demux->streams[vidx]->codecpar); avcodec_open2(dec, dec->codec, 0); @@ -71,8 +69,8 @@ int main(int argc, char *argv[]) avio_open(&mux->pb, tmp, AVIO_FLAG_WRITE); avcodec_open2(enc, enc->codec, 0); avcodec_parameters_from_context(mux->streams[0]->codecpar, enc); - if (avformat_write_header(mux, 0) < 0) - goto fail; + if ((err = avformat_write_header(mux, 0)) < 0) + job_failed("Writing temporary file failed: %s", av_err2str(err)); /* Create thumbnail */ av_seek_frame(demux, -1, (2L*demux->duration)/5L, 0); @@ -81,8 +79,8 @@ int main(int argc, char *argv[]) frame = av_frame_alloc(); do { - if (av_read_frame(demux, &pkt)) - goto fail; + if (err = av_read_frame(demux, &pkt)) + job_failed("Reading frame failed: %s", av_err2str(err)); if (pkt.stream_index == vidx) avcodec_send_packet(dec, &pkt); } @@ -97,17 +95,12 @@ int main(int argc, char *argv[]) av_interleaved_write_frame(mux, &pkt); } av_interleaved_write_frame(mux, 0); - if (av_write_trailer(mux)) - goto fail; + if (err = av_write_trailer(mux)) + job_failed("Error writing trailer to temporary file: %s", av_err2str(err)); avio_closep(&mux->pb); if (rename(tmp, dest)) - goto fail; + job_failed("Overwriting output file failed: %s", strerror(errno)); unlink(tmp); - ping_job(jobid, "finished", "{\"log\": \"%s\"}", get_avlogbuf()); + ping_job(jobid, "finished", "{\"log\": \"%s\"}", jescape(get_avlogbuf())); return 0; - -fail: - unlink(tmp); - ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf()); - return 1; } diff --git a/util.h b/util.h index 898e5e17afbba86def6771d4c1b58679308b0dc5..1a86f0ffcd538755445120b8017cc36ef5af0c52 100644 --- a/util.h +++ b/util.h @@ -4,14 +4,19 @@ /* Generic */ #define SL(s) (s), (sizeof(s)-1) #define BL(b) (b), (sizeof(b)) -size_t xvsnprintf(char *p, size_t len, const char *fmt, va_list ap); -size_t xsnprintf(char *p, size_t len, const char *fmt, ...); +extern int jobid; +char *vmprintf(const char *fmt, va_list ap); +char *mprintf(const char *fmt, ...); + +/* Logging */ +void init_avlogbuf(void); void avlogbuf_callback(void *classp, int level, const char *fmt, va_list ap); char *get_avlogbuf(void); /* API */ int ping_job(int id, char *state, char *status, ...); +void job_failed(char *msg, ...); /* JSON parser */ #define JSTR_SIZE 100 @@ -24,4 +29,6 @@ char *jenter(char *s); char *jnext(char *s); char *jvalue(char *s); char *jlookup(char *s, char *key); + +/* JSON util */ char *jescape(char *s); diff --git a/util/api.c b/util/api.c index 07fe0eb4f45512099e4ebe34f31bf6f1453204cd..fe4c769557df72273c1ec78cdc9ad294736271ac 100644 --- a/util/api.c +++ b/util/api.c @@ -1,6 +1,7 @@ #include <curl/curl.h> #include <stdio.h> #include <stdarg.h> +#include <stdlib.h> #include <unistd.h> #include <string.h> @@ -9,7 +10,7 @@ static size_t curl_write_cb(char *ptr, size_t size, size_t nmemb, void *user) { - return size; + return size*nmemb; } int ping_job(int id, char *state, char *status, ...) @@ -17,39 +18,44 @@ int ping_job(int id, char *state, char *status, ...) int ret; CURL *curl; va_list ap; - char *e_host, *e_status, *e_apikey, *e_state; - char url[100], buf[500]; + char *p, *url, *e_host, *e_status, *e_apikey, *e_state; + char hostbuf[HOST_NAME_MAX+1]; if (!(curl = curl_easy_init())) return -1; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); e_apikey = curl_easy_escape(curl, CONFIG_APIKEY, 0); e_state = curl_easy_escape(curl, state, 0); - if (gethostname(BL(buf))) - strcpy(buf, "localhost"); - buf[sizeof(buf)-1] = 0; - e_host = curl_easy_escape(curl, buf, 0); + if (gethostname(BL(hostbuf))) + strcpy(hostbuf, "localhost"); + e_host = curl_easy_escape(curl, hostbuf, 0); if (status) { va_start(ap, status); - xvsnprintf(BL(buf), status, ap); + p = vmprintf(status, ap); va_end(ap); } else - strcpy(buf, "{}"); - e_status = curl_easy_escape(curl, buf, 0); - xsnprintf(BL(buf), "apikey=%s&state=%s&host=%s&status=%s", - e_apikey, e_state, e_host, e_status); + p = strdup("{}"); + e_status = curl_easy_escape(curl, p, 0); + free(p); + p = mprintf("apikey=%s&state=%s&host=%s&status=%s", e_apikey, e_state, + e_host, e_status); curl_free(e_apikey); curl_free(e_state); curl_free(e_host); curl_free(e_status); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf); - xsnprintf(BL(url), "%s/internal/jobs/api/job/%i/ping", CONFIG_APIBASE, id); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, p); + url = mprintf("%s/internal/jobs/api/job/%i/ping", CONFIG_APIBASE, id); curl_easy_setopt(curl, CURLOPT_URL, url); ret = curl_easy_perform(curl); + free(p); + free(url); curl_easy_cleanup(curl); if (ret) + { + fprintf(stderr, "API call to \"%s\" failed: %s\n", url, curl_easy_strerror(ret)); return -1; + } else return 0; } diff --git a/util/avlogbuf.c b/util/avlogbuf.c index eb422d2ceb3a4f402677bc3e69428f7ee1a50c8d..6ccaa13cc82891b11378f5eebc9314c56f9e8932 100644 --- a/util/avlogbuf.c +++ b/util/avlogbuf.c @@ -3,41 +3,38 @@ #include "../util.h" -static char *logbuffer = 0; -static size_t logoffset = 0, logsize = 0; -static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER;; +static char *logbuffer; +static size_t logsize; +static FILE *logstream; + +void init_avlogbuf(void) +{ + logbuffer = 0; + logsize = 0; + if (!(logstream = open_memstream(&logbuffer, &logsize))) + exit(99); + av_log_set_callback(avlogbuf_callback); +} void avlogbuf_callback(void *class, int level, const char *fmt, va_list ap) { - int len, print_prefix; + int print_prefix; + char buf[200]; if (level >= AV_LOG_VERBOSE) return; - av_log_default_callback(class, level, fmt, ap); - if (pthread_mutex_lock(&loglock)) - return; - if (logsize-logoffset < 1024) - logbuffer = realloc(logbuffer, (logsize += 1024)); - if (!logbuffer) - return; print_prefix = 1; - len = av_log_format_line2(class, level, fmt, ap, logbuffer+logoffset, - logsize-logoffset, &print_prefix); - if (len < logsize-logoffset) - logoffset += len; - else - logoffset = logsize-1; - pthread_mutex_unlock(&loglock); + av_log_format_line(class, level, fmt, ap, BL(buf), &print_prefix); + fputs(buf, logstream); } char *get_avlogbuf(void) { - char *ret, *p; - if (!logbuffer || pthread_mutex_lock(&loglock)) - return strdup(""); - ret = strdup(logbuffer); - pthread_mutex_unlock(&loglock); - for (p = ret; *p; p ++) - if (*p == '"') - *p = '\''; - return ret; + char *p; + if (!logstream) + return ""; + fclose(logstream); + p = logbuffer; + init_avlogbuf(); + fputs(p, logstream); + return p; } diff --git a/util/jescape.c b/util/jescape.c new file mode 100644 index 0000000000000000000000000000000000000000..a2e8decb2c89832f3f04b596719911b672b6b7d9 --- /dev/null +++ b/util/jescape.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "../util.h" + +char *jescape(char *s) +{ + int i; + char *ptr; + size_t size; + FILE *stream; + if (!(stream = open_memstream(&ptr, &size))) + exit(99); + for (; *s; s ++) + switch (*s) + { + case '"': + fputs("\\\"", stream); break; + case '\\': + fputs("\\\\", stream); break; + case '\b': + fputs("\\b", stream); break; + case '\f': + fputs("\\f", stream); break; + case '\n': + fputs("\\n", stream); break; + case '\r': + fputs("\\r", stream); break; + case '\t': + fputs("\\t", stream); break; + default: + if (*s <= 0x1f) + fprintf(stream, "\\u%04x", *s); + else + fputc(*s, stream); + } + fclose(stream); + return ptr; +} diff --git a/util/job_error.c b/util/job_error.c new file mode 100644 index 0000000000000000000000000000000000000000..8ddd3d5c93ea50e491843b6de702e1d7d74203e4 --- /dev/null +++ b/util/job_error.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "../util.h" + +int jobid; + +void job_failed(char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + ping_job(jobid, "failed", "{\"reason\": \"%s\", \"log\": \"%s\"}", + jescape(vmprintf(msg, ap)), jescape(get_avlogbuf())); + va_end(ap); + exit(2); +} diff --git a/util/json.c b/util/json.c index b9fa3dcf12bbeee6ee183f4a4eebf914dc663403..9fb84e276846e97683168730b10c548b62bc96a9 100644 --- a/util/json.c +++ b/util/json.c @@ -5,9 +5,6 @@ #include "../util.h" -static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F'}; - static char *skip_ws(char *s) { for (; *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r'; s ++); @@ -213,41 +210,3 @@ char *jlookup(char *s, char *key) return jvalue(s); return 0; } - -char *jescape(char *s) -{ - int i; - static __thread char buf[JSTR_SIZE]; - for (i = 0; *s && i+7 < sizeof(buf); s ++) - switch (*s) - { - case '"': - buf[i ++] = '\\'; buf[i ++] = '"'; break; - case '\\': - buf[i ++] = '\\'; buf[i ++] = '\\'; break; - case '\b': - buf[i ++] = '\\'; buf[i ++] = 'b'; break; - case '\f': - buf[i ++] = '\\'; buf[i ++] = 'f'; break; - case '\n': - buf[i ++] = '\\'; buf[i ++] = 'n'; break; - case '\r': - buf[i ++] = '\\'; buf[i ++] = 'r'; break; - case '\t': - buf[i ++] = '\\'; buf[i ++] = 't'; break; - default: - if (*s <= 0x1f) - { - buf[i ++] = '\\'; - buf[i ++] = 'u'; - buf[i ++] = '0'; - buf[i ++] = '0'; - buf[i ++] = hex[*s >> 4]; - buf[i ++] = hex[*s & 0xf]; - } - else - buf[i ++] = *s; - } - buf[i] = 0; - return buf; -} diff --git a/util/mprintf.c b/util/mprintf.c new file mode 100644 index 0000000000000000000000000000000000000000..fd850611dfbcce431230eb8b8caed89db7077c0f --- /dev/null +++ b/util/mprintf.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "../util.h" + +char *vmprintf(const char *fmt, va_list ap) +{ + char *ptr; + size_t size; + FILE *stream; + ptr = 0; + size = 0; + if (!(stream = open_memstream(&ptr, &size))) + exit(99); + if (vfprintf(stream, fmt, ap) < 0) + exit(99); + fclose(stream); + return ptr; +} + +char *mprintf(const char *fmt, ...) +{ + char *ret; + va_list ap; + va_start(ap, fmt); + ret = vmprintf(fmt, ap); + va_end(ap); + return ret; +} diff --git a/util/xprintf.c b/util/xprintf.c deleted file mode 100644 index d20cb2a1966c74eb2b540cb3a8d76c50b18a1baf..0000000000000000000000000000000000000000 --- a/util/xprintf.c +++ /dev/null @@ -1,25 +0,0 @@ -#include <stdio.h> -#include <stdarg.h> - -#include "../util.h" - -size_t xvsnprintf(char *p, size_t len, const char *fmt, va_list ap) -{ - int ret; - if ((ret = vsnprintf(p, len, fmt, ap)) >= len) - { - p[len-1] = 0; - return len; - } - return ret; -} - -size_t xsnprintf(char *p, size_t len, const char *fmt, ...) -{ - size_t ret; - va_list ap; - va_start(ap, fmt); - ret = vsnprintf(p, len, fmt, ap); - va_end(ap); - return ret; -}