1 /// 2 /// Public interface for media players. 3 /// Media players are responsible for playing audio. 4 /// @file mediaplayer.cpp - pianod project 5 /// @author Perette Barella 6 /// @date 2014-10-23 7 /// @copyright Copyright 2012-2017 Devious Fish. All rights reserved. 8 /// 9 10 #include <config.h> 11 12 #include <assert.h> 13 14 #include <iostream> 15 #include <thread> 16 17 #include "utility.h" 18 #include "fundamentals.h" 19 #include "logging.h" 20 #include "audiooutput.h" 21 #include "mediaplayer.h" 22 23 #pragma GCC diagnostic push 24 #pragma GCC diagnostic ignored "-Wdocumentation" 25 #pragma GCC diagnostic ignored "-Wshadow" 26 27 #ifdef WITH_GSTREAMER 28 29 #include <gst/gst.h> 30 31 #include "audio/gstreamplayer.h" 32 33 #elif defined(WITH_FFMPEG) 34 35 extern "C" { 36 #include <libavformat/avformat.h> 37 #include <libavcodec/avcodec.h> 38 #include <libavutil/avutil.h> 39 #include <libavfilter/avfilter.h> 40 } 41 42 #include "audio/ffmpegplayer.h" 43 44 #ifdef WITH_LIBAVDEVICE 45 extern "C" { 46 #include <libavdevice/avdevice.h> 47 } 48 #endif 49 #elif defined(WITH_AVFOUNDATION) 50 #include "audio/osxplayer.h" 51 #endif 52 53 #pragma GCC diagnostic pop 54 55 56 namespace Media { 57 Player::~Player (void) { 58 // Nothing in this base class 59 }; 60 61 62 time_t Player::getPauseTimeout (void) { 63 return FAR_FUTURE; 64 }; 65 66 67 68 /** Report media libraries in use and their versions. 69 @param verbose If >0, include additional details. */ 70 void reportLibrariesAndVersions(int verbose) { 71 using namespace std; 72 73 cerr << " Media libraries:\n"; 74 #ifdef WITH_GSTREAMER 75 (void) verbose; 76 guint major, minor, micro, nano; 77 gst_version(&major, &minor, µ, &nano); 78 cerr << " gstreamer\n"; 79 { 80 cerr << " gstreamer library\n" 81 << " runtime version " << major << '.' << minor << '.' << micro << '.' << nano << '\n'; 82 } 83 84 #elif WITH_FFMPEG 85 86 // Libav doesn't provide these macros. Yet another source of irritation it provides. 87 #ifndef AV_VERSION_MAJOR 88 #define AV_VERSION_MAJOR(x) ((x) >> 16) 89 #define AV_VERSION_MINOR(x) (((x) >> 8) & 0xff) 90 #define AV_VERSION_MICRO(x) ((x) & 0xff) 91 #endif 92 93 cerr << " ffmpeg/libav (seems to be " 94 << (LIBAVFORMAT_VERSION_MICRO < 100 ? "libav" : "ffmpeg") << ")\n"; 95 96 { 97 unsigned linked = avformat_version(); 98 cerr << " avformat\n" 99 << " compile version " << LIBAVFORMAT_VERSION_MAJOR << '.' << LIBAVFORMAT_VERSION_MINOR << '.' << LIBAVFORMAT_VERSION_MICRO << '\n' 100 << " runtime version " << AV_VERSION_MAJOR (linked) << '.' << AV_VERSION_MINOR (linked) << '.' << AV_VERSION_MICRO (linked) << '\n'; 101 if (verbose) { 102 cerr << " configuration " << avformat_configuration() << '\n'; 103 } 104 } 105 { 106 unsigned linked = avcodec_version(); 107 cerr << " avcodec\n" 108 << " compile version " << LIBAVCODEC_VERSION_MAJOR << '.' << LIBAVCODEC_VERSION_MINOR << '.' << LIBAVCODEC_VERSION_MICRO << '\n' 109 << " runtime version " << AV_VERSION_MAJOR (linked) << '.' << AV_VERSION_MINOR (linked) << '.' << AV_VERSION_MICRO (linked) << '\n'; 110 if (verbose) { 111 cerr << " configuration " << avcodec_configuration() << '\n'; 112 } 113 } 114 { 115 unsigned linked = avfilter_version(); 116 cerr << " avfilter\n" 117 << " compile version " << LIBAVFILTER_VERSION_MAJOR << '.' << LIBAVFILTER_VERSION_MINOR << '.' << LIBAVFILTER_VERSION_MICRO << '\n' 118 << " runtime version " << AV_VERSION_MAJOR (linked) << '.' << AV_VERSION_MINOR (linked) << '.' << AV_VERSION_MICRO (linked) << '\n'; 119 if (verbose) { 120 cerr << " configuration " << avfilter_configuration() << '\n'; 121 } 122 } 123 { 124 unsigned linked = avutil_version(); 125 cerr << " avutil\n" 126 << " compile version " << LIBAVUTIL_VERSION_MAJOR << '.' << LIBAVUTIL_VERSION_MINOR << '.' << LIBAVUTIL_VERSION_MICRO << '\n' 127 << " runtime version " << AV_VERSION_MAJOR (linked) << '.' << AV_VERSION_MINOR (linked) << '.' << AV_VERSION_MICRO (linked) << '\n'; 128 if (verbose) { 129 cerr << " configuration " << avutil_configuration() << '\n'; 130 } 131 } 132 #elif defined(WITH_AVFOUNDATION) 133 cerr << " avfoundation\n"; 134 #endif 135 }; 136 137 /** Determine the length of track remaining to play. 138 @return The length in seconds, floating point, or a negative number 139 if the value could not be ascertained. 140 @note Although it "shouldn't" happen, return value may also be negative 141 if actual playpoint exceeds expected duration, which can happen if 142 ID3 data is wrong, encoding is wrong, etc. */ 143 float Player::playRemaining() const { 144 float length = trackDuration(); 145 float point = playPoint(); 146 return (length < 0.0 || point < 0.0) ? -1 : length - point; 147 } 148 149 150 /** Get the a media player currently compiled in the code. 151 @param settings Describe the output device. 152 @param media_url A URL indicating the media to play. 153 @param initial_gain A suggested gain for the media to play. 154 This may be superceded if the media stream contains its own gain value. 155 @return A media player. 156 @throw AudioException or a derived exception in the event of media 157 problems, output device problems, etc. */ 158 Media::Player *Player::getPlayer (const AudioSettings &settings, 159 const std::string &media_url, 160 float initial_gain) { 161 #ifdef WITH_FFMPEG 162 return new Audio::LavPlayer (settings, media_url, initial_gain); 163 #elif defined(WITH_AVFOUNDATION) 164 return Audio::getAVFoundationPlayer (settings, media_url, initial_gain); 165 #elif defined(WITH_GSTREAMER) 166 return new Audio::GstreamerPlayer (settings, media_url, initial_gain); 167 #else 168 #error No media engine selected for inclusion. Check configure settings and config.log. 169 #endif 170 171 }; 172 173 #ifdef WITH_GSTREAMER 174 static GMainLoop *glib_main_loop = nullptr; ///< Needed to dispatch events from bus. 175 static std::thread *glib_run_loop_thread = nullptr; ///< Thread for the event dispatcher run loop. 176 #endif 177 178 179 180 /** Initialize the media engine on startup. */ 181 Initializer::Initializer() { 182 #ifdef WITH_FFMPEG 183 av_register_all(); 184 avfilter_register_all(); 185 avformat_network_init(); 186 // Check that linked & compiled libraries are the same. 187 unsigned linked_micro = AV_VERSION_MICRO (avformat_version()); 188 if((LIBAVFORMAT_VERSION_MICRO < 100) != (linked_micro < 100)) { 189 flog (LOG_WHERE (LOG_ERROR), "compiled with %s but linking with %s.", 190 LIBAVFORMAT_VERSION_MICRO < 100 ? "libav" : "ffmpeg", 191 linked_micro < 100 ? "libav" : "ffmpeg"); 192 } 193 // Throw an error if we're using libav. 194 if (LIBAVFORMAT_VERSION_MICRO < 100) { 195 flog (LOG_WHERE (LOG_ERROR), 196 "libav is not supported. For best results recompile with ffmpeg, gstreamer or AVFoundation."); 197 } 198 #ifdef WITH_LIBAVDEVICE 199 avdevice_register_all(); 200 #endif 201 202 #endif 203 #ifdef WITH_GSTREAMER 204 assert (glib_run_loop_thread == nullptr); 205 gst_init (0, nullptr); 206 207 glib_main_loop = g_main_loop_new (nullptr, false); 208 glib_run_loop_thread = new std::thread {[]() { 209 g_main_loop_run (glib_main_loop); 210 }}; 211 #endif 212 } 213 214 /** Uninitialize the media engine on prior to shutdown. */ 215 Initializer::~Initializer() { 216 #ifdef WITH_FFMPEG 217 avformat_network_deinit(); 218 #endif 219 #ifdef WITH_GSTREAMER 220 g_main_loop_quit (glib_main_loop); 221 g_main_context_wakeup (g_main_loop_get_context (glib_main_loop)); 222 glib_run_loop_thread->join(); 223 g_main_loop_unref (glib_main_loop); 224 delete glib_run_loop_thread; 225 glib_run_loop_thread = nullptr; 226 gst_deinit(); 227 #endif 228 } 229 } 230