You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
11KB

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