/* ------------------------------------------------------------------ * Copyright (C) 2015 Matthias P. Braendli * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ #include #include #include "VLCInput.h" #include "config.h" #if HAVE_VLC #include #include using namespace std; // VLC Audio prerender callback void prepareRender( void* p_audio_data, uint8_t** pp_pcm_buffer, size_t size) { VLCInput* in = (VLCInput*)p_audio_data; in->preRender_cb(pp_pcm_buffer, size); } // Audio postrender callback 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, size_t size, int64_t pts) { VLCInput* in = (VLCInput*)p_audio_data; assert(channels == in->getChannels()); assert(rate == in->getRate()); assert(bits_per_sample == 8*BYTES_PER_SAMPLE); // This assumes VLC always gives back the full // buffer it asked for. According to VLC code // smem.c for v2.2.0 this holds. in->postRender_cb(); } // VLC Exit callback void handleVLCExit(void* opaque) { ((VLCInput*)opaque)->exit_cb(); } int VLCInput::prepare() { int err; fprintf(stderr, "Initialising VLC...\n"); // 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," "audio-data=%lld" "}", m_rate, (long long int)(intptr_t)(void*)&handleStream, (long long int)(intptr_t)(void*)&prepareRender, (long long int)(intptr_t)this); char verb_options[512]; snprintf(verb_options, sizeof(verb_options), "--verbose=%d", m_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); libvlc_set_exit_handler(m_vlc, handleVLCExit, this); // Load the media libvlc_media_t *m; m = libvlc_media_new_location(m_vlc, m_uri.c_str()); m_mp = libvlc_media_player_new_from_media(m); libvlc_media_release(m); // 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; for (int timeout = 0; timeout < 100; timeout++) { st = libvlc_media_get_state(media); boost::this_thread::sleep(boost::posix_time::milliseconds(10)); if (st != libvlc_NothingSpecial) { ret = 0; break; } } } return ret; } void VLCInput::preRender_cb(uint8_t** pp_pcm_buffer, size_t size) { const size_t max_length = 20 * size; for (;;) { boost::mutex::scoped_lock lock(m_queue_mutex); if (m_queue.size() < max_length) { m_current_buf.resize(size); *pp_pcm_buffer = &m_current_buf[0]; return; } lock.unlock(); boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } } void VLCInput::exit_cb() { boost::mutex::scoped_lock lock(m_queue_mutex); fprintf(stderr, "VLC exit, restarting...\n"); cleanup(); m_current_buf.empty(); prepare(); } void VLCInput::cleanup() { if (m_mp) { /* Stop playing */ libvlc_media_player_stop(m_mp); /* Free the media_player */ libvlc_media_player_release(m_mp); } if (m_vlc) { libvlc_release(m_vlc); m_vlc = NULL; } } void VLCInput::postRender_cb() { boost::mutex::scoped_lock lock(m_queue_mutex); size_t queue_size = m_queue.size(); m_queue.resize(m_queue.size() + m_current_buf.size()); std::copy(m_current_buf.begin(), m_current_buf.end(), m_queue.begin() + queue_size); } ssize_t VLCInput::m_read(uint8_t* buf, size_t length) { ssize_t err = 0; for (;;) { boost::mutex::scoped_lock lock(m_queue_mutex); if (m_queue.size() >= length) { std::copy(m_queue.begin(), m_queue.begin() + length, buf); m_queue.erase(m_queue.begin(), m_queue.begin() + length); return length; } lock.unlock(); boost::this_thread::sleep(boost::posix_time::milliseconds(1)); 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) ) { fprintf(stderr, "VLC state is %d\n", st); err = -1; break; } char* nowplaying_sz = libvlc_media_get_meta(media, libvlc_meta_NowPlaying); if (nowplaying_sz) { m_nowplaying = nowplaying_sz; free(nowplaying_sz); } } return err; } ssize_t VLCInput::read(uint8_t* buf, size_t length) { int bytes_per_frame = m_channels * BYTES_PER_SAMPLE; assert(length % bytes_per_frame == 0); ssize_t read = m_read(buf, length); return read; } void VLCInput::write_icy_text(const std::string& filename) const { FILE* fd = fopen(filename.c_str(), "wb"); fputs_unlocked(m_nowplaying.c_str(), fd); fclose(fd); } #endif // HAVE_VLC