UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
export.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2021 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif /* HAVE_CONFIG_H */
43
44#include <pthread.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <unistd.h>
48
49#include "audio/audio.h"
50#include "audio/utils.h"
51#include "audio/wav_writer.h"
52#include "debug.h"
53#include "export.h"
54#include "utils/misc.h" // ug_strerror
55#include "utils/ring_buffer.h"
56
57#define CACHE_SECONDS 10
58
59/*
60 * we do not need to have possible stalls, so IO is performend in a separate thread
61 */
62static void *audio_export_thread(void *arg);
63static bool configure(struct audio_export *s, struct audio_desc fmt);
64
66 char *filename;
68
70
72
73 pthread_t thread_id;
74 pthread_mutex_t lock;
75 pthread_cond_t worker_cv;
76 volatile bool new_work_ready;
77 volatile bool worker_waiting;
78
79 volatile bool should_exit_worker;
80};
81
82static void *audio_export_thread(void *arg)
83{
84 struct audio_export *s = arg;
85
86 while(!s->should_exit_worker) {
87 pthread_mutex_lock(&s->lock);
88 while(!s->new_work_ready) {
89 s->worker_waiting = true;
90 pthread_cond_wait(&s->worker_cv, &s->lock);
91 s->worker_waiting = false;
92 }
93
94 int size = ring_get_current_size(s->ring);
95
96 if(s->should_exit_worker && size == 0) {
97 pthread_mutex_unlock(&s->lock);
98 break;
99 }
100
101 char *data = malloc(size);
102 assert(data);
103 size_t res = ring_buffer_read(s->ring, data, size);
104 assert(res == (size_t) size);
105 assert(ring_get_current_size(s->ring) == 0);
106 s->new_work_ready = false;
107
108 pthread_mutex_unlock(&s->lock);
109
110 const int sample_size = s->saved_format.bps * s->saved_format.ch_count;
111 int rc = wav_writer_write(s->wav, size / sample_size, data);
112 if (rc != 0) {
113 fprintf(stderr, "[Audio export] Problem writing audio samples: %s\n", ug_strerror(-rc));
114 }
115
116 free(data);
117 }
118
119 return NULL;
120}
121
122static bool configure(struct audio_export *s, struct audio_desc fmt) {
123 s->saved_format = fmt;
124
125 if ((s->wav = wav_writer_create(s->filename, fmt)) == NULL) {
126 fprintf(stderr, "[Audio export] Error writting header!\n");
127 return false;
128 }
129
130 s->ring = ring_buffer_init(CACHE_SECONDS * fmt.sample_rate * fmt.bps *
131 fmt.ch_count);
132
133 return true;
134}
135
137 int bps, int sample_rate, int ch_count)
138{
139
140 assert(s->saved_format.ch_count == 0 && "Audio export already configured");
141 struct audio_desc fmt;
142
143 fmt.codec = AC_PCM;
144 fmt.bps = bps;
145 fmt.sample_rate = sample_rate;
146 fmt.ch_count = ch_count;
147
148 bool res = configure(s, fmt);
149
150 if(!res) {
151 fprintf(stderr, "[Audio export] Configuration failed.\n");
152 return false;
153 }
154 pthread_create(&s->thread_id, NULL, audio_export_thread, s);
155
156 return true;
157}
158
160{
161 struct audio_export *s = calloc(1, sizeof(struct audio_export));
162 if(!s) {
163 return NULL;
164 }
165
166 unlink(filename);
167 s->filename = strdup(filename);
168 s->thread_id = 0;
169 s->ring = NULL;
170
171 pthread_mutex_init(&s->lock, NULL);
172 pthread_cond_init(&s->worker_cv, NULL);
173 s->new_work_ready = false;
174 s->worker_waiting = false;
175 s->should_exit_worker = false;
176
177 s->saved_format = (struct audio_desc) { 0, 0, 0, 0 };
178
179 return s;
180}
181
183{
184 if(s) {
185 if(s->thread_id) {
186 pthread_mutex_lock(&s->lock);
187 s->should_exit_worker = true;
188 s->new_work_ready = true;
189 if(s->worker_waiting) {
190 pthread_cond_signal(&s->worker_cv);
191 }
192 pthread_mutex_unlock(&s->lock);
193 pthread_join(s->thread_id, NULL);
194 }
195
196 if (s->wav != NULL) {
198 }
199 if (s->ring != NULL) {
201 }
202 pthread_cond_destroy(&s->worker_cv);
203 pthread_mutex_destroy(&s->lock);
204 free(s->filename);
205 free(s);
206 }
207}
208
209void audio_export_raw(struct audio_export *s, void *data, unsigned len){
210 assert(s->saved_format.ch_count != 0 && "Export not configured");
211 pthread_mutex_lock(&s->lock);
212 ring_buffer_write(s->ring, data, len);
213 s->new_work_ready = true;
214
215 if(s->worker_waiting) {
216 pthread_cond_signal(&s->worker_cv);
217 }
218 pthread_mutex_unlock(&s->lock);
219}
220
221void audio_export(struct audio_export *s, const struct audio_frame *frame)
222{
223 if(!s) {
224 return;
225 }
226
227 if(s->saved_format.ch_count == 0) {
228 bool res;
230 if(!res) {
231 fprintf(stderr, "[Audio export] Configuration failed.\n");
232 return;
233 }
234 pthread_create(&s->thread_id, NULL, audio_export_thread, s);
235 } else {
238 return;
239 }
240 }
241
242 audio_export_raw(s, frame->data, frame->data_len);
243}
244
245static int write_samples(char *out, const void **channels_data,
246 int bps, int ch_count, int size, int start_sample)
247{
248 int written = 0;
249 int in_offset = start_sample*bps;
250 for(; written * bps * ch_count < size; written++){
251 for(int ch = 0; ch < ch_count; ch++){
252 const char *in_ptr = ((const char *)channels_data[ch]) + in_offset;
253 memcpy(out, in_ptr, bps);
254 out += bps;
255 }
256 in_offset += bps;
257 }
258
259 return written;
260}
261
263 const void **channels_data, unsigned sample_count)
264{
265 const int ch_count = s->saved_format.ch_count;
266 const int bps = s->saved_format.bps;
267 assert(ch_count != 0 && "Export not configured");
268
269 int len = s->saved_format.ch_count * s->saved_format.bps * sample_count;
270
271 pthread_mutex_lock(&s->lock);
272 void *ptr1;
273 int size1;
274 void *ptr2;
275 int size2;
276 int avail = ring_get_write_regions(s->ring, len, &ptr1, &size1, &ptr2, &size2);
277
278 assert((size1 % (bps * ch_count)) == 0);
279 assert(!ptr2 || (size2 % (bps * ch_count)) == 0);
280
281 int written = write_samples(ptr1, channels_data, bps, ch_count, size1, 0);
282 if(ptr2){
283 write_samples(ptr2, channels_data, bps, ch_count, size2, written);
284 }
285
286 ring_advance_write_idx(s->ring, avail);
287
288 s->new_work_ready = true;
289 if(s->worker_waiting) {
290 pthread_cond_signal(&s->worker_cv);
291 }
292 pthread_mutex_unlock(&s->lock);
293}
294
int int int sample_rate
struct video_frame * frame
const char * fmt
int void size_t * len
static int write_samples(char *out, const void **channels_data, int bps, int ch_count, int size, int start_sample)
Definition export.c:245
void audio_export_raw(struct audio_export *s, void *data, unsigned len)
Definition export.c:209
struct audio_export * audio_export_init(const char *filename)
Definition export.c:159
static bool configure(struct audio_export *s, struct audio_desc fmt)
Definition export.c:122
static void * audio_export_thread(void *arg)
Definition export.c:82
void audio_export_destroy(struct audio_export *s)
Definition export.c:182
void audio_export_raw_ch(struct audio_export *s, const void **channels_data, unsigned sample_count)
Definition export.c:262
bool audio_export_configure_raw(struct audio_export *s, int bps, int sample_rate, int ch_count)
Definition export.c:136
void audio_export(struct audio_export *s, const struct audio_frame *frame)
Definition export.c:221
#define CACHE_SECONDS
Definition export.c:57
@ AC_PCM
Definition types.h:53
const char * ug_strerror(int errnum)
Definition misc.cpp:203
int ring_get_current_size(struct ring_buffer *ring)
void ring_buffer_destroy(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
bool ring_advance_write_idx(struct ring_buffer *ring, int amount)
int ring_get_write_regions(struct ring_buffer *ring, int requested_len, void **ptr1, int *size1, void **ptr2, int *size2)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
int ch_count
Definition types.h:79
int bps
Definition types.h:77
audio_codec_t codec
Definition types.h:80
struct wav_writer_file * wav
Definition export.c:67
ring_buffer_t * ring
Definition export.c:71
pthread_cond_t worker_cv
Definition export.c:75
volatile bool should_exit_worker
Definition export.c:79
pthread_mutex_t lock
Definition export.c:74
char * filename
Definition export.c:66
volatile bool new_work_ready
Definition export.c:76
struct audio_desc saved_format
Definition export.c:69
volatile bool worker_waiting
Definition export.c:77
pthread_t thread_id
Definition export.c:73
struct used to comunicate with audio capture/playback device
Definition types.h:99
struct audio_desc audio_desc_from_frame(const struct audio_frame *frame)
Definition utils.cpp:196
bool audio_desc_eq(struct audio_desc a1, struct audio_desc a2)
Definition utils.cpp:189
bool wav_writer_close(struct wav_writer_file *wav)
Definition wav_writer.c:169
int wav_writer_write(struct wav_writer_file *wav, long long sample_count, const char *data)
Definition wav_writer.c:151
struct wav_writer_file * wav_writer_create(const char *filename, struct audio_desc fmt)
Definition wav_writer.c:134