beosaudio.cpp 13 KB

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