1 ///
2 /// Output using ffmpeg's libavdevices libraries.
3 ///	@file		avdeviceoutput.cpp - pianod2
4 ///	@author		Perette Barella
5 ///	@date		2016-02-04
6 ///	@copyright	Copyright (c) 2016-2018 Devious Fish. All rights reserved.
7 ///
8 
9 #include <config.h>
10 
11 #include <chrono>
12 #include <thread>
13 #include <exception>
14 
15 #pragma GCC diagnostic push
16 #pragma GCC diagnostic ignored "-Wdocumentation"
17 extern "C" {
18 #include <libavformat/avformat.h>
19 #include <libavcodec/avcodec.h>
20 #include <libavdevice/avdevice.h>
21 #include <libavutil/avutil.h>
22 #include <libavutil/avstring.h>
23 #include <libavutil/opt.h>
24 }
25 #pragma GCC diagnostic pop
26 
27 #include "utility.h"
28 #include "logging.h"
29 
30 #include "avdeviceoutput.h"
31 #include "ffmpegplayer.h"
32 
33 
34 
35 using namespace std;
36 
37 namespace Audio {
38     /** Find the audio output device.  If a driver is specified in the settings,
39         use that.  Otherwise, use the first device found.
40         @param settings The audio settings to match. */
findDevice(const AudioSettings & settings)41     static const AVOutputFormat *findDevice (const AudioSettings &settings) {
42         const AVOutputFormat *output = nullptr;
43 #ifdef HAVE_AV_OUTPUT_AUDIO_DEVICE_NEXT
44         if (settings.output_driver.empty()) {
45             output = av_output_audio_device_next (nullptr);
46             if (output) return output;
47         }
48 #endif
49 #ifdef HAVE_AV_MUXER_ITERATE
50         void *muxer_iterator = nullptr;
51         while ((output = av_muxer_iterate (&muxer_iterator))) {
52 #else
53         while ((output = av_oformat_next (output))) {
54 #endif
55             if ((settings.output_driver.empty() && output->flags & AVFMT_NOFILE) ||
56                     strcasecmp (settings.output_driver, output->name) == 0) {
57                 return output;
58             }
59         }
60         return nullptr;
61     };
62 
63 
64     /** Create an audio outputter for ffmpeg's libavdevice.
65         @param settings The output settings.
66         @param format The format of the data to be played. */
67     AvDeviceOutput::AvDeviceOutput (const AudioSettings &settings,
68                                     const AudioFormat &format) {
69         const AVOutputFormat *device = findDevice (settings);
70         if (!device)
71             throw invalid_argument (settings.output_driver.empty() ?
72                                     "No audio output found" :
73                                     "Requested audio output not found.");
74 
75         // open the output file, if needed */
76         if (!(device->flags & AVFMT_NOFILE)) {
77             if (settings.output_server.empty())
78                 throw AudioException ("No output destination specified.");
79 
80         }
81 
82 
83         int status = avformat_alloc_output_context2 (&context,
84                      const_cast <AVOutputFormat *> (device),
85                      device->name, settings.output_server.c_str());
86         if (status < 0)
87             throw LavAudioException ("avformat_alloc_output_context2", status);
88 
89         try {
90             bytes_per_sample_set = format.sampleGroupSize();
91             codec = avcodec_find_encoder (device->audio_codec);
92             assert (device->audio_codec == AV_CODEC_ID_NONE || codec);
93 
94             stream = avformat_new_stream (context, codec);
95             if (!stream)
96                 throw bad_alloc();
97 
98 #ifdef HAVE_AVCODECPARAMETERS
99             AVCodecParameters *codec_parameters = stream->codecpar;
100 #else
101             AVCodecContext *codec_parameters = stream->codec;
102 #endif
103 
104             // Set in parameter structure and copy into context at end.
105             codec_parameters->bit_rate = bytes_per_sample_set * format.rate;
106             codec_parameters->sample_rate = format.rate;
107             codec_parameters->channels = format.channels;
108             if (!format.isNativeArrangement())
109                 throw AudioException ("Non-native byte ordering not supported");
110             switch (format.bits) {
111                 case 8:
112                     if (format.signedness == Signed)
113                         throw AudioException ("Signed 8-bit format not supported");
114 #ifdef HAVE_AVCODECPARAMETERS
115                     codec_parameters->format = AV_SAMPLE_FMT_U8;
116 #else
117                     codec_parameters->sample_fmt = AV_SAMPLE_FMT_U8;
118 #endif
119                     break;
120                 case 16:
121                     if (format.signedness == Unsigned)
122                         throw AudioException ("Unsigned 16-bit format not supported");
123 #ifdef HAVE_AVCODECPARAMETERS
124                     codec_parameters->format = AV_SAMPLE_FMT_S16;
125 #else
126                     codec_parameters->sample_fmt = AV_SAMPLE_FMT_S16;
127 #endif
128                     break;
129                 case 32:
130                     if (format.signedness == Unsigned)
131                         throw AudioException ("Unsigned 32-bit format not supported");
132 #ifdef HAVE_AVCODECPARAMETERS
133                     codec_parameters->format = AV_SAMPLE_FMT_S32;
134 #else
135                     codec_parameters->sample_fmt = AV_SAMPLE_FMT_S32;
136 #endif
137                     break;
138                 default:
139                     throw AudioException (to_string (format.bits) + "-bit format not supported");
140             }
141 
142             codec_context.reset (avcodec_alloc_context3 (codec));
143             if (!codec_context) {
144                 throw AudioException ("avcodec_alloc_context3: allocation failed");
145             }
146 
147 #ifdef HAVE_AVCODECPARAMETERS
148             status = avcodec_parameters_to_context (codec_context.get(), codec_parameters);
149             if (status < 0) {
150                 throw LavAudioException ("avcodec_parameters_to_context", status);
151             }
152 #else
153             status = avcodec_copy_context (codec_context.get(), codec_parameters);
154             if (status < 0) {
155                 throw LavAudioException ("avcodec_copy_context", status);
156             }
157 #endif
158 
159             // some formats want stream headers to be separate
160             if (context->oformat && context->oformat->flags & AVFMT_GLOBALHEADER)
161 #ifdef CODEC_FLAG_GLOBAL_HEADER
162                 codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
163 #else
164                 codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
165 #endif
166 
167             av_dump_format (context, 0, settings.output_driver.empty() ?
168                             "Default audio output driver" : settings.output_driver.c_str(), 1);
169 
170             AVDictionary *options = nullptr;
171             status = av_dict_parse_string (&options, settings.output_options.c_str(), "=", ";", 0);
172             if (status < 0) {
173                 av_dict_free (&options);
174                 throw LavAudioException ("av_dict_parse_string", status);
175             }
176 
177             if (auto entry = av_dict_get (options, "min_flush_time", NULL, 0)) {
178                 min_flush_time = strtol (entry->value, NULL, 10);
179             }
180 
181             if (auto entry = av_dict_get (options, "max_flush_time", NULL, 0)) {
182                 max_flush_time = strtol (entry->value, NULL, 10);
183             }
184 
185             status = avcodec_open2 (codec_context.get(), codec, &options);
186             av_dict_free (&options);
187             if (status < 0) {
188                 throw LavAudioException ("avcodec_open2", status);
189             }
190 
191             try {
192                 status = avformat_write_header (context, nullptr);
193                 if (status < 0)
194                     throw LavAudioException ("avformat_write_header", status);
195 
196                 av_init_packet (&packet);
197             } catch (...) {
198                 avcodec_close (codec_context.get());
199             }
200         } catch (...) {
201             avformat_free_context (context);
202             throw;
203         }
204     }
205 
206     AvDeviceOutput::~AvDeviceOutput () {
207         int status, timeout;
208         std::this_thread::sleep_for (std::chrono::milliseconds (min_flush_time));
209         for (timeout = min_flush_time;
210                 (status = av_write_frame (context, NULL)) == 0 && timeout < max_flush_time;
211                 timeout += flush_time_quanta) {
212             std::this_thread::sleep_for (std::chrono::milliseconds (flush_time_quanta));
213         }
214         if (status < 0) {
215             flogav ("av_write_frame", status);
216         } else if (status == 0) {
217             flog (LOG_WHERE (LOG_WARNING), "Output did not fully flush.");
218         }
219 #ifndef NDEBUG
220         else if (timeout > min_flush_time) {
221             flog (LOG_WHERE (LOG_GENERAL), "Output took ", timeout, "ms to flush");
222         }
223 #endif
224         // std::this_thread::sleep_for (std::chrono::milliseconds (2000));
225         if ((status = av_write_trailer (context)) != 0) {
226             flogav ("av_write_trailer", status);
227         }
228         avcodec_close (codec_context.get());
229         avformat_free_context (context);
230     }
231 
232     /** Play output. */
233     bool AvDeviceOutput::play (void *buffer, unsigned numberOfBytes) {
234         packet.data = (uint8_t *) buffer;
235         packet.size = numberOfBytes;
236         int status = av_write_frame (context, &packet);
237         if (status != 0)
238             flogav("av_write_frame", status);
239         return (status == 0);
240     }
241 }
242