aboutsummaryrefslogtreecommitdiffstats
path: root/libtoolame-dab/vlc_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtoolame-dab/vlc_input.c')
-rw-r--r--libtoolame-dab/vlc_input.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/libtoolame-dab/vlc_input.c b/libtoolame-dab/vlc_input.c
new file mode 100644
index 0000000..11e04c7
--- /dev/null
+++ b/libtoolame-dab/vlc_input.c
@@ -0,0 +1,430 @@
+#include "vlc_input.h"
+
+#if defined(VLC_INPUT)
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+int check_vlc_uses_size_t();
+struct vlc_buffer* vlc_buffer_new();
+void vlc_buffer_free(struct vlc_buffer* node);
+
+libvlc_instance_t *m_vlc;
+libvlc_media_player_t *m_mp;
+
+unsigned int vlc_rate;
+unsigned int vlc_channels;
+
+struct vlc_buffer *head_buffer;
+
+// now playing information can get written to
+// a file. This writing happens in a separate thread
+#define NOWPLAYING_LEN 512
+char vlc_nowplaying[NOWPLAYING_LEN];
+int vlc_nowplaying_running;
+pthread_t vlc_nowplaying_thread;
+const char* vlc_nowplaying_filename;
+
+struct icywriter_task_data {
+ char text[NOWPLAYING_LEN];
+ int success;
+ sem_t sem;
+};
+
+struct icywriter_task_data icy_task_data;
+
+
+pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct vlc_buffer* vlc_buffer_new()
+{
+ struct vlc_buffer* node;
+ node = malloc(sizeof(struct vlc_buffer));
+ memset(node, 0, sizeof(struct vlc_buffer));
+ return node;
+}
+
+void vlc_buffer_free(struct vlc_buffer* node)
+{
+ if (node->buf) {
+ free(node->buf);
+ }
+
+ free(node);
+}
+
+size_t vlc_buffer_totalsize(struct vlc_buffer* node)
+{
+ size_t totalsize = 0;
+ for (; node != NULL; node = node->next) {
+ totalsize += node->size;
+ }
+
+ return totalsize;
+}
+
+// VLC Audio prerender callback, we must allocate a buffer here
+void prepareRender_size_t(
+ void* p_audio_data,
+ uint8_t** pp_pcm_buffer,
+ size_t size)
+{
+ *pp_pcm_buffer = malloc(size);
+}
+
+void prepareRender(
+ void* p_audio_data,
+ uint8_t** pp_pcm_buffer,
+ unsigned int size)
+{
+ *pp_pcm_buffer = malloc(size);
+}
+
+
+// Audio postrender callback
+void handleStream_size_t(
+ void* p_audio_data,
+ uint8_t* p_pcm_buffer,
+ unsigned int channels,
+ unsigned int rate,
+ unsigned int nb_samples,
+ unsigned int bits_per_sample,
+ size_t size,
+ int64_t pts)
+{
+ assert(channels == vlc_channels);
+ assert(rate == vlc_rate);
+ assert(bits_per_sample == 16);
+
+ // 16 is a bit arbitrary, if it's too small we might enter
+ // a deadlock if toolame asks for too much data
+ const size_t max_length = 16 * size;
+
+ for (;;) {
+ pthread_mutex_lock(&buffer_lock);
+
+ if (vlc_buffer_totalsize(head_buffer) < max_length) {
+ struct vlc_buffer* newbuf = vlc_buffer_new();
+
+ newbuf->buf = p_pcm_buffer;
+ newbuf->size = size;
+
+ // Append the new buffer to the end of the linked list
+ struct vlc_buffer* tail = head_buffer;
+ while (tail->next) {
+ tail = tail->next;
+ }
+ tail->next = newbuf;
+
+ pthread_mutex_unlock(&buffer_lock);
+ return;
+ }
+
+ pthread_mutex_unlock(&buffer_lock);
+ usleep(100);
+ }
+}
+
+// convert from unsigned int size to size_t size
+void handleStream(
+ void* p_audio_data,
+ uint8_t* p_pcm_buffer,
+ unsigned int channels,
+ unsigned int rate,
+ unsigned int nb_samples,
+ unsigned int bits_per_sample,
+ unsigned int size,
+ int64_t pts)
+{
+ handleStream_size_t(
+ p_audio_data,
+ p_pcm_buffer,
+ channels,
+ rate,
+ nb_samples,
+ bits_per_sample,
+ size,
+ pts);
+}
+
+int vlc_in_prepare(
+ unsigned verbosity,
+ unsigned int rate,
+ const char* uri,
+ unsigned channels,
+ const char* icy_write_file
+ )
+{
+ fprintf(stderr, "Initialising VLC...\n");
+
+ vlc_nowplaying_running = 0;
+ vlc_nowplaying_filename = icy_write_file;
+
+ long long int handleStream_address;
+ long long int prepareRender_address;
+
+ int vlc_version_check = check_vlc_uses_size_t();
+ if (vlc_version_check == 0) {
+ fprintf(stderr, "You are using VLC with unsigned int size callbacks\n");
+
+ handleStream_address = (long long int)(intptr_t)(void*)&handleStream;
+ prepareRender_address = (long long int)(intptr_t)(void*)&prepareRender;
+ }
+ else if (vlc_version_check == 1) {
+ fprintf(stderr, "You are using VLC with size_t size callbacks\n");
+
+ handleStream_address = (long long int)(intptr_t)(void*)&handleStream_size_t;
+ prepareRender_address = (long long int)(intptr_t)(void*)&prepareRender_size_t;
+ }
+ else {
+ fprintf(stderr, "Error detecting VLC version!\n");
+ fprintf(stderr, " you are using %s\n", libvlc_get_version());
+ return -1;
+ }
+
+ vlc_rate = rate;
+ vlc_channels = channels;
+
+ // VLC options
+ char smem_options[512];
+ snprintf(smem_options, sizeof(smem_options),
+ "#transcode{acodec=s16l,samplerate=%d}:"
+ // We are using transcode because smem only support raw audio and
+ // video formats
+ "smem{"
+ "audio-postrender-callback=%lld,"
+ "audio-prerender-callback=%lld"
+ "}",
+ vlc_rate,
+ handleStream_address,
+ prepareRender_address);
+
+ char verb_options[512];
+ snprintf(verb_options, sizeof(verb_options),
+ "--verbose=%d", verbosity);
+
+ const char * const vlc_args[] = {
+ verb_options,
+ "--sout", smem_options // Stream to memory
+ };
+
+ // Launch VLC
+ m_vlc = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
+
+ // Load the media
+ libvlc_media_t *m;
+ m = libvlc_media_new_location(m_vlc, uri);
+ m_mp = libvlc_media_player_new_from_media(m);
+ libvlc_media_release(m);
+
+ // Allocate the list
+ head_buffer = vlc_buffer_new();
+
+ // Start playing
+ int ret = libvlc_media_player_play(m_mp);
+
+ if (ret == 0) {
+ libvlc_media_t *media = libvlc_media_player_get_media(m_mp);
+ libvlc_state_t st;
+
+ ret = -1;
+
+ int timeout;
+ for (timeout = 0; timeout < 100; timeout++) {
+ st = libvlc_media_get_state(media);
+ usleep(10*1000);
+ if (st != libvlc_NothingSpecial) {
+ ret = 0;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+ssize_t vlc_in_read(void *buf, size_t len)
+{
+ if (len == 0) {
+ return 0;
+ }
+
+ assert(buf);
+
+ size_t requested = len;
+ for (;;) {
+ pthread_mutex_lock(&buffer_lock);
+
+ if (vlc_buffer_totalsize(head_buffer) >= len) {
+ while (len >= head_buffer->size) {
+ if (head_buffer->buf && head_buffer->size) {
+ // Get all the data from this list element
+ memcpy(buf, head_buffer->buf, head_buffer->size);
+
+ buf += head_buffer->size;
+ len -= head_buffer->size;
+ }
+
+ if (head_buffer->next) {
+ struct vlc_buffer *next_head = head_buffer->next;
+ vlc_buffer_free(head_buffer);
+ head_buffer = next_head;
+ }
+ else {
+ vlc_buffer_free(head_buffer);
+ head_buffer = vlc_buffer_new();
+ break;
+ }
+ }
+
+ if (len > 0) {
+ assert(len < head_buffer->size);
+ assert(head_buffer->buf);
+
+ memcpy(buf, head_buffer->buf, len);
+
+ // split the current head into two parts
+ size_t remaining = head_buffer->size - len;
+ uint8_t *newbuf = malloc(remaining);
+
+ memcpy(newbuf, head_buffer->buf + len, remaining);
+ free(head_buffer->buf);
+ head_buffer->buf = newbuf;
+ head_buffer->size = remaining;
+ }
+
+ pthread_mutex_unlock(&buffer_lock);
+ return requested;
+ }
+
+ pthread_mutex_unlock(&buffer_lock);
+ usleep(100);
+
+ libvlc_media_t *media = libvlc_media_player_get_media(m_mp);
+ libvlc_state_t st = libvlc_media_get_state(media);
+ if (!(st == libvlc_Opening ||
+ st == libvlc_Buffering ||
+ st == libvlc_Playing) ) {
+ return -1;
+ }
+
+ char* nowplaying_sz = libvlc_media_get_meta(media, libvlc_meta_NowPlaying);
+ if (nowplaying_sz) {
+ snprintf(vlc_nowplaying, NOWPLAYING_LEN, "%s", nowplaying_sz);
+ free(nowplaying_sz);
+ }
+ }
+
+ abort();
+}
+
+// This task is run in a separate thread
+void* vlc_in_write_icy_task(void* arg)
+{
+ struct icywriter_task_data* data = arg;
+
+ FILE* fd = fopen(vlc_nowplaying_filename, "wb");
+ if (fd) {
+ int ret = fputs(data->text, fd);
+ fclose(fd);
+
+ if (ret >= 0) {
+ data->success = 1;
+ }
+ }
+ else {
+ data->success = 0;
+ }
+
+ sem_post(&data->sem);
+ return NULL;
+}
+
+void vlc_in_write_icy(void)
+{
+ if (vlc_nowplaying_filename == NULL) {
+ return;
+ }
+ else if (vlc_nowplaying_running == 0) {
+ memcpy(icy_task_data.text, vlc_nowplaying, NOWPLAYING_LEN);
+ icy_task_data.success = 0;
+
+ int ret = sem_init(&icy_task_data.sem, 0, 0);
+ if (ret == 0) {
+ ret = pthread_create(&vlc_nowplaying_thread, NULL, vlc_in_write_icy_task, &icy_task_data);
+
+ if (ret == 0) {
+ vlc_nowplaying_running = 1;
+ }
+ else {
+ fprintf(stderr, "ICY Text writer: thread start failed: %s\n", strerror(ret));
+ }
+ }
+ else {
+ fprintf(stderr, "ICY Text writer: semaphore init failed: %s\n", strerror(errno));
+ }
+
+ }
+ else {
+ int ret = sem_trywait(&icy_task_data.sem);
+ if (ret == -1 && errno == EAGAIN) {
+ return;
+ }
+ else if (ret == 0) {
+ ret = pthread_join(vlc_nowplaying_thread, NULL);
+ if (ret != 0) {
+ fprintf(stderr, "ICY Text writer: pthread_join error: %s\n", strerror(ret));
+ }
+
+ vlc_nowplaying_running = 0;
+ }
+ else {
+ fprintf(stderr, "ICY Text writer: semaphore trywait failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+
+/* VLC up to version 2.1.0 used a different callback function signature.
+ * VLC 2.2.0 uses size_t
+ *
+ * \return 1 if the callback with size_t size should be used.
+ * 0 if the callback with unsigned int size should be used.
+ * -1 if there was an error.
+ */
+int check_vlc_uses_size_t()
+{
+ int retval = -1;
+
+ char libvlc_version[256];
+ strncpy(libvlc_version, libvlc_get_version(), 255);
+
+ char *space_position = strstr(libvlc_version, " ");
+
+ if (space_position) {
+ *space_position = '\0';
+ }
+
+ char *saveptr;
+ char *major_ver_sz = strtok_r(libvlc_version, ".", &saveptr);
+ if (major_ver_sz) {
+ int major_ver = atoi(major_ver_sz);
+
+ char *minor_ver_sz = strtok_r(NULL, ".", &saveptr);
+ if (minor_ver_sz) {
+ int minor_ver = atoi(minor_ver_sz);
+
+ retval = (major_ver >= 2 && minor_ver >= 2) ? 1 : 0;
+ }
+ }
+
+ return retval;
+}
+
+#endif // defined(VLC_INPUT)
+