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, &micro, &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