audio.c (6857B)
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 22#include "guacamole/mem.h" 23#include "guacamole/audio.h" 24#include "guacamole/client.h" 25#include "guacamole/protocol.h" 26#include "guacamole/stream.h" 27#include "guacamole/user.h" 28#include "raw_encoder.h" 29 30#include <stdlib.h> 31#include <string.h> 32 33/** 34 * Sets the encoder associated with the given guac_audio_stream, automatically 35 * invoking its begin_handler. The guac_audio_stream MUST NOT already be 36 * associated with an encoder. 37 * 38 * @param audio 39 * The guac_audio_stream whose encoder is being set. 40 * 41 * @param encoder 42 * The encoder to associate with the given guac_audio_stream. 43 */ 44static void guac_audio_stream_set_encoder(guac_audio_stream* audio, 45 guac_audio_encoder* encoder) { 46 47 /* Call handler, if defined */ 48 if (encoder != NULL && encoder->begin_handler) 49 encoder->begin_handler(audio); 50 51 /* Assign encoder, which may be NULL */ 52 audio->encoder = encoder; 53 54} 55 56/** 57 * Assigns a new audio encoder to the given guac_audio_stream based on the 58 * audio mimetypes declared as supported by the given user. If no audio encoder 59 * can be found, no new audio encoder is assigned, and the existing encoder is 60 * left untouched (if any). 61 * 62 * @param user 63 * The user whose supported audio mimetypes should determine the audio 64 * encoder selected. 65 * 66 * @param data 67 * The guac_audio_stream to which the new encoder should be assigned. 68 * Existing properties set on this audio stream, such as the bits per 69 * sample, may affect the encoder chosen. 70 * 71 * @return 72 * The assigned audio encoder. If no new audio encoder can be assigned, 73 * this will be the currently-assigned audio encoder (which may be NULL). 74 */ 75static void* guac_audio_assign_encoder(guac_user* user, void* data) { 76 77 int i; 78 79 guac_audio_stream* audio = (guac_audio_stream*) data; 80 int bps = audio->bps; 81 82 /* If no user is provided, or an encoder has already been assigned, 83 * do not attempt to assign a new encoder */ 84 if (user == NULL || audio->encoder != NULL) 85 return audio->encoder; 86 87 /* For each supported mimetype, check for an associated encoder */ 88 for (i=0; user->info.audio_mimetypes[i] != NULL; i++) { 89 90 const char* mimetype = user->info.audio_mimetypes[i]; 91 92 /* If 16-bit raw audio is supported, done. */ 93 if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) { 94 guac_audio_stream_set_encoder(audio, raw16_encoder); 95 break; 96 } 97 98 /* If 8-bit raw audio is supported, done. */ 99 if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) { 100 guac_audio_stream_set_encoder(audio, raw8_encoder); 101 break; 102 } 103 104 } /* end for each mimetype */ 105 106 /* Return assigned encoder, if any */ 107 return audio->encoder; 108 109} 110 111guac_audio_stream* guac_audio_stream_alloc(guac_client* client, 112 guac_audio_encoder* encoder, int rate, int channels, int bps) { 113 114 guac_audio_stream* audio; 115 116 /* Allocate stream */ 117 audio = (guac_audio_stream*) guac_mem_zalloc(sizeof(guac_audio_stream)); 118 audio->client = client; 119 audio->stream = guac_client_alloc_stream(client); 120 121 /* Abort allocation if underlying stream cannot be allocated */ 122 if (audio->stream == NULL) { 123 guac_mem_free(audio); 124 return NULL; 125 } 126 127 /* Load PCM properties */ 128 audio->rate = rate; 129 audio->channels = channels; 130 audio->bps = bps; 131 132 /* Assign encoder if explicitly provided */ 133 if (encoder != NULL) 134 guac_audio_stream_set_encoder(audio, encoder); 135 136 /* Otherwise, attempt to automatically assign encoder for owner */ 137 if (audio->encoder == NULL) 138 guac_client_for_owner(client, guac_audio_assign_encoder, audio); 139 140 /* Failing that, attempt to assign encoder for ANY connected user */ 141 if (audio->encoder == NULL) 142 guac_client_foreach_user(client, guac_audio_assign_encoder, audio); 143 144 return audio; 145 146} 147 148void guac_audio_stream_reset(guac_audio_stream* audio, 149 guac_audio_encoder* encoder, int rate, int channels, int bps) { 150 151 /* Pull assigned encoder if no other encoder is requested */ 152 if (encoder == NULL) 153 encoder = audio->encoder; 154 155 /* Do nothing if nothing is changing */ 156 if (encoder == audio->encoder 157 && rate == audio->rate 158 && channels == audio->channels 159 && bps == audio->bps) { 160 return; 161 } 162 163 /* Free old encoder data */ 164 if (audio->encoder != NULL && audio->encoder->end_handler) 165 audio->encoder->end_handler(audio); 166 167 /* Set PCM properties */ 168 audio->rate = rate; 169 audio->channels = channels; 170 audio->bps = bps; 171 172 /* Re-init encoder */ 173 guac_audio_stream_set_encoder(audio, encoder); 174 175} 176 177void guac_audio_stream_add_user(guac_audio_stream* audio, guac_user* user) { 178 179 /* Attempt to assign encoder if no encoder has yet been assigned */ 180 if (audio->encoder == NULL) 181 guac_audio_assign_encoder(user, audio); 182 183 /* Notify encoder that a new user is present */ 184 if (audio->encoder != NULL && audio->encoder->join_handler) 185 audio->encoder->join_handler(audio, user); 186 187} 188 189void guac_audio_stream_free(guac_audio_stream* audio) { 190 191 /* Flush stream encoding */ 192 guac_audio_stream_flush(audio); 193 194 /* Clean up encoder */ 195 if (audio->encoder != NULL && audio->encoder->end_handler) 196 audio->encoder->end_handler(audio); 197 198 /* Release stream back to client pool */ 199 guac_client_free_stream(audio->client, audio->stream); 200 201 /* Free associated data */ 202 guac_mem_free(audio); 203 204} 205 206void guac_audio_stream_write_pcm(guac_audio_stream* audio, 207 const unsigned char* data, int length) { 208 209 /* Write data */ 210 if (audio->encoder != NULL && audio->encoder->write_handler) 211 audio->encoder->write_handler(audio, data, length); 212 213} 214 215void guac_audio_stream_flush(guac_audio_stream* audio) { 216 217 /* Flush any buffered data */ 218 if (audio->encoder != NULL && audio->encoder->flush_handler) 219 audio->encoder->flush_handler(audio); 220 221} 222