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.

456 lines
13KB

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