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