ffmpeg / libavformat / beosaudio.cpp @ 5509bffa
History | View | Annotate | Download (12.8 KB)
1 |
/*
|
---|---|
2 |
* BeOS audio play interface
|
3 |
* Copyright (c) 2000, 2001 Fabrice Bellard.
|
4 |
*
|
5 |
* This library is free software; you can redistribute it and/or
|
6 |
* modify it under the terms of the GNU Lesser General Public
|
7 |
* License as published by the Free Software Foundation; either
|
8 |
* version 2 of the License, or (at your option) any later version.
|
9 |
*
|
10 |
* This library is distributed in the hope that it will be useful,
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 |
* Lesser General Public License for more details.
|
14 |
*
|
15 |
* You should have received a copy of the GNU Lesser General Public
|
16 |
* License along with this library; if not, write to the Free Software
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18 |
*/
|
19 |
|
20 |
#include <signal.h> |
21 |
#include <stdlib.h> |
22 |
#include <stdio.h> |
23 |
#include <string.h> |
24 |
#include <unistd.h> |
25 |
#include <sys/time.h> |
26 |
|
27 |
#include <Application.h> |
28 |
#include <SoundPlayer.h> |
29 |
|
30 |
extern "C" { |
31 |
#include "avformat.h" |
32 |
} |
33 |
|
34 |
#ifdef HAVE_BSOUNDRECORDER
|
35 |
#include <SoundRecorder.h> |
36 |
using namespace BPrivate::Media::Experimental; |
37 |
#endif
|
38 |
|
39 |
/* enable performance checks */
|
40 |
//#define PERF_CHECK
|
41 |
|
42 |
/* enable Media Kit latency checks */
|
43 |
//#define LATENCY_CHECK
|
44 |
|
45 |
#define AUDIO_BLOCK_SIZE 4096 |
46 |
#define AUDIO_BLOCK_COUNT 8 |
47 |
|
48 |
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
|
49 |
|
50 |
typedef struct { |
51 |
int fd; // UNUSED |
52 |
int sample_rate;
|
53 |
int channels;
|
54 |
int frame_size; /* in bytes ! */ |
55 |
CodecID codec_id; |
56 |
uint8_t buffer[AUDIO_BUFFER_SIZE]; |
57 |
int buffer_ptr;
|
58 |
/* ring buffer */
|
59 |
sem_id input_sem; |
60 |
int input_index;
|
61 |
sem_id output_sem; |
62 |
int output_index;
|
63 |
BSoundPlayer *player; |
64 |
#ifdef HAVE_BSOUNDRECORDER
|
65 |
BSoundRecorder *recorder; |
66 |
#endif
|
67 |
int has_quit; /* signal callbacks not to wait */ |
68 |
volatile bigtime_t starve_time;
|
69 |
} AudioData; |
70 |
|
71 |
static thread_id main_thid;
|
72 |
static thread_id bapp_thid;
|
73 |
static int own_BApp_created = 0; |
74 |
static int refcount = 0; |
75 |
|
76 |
/* create the BApplication and Run() it */
|
77 |
static int32 bapp_thread(void *arg) |
78 |
{ |
79 |
new BApplication("application/x-vnd.ffmpeg"); |
80 |
own_BApp_created = 1;
|
81 |
be_app->Run(); |
82 |
/* kill the process group */
|
83 |
// kill(0, SIGINT);
|
84 |
// kill(main_thid, SIGHUP);
|
85 |
return B_OK;
|
86 |
} |
87 |
|
88 |
/* create the BApplication only if needed */
|
89 |
static void create_bapp_if_needed(void) |
90 |
{ |
91 |
if (refcount++ == 0) { |
92 |
/* needed by libmedia */
|
93 |
if (be_app == NULL) { |
94 |
bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL); |
95 |
resume_thread(bapp_thid); |
96 |
while (!own_BApp_created)
|
97 |
snooze(50000);
|
98 |
} |
99 |
} |
100 |
} |
101 |
|
102 |
static void destroy_bapp_if_needed(void) |
103 |
{ |
104 |
if (--refcount == 0 && own_BApp_created) { |
105 |
be_app->Lock(); |
106 |
be_app->Quit(); |
107 |
be_app = NULL;
|
108 |
} |
109 |
} |
110 |
|
111 |
/* called back by BSoundPlayer */
|
112 |
static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format) |
113 |
{ |
114 |
AudioData *s; |
115 |
size_t len, amount; |
116 |
unsigned char *buf = (unsigned char *)buffer; |
117 |
|
118 |
s = (AudioData *)cookie; |
119 |
if (s->has_quit)
|
120 |
return;
|
121 |
while (bufferSize > 0) { |
122 |
#ifdef PERF_CHECK
|
123 |
bigtime_t t; |
124 |
t = system_time(); |
125 |
#endif
|
126 |
len = MIN(AUDIO_BLOCK_SIZE, bufferSize); |
127 |
if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) { |
128 |
s->has_quit = 1;
|
129 |
s->player->SetHasData(false);
|
130 |
return;
|
131 |
} |
132 |
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index)); |
133 |
memcpy(buf, &s->buffer[s->output_index], amount); |
134 |
s->output_index += amount; |
135 |
if (s->output_index >= AUDIO_BUFFER_SIZE) {
|
136 |
s->output_index %= AUDIO_BUFFER_SIZE; |
137 |
memcpy(buf + amount, &s->buffer[s->output_index], len - amount); |
138 |
s->output_index += len-amount; |
139 |
s->output_index %= AUDIO_BUFFER_SIZE; |
140 |
} |
141 |
release_sem_etc(s->input_sem, len, 0);
|
142 |
#ifdef PERF_CHECK
|
143 |
t = system_time() - t; |
144 |
s->starve_time = MAX(s->starve_time, t); |
145 |
#endif
|
146 |
buf += len; |
147 |
bufferSize -= len; |
148 |
} |
149 |
} |
150 |
|
151 |
#ifdef HAVE_BSOUNDRECORDER
|
152 |
/* called back by BSoundRecorder */
|
153 |
static void audiorecord_callback(void *cookie, bigtime_t timestamp, void *buffer, size_t bufferSize, const media_multi_audio_format &format) |
154 |
{ |
155 |
AudioData *s; |
156 |
size_t len, amount; |
157 |
unsigned char *buf = (unsigned char *)buffer; |
158 |
|
159 |
s = (AudioData *)cookie; |
160 |
if (s->has_quit)
|
161 |
return;
|
162 |
|
163 |
while (bufferSize > 0) { |
164 |
len = MIN(bufferSize, AUDIO_BLOCK_SIZE); |
165 |
//printf("acquire_sem(input, %d)\n", len);
|
166 |
if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) { |
167 |
s->has_quit = 1;
|
168 |
return;
|
169 |
} |
170 |
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index)); |
171 |
memcpy(&s->buffer[s->input_index], buf, amount); |
172 |
s->input_index += amount; |
173 |
if (s->input_index >= AUDIO_BUFFER_SIZE) {
|
174 |
s->input_index %= AUDIO_BUFFER_SIZE; |
175 |
memcpy(&s->buffer[s->input_index], buf + amount, len - amount); |
176 |
s->input_index += len - amount; |
177 |
} |
178 |
release_sem_etc(s->output_sem, len, 0);
|
179 |
//printf("release_sem(output, %d)\n", len);
|
180 |
buf += len; |
181 |
bufferSize -= len; |
182 |
} |
183 |
} |
184 |
#endif
|
185 |
|
186 |
static int audio_open(AudioData *s, int is_output, const char *audio_device) |
187 |
{ |
188 |
int p[2]; |
189 |
int ret;
|
190 |
media_raw_audio_format format; |
191 |
media_multi_audio_format iformat; |
192 |
|
193 |
#ifndef HAVE_BSOUNDRECORDER
|
194 |
if (!is_output)
|
195 |
return -EIO; /* not for now */ |
196 |
#endif
|
197 |
s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
|
198 |
if (s->input_sem < B_OK)
|
199 |
return -EIO;
|
200 |
s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output"); |
201 |
if (s->output_sem < B_OK) {
|
202 |
delete_sem(s->input_sem); |
203 |
return -EIO;
|
204 |
} |
205 |
s->input_index = 0;
|
206 |
s->output_index = 0;
|
207 |
create_bapp_if_needed(); |
208 |
s->frame_size = AUDIO_BLOCK_SIZE; |
209 |
/* bump up the priority (avoid realtime though) */
|
210 |
set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1); |
211 |
#ifdef HAVE_BSOUNDRECORDER
|
212 |
if (!is_output) {
|
213 |
bool wait_for_input = false; |
214 |
if (audio_device && !strcmp(audio_device, "wait:")) |
215 |
wait_for_input = true;
|
216 |
s->recorder = new BSoundRecorder(&iformat, wait_for_input, "ffmpeg input", audiorecord_callback); |
217 |
if (wait_for_input && (s->recorder->InitCheck() == B_OK)) {
|
218 |
s->recorder->WaitForIncomingConnection(&iformat); |
219 |
} |
220 |
if (s->recorder->InitCheck() != B_OK || iformat.format != media_raw_audio_format::B_AUDIO_SHORT) {
|
221 |
delete s->recorder;
|
222 |
s->recorder = NULL;
|
223 |
if (s->input_sem)
|
224 |
delete_sem(s->input_sem); |
225 |
if (s->output_sem)
|
226 |
delete_sem(s->output_sem); |
227 |
return -EIO;
|
228 |
} |
229 |
s->codec_id = (iformat.byte_order == B_MEDIA_LITTLE_ENDIAN)?CODEC_ID_PCM_S16LE:CODEC_ID_PCM_S16BE; |
230 |
s->channels = iformat.channel_count; |
231 |
s->sample_rate = (int)iformat.frame_rate;
|
232 |
s->frame_size = iformat.buffer_size; |
233 |
s->recorder->SetCookie(s); |
234 |
s->recorder->SetVolume(1.0); |
235 |
s->recorder->Start(); |
236 |
return 0; |
237 |
} |
238 |
#endif
|
239 |
format = media_raw_audio_format::wildcard; |
240 |
format.format = media_raw_audio_format::B_AUDIO_SHORT; |
241 |
format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN; |
242 |
format.channel_count = s->channels; |
243 |
format.buffer_size = s->frame_size; |
244 |
format.frame_rate = s->sample_rate; |
245 |
s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback); |
246 |
if (s->player->InitCheck() != B_OK) {
|
247 |
delete s->player;
|
248 |
s->player = NULL;
|
249 |
if (s->input_sem)
|
250 |
delete_sem(s->input_sem); |
251 |
if (s->output_sem)
|
252 |
delete_sem(s->output_sem); |
253 |
return -EIO;
|
254 |
} |
255 |
s->player->SetCookie(s); |
256 |
s->player->SetVolume(1.0); |
257 |
s->player->Start(); |
258 |
s->player->SetHasData(true);
|
259 |
return 0; |
260 |
} |
261 |
|
262 |
static int audio_close(AudioData *s) |
263 |
{ |
264 |
if (s->input_sem)
|
265 |
delete_sem(s->input_sem); |
266 |
if (s->output_sem)
|
267 |
delete_sem(s->output_sem); |
268 |
s->has_quit = 1;
|
269 |
if (s->player) {
|
270 |
s->player->Stop(); |
271 |
} |
272 |
if (s->player)
|
273 |
delete s->player;
|
274 |
#ifdef HAVE_BSOUNDRECORDER
|
275 |
if (s->recorder)
|
276 |
delete s->recorder;
|
277 |
#endif
|
278 |
destroy_bapp_if_needed(); |
279 |
return 0; |
280 |
} |
281 |
|
282 |
/* sound output support */
|
283 |
static int audio_write_header(AVFormatContext *s1) |
284 |
{ |
285 |
AudioData *s = (AudioData *)s1->priv_data; |
286 |
AVStream *st; |
287 |
int ret;
|
288 |
|
289 |
st = s1->streams[0];
|
290 |
s->sample_rate = st->codec->sample_rate; |
291 |
s->channels = st->codec->channels; |
292 |
ret = audio_open(s, 1, NULL); |
293 |
if (ret < 0) |
294 |
return -EIO;
|
295 |
return 0; |
296 |
} |
297 |
|
298 |
static int audio_write_packet(AVFormatContext *s1, int stream_index, |
299 |
const uint8_t *buf, int size, int64_t force_pts) |
300 |
{ |
301 |
AudioData *s = (AudioData *)s1->priv_data; |
302 |
int len, ret;
|
303 |
#ifdef LATENCY_CHECK
|
304 |
bigtime_t lat1, lat2; |
305 |
lat1 = s->player->Latency(); |
306 |
#endif
|
307 |
#ifdef PERF_CHECK
|
308 |
bigtime_t t = s->starve_time; |
309 |
s->starve_time = 0;
|
310 |
printf("starve_time: %lld \n", t);
|
311 |
#endif
|
312 |
while (size > 0) { |
313 |
int amount;
|
314 |
len = MIN(size, AUDIO_BLOCK_SIZE); |
315 |
if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) |
316 |
return -EIO;
|
317 |
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index)); |
318 |
memcpy(&s->buffer[s->input_index], buf, amount); |
319 |
s->input_index += amount; |
320 |
if (s->input_index >= AUDIO_BUFFER_SIZE) {
|
321 |
s->input_index %= AUDIO_BUFFER_SIZE; |
322 |
memcpy(&s->buffer[s->input_index], buf + amount, len - amount); |
323 |
s->input_index += len - amount; |
324 |
} |
325 |
release_sem_etc(s->output_sem, len, 0);
|
326 |
buf += len; |
327 |
size -= len; |
328 |
} |
329 |
#ifdef LATENCY_CHECK
|
330 |
lat2 = s->player->Latency(); |
331 |
printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2);
|
332 |
#endif
|
333 |
return 0; |
334 |
} |
335 |
|
336 |
static int audio_write_trailer(AVFormatContext *s1) |
337 |
{ |
338 |
AudioData *s = (AudioData *)s1->priv_data; |
339 |
|
340 |
audio_close(s); |
341 |
return 0; |
342 |
} |
343 |
|
344 |
/* grab support */
|
345 |
|
346 |
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap) |
347 |
{ |
348 |
AudioData *s = (AudioData *)s1->priv_data; |
349 |
AVStream *st; |
350 |
int ret;
|
351 |
|
352 |
if (!ap || ap->sample_rate <= 0 || ap->channels <= 0) |
353 |
return -1; |
354 |
|
355 |
st = av_new_stream(s1, 0);
|
356 |
if (!st) {
|
357 |
return -ENOMEM;
|
358 |
} |
359 |
s->sample_rate = ap->sample_rate; |
360 |
s->channels = ap->channels; |
361 |
|
362 |
ret = audio_open(s, 0, ap->device);
|
363 |
if (ret < 0) { |
364 |
av_free(st); |
365 |
return -EIO;
|
366 |
} |
367 |
/* take real parameters */
|
368 |
st->codec->codec_type = CODEC_TYPE_AUDIO; |
369 |
st->codec->codec_id = s->codec_id; |
370 |
st->codec->sample_rate = s->sample_rate; |
371 |
st->codec->channels = s->channels; |
372 |
return 0; |
373 |
av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */ |
374 |
} |
375 |
|
376 |
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) |
377 |
{ |
378 |
AudioData *s = (AudioData *)s1->priv_data; |
379 |
int size;
|
380 |
size_t len, amount; |
381 |
unsigned char *buf; |
382 |
status_t err; |
383 |
|
384 |
if (av_new_packet(pkt, s->frame_size) < 0) |
385 |
return -EIO;
|
386 |
buf = (unsigned char *)pkt->data; |
387 |
size = pkt->size; |
388 |
while (size > 0) { |
389 |
len = MIN(AUDIO_BLOCK_SIZE, size); |
390 |
//printf("acquire_sem(output, %d)\n", len);
|
391 |
while ((err=acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL)) == B_INTERRUPTED); |
392 |
if (err < B_OK) {
|
393 |
av_free_packet(pkt); |
394 |
return -EIO;
|
395 |
} |
396 |
amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index)); |
397 |
memcpy(buf, &s->buffer[s->output_index], amount); |
398 |
s->output_index += amount; |
399 |
if (s->output_index >= AUDIO_BUFFER_SIZE) {
|
400 |
s->output_index %= AUDIO_BUFFER_SIZE; |
401 |
memcpy(buf + amount, &s->buffer[s->output_index], len - amount); |
402 |
s->output_index += len-amount; |
403 |
s->output_index %= AUDIO_BUFFER_SIZE; |
404 |
} |
405 |
release_sem_etc(s->input_sem, len, 0);
|
406 |
//printf("release_sem(input, %d)\n", len);
|
407 |
buf += len; |
408 |
size -= len; |
409 |
} |
410 |
//XXX: add pts info
|
411 |
return 0; |
412 |
} |
413 |
|
414 |
static int audio_read_close(AVFormatContext *s1) |
415 |
{ |
416 |
AudioData *s = (AudioData *)s1->priv_data; |
417 |
|
418 |
audio_close(s); |
419 |
return 0; |
420 |
} |
421 |
|
422 |
static AVInputFormat audio_in_format = {
|
423 |
"audio_device",
|
424 |
"audio grab and output",
|
425 |
sizeof(AudioData),
|
426 |
NULL,
|
427 |
audio_read_header, |
428 |
audio_read_packet, |
429 |
audio_read_close, |
430 |
NULL,
|
431 |
AVFMT_NOFILE, |
432 |
}; |
433 |
|
434 |
AVOutputFormat audio_out_format = { |
435 |
"audio_device",
|
436 |
"audio grab and output",
|
437 |
"",
|
438 |
"",
|
439 |
sizeof(AudioData),
|
440 |
#ifdef WORDS_BIGENDIAN
|
441 |
CODEC_ID_PCM_S16BE, |
442 |
#else
|
443 |
CODEC_ID_PCM_S16LE, |
444 |
#endif
|
445 |
CODEC_ID_NONE, |
446 |
audio_write_header, |
447 |
audio_write_packet, |
448 |
audio_write_trailer, |
449 |
AVFMT_NOFILE, |
450 |
}; |
451 |
|
452 |
extern "C" { |
453 |
|
454 |
int audio_init(void) |
455 |
{ |
456 |
main_thid = find_thread(NULL);
|
457 |
av_register_input_format(&audio_in_format); |
458 |
av_register_output_format(&audio_out_format); |
459 |
return 0; |
460 |
} |
461 |
|
462 |
} // "C"
|
463 |
|