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