Libav
|
00001 /* 00002 * BeOS audio play interface 00003 * Copyright (c) 2000, 2001 Fabrice Bellard 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include <signal.h> 00023 #include <stdlib.h> 00024 #include <stdio.h> 00025 #include <string.h> 00026 #include <unistd.h> 00027 #include <sys/time.h> 00028 00029 #include <Application.h> 00030 #include <SoundPlayer.h> 00031 00032 extern "C" { 00033 #include "libavformat/avformat.h" 00034 } 00035 00036 #if HAVE_BSOUNDRECORDER 00037 #include <SoundRecorder.h> 00038 using namespace BPrivate::Media::Experimental; 00039 #endif 00040 00041 /* enable performance checks */ 00042 //#define PERF_CHECK 00043 00044 /* enable Media Kit latency checks */ 00045 //#define LATENCY_CHECK 00046 00047 #define AUDIO_BLOCK_SIZE 4096 00048 #define AUDIO_BLOCK_COUNT 8 00049 00050 #define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT) 00051 00052 typedef struct { 00053 int fd; // UNUSED 00054 int sample_rate; 00055 int channels; 00056 int frame_size; /* in bytes ! */ 00057 CodecID codec_id; 00058 uint8_t buffer[AUDIO_BUFFER_SIZE]; 00059 int buffer_ptr; 00060 /* ring buffer */ 00061 sem_id input_sem; 00062 int input_index; 00063 sem_id output_sem; 00064 int output_index; 00065 BSoundPlayer *player; 00066 #if HAVE_BSOUNDRECORDER 00067 BSoundRecorder *recorder; 00068 #endif 00069 int has_quit; /* signal callbacks not to wait */ 00070 volatile bigtime_t starve_time; 00071 } AudioData; 00072 00073 static thread_id main_thid; 00074 static thread_id bapp_thid; 00075 static int own_BApp_created = 0; 00076 static int refcount = 0; 00077 00078 /* create the BApplication and Run() it */ 00079 static int32 bapp_thread(void *arg) 00080 { 00081 new BApplication("application/x-vnd.ffmpeg"); 00082 own_BApp_created = 1; 00083 be_app->Run(); 00084 /* kill the process group */ 00085 // kill(0, SIGINT); 00086 // kill(main_thid, SIGHUP); 00087 return B_OK; 00088 } 00089 00090 /* create the BApplication only if needed */ 00091 static void create_bapp_if_needed(void) 00092 { 00093 if (refcount++ == 0) { 00094 /* needed by libmedia */ 00095 if (be_app == NULL) { 00096 bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL); 00097 resume_thread(bapp_thid); 00098 while (!own_BApp_created) 00099 snooze(50000); 00100 } 00101 } 00102 } 00103 00104 static void destroy_bapp_if_needed(void) 00105 { 00106 if (--refcount == 0 && own_BApp_created) { 00107 be_app->Lock(); 00108 be_app->Quit(); 00109 be_app = NULL; 00110 } 00111 } 00112 00113 /* called back by BSoundPlayer */ 00114 static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format) 00115 { 00116 AudioData *s; 00117 size_t len, amount; 00118 unsigned char *buf = (unsigned char *)buffer; 00119 00120 s = (AudioData *)cookie; 00121 if (s->has_quit) 00122 return; 00123 while (bufferSize > 0) { 00124 #ifdef PERF_CHECK 00125 bigtime_t t; 00126 t = system_time(); 00127 #endif 00128 len = MIN(AUDIO_BLOCK_SIZE, bufferSize); 00129 if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) { 00130 s->has_quit = 1; 00131 s->player->SetHasData(false); 00132 return; 00133 } 00134 amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index)); 00135 memcpy(buf, &s->buffer[s->output_index], amount); 00136 s->output_index += amount; 00137 if (s->output_index >= AUDIO_BUFFER_SIZE) { 00138 s->output_index %= AUDIO_BUFFER_SIZE; 00139 memcpy(buf + amount, &s->buffer[s->output_index], len - amount); 00140 s->output_index += len-amount; 00141 s->output_index %= AUDIO_BUFFER_SIZE; 00142 } 00143 release_sem_etc(s->input_sem, len, 0); 00144 #ifdef PERF_CHECK 00145 t = system_time() - t; 00146 s->starve_time = MAX(s->starve_time, t); 00147 #endif 00148 buf += len; 00149 bufferSize -= len; 00150 } 00151 } 00152 00153 #if HAVE_BSOUNDRECORDER 00154 /* called back by BSoundRecorder */ 00155 static void audiorecord_callback(void *cookie, bigtime_t timestamp, void *buffer, size_t bufferSize, const media_multi_audio_format &format) 00156 { 00157 AudioData *s; 00158 size_t len, amount; 00159 unsigned char *buf = (unsigned char *)buffer; 00160 00161 s = (AudioData *)cookie; 00162 if (s->has_quit) 00163 return; 00164 00165 while (bufferSize > 0) { 00166 len = MIN(bufferSize, AUDIO_BLOCK_SIZE); 00167 //printf("acquire_sem(input, %d)\n", len); 00168 if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) { 00169 s->has_quit = 1; 00170 return; 00171 } 00172 amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index)); 00173 memcpy(&s->buffer[s->input_index], buf, amount); 00174 s->input_index += amount; 00175 if (s->input_index >= AUDIO_BUFFER_SIZE) { 00176 s->input_index %= AUDIO_BUFFER_SIZE; 00177 memcpy(&s->buffer[s->input_index], buf + amount, len - amount); 00178 s->input_index += len - amount; 00179 } 00180 release_sem_etc(s->output_sem, len, 0); 00181 //printf("release_sem(output, %d)\n", len); 00182 buf += len; 00183 bufferSize -= len; 00184 } 00185 } 00186 #endif 00187 00188 static int audio_open(AudioData *s, int is_output, const char *audio_device) 00189 { 00190 int p[2]; 00191 int ret; 00192 media_raw_audio_format format; 00193 media_multi_audio_format iformat; 00194 00195 #if !HAVE_BSOUNDRECORDER 00196 if (!is_output) 00197 return AVERROR(EIO); /* not for now */ 00198 #endif 00199 s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input"); 00200 if (s->input_sem < B_OK) 00201 return AVERROR(EIO); 00202 s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output"); 00203 if (s->output_sem < B_OK) { 00204 delete_sem(s->input_sem); 00205 return AVERROR(EIO); 00206 } 00207 s->input_index = 0; 00208 s->output_index = 0; 00209 create_bapp_if_needed(); 00210 s->frame_size = AUDIO_BLOCK_SIZE; 00211 /* bump up the priority (avoid realtime though) */ 00212 set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1); 00213 #if HAVE_BSOUNDRECORDER 00214 if (!is_output) { 00215 bool wait_for_input = false; 00216 if (audio_device && !strcmp(audio_device, "wait:")) 00217 wait_for_input = true; 00218 s->recorder = new BSoundRecorder(&iformat, wait_for_input, "ffmpeg input", audiorecord_callback); 00219 if (wait_for_input && (s->recorder->InitCheck() == B_OK)) { 00220 s->recorder->WaitForIncomingConnection(&iformat); 00221 } 00222 if (s->recorder->InitCheck() != B_OK || iformat.format != media_raw_audio_format::B_AUDIO_SHORT) { 00223 delete s->recorder; 00224 s->recorder = NULL; 00225 if (s->input_sem) 00226 delete_sem(s->input_sem); 00227 if (s->output_sem) 00228 delete_sem(s->output_sem); 00229 return AVERROR(EIO); 00230 } 00231 s->codec_id = (iformat.byte_order == B_MEDIA_LITTLE_ENDIAN)?CODEC_ID_PCM_S16LE:CODEC_ID_PCM_S16BE; 00232 s->channels = iformat.channel_count; 00233 s->sample_rate = (int)iformat.frame_rate; 00234 s->frame_size = iformat.buffer_size; 00235 s->recorder->SetCookie(s); 00236 s->recorder->SetVolume(1.0); 00237 s->recorder->Start(); 00238 return 0; 00239 } 00240 #endif 00241 format = media_raw_audio_format::wildcard; 00242 format.format = media_raw_audio_format::B_AUDIO_SHORT; 00243 format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN; 00244 format.channel_count = s->channels; 00245 format.buffer_size = s->frame_size; 00246 format.frame_rate = s->sample_rate; 00247 s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback); 00248 if (s->player->InitCheck() != B_OK) { 00249 delete s->player; 00250 s->player = NULL; 00251 if (s->input_sem) 00252 delete_sem(s->input_sem); 00253 if (s->output_sem) 00254 delete_sem(s->output_sem); 00255 return AVERROR(EIO); 00256 } 00257 s->player->SetCookie(s); 00258 s->player->SetVolume(1.0); 00259 s->player->Start(); 00260 s->player->SetHasData(true); 00261 return 0; 00262 } 00263 00264 static int audio_close(AudioData *s) 00265 { 00266 if (s->input_sem) 00267 delete_sem(s->input_sem); 00268 if (s->output_sem) 00269 delete_sem(s->output_sem); 00270 s->has_quit = 1; 00271 if (s->player) { 00272 s->player->Stop(); 00273 } 00274 if (s->player) 00275 delete s->player; 00276 #if HAVE_BSOUNDRECORDER 00277 if (s->recorder) 00278 delete s->recorder; 00279 #endif 00280 destroy_bapp_if_needed(); 00281 return 0; 00282 } 00283 00284 /* sound output support */ 00285 static int audio_write_header(AVFormatContext *s1) 00286 { 00287 AudioData *s = (AudioData *)s1->priv_data; 00288 AVStream *st; 00289 int ret; 00290 00291 st = s1->streams[0]; 00292 s->sample_rate = st->codec->sample_rate; 00293 s->channels = st->codec->channels; 00294 ret = audio_open(s, 1, NULL); 00295 if (ret < 0) 00296 return AVERROR(EIO); 00297 return 0; 00298 } 00299 00300 static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt) 00301 { 00302 AudioData *s = (AudioData *)s1->priv_data; 00303 int len, ret; 00304 const uint8_t *buf = pkt->data; 00305 int size = pkt->size; 00306 #ifdef LATENCY_CHECK 00307 bigtime_t lat1, lat2; 00308 lat1 = s->player->Latency(); 00309 #endif 00310 #ifdef PERF_CHECK 00311 bigtime_t t = s->starve_time; 00312 s->starve_time = 0; 00313 printf("starve_time: %lld \n", t); 00314 #endif 00315 while (size > 0) { 00316 int amount; 00317 len = MIN(size, AUDIO_BLOCK_SIZE); 00318 if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) 00319 return AVERROR(EIO); 00320 amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index)); 00321 memcpy(&s->buffer[s->input_index], buf, amount); 00322 s->input_index += amount; 00323 if (s->input_index >= AUDIO_BUFFER_SIZE) { 00324 s->input_index %= AUDIO_BUFFER_SIZE; 00325 memcpy(&s->buffer[s->input_index], buf + amount, len - amount); 00326 s->input_index += len - amount; 00327 } 00328 release_sem_etc(s->output_sem, len, 0); 00329 buf += len; 00330 size -= len; 00331 } 00332 #ifdef LATENCY_CHECK 00333 lat2 = s->player->Latency(); 00334 printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2); 00335 #endif 00336 return 0; 00337 } 00338 00339 static int audio_write_trailer(AVFormatContext *s1) 00340 { 00341 AudioData *s = (AudioData *)s1->priv_data; 00342 00343 audio_close(s); 00344 return 0; 00345 } 00346 00347 /* grab support */ 00348 00349 static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap) 00350 { 00351 AudioData *s = (AudioData *)s1->priv_data; 00352 AVStream *st; 00353 int ret; 00354 00355 if (!ap || ap->sample_rate <= 0 || ap->channels <= 0) 00356 return -1; 00357 00358 st = av_new_stream(s1, 0); 00359 if (!st) { 00360 return AVERROR(ENOMEM); 00361 } 00362 s->sample_rate = ap->sample_rate; 00363 s->channels = ap->channels; 00364 00365 ret = audio_open(s, 0, s1->filename); 00366 if (ret < 0) { 00367 av_free(st); 00368 return AVERROR(EIO); 00369 } 00370 /* take real parameters */ 00371 st->codec->codec_type = AVMEDIA_TYPE_AUDIO; 00372 st->codec->codec_id = s->codec_id; 00373 st->codec->sample_rate = s->sample_rate; 00374 st->codec->channels = s->channels; 00375 return 0; 00376 av_set_pts_info(st, 48, 1, 1000000); /* 48 bits pts in us */ 00377 } 00378 00379 static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) 00380 { 00381 AudioData *s = (AudioData *)s1->priv_data; 00382 int size; 00383 size_t len, amount; 00384 unsigned char *buf; 00385 status_t err; 00386 00387 if (av_new_packet(pkt, s->frame_size) < 0) 00388 return AVERROR(EIO); 00389 buf = (unsigned char *)pkt->data; 00390 size = pkt->size; 00391 while (size > 0) { 00392 len = MIN(AUDIO_BLOCK_SIZE, size); 00393 //printf("acquire_sem(output, %d)\n", len); 00394 while ((err=acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL)) == B_INTERRUPTED); 00395 if (err < B_OK) { 00396 av_free_packet(pkt); 00397 return AVERROR(EIO); 00398 } 00399 amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index)); 00400 memcpy(buf, &s->buffer[s->output_index], amount); 00401 s->output_index += amount; 00402 if (s->output_index >= AUDIO_BUFFER_SIZE) { 00403 s->output_index %= AUDIO_BUFFER_SIZE; 00404 memcpy(buf + amount, &s->buffer[s->output_index], len - amount); 00405 s->output_index += len-amount; 00406 s->output_index %= AUDIO_BUFFER_SIZE; 00407 } 00408 release_sem_etc(s->input_sem, len, 0); 00409 //printf("release_sem(input, %d)\n", len); 00410 buf += len; 00411 size -= len; 00412 } 00413 //XXX: add pts info 00414 return 0; 00415 } 00416 00417 static int audio_read_close(AVFormatContext *s1) 00418 { 00419 AudioData *s = (AudioData *)s1->priv_data; 00420 00421 audio_close(s); 00422 return 0; 00423 } 00424 00425 static AVInputFormat audio_beos_demuxer = { 00426 "audio_beos", 00427 NULL_IF_CONFIG_SMALL("audio grab and output"), 00428 sizeof(AudioData), 00429 NULL, 00430 audio_read_header, 00431 audio_read_packet, 00432 audio_read_close, 00433 NULL, 00434 NULL, 00435 AVFMT_NOFILE, 00436 }; 00437 00438 AVOutputFormat audio_beos_muxer = { 00439 "audio_beos", 00440 NULL_IF_CONFIG_SMALL("audio grab and output"), 00441 "", 00442 "", 00443 sizeof(AudioData), 00444 #if HAVE_BIGENDIAN 00445 CODEC_ID_PCM_S16BE, 00446 #else 00447 CODEC_ID_PCM_S16LE, 00448 #endif 00449 CODEC_ID_NONE, 00450 audio_write_header, 00451 audio_write_packet, 00452 audio_write_trailer, 00453 AVFMT_NOFILE, 00454 }; 00455 00456 extern "C" { 00457 00458 int audio_init(void) 00459 { 00460 main_thid = find_thread(NULL); 00461 av_register_input_format(&audio_beos_demuxer); 00462 av_register_output_format(&audio_beos_muxer); 00463 return 0; 00464 } 00465 00466 } // "C" 00467