2
* BeOS audio play interface
3
* Copyright (c) 2000, 2001 Fabrice Bellard.
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.
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.
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
27
#include <Application.h>
28
#include <SoundPlayer.h>
34
#ifdef HAVE_BSOUNDRECORDER
35
#include <SoundRecorder.h>
36
using namespace BPrivate::Media::Experimental;
39
/* enable performance checks */
42
/* enable Media Kit latency checks */
43
//#define LATENCY_CHECK
45
#define AUDIO_BLOCK_SIZE 4096
46
#define AUDIO_BLOCK_COUNT 8
48
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
54
int frame_size; /* in bytes ! */
56
uint8_t buffer[AUDIO_BUFFER_SIZE];
64
#ifdef HAVE_BSOUNDRECORDER
65
BSoundRecorder *recorder;
67
int has_quit; /* signal callbacks not to wait */
68
volatile bigtime_t starve_time;
71
static thread_id main_thid;
72
static thread_id bapp_thid;
73
static int own_BApp_created = 0;
74
static int refcount = 0;
76
/* create the BApplication and Run() it */
77
static int32 bapp_thread(void *arg)
79
new BApplication("application/x-vnd.ffmpeg");
82
/* kill the process group */
84
// kill(main_thid, SIGHUP);
88
/* create the BApplication only if needed */
89
static void create_bapp_if_needed(void)
91
if (refcount++ == 0) {
92
/* needed by libmedia */
94
bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL);
95
resume_thread(bapp_thid);
96
while (!own_BApp_created)
102
static void destroy_bapp_if_needed(void)
104
if (--refcount == 0 && own_BApp_created) {
111
/* called back by BSoundPlayer */
112
static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format)
116
unsigned char *buf = (unsigned char *)buffer;
118
s = (AudioData *)cookie;
121
while (bufferSize > 0) {
126
len = MIN(AUDIO_BLOCK_SIZE, bufferSize);
127
if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
129
s->player->SetHasData(false);
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;
141
release_sem_etc(s->input_sem, len, 0);
143
t = system_time() - t;
144
s->starve_time = MAX(s->starve_time, t);
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)
157
unsigned char *buf = (unsigned char *)buffer;
159
s = (AudioData *)cookie;
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) {
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;
178
release_sem_etc(s->output_sem, len, 0);
179
//printf("release_sem(output, %d)\n", len);
186
static int audio_open(AudioData *s, int is_output, const char *audio_device)
190
media_raw_audio_format format;
191
media_multi_audio_format iformat;
193
#ifndef HAVE_BSOUNDRECORDER
195
return -EIO; /* not for now */
197
s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
198
if (s->input_sem < B_OK)
200
s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output");
201
if (s->output_sem < B_OK) {
202
delete_sem(s->input_sem);
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
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);
220
if (s->recorder->InitCheck() != B_OK || iformat.format != media_raw_audio_format::B_AUDIO_SHORT) {
224
delete_sem(s->input_sem);
226
delete_sem(s->output_sem);
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();
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) {
250
delete_sem(s->input_sem);
252
delete_sem(s->output_sem);
255
s->player->SetCookie(s);
256
s->player->SetVolume(1.0);
258
s->player->SetHasData(true);
262
static int audio_close(AudioData *s)
265
delete_sem(s->input_sem);
267
delete_sem(s->output_sem);
274
#ifdef HAVE_BSOUNDRECORDER
278
destroy_bapp_if_needed();
282
/* sound output support */
283
static int audio_write_header(AVFormatContext *s1)
285
AudioData *s = (AudioData *)s1->priv_data;
290
s->sample_rate = st->codec->sample_rate;
291
s->channels = st->codec->channels;
292
ret = audio_open(s, 1, NULL);
298
static int audio_write_packet(AVFormatContext *s1, int stream_index,
299
const uint8_t *buf, int size, int64_t force_pts)
301
AudioData *s = (AudioData *)s1->priv_data;
304
bigtime_t lat1, lat2;
305
lat1 = s->player->Latency();
308
bigtime_t t = s->starve_time;
310
printf("starve_time: %lld \n", t);
314
len = MIN(size, AUDIO_BLOCK_SIZE);
315
if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK)
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;
325
release_sem_etc(s->output_sem, len, 0);
330
lat2 = s->player->Latency();
331
printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2);
336
static int audio_write_trailer(AVFormatContext *s1)
338
AudioData *s = (AudioData *)s1->priv_data;
346
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
348
AudioData *s = (AudioData *)s1->priv_data;
352
if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
355
st = av_new_stream(s1, 0);
359
s->sample_rate = ap->sample_rate;
360
s->channels = ap->channels;
362
ret = audio_open(s, 0, ap->device);
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;
373
av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */
376
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
378
AudioData *s = (AudioData *)s1->priv_data;
384
if (av_new_packet(pkt, s->frame_size) < 0)
386
buf = (unsigned char *)pkt->data;
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);
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;
405
release_sem_etc(s->input_sem, len, 0);
406
//printf("release_sem(input, %d)\n", len);
414
static int audio_read_close(AVFormatContext *s1)
416
AudioData *s = (AudioData *)s1->priv_data;
422
static AVInputFormat audio_in_format = {
424
"audio grab and output",
434
AVOutputFormat audio_out_format = {
436
"audio grab and output",
440
#ifdef WORDS_BIGENDIAN
456
main_thid = find_thread(NULL);
457
av_register_input_format(&audio_in_format);
458
av_register_output_format(&audio_out_format);