cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

encode.c (4813B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21#include "display.h"
     22#include "instructions.h"
     23#include "log.h"
     24
     25#include <guacamole/client.h>
     26#include <guacamole/error.h>
     27#include <guacamole/parser.h>
     28#include <guacamole/socket.h>
     29
     30#include <sys/stat.h>
     31#include <sys/types.h>
     32#include <errno.h>
     33#include <fcntl.h>
     34#include <stdbool.h>
     35#include <string.h>
     36#include <unistd.h>
     37
     38/**
     39 * Reads and handles all Guacamole instructions from the given guac_socket
     40 * until end-of-stream is reached.
     41 *
     42 * @param display
     43 *     The current internal display of the Guacamole video encoder.
     44 *
     45 * @param path
     46 *     The name of the file being parsed (for logging purposes). This file
     47 *     must already be open and available through the given socket.
     48 *
     49 * @param socket
     50 *     The guac_socket through which instructions should be read.
     51 *
     52 * @return
     53 *     Zero on success, non-zero if parsing of Guacamole protocol data through
     54 *     the given socket fails.
     55 */
     56static int guacenc_read_instructions(guacenc_display* display,
     57        const char* path, guac_socket* socket) {
     58
     59    /* Obtain Guacamole protocol parser */
     60    guac_parser* parser = guac_parser_alloc();
     61    if (parser == NULL)
     62        return 1;
     63
     64    /* Continuously read and handle all instructions */
     65    while (!guac_parser_read(parser, socket, -1)) {
     66        if (guacenc_handle_instruction(display, parser->opcode,
     67                parser->argc, parser->argv)) {
     68            guacenc_log(GUAC_LOG_DEBUG, "Handling of \"%s\" instruction "
     69                    "failed.", parser->opcode);
     70        }
     71    }
     72
     73    /* Fail on read/parse error */
     74    if (guac_error != GUAC_STATUS_CLOSED) {
     75        guacenc_log(GUAC_LOG_ERROR, "%s: %s",
     76                path, guac_status_string(guac_error));
     77        guac_parser_free(parser);
     78        return 1;
     79    }
     80
     81    /* Parse complete */
     82    guac_parser_free(parser);
     83    return 0;
     84
     85}
     86
     87int guacenc_encode(const char* path, const char* out_path, const char* codec,
     88        int width, int height, int bitrate, bool force) {
     89
     90    /* Open input file */
     91    int fd = open(path, O_RDONLY);
     92    if (fd < 0) {
     93        guacenc_log(GUAC_LOG_ERROR, "%s: %s", path, strerror(errno));
     94        return 1;
     95    }
     96
     97    /* Lock entire input file for reading by the current process */
     98    struct flock file_lock = {
     99        .l_type   = F_RDLCK,
    100        .l_whence = SEEK_SET,
    101        .l_start  = 0,
    102        .l_len    = 0,
    103        .l_pid    = getpid()
    104    };
    105
    106    /* Abort if file cannot be locked for reading */
    107    if (!force && fcntl(fd, F_SETLK, &file_lock) == -1) {
    108
    109        /* Warn if lock cannot be acquired */
    110        if (errno == EACCES || errno == EAGAIN)
    111            guacenc_log(GUAC_LOG_WARNING, "Refusing to encode in-progress "
    112                    "recording \"%s\" (specify the -f option to override "
    113                    "this behavior).", path);
    114
    115        /* Log an error if locking fails in an unexpected way */
    116        else
    117            guacenc_log(GUAC_LOG_ERROR, "Cannot lock \"%s\" for reading: %s",
    118                    path, strerror(errno));
    119
    120        close(fd);
    121        return 1;
    122    }
    123
    124    /* Allocate display for encoding process */
    125    guacenc_display* display = guacenc_display_alloc(out_path, codec,
    126            width, height, bitrate);
    127    if (display == NULL) {
    128        close(fd);
    129        return 1;
    130    }
    131
    132    /* Obtain guac_socket wrapping file descriptor */
    133    guac_socket* socket = guac_socket_open(fd);
    134    if (socket == NULL) {
    135        guacenc_log(GUAC_LOG_ERROR, "%s: %s", path,
    136                guac_status_string(guac_error));
    137        close(fd);
    138        guacenc_display_free(display);
    139        return 1;
    140    }
    141
    142    guacenc_log(GUAC_LOG_INFO, "Encoding \"%s\" to \"%s\" ...", path, out_path);
    143
    144    /* Attempt to read all instructions in the file */
    145    if (guacenc_read_instructions(display, path, socket)) {
    146        guac_socket_free(socket);
    147        guacenc_display_free(display);
    148        return 1;
    149    }
    150
    151    /* Close input and finish encoding process */
    152    guac_socket_free(socket);
    153    return guacenc_display_free(display);
    154
    155}
    156