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